Search code examples
symfonyphp-8

Support PHP 8's attributes for my routes in custom Symfony framework


I've followed the Symfony Tutorial Series to create my own Symfony framework. Currently I have the following code to define my routes and add them to the UrlMatcher:

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

$context = new Routing\RequestContext();
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);

Here's my app.php file for reference:

<?php

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

$routes = new Routing\RouteCollection();

$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', [
    'year' => null,
    '_controller' => 'App\Controller\LeapYearController::index',
]));

return $routes;

What's the simplest way this can be modified to support PHP 8's attributes against the LeapYearController's actions for my routes instead of defining them in the app.php file?

It's been a while since I last looked at Symfony and a lot has changed in the framework aswell as in PHP itself and so far everything I have found is no longer supported.


Solution

  • I've managed to get the following working:

    $loader = new AnnotationDirectoryLoader(
        new FileLocator(),
        new class() extends AnnotationClassLoader {
            protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot) {
                $route->setDefault('_controller', $class->getName() . '::' . $method->getName());
            }
        }
    );
    
    $routes = $loader->load(__DIR__ . '/../src/App/Controller');
    

    Alternatively this works using the Psr4DirectoryLoader introduced in version 6.2:

    $loader = new DelegatingLoader(
        new LoaderResolver([
            new Psr4DirectoryLoader(
                new FileLocator()
            ),
            new class() extends AnnotationClassLoader {
                protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot) {
                    $route->setDefault('_controller', $class->getName() . '::' . $method->getName());
                }
            }
        ])
    );
    
    $routes = $loader->load(['path' => __DIR__ . '/../src/App/Controller', 'namespace' => 'App\Controller'], 'attribute');