vendor/symfony/routing/Loader/AnnotationClassLoader.php line 107

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Routing\Loader;
  11. use Doctrine\Common\Annotations\Reader;
  12. use Symfony\Component\Config\Loader\LoaderInterface;
  13. use Symfony\Component\Config\Loader\LoaderResolverInterface;
  14. use Symfony\Component\Config\Resource\FileResource;
  15. use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
  16. use Symfony\Component\Routing\Route;
  17. use Symfony\Component\Routing\RouteCollection;
  18. /**
  19.  * AnnotationClassLoader loads routing information from a PHP class and its methods.
  20.  *
  21.  * You need to define an implementation for the configureRoute() method. Most of the
  22.  * time, this method should define some PHP callable to be called for the route
  23.  * (a controller in MVC speak).
  24.  *
  25.  * The @Route annotation can be set on the class (for global parameters),
  26.  * and on each method.
  27.  *
  28.  * The @Route annotation main value is the route path. The annotation also
  29.  * recognizes several parameters: requirements, options, defaults, schemes,
  30.  * methods, host, and name. The name parameter is mandatory.
  31.  * Here is an example of how you should be able to use it:
  32.  *     /**
  33.  *      * @Route("/Blog")
  34.  *      * /
  35.  *     class Blog
  36.  *     {
  37.  *         /**
  38.  *          * @Route("/", name="blog_index")
  39.  *          * /
  40.  *         public function index()
  41.  *         {
  42.  *         }
  43.  *         /**
  44.  *          * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
  45.  *          * /
  46.  *         public function show()
  47.  *         {
  48.  *         }
  49.  *     }
  50.  *
  51.  * @author Fabien Potencier <fabien@symfony.com>
  52.  */
  53. abstract class AnnotationClassLoader implements LoaderInterface
  54. {
  55.     protected $reader;
  56.     /**
  57.      * @var string
  58.      */
  59.     protected $routeAnnotationClass 'Symfony\\Component\\Routing\\Annotation\\Route';
  60.     /**
  61.      * @var int
  62.      */
  63.     protected $defaultRouteIndex 0;
  64.     public function __construct(Reader $reader)
  65.     {
  66.         $this->reader $reader;
  67.     }
  68.     /**
  69.      * Sets the annotation class to read route properties from.
  70.      *
  71.      * @param string $class A fully-qualified class name
  72.      */
  73.     public function setRouteAnnotationClass($class)
  74.     {
  75.         $this->routeAnnotationClass $class;
  76.     }
  77.     /**
  78.      * Loads from annotations from a class.
  79.      *
  80.      * @param string      $class A class name
  81.      * @param string|null $type  The resource type
  82.      *
  83.      * @return RouteCollection A RouteCollection instance
  84.      *
  85.      * @throws \InvalidArgumentException When route can't be parsed
  86.      */
  87.     public function load($class$type null)
  88.     {
  89.         if (!class_exists($class)) {
  90.             throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.'$class));
  91.         }
  92.         $class = new \ReflectionClass($class);
  93.         if ($class->isAbstract()) {
  94.             throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.'$class->getName()));
  95.         }
  96.         $globals $this->getGlobals($class);
  97.         $collection = new RouteCollection();
  98.         $collection->addResource(new FileResource($class->getFileName()));
  99.         foreach ($class->getMethods() as $method) {
  100.             $this->defaultRouteIndex 0;
  101.             foreach ($this->reader->getMethodAnnotations($method) as $annot) {
  102.                 if ($annot instanceof $this->routeAnnotationClass) {
  103.                     $this->addRoute($collection$annot$globals$class$method);
  104.                 }
  105.             }
  106.         }
  107.         if (=== $collection->count() && $class->hasMethod('__invoke')) {
  108.             $globals $this->resetGlobals();
  109.             foreach ($this->reader->getClassAnnotations($class) as $annot) {
  110.                 if ($annot instanceof $this->routeAnnotationClass) {
  111.                     $this->addRoute($collection$annot$globals$class$class->getMethod('__invoke'));
  112.                 }
  113.             }
  114.         }
  115.         return $collection;
  116.     }
  117.     /**
  118.      * @param RouteAnnotation $annot   or an object that exposes a similar interface
  119.      * @param array           $globals
  120.      */
  121.     protected function addRoute(RouteCollection $collection$annot$globals, \ReflectionClass $class, \ReflectionMethod $method)
  122.     {
  123.         $name $annot->getName();
  124.         if (null === $name) {
  125.             $name $this->getDefaultRouteName($class$method);
  126.         }
  127.         $name $globals['name'].$name;
  128.         $defaults array_replace($globals['defaults'], $annot->getDefaults());
  129.         foreach ($method->getParameters() as $param) {
  130.             if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}'$param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
  131.                 $defaults[$param->getName()] = $param->getDefaultValue();
  132.             }
  133.         }
  134.         $requirements array_replace($globals['requirements'], $annot->getRequirements());
  135.         $options array_replace($globals['options'], $annot->getOptions());
  136.         $schemes array_merge($globals['schemes'], $annot->getSchemes());
  137.         $methods array_merge($globals['methods'], $annot->getMethods());
  138.         $host $annot->getHost();
  139.         if (null === $host) {
  140.             $host $globals['host'];
  141.         }
  142.         $condition $annot->getCondition();
  143.         if (null === $condition) {
  144.             $condition $globals['condition'];
  145.         }
  146.         $route $this->createRoute($globals['path'].$annot->getPath(), $defaults$requirements$options$host$schemes$methods$condition);
  147.         $this->configureRoute($route$class$method$annot);
  148.         $collection->add($name$route);
  149.     }
  150.     /**
  151.      * {@inheritdoc}
  152.      */
  153.     public function supports($resource$type null)
  154.     {
  155.         return \is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/'$resource) && (!$type || 'annotation' === $type);
  156.     }
  157.     /**
  158.      * {@inheritdoc}
  159.      */
  160.     public function setResolver(LoaderResolverInterface $resolver)
  161.     {
  162.     }
  163.     /**
  164.      * {@inheritdoc}
  165.      */
  166.     public function getResolver()
  167.     {
  168.     }
  169.     /**
  170.      * Gets the default route name for a class method.
  171.      *
  172.      * @return string
  173.      */
  174.     protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
  175.     {
  176.         $name str_replace('\\''_'$class->name).'_'.$method->name;
  177.         $name = \function_exists('mb_strtolower') && preg_match('//u'$name) ? mb_strtolower($name'UTF-8') : strtolower($name);
  178.         if ($this->defaultRouteIndex 0) {
  179.             $name .= '_'.$this->defaultRouteIndex;
  180.         }
  181.         ++$this->defaultRouteIndex;
  182.         return $name;
  183.     }
  184.     protected function getGlobals(\ReflectionClass $class)
  185.     {
  186.         $globals $this->resetGlobals();
  187.         if ($annot $this->reader->getClassAnnotation($class$this->routeAnnotationClass)) {
  188.             if (null !== $annot->getName()) {
  189.                 $globals['name'] = $annot->getName();
  190.             }
  191.             if (null !== $annot->getPath()) {
  192.                 $globals['path'] = $annot->getPath();
  193.             }
  194.             if (null !== $annot->getRequirements()) {
  195.                 $globals['requirements'] = $annot->getRequirements();
  196.             }
  197.             if (null !== $annot->getOptions()) {
  198.                 $globals['options'] = $annot->getOptions();
  199.             }
  200.             if (null !== $annot->getDefaults()) {
  201.                 $globals['defaults'] = $annot->getDefaults();
  202.             }
  203.             if (null !== $annot->getSchemes()) {
  204.                 $globals['schemes'] = $annot->getSchemes();
  205.             }
  206.             if (null !== $annot->getMethods()) {
  207.                 $globals['methods'] = $annot->getMethods();
  208.             }
  209.             if (null !== $annot->getHost()) {
  210.                 $globals['host'] = $annot->getHost();
  211.             }
  212.             if (null !== $annot->getCondition()) {
  213.                 $globals['condition'] = $annot->getCondition();
  214.             }
  215.         }
  216.         return $globals;
  217.     }
  218.     private function resetGlobals()
  219.     {
  220.         return [
  221.             'path' => '',
  222.             'requirements' => [],
  223.             'options' => [],
  224.             'defaults' => [],
  225.             'schemes' => [],
  226.             'methods' => [],
  227.             'host' => '',
  228.             'condition' => '',
  229.             'name' => '',
  230.         ];
  231.     }
  232.     protected function createRoute($path$defaults$requirements$options$host$schemes$methods$condition)
  233.     {
  234.         return new Route($path$defaults$requirements$options$host$schemes$methods$condition);
  235.     }
  236.     abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method$annot);
  237. }