Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Routing\Loader; Chris@0: Chris@17: use Symfony\Component\Config\FileLocatorInterface; Chris@17: use Symfony\Component\Config\Loader\FileLoader; Chris@17: use Symfony\Component\Config\Resource\FileResource; Chris@0: use Symfony\Component\Routing\RouteCollection; Chris@0: Chris@0: /** Chris@0: * AnnotationFileLoader loads routing information from annotations set Chris@0: * on a PHP class and its methods. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: */ Chris@0: class AnnotationFileLoader extends FileLoader Chris@0: { Chris@0: protected $loader; Chris@0: Chris@0: /** Chris@0: * @throws \RuntimeException Chris@0: */ Chris@0: public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader) Chris@0: { Chris@17: if (!\function_exists('token_get_all')) { Chris@0: throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.'); Chris@0: } Chris@0: Chris@0: parent::__construct($locator); Chris@0: Chris@0: $this->loader = $loader; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Loads from annotations from a file. Chris@0: * Chris@0: * @param string $file A PHP file path Chris@0: * @param string|null $type The resource type Chris@0: * Chris@0: * @return RouteCollection A RouteCollection instance Chris@0: * Chris@0: * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed Chris@0: */ Chris@0: public function load($file, $type = null) Chris@0: { Chris@0: $path = $this->locator->locate($file); Chris@0: Chris@0: $collection = new RouteCollection(); Chris@0: if ($class = $this->findClass($path)) { Chris@18: $refl = new \ReflectionClass($class); Chris@18: if ($refl->isAbstract()) { Chris@18: return; Chris@18: } Chris@18: Chris@0: $collection->addResource(new FileResource($path)); Chris@0: $collection->addCollection($this->loader->load($class, $type)); Chris@0: } Chris@12: if (\PHP_VERSION_ID >= 70000) { Chris@0: // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 Chris@0: gc_mem_caches(); Chris@0: } Chris@0: Chris@0: return $collection; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function supports($resource, $type = null) Chris@0: { Chris@17: return \is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the full class name for the first class in the file. Chris@0: * Chris@0: * @param string $file A PHP file path Chris@0: * Chris@0: * @return string|false Full class name if found, false otherwise Chris@0: */ Chris@0: protected function findClass($file) Chris@0: { Chris@0: $class = false; Chris@0: $namespace = false; Chris@0: $tokens = token_get_all(file_get_contents($file)); Chris@0: Chris@17: if (1 === \count($tokens) && T_INLINE_HTML === $tokens[0][0]) { Chris@0: throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the " 0; --$j) { Chris@0: if (!isset($tokens[$j][1])) { Chris@0: break; Chris@0: } Chris@0: Chris@14: if (T_DOUBLE_COLON === $tokens[$j][0] || T_NEW === $tokens[$j][0]) { Chris@14: $skipClassToken = true; Chris@0: break; Chris@17: } elseif (!\in_array($tokens[$j][0], [T_WHITESPACE, T_DOC_COMMENT, T_COMMENT])) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@14: if (!$skipClassToken) { Chris@0: $class = true; Chris@0: } Chris@0: } Chris@0: Chris@0: if (T_NAMESPACE === $token[0]) { Chris@0: $namespace = true; Chris@0: } Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: }