034a1c580c770b503d8a1b23823918d77a1e6af7
[yaffs-website] / vendor / symfony / routing / Loader / AnnotationFileLoader.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Routing\Loader;
13
14 use Symfony\Component\Config\FileLocatorInterface;
15 use Symfony\Component\Config\Loader\FileLoader;
16 use Symfony\Component\Config\Resource\FileResource;
17 use Symfony\Component\Routing\RouteCollection;
18
19 /**
20  * AnnotationFileLoader loads routing information from annotations set
21  * on a PHP class and its methods.
22  *
23  * @author Fabien Potencier <fabien@symfony.com>
24  */
25 class AnnotationFileLoader extends FileLoader
26 {
27     protected $loader;
28
29     /**
30      * @throws \RuntimeException
31      */
32     public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader)
33     {
34         if (!\function_exists('token_get_all')) {
35             throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
36         }
37
38         parent::__construct($locator);
39
40         $this->loader = $loader;
41     }
42
43     /**
44      * Loads from annotations from a file.
45      *
46      * @param string      $file A PHP file path
47      * @param string|null $type The resource type
48      *
49      * @return RouteCollection A RouteCollection instance
50      *
51      * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
52      */
53     public function load($file, $type = null)
54     {
55         $path = $this->locator->locate($file);
56
57         $collection = new RouteCollection();
58         if ($class = $this->findClass($path)) {
59             $collection->addResource(new FileResource($path));
60             $collection->addCollection($this->loader->load($class, $type));
61         }
62         if (\PHP_VERSION_ID >= 70000) {
63             // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
64             gc_mem_caches();
65         }
66
67         return $collection;
68     }
69
70     /**
71      * {@inheritdoc}
72      */
73     public function supports($resource, $type = null)
74     {
75         return \is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
76     }
77
78     /**
79      * Returns the full class name for the first class in the file.
80      *
81      * @param string $file A PHP file path
82      *
83      * @return string|false Full class name if found, false otherwise
84      */
85     protected function findClass($file)
86     {
87         $class = false;
88         $namespace = false;
89         $tokens = token_get_all(file_get_contents($file));
90
91         if (1 === \count($tokens) && T_INLINE_HTML === $tokens[0][0]) {
92             throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the "<?php" start tag at the beginning of the file?', $file));
93         }
94
95         for ($i = 0; isset($tokens[$i]); ++$i) {
96             $token = $tokens[$i];
97
98             if (!isset($token[1])) {
99                 continue;
100             }
101
102             if (true === $class && T_STRING === $token[0]) {
103                 return $namespace.'\\'.$token[1];
104             }
105
106             if (true === $namespace && T_STRING === $token[0]) {
107                 $namespace = $token[1];
108                 while (isset($tokens[++$i][1]) && \in_array($tokens[$i][0], array(T_NS_SEPARATOR, T_STRING))) {
109                     $namespace .= $tokens[$i][1];
110                 }
111                 $token = $tokens[$i];
112             }
113
114             if (T_CLASS === $token[0]) {
115                 // Skip usage of ::class constant and anonymous classes
116                 $skipClassToken = false;
117                 for ($j = $i - 1; $j > 0; --$j) {
118                     if (!isset($tokens[$j][1])) {
119                         break;
120                     }
121
122                     if (T_DOUBLE_COLON === $tokens[$j][0] || T_NEW === $tokens[$j][0]) {
123                         $skipClassToken = true;
124                         break;
125                     } elseif (!\in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
126                         break;
127                     }
128                 }
129
130                 if (!$skipClassToken) {
131                     $class = true;
132                 }
133             }
134
135             if (T_NAMESPACE === $token[0]) {
136                 $namespace = true;
137             }
138         }
139
140         return false;
141     }
142 }