Created
September 25, 2018 13:20
-
-
Save standa/7c07f457d158bfe29297df627b13ab33 to your computer and use it in GitHub Desktop.
Router with callables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace League\Framework\Server\Controller; | |
use League\Plates\Engine; | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
/** | |
* Example implementation of the default routing strategy (RequestResponseStrategy) | |
* | |
* Each action may have up to three parameters: request, response and an array of arguments | |
*/ | |
class DefaultController | |
{ | |
private $tpl; | |
/** | |
* Example of how to inject dependencies | |
* | |
* @param Engine $tpl | |
*/ | |
public function __construct(Engine $tpl) | |
{ | |
$this->tpl = $tpl; | |
} | |
/** | |
* GET /server.html | |
* | |
* @param ServerRequestInterface $request | |
* @param ResponseInterface $response | |
* @return ResponseInterface | |
* @throws \InvalidArgumentException | |
* @throws \RuntimeException | |
*/ | |
public function serverTemplateAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface | |
{ | |
$response->getBody()->write($this->tpl->render('Default/Index', [ | |
'title' => 'Default route', 'name' => $request->getServerParams()['HTTP_HOST'] | |
])); | |
return $response->withStatus(200); | |
} | |
/** | |
* GET /user/{id:number} | |
* | |
* @param ServerRequestInterface $request | |
* @param ResponseInterface $response | |
* @param string[] $args | |
* @return ResponseInterface | |
* @throws \InvalidArgumentException | |
* @throws \RuntimeException | |
*/ | |
public function userIdAction(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface | |
{ | |
$response->getBody()->write($this->tpl->render('Default/Index', [ | |
'title' => 'User '.$args['id'].' route', 'name' => 'User ID: '.$args['id'] | |
])); | |
return $response->withStatus(200); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @var League\Plates\Template\Template $this | |
* @var string $name name of the current user | |
* @var string $title <title> of the page | |
*/ | |
// $this->layout('Layout/default', ['title' => 'User Profile']) | |
use League\Framework\Server\Core\Router; | |
$this->layout('Layout/NarrowJumbotron', ['title' => $title]); | |
?> | |
<div class="jumbotron"> | |
<?php if ($name !== '') { ?> | |
<h1 class="display-3">Hello <?=$this->e($name);?>!</h1> | |
<p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> | |
<p><a class="btn btn-lg btn-success" href="<?=$this->route(Router::GET_SERVER_HTML); ?>" role="button">Hello <?= $this->e($name); ?></a></p> | |
<?php } else { ?> | |
<h1 class="display-3">Register now!</h1> | |
<p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> | |
<p><a class="btn btn-lg btn-success" href="<?=$this->route(Router::GET_SERVER_HTML); ?>" role="button">Sign up today</a></p> | |
<?php } ?> | |
</div> | |
<div class="row marketing"> | |
<div class="col-lg-6"> | |
<h4>Subheading</h4> | |
<p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p> | |
<h4>Subheading</h4> | |
<p>Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.</p> | |
<h4>Subheading</h4> | |
<p>Maecenas sed diam eget risus varius blandit sit amet non magna.</p> | |
</div> | |
<div class="col-lg-6"> | |
<h4>Subheading</h4> | |
<p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p> | |
<h4>Subheading</h4> | |
<p>Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.</p> | |
<h4>Subheading</h4> | |
<p>Maecenas sed diam eget risus varius blandit sit amet non magna.</p> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace League\Framework\Server\Controller; | |
use League\Route\Http\Exception\ImATeapotException; | |
use Psr\Http\Message\ServerRequestInterface; | |
/** | |
* Controllers with JsonStrategy do not have response objects, response is a plain array of data. | |
* | |
* @todo use fractal library here | |
* | |
*/ | |
class JsonController | |
{ | |
/** | |
* GET /test.json | |
* | |
* Example of the simplest implementation | |
* | |
* @param ServerRequestInterface $request | |
* @return array | |
*/ | |
public function testAction(ServerRequestInterface $request): array | |
{ | |
return [ | |
'error' => false, | |
'code' => 200, | |
'message' => 'Hello World' | |
]; | |
} | |
/** | |
* GET /user/1.json | |
* | |
* Example of getting URI parameters | |
* | |
* @param ServerRequestInterface $request | |
* @param array $args | |
* @return array | |
*/ | |
public function userIdAction(ServerRequestInterface $request, array $args): array { | |
return [ | |
'error' => false, | |
'code' => 200, | |
'ip' => $request->getServerParams()['REMOTE_ADDR'], | |
'message' => 'User ID '.$args['id'].' found.' | |
]; | |
} | |
/** | |
* GET /teapot.json | |
* | |
* Return HTTP 418 I'm a teapot | |
* | |
* {"status_code":418,"reason_phrase":"I'm a teapot"} | |
* | |
* @param ServerRequestInterface $request | |
* @throws ImATeapotException | |
*/ | |
public function teaPotAction(ServerRequestInterface $request) | |
{ | |
throw new ImATeapotException(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace League\Framework\Server\Core; | |
use function League\Framework\Server\container; | |
use League\Framework\Server\Controller\BazosController; | |
use League\Framework\Server\Controller\ExceptionsController; | |
use League\Framework\Server\Controller\DefaultController; | |
use League\Framework\Server\Controller\JsonController; | |
use function League\Framework\Server\tpl; | |
use League\Route\Http\Exception as HttpException; | |
use League\Route\Route; | |
use League\Route\RouteCollection; | |
use League\Route\Strategy\JsonStrategy; | |
use League\Route\Strategy\RequestResponseStrategy; | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
/** | |
* After you have defined your routes in routes(), simply call dispatch() in your index.php script | |
* | |
* @example Router::dispatch() called in index.php to generate the output | |
* @example Router::route(Router::GET_TEAPOT_JSON) to get a path to the route | |
*/ | |
class Router implements RouterInterface | |
{ | |
const GET_SERVER_HTML = '/server.html'; | |
const GET_USER_ID_NUMBER = '/user/{id:number}'; | |
const GET_TEST_JSON = '/test.json'; | |
const GET_USER_ID_NUMBER_JSON = '/user/{id:number}.json'; | |
const GET_TEAPOT_JSON = '/teapot.json'; | |
const GET_CSS_BOOTSTRAP_MIN_CSS = '/css/bootstrap.min.css'; | |
const GET_JS_BOOTSTRAP_MIN_JS = '/js/bootstrap.min.js'; | |
const GET_JS_JQUERY_MIN_JS = '/js/jquery.min.js'; | |
/** | |
* @todo Define your routing here | |
* | |
* @param RouteCollection $routes | |
* @throws \InvalidArgumentException | |
* @throws \RuntimeException | |
*/ | |
private static function mapRoutes(RouteCollection $routes) | |
{ | |
self::mapExampleRoutes($routes); | |
self::mapBootstrapRoutes($routes); | |
} | |
public static function dispatch() | |
{ | |
// @todo emit events here that will be processed by individual implementations: react, plates, json | |
try { | |
$route = new RouteCollection(container()); | |
$route->setStrategy(new RequestResponseStrategy()); | |
//$route->setStrategy(new ParamStrategy()); | |
// A long row of $route->get() calls | |
self::mapRoutes($route); | |
$response = $route->dispatch(container()->get('request'), container()->get('response')); | |
container()->get('emitter')->emit($response); | |
} catch (HttpException\NotFoundException $e) { | |
(new ExceptionsController( | |
container(), | |
container()->get('request'), | |
container()->get('response')) | |
)->notFoundException($e); | |
} catch (HttpException $e) { | |
(new ExceptionsController( | |
container(), | |
container()->get('request'), | |
container()->get('response')) | |
)->httpException($e); | |
} | |
} | |
/** | |
* Get path a to a specific resource | |
* | |
* @todo route params validation, or maybe use something from fast-route directly | |
* | |
* @param string $route | |
* @param array ...$params | |
* @return string | |
* @throws \InvalidArgumentException if route params count does not match $params count | |
*/ | |
public static function route(string $route, ...$params) | |
{ | |
if (!self::isClassConstantByValue($route)) { | |
throw new \InvalidArgumentException('Route has to be one of the '.__CLASS__.' class constants.'); | |
} | |
$matches = []; | |
if (preg_match_all('/\{[^}]+\}/', $route, $matches, PREG_SET_ORDER) | |
&& count($params) !== count($matches) | |
) { | |
throw new \InvalidArgumentException(sprintf( | |
'Route %s has %d params but %d received.', | |
$route, | |
count($matches), | |
count($params) | |
)); | |
} | |
if (count($params) < 1) { | |
return $route; | |
} | |
foreach ($matches as $i => $match) { | |
$route = str_replace($match, $params[$i], $route); | |
} | |
return $route; | |
} | |
/** | |
* Example routes | |
* | |
* @param RouteCollection $routes | |
*/ | |
private static function mapExampleRoutes(RouteCollection $routes) | |
{ | |
// Basic html template using a templating system | |
$routes->get(self::GET_SERVER_HTML, [new DefaultController(tpl()), 'serverTemplateAction']); | |
// Pass on user id parameter into an html template | |
$routes->get(self::GET_USER_ID_NUMBER, [new DefaultController(tpl()), 'userIdAction']); | |
// The simplest implementation of json routing | |
$routes->get(self::GET_TEST_JSON, [new JsonController(), 'testAction'])->setStrategy(new JsonStrategy()); | |
// Json routing with user id parameter | |
$routes->get(self::GET_USER_ID_NUMBER_JSON, [new JsonController(), 'userIdAction'])->setStrategy(new JsonStrategy()); | |
// Json HTTP status code: returns HTTP 418 | |
$routes->get(self::GET_TEAPOT_JSON, [new JsonController(), 'teaPotAction'])->setStrategy(new JsonStrategy()); | |
} | |
/** | |
* Twitter bootstrap - custom mapping with no controller | |
* | |
* @param RouteCollection $routes | |
* @throws \RuntimeException | |
*/ | |
private static function mapBootstrapRoutes(RouteCollection $routes) | |
{ | |
$routes->get(self::GET_CSS_BOOTSTRAP_MIN_CSS, | |
function (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { | |
$response->getBody()->write(file_get_contents(__DIR__.'/../../vendor/twbs/bootstrap/dist/css/bootstrap.min.css')); | |
return $response->withHeader('Content-Type', 'text/css'); | |
} | |
); | |
$routes->get(self::GET_JS_BOOTSTRAP_MIN_JS, | |
function (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { | |
$response->getBody()->write(file_get_contents(__DIR__.'/../../vendor/twbs/bootstrap/dist/js/bootstrap.min.js')); | |
return $response->withHeader('Content-Type', 'text/javascript'); | |
} | |
); | |
$routes->get(self::GET_JS_JQUERY_MIN_JS, | |
function (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { | |
$response->getBody()->write(file_get_contents(__DIR__.'/../../vendor/components/jquery/jquery.min.js')); | |
return $response->withHeader('Content-Type', 'text/javascript'); | |
} | |
); | |
} | |
private static function isClassConstantByValue(string $value): bool | |
{ | |
static $constants; | |
if ($constants === null) { | |
$class = new \ReflectionClass(__CLASS__); | |
$constants = array_values($class->getConstants()); | |
} | |
return in_array($value, $constants, true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment