PROGRAMMING

m9/ PHP
REMEMBERS




Last update:   23-07-2021

Routing

We have decupled the URL from the code (with $map) v2.2 Stil, it's not flexible enough if we want something like: /hello/Fabian ! One important aspect of any website is the form of the URLs. To support this features, add Symfony Routing component as a dependency.
 
composer require symfony/routing
Let's write the new version of our framework
 
// public/front.php

require_once __DIR__.'/../vendor/autoload.php';

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing;

$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';

$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);

try {
    extract($matcher->match($request->getPathInfo(), EXTR_SKIP));
    ob_start();
    include sprintf(__DIR__.'/../src/pages/%s.phtml', $_route);
    $response = new Response(ob_get_clean());
} catch(Routing\Exception\ResourceNotFoundException $e) {
    $response = new Response('Not found', 404);
} catch (Exception $e) {
    $response = new Response('An error occured', 500);
}

$response->send();
... 15 lines
 
We separated the configuration (everything specific to our application in app.php) from the front controller (front.php).
 
// src/app.php

use Symfony\Component\Routing;

$routes = new Routing\RouteCollection();
$routes->add('hello', new Routing\Route(
    '/hello/{name}', ['name' => 'World']
));
$routes->add('bye', new Routing\Route('/bye'));

return $routes;
... 2 lines
 
 
http://tests.local/php/framework/public/hello/Fabian  
    // Hello Fabian

http://tests.local/php/framework/public/hello 
    // Hello World

http://tests.local/php/framework/public/hello/ 
    // Not found
Framework v3.0 Fork v3.0c Details    (2/2)

Details

There are a few new things in the code v3.0
 
- routes names are used for template names
- 500 errors are managed correctly
- attributes are extracted to keep template simple
- route configuration moved to its own file
Based on $routes information (RouteCollection instance), a UrlMatcher instance can match URL paths.
 
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;

$context = new RequestContext();
$context->fromRequest($request);
$matcher = new UrlMatcher($routes, $context);

$attributes = $matcher->match($request->getPathInfo());
The match() method takes a request path and returns an array of attributes. The matched route is automaticaly stored in _route attribute.
 
$matcher->match('/bye'); 
    // ['_route' => 'bye']

$matcher->match('/hello/Fabian');
    // ['_route' => 'hello',
    // 'name' => 'Fabian']

$matcher->match('/hello');
    // ['_route' => 'hello',
    // 'name' => 'World']
... 2 lines
 
The request context is not strictly needed, but it is used in real-world applications to enforce method requirements and more.
Questions    
Templates