changed it so runway commands are run from any repo

pull/586/head
n0nag0n 9 months ago
parent ad58c09b57
commit 234b3ddb0a

1
.gitignore vendored

@ -8,3 +8,4 @@ coverage/
*.sublime* *.sublime*
clover.xml clover.xml
phpcs.xml phpcs.xml
.runway-config.json

@ -41,6 +41,7 @@
}, },
"require-dev": { "require-dev": {
"ext-pdo_sqlite": "*", "ext-pdo_sqlite": "*",
"flightphp/runway": "^0.2.0",
"league/container": "^4.2", "league/container": "^4.2",
"level-2/dice": "^4.0", "level-2/dice": "^4.0",
"phpstan/extension-installer": "^1.3", "phpstan/extension-installer": "^1.3",

@ -652,10 +652,12 @@ class Engine
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callable|string $callback Callback function or string class->method * @param callable|string $callback Callback function or string class->method
* @param bool $pass_route Pass the matching route object to the callback * @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 string $pattern URL pattern to match
* @param callable|string $callback Callback function or string class->method * @param callable|string $callback Callback function or string class->method
* @param bool $pass_route Pass the matching route object to the callback * @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 string $pattern URL pattern to match
* @param callable|string $callback Callback function or string class->method * @param callable|string $callback Callback function or string class->method
* @param bool $pass_route Pass the matching route object to the callback * @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 string $pattern URL pattern to match
* @param callable|string $callback Callback function or string class->method * @param callable|string $callback Callback function or string class->method
* @param bool $pass_route Pass the matching route object to the callback * @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);
} }
/** /**

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace flight\commands;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;
class ControllerCommand extends AbstractBaseCommand
{
/**
* Construct
*
* @param array<string,mixed> $config JSON config from .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('make:controller', 'Create a controller', $config);
$this->argument('<controller>', '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));
}
}

@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace flight\commands;
use Flight;
use flight\net\Route;
/**
* @property-read ?bool $get
* @property-read ?bool $post
* @property-read ?bool $delete
* @property-read ?bool $put
* @property-read ?bool $patch
*/
class RouteCommand extends AbstractBaseCommand
{
/**
* Construct
*
* @param array<string,mixed> $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;
}
}

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
require 'flight/Flight.php'; require_once 'flight/Flight.php';
// require 'flight/autoload.php'; // require 'flight/autoload.php';
Flight::route('/', function () { Flight::route('/', function () {

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace tests\commands;
use Ahc\Cli\Application;
use Ahc\Cli\IO\Interactor;
use flight\commands\ControllerCommand;
use PHPUnit\Framework\TestCase;
class ControllerCommandTest extends TestCase
{
protected static $in = __DIR__ . '/input.test';
protected static $ou = __DIR__ . '/output.test';
public function setUp(): void
{
file_put_contents(static::$in, '', LOCK_EX);
file_put_contents(static::$ou, '', LOCK_EX);
}
public function tearDown(): void
{
// Make sure we clean up after ourselves:
if (file_exists(static::$in)) {
unlink(static::$in);
}
if (file_exists(static::$ou)) {
unlink(static::$ou);
}
if (file_exists(__DIR__.'/controllers/TestController.php')) {
unlink(__DIR__.'/controllers/TestController.php');
}
if (file_exists(__DIR__.'/controllers/')) {
rmdir(__DIR__.'/controllers/');
}
}
protected function newApp(string $name, string $version = '')
{
$app = new Application($name, $version ?: '0.0.1', fn () => 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', '<?php class TestController {}');
$app->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');
}
}

@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace tests\commands;
use Ahc\Cli\Application;
use Ahc\Cli\IO\Interactor;
use Flight;
use flight\commands\RouteCommand;
use flight\Engine;
use PHPUnit\Framework\TestCase;
class RouteCommandTest extends TestCase
{
protected static $in = __DIR__ . '/input.test';
protected static $ou = __DIR__ . '/output.test';
public function setUp(): void
{
file_put_contents(static::$in, '', LOCK_EX);
file_put_contents(static::$ou, '', LOCK_EX);
$_SERVER = [];
$_REQUEST = [];
Flight::init();
Flight::setEngine(new Engine());
}
public function tearDown(): void
{
// Make sure we clean up after ourselves:
if (file_exists(static::$in)) {
unlink(static::$in);
}
if (file_exists(static::$ou)) {
unlink(static::$ou);
}
if (file_exists(__DIR__.'/index.php')) {
unlink(__DIR__.'/index.php');
}
unset($_REQUEST);
unset($_SERVER);
Flight::clear();
}
protected function newApp(string $name, string $version = '')
{
$app = new Application($name, $version ?: '0.0.1', fn () => false);
return $app->io(new Interactor(static::$in, static::$ou));
}
protected function createIndexFile()
{
$index = <<<PHP
<?php
require __DIR__ . '/../../vendor/autoload.php';
Flight::route('GET /', function () {});
Flight::post('/post', function () {})->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)));
}
}
Loading…
Cancel
Save