Skip to content

Commit

Permalink
Merge pull request #586 from flightphp/runway-reconfig
Browse files Browse the repository at this point in the history
Changed it so runway commands are run from any repo
  • Loading branch information
n0nag0n authored May 9, 2024
2 parents ad58c09 + 234b3dd commit e18c8a7
Show file tree
Hide file tree
Showing 8 changed files with 438 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ coverage/
*.sublime*
clover.xml
phpcs.xml
.runway-config.json
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
24 changes: 16 additions & 8 deletions flight/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,12 @@ public function _group(string $pattern, callable $callback, array $group_middlew
* @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);
}

/**
Expand All @@ -664,10 +666,12 @@ public function _post(string $pattern, $callback, bool $pass_route = false, stri
* @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);
}

/**
Expand All @@ -676,10 +680,12 @@ public function _put(string $pattern, $callback, bool $pass_route = false, strin
* @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);
}

/**
Expand All @@ -688,10 +694,12 @@ public function _patch(string $pattern, $callback, bool $pass_route = false, str
* @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);
}

/**
Expand Down
91 changes: 91 additions & 0 deletions flight/commands/ControllerCommand.php
Original file line number Diff line number Diff line change
@@ -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));
}
}
126 changes: 126 additions & 0 deletions flight/commands/RouteCommand.php
Original file line number Diff line number Diff line change
@@ -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 changes: 1 addition & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

require 'flight/Flight.php';
require_once 'flight/Flight.php';
// require 'flight/autoload.php';

Flight::route('/', function () {
Expand Down
79 changes: 79 additions & 0 deletions tests/commands/ControllerCommandTest.php
Original file line number Diff line number Diff line change
@@ -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');
}

}
Loading

0 comments on commit e18c8a7

Please sign in to comment.