From 234b3ddb0a10112ea0d9ed2b4cab58df397e89c5 Mon Sep 17 00:00:00 2001 From: n0nag0n Date: Sun, 5 May 2024 00:26:05 -0600 Subject: [PATCH] changed it so runway commands are run from any repo --- .gitignore | 1 + composer.json | 1 + flight/Engine.php | 24 +++-- flight/commands/ControllerCommand.php | 91 ++++++++++++++++ flight/commands/RouteCommand.php | 126 +++++++++++++++++++++++ index.php | 2 +- tests/commands/ControllerCommandTest.php | 79 ++++++++++++++ tests/commands/RouteCommandTest.php | 123 ++++++++++++++++++++++ 8 files changed, 438 insertions(+), 9 deletions(-) create mode 100644 flight/commands/ControllerCommand.php create mode 100644 flight/commands/RouteCommand.php create mode 100644 tests/commands/ControllerCommandTest.php create mode 100644 tests/commands/RouteCommandTest.php diff --git a/.gitignore b/.gitignore index 6e9a244..7f0b216 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ coverage/ *.sublime* clover.xml phpcs.xml +.runway-config.json \ No newline at end of file diff --git a/composer.json b/composer.json index 9384c12..ff82cab 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ }, "require-dev": { "ext-pdo_sqlite": "*", + "flightphp/runway": "^0.2.0", "league/container": "^4.2", "level-2/dice": "^4.0", "phpstan/extension-installer": "^1.3", diff --git a/flight/Engine.php b/flight/Engine.php index 0d32f0a..e0b4db2 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -652,10 +652,12 @@ class Engine * @param string $pattern URL pattern to match * @param callable|string $callback Callback function or string class->method * @param bool $pass_route Pass the matching route object to the callback + * + * @return Route */ - public function _post(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): void + public function _post(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): Route { - $this->router()->map('POST ' . $pattern, $callback, $pass_route, $route_alias); + return $this->router()->map('POST ' . $pattern, $callback, $pass_route, $route_alias); } /** @@ -664,10 +666,12 @@ class Engine * @param string $pattern URL pattern to match * @param callable|string $callback Callback function or string class->method * @param bool $pass_route Pass the matching route object to the callback + * + * @return Route */ - public function _put(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): void + public function _put(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): Route { - $this->router()->map('PUT ' . $pattern, $callback, $pass_route, $route_alias); + return $this->router()->map('PUT ' . $pattern, $callback, $pass_route, $route_alias); } /** @@ -676,10 +680,12 @@ class Engine * @param string $pattern URL pattern to match * @param callable|string $callback Callback function or string class->method * @param bool $pass_route Pass the matching route object to the callback + * + * @return Route */ - public function _patch(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): void + public function _patch(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): Route { - $this->router()->map('PATCH ' . $pattern, $callback, $pass_route, $route_alias); + return $this->router()->map('PATCH ' . $pattern, $callback, $pass_route, $route_alias); } /** @@ -688,10 +694,12 @@ class Engine * @param string $pattern URL pattern to match * @param callable|string $callback Callback function or string class->method * @param bool $pass_route Pass the matching route object to the callback + * + * @return Route */ - public function _delete(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): void + public function _delete(string $pattern, $callback, bool $pass_route = false, string $route_alias = ''): Route { - $this->router()->map('DELETE ' . $pattern, $callback, $pass_route, $route_alias); + return $this->router()->map('DELETE ' . $pattern, $callback, $pass_route, $route_alias); } /** diff --git a/flight/commands/ControllerCommand.php b/flight/commands/ControllerCommand.php new file mode 100644 index 0000000..9706d97 --- /dev/null +++ b/flight/commands/ControllerCommand.php @@ -0,0 +1,91 @@ + $config JSON config from .runway-config.json + */ + public function __construct(array $config) + { + parent::__construct('make:controller', 'Create a controller', $config); + $this->argument('', 'The name of the controller to create (with or without the Controller suffix)'); + } + + /** + * Executes the function + * + * @return void + */ + public function execute(string $controller) + { + $io = $this->app()->io(); + if (isset($this->config['app_root']) === false) { + $io->error('app_root not set in .runway-config.json', true); + return; + } + + if (!preg_match('/Controller$/', $controller)) { + $controller .= 'Controller'; + } + + $controllerPath = getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controller . '.php'; + if (file_exists($controllerPath) === true) { + $io->error($controller . ' already exists.', true); + return; + } + + if (is_dir(dirname($controllerPath)) === false) { + $io->info('Creating directory ' . dirname($controllerPath), true); + mkdir(dirname($controllerPath), 0755, true); + } + + $file = new PhpFile(); + $file->setStrictTypes(); + + $namespace = new PhpNamespace('app\\controllers'); + $namespace->addUse('flight\\Engine'); + + $class = new ClassType($controller); + $class->addProperty('app') + ->setVisibility('protected') + ->setType('flight\\Engine') + ->addComment('@var Engine'); + $method = $class->addMethod('__construct') + ->addComment('Constructor') + ->setVisibility('public') + ->setBody('$this->app = $app;'); + $method->addParameter('app') + ->setType('flight\\Engine'); + + $namespace->add($class); + $file->addNamespace($namespace); + + $this->persistClass($controller, $file); + + $io->ok('Controller successfully created at ' . $controllerPath, true); + } + + /** + * Saves the class name to a file + * + * @param string $controllerName Name of the Controller + * @param PhpFile $file Class Object from Nette\PhpGenerator + * + * @return void + */ + protected function persistClass(string $controllerName, PhpFile $file) + { + $printer = new \Nette\PhpGenerator\PsrPrinter(); + file_put_contents(getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controllerName . '.php', $printer->printFile($file)); + } +} diff --git a/flight/commands/RouteCommand.php b/flight/commands/RouteCommand.php new file mode 100644 index 0000000..6ba661a --- /dev/null +++ b/flight/commands/RouteCommand.php @@ -0,0 +1,126 @@ + $config JSON config from .runway-config.json + */ + public function __construct(array $config) + { + parent::__construct('routes', 'Gets all routes for an application', $config); + + $this->option('--get', 'Only return GET requests'); + $this->option('--post', 'Only return POST requests'); + $this->option('--delete', 'Only return DELETE requests'); + $this->option('--put', 'Only return PUT requests'); + $this->option('--patch', 'Only return PATCH requests'); + } + + /** + * Executes the function + * + * @return void + */ + public function execute() + { + $io = $this->app()->io(); + + if(isset($this->config['index_root']) === false) { + $io->error('index_root not set in .runway-config.json', true); + return; + } + + $io->bold('Routes', true); + + $cwd = getcwd(); + + $index_root = $cwd . '/' . $this->config['index_root']; + + // This makes it so the framework doesn't actually execute + Flight::map('start', function () { + return; + }); + include($index_root); + $routes = Flight::router()->getRoutes(); + $arrayOfRoutes = []; + foreach ($routes as $route) { + if ($this->shouldAddRoute($route) === true) { + $middlewares = []; + if (!empty($route->middleware)) { + try { + $middlewares = array_map(function ($middleware) { + $middleware_class_name = explode("\\", get_class($middleware)); + return preg_match("/^class@anonymous/", end($middleware_class_name)) ? 'Anonymous' : end($middleware_class_name); + }, $route->middleware); + } catch (\TypeError $e) { + $middlewares[] = 'Bad Middleware'; + } finally { + if(is_string($route->middleware) === true) { + $middlewares[] = $route->middleware; + } + } + } + + $arrayOfRoutes[] = [ + 'Pattern' => $route->pattern, + 'Methods' => implode(', ', $route->methods), + 'Alias' => $route->alias ?? '', + 'Streamed' => $route->is_streamed ? 'Yes' : 'No', + 'Middleware' => !empty($middlewares) ? implode(",", $middlewares) : '-' + ]; + } + } + $io->table($arrayOfRoutes, [ + 'head' => 'boldGreen' + ]); + } + + /** + * Whether or not to add the route based on the request + * + * @param Route $route Flight Route object + * + * @return boolean + */ + public function shouldAddRoute(Route $route) + { + $boolval = false; + + $showAll = !$this->get && !$this->post && !$this->put && !$this->delete && !$this->patch; + if ($showAll === true) { + $boolval = true; + } else { + $methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH' ]; + foreach ($methods as $method) { + $lowercaseMethod = strtolower($method); + if ( + $this->{$lowercaseMethod} === true && + ( + $route->methods[0] === '*' || + in_array($method, $route->methods, true) === true + ) + ) { + $boolval = true; + break; + } + } + } + return $boolval; + } +} diff --git a/index.php b/index.php index 65ea154..0db24be 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ declare(strict_types=1); -require 'flight/Flight.php'; +require_once 'flight/Flight.php'; // require 'flight/autoload.php'; Flight::route('/', function () { diff --git a/tests/commands/ControllerCommandTest.php b/tests/commands/ControllerCommandTest.php new file mode 100644 index 0000000..cee5c83 --- /dev/null +++ b/tests/commands/ControllerCommandTest.php @@ -0,0 +1,79 @@ + false); + + return $app->io(new Interactor(static::$in, static::$ou)); + } + + public function testConfigAppRootNotSet() + { + $app = $this->newApp('test', '0.0.1'); + $app->add(new ControllerCommand([])); + $app->handle(['runway', 'make:controller', 'Test']); + + $this->assertStringContainsString('app_root not set in .runway-config.json', file_get_contents(static::$ou)); + } + + public function testControllerAlreadyExists() + { + $app = $this->newApp('test', '0.0.1'); + mkdir(__DIR__.'/controllers/'); + file_put_contents(__DIR__.'/controllers/TestController.php', 'add(new ControllerCommand(['app_root' => 'tests/commands/'])); + $app->handle(['runway', 'make:controller', 'Test']); + + $this->assertStringContainsString('TestController already exists.', file_get_contents(static::$ou)); + } + + public function testCreateController() + { + $app = $this->newApp('test', '0.0.1'); + $app->add(new ControllerCommand(['app_root' => 'tests/commands/'])); + $app->handle(['runway', 'make:controller', 'Test']); + + $this->assertFileExists(__DIR__.'/controllers/TestController.php'); + } + +} diff --git a/tests/commands/RouteCommandTest.php b/tests/commands/RouteCommandTest.php new file mode 100644 index 0000000..b459734 --- /dev/null +++ b/tests/commands/RouteCommandTest.php @@ -0,0 +1,123 @@ + false); + + return $app->io(new Interactor(static::$in, static::$ou)); + } + + protected function createIndexFile() + { + $index = <<addMiddleware(function() {}); +Flight::delete('/delete', function () {}); +Flight::put('/put', function () {}); +Flight::patch('/patch', function () {})->addMiddleware('SomeMiddleware'); +Flight::router()->case_sensitive = true; + +Flight::start(); +PHP; + + file_put_contents(__DIR__.'/index.php', $index); + } + + protected function removeColors(string $str): string + { + return preg_replace('/\e\[[\d;]*m/', '', $str); + } + + public function testConfigIndexRootNotSet() + { + $app = $this->newApp('test', '0.0.1'); + $app->add(new RouteCommand([])); + $app->handle(['runway', 'routes']); + + $this->assertStringContainsString('index_root not set in .runway-config.json', file_get_contents(static::$ou)); + } + + public function testGetRoutes() + { + $app = $this->newApp('test', '0.0.1'); + $this->createIndexFile(); + $app->add(new RouteCommand(['index_root' => 'tests/commands/index.php'])); + $app->handle(['runway', 'routes']); + + $this->assertStringContainsString('Routes', file_get_contents(static::$ou)); + $this->assertStringContainsString('+---------+-----------+-------+----------+----------------+ +| Pattern | Methods | Alias | Streamed | Middleware | ++---------+-----------+-------+----------+----------------+ +| / | GET, HEAD | | No | - | +| /post | POST | | No | Closure | +| /delete | DELETE | | No | - | +| /put | PUT | | No | - | +| /patch | PATCH | | No | Bad Middleware | ++---------+-----------+-------+----------+----------------+', $this->removeColors(file_get_contents(static::$ou))); + } + + public function testGetPostRoute() { + $app = $this->newApp('test', '0.0.1'); + $this->createIndexFile(); + $app->add(new RouteCommand(['index_root' => 'tests/commands/index.php'])); + $app->handle(['runway', 'routes', '--post']); + + $this->assertStringContainsString('Routes', file_get_contents(static::$ou)); + $this->assertStringContainsString('+---------+---------+-------+----------+------------+ +| Pattern | Methods | Alias | Streamed | Middleware | ++---------+---------+-------+----------+------------+ +| /post | POST | | No | Closure | ++---------+---------+-------+----------+------------+', $this->removeColors(file_get_contents(static::$ou))); + } + +}