annotate vendor/symfony/routing/Loader/AnnotationClassLoader.php @ 8:50b0d041100e

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