2018-02-01 20:01:12 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Routing\Loader ;
use Symfony\Component\Config\FileLocatorInterface ;
2018-10-14 19:50:32 +00:00
use Symfony\Component\Config\Loader\FileLoader ;
use Symfony\Component\Config\Resource\FileResource ;
use Symfony\Component\Routing\RouteCollection ;
2018-02-01 20:01:12 +00:00
/**
* AnnotationFileLoader loads routing information from annotations set
* on a PHP class and its methods .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class AnnotationFileLoader extends FileLoader
{
protected $loader ;
public function __construct ( FileLocatorInterface $locator , AnnotationClassLoader $loader )
{
2018-10-14 19:50:32 +00:00
if ( ! \function_exists ( 'token_get_all' )) {
2019-01-18 18:33:28 +00:00
throw new \LogicException ( 'The Tokenizer extension is required for the routing annotation loaders.' );
2018-02-01 20:01:12 +00:00
}
parent :: __construct ( $locator );
$this -> loader = $loader ;
}
/**
* Loads from annotations from a file .
*
* @ param string $file A PHP file path
* @ param string | null $type The resource type
*
2022-03-10 11:54:29 +00:00
* @ return RouteCollection | null
2018-02-01 20:01:12 +00:00
*
* @ throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
*/
2022-03-10 11:54:29 +00:00
public function load ( $file , string $type = null )
2018-02-01 20:01:12 +00:00
{
$path = $this -> locator -> locate ( $file );
$collection = new RouteCollection ();
if ( $class = $this -> findClass ( $path )) {
2019-06-11 11:29:32 +00:00
$refl = new \ReflectionClass ( $class );
if ( $refl -> isAbstract ()) {
2022-03-10 11:54:29 +00:00
return null ;
2019-06-11 11:29:32 +00:00
}
2018-02-01 20:01:12 +00:00
$collection -> addResource ( new FileResource ( $path ));
$collection -> addCollection ( $this -> loader -> load ( $class , $type ));
}
2018-10-14 19:50:32 +00:00
gc_mem_caches ();
2018-02-01 20:01:12 +00:00
return $collection ;
}
/**
* { @ inheritdoc }
*/
2022-03-10 11:54:29 +00:00
public function supports ( $resource , string $type = null )
2018-02-01 20:01:12 +00:00
{
2022-03-10 11:54:29 +00:00
return \is_string ( $resource ) && 'php' === pathinfo ( $resource , \PATHINFO_EXTENSION ) && ( ! $type || 'annotation' === $type );
2018-02-01 20:01:12 +00:00
}
/**
* Returns the full class name for the first class in the file .
*
2022-03-10 11:54:29 +00:00
* @ return string | false
2018-02-01 20:01:12 +00:00
*/
2022-03-10 11:54:29 +00:00
protected function findClass ( string $file )
2018-02-01 20:01:12 +00:00
{
$class = false ;
$namespace = false ;
$tokens = token_get_all ( file_get_contents ( $file ));
2022-03-10 11:54:29 +00:00
if ( 1 === \count ( $tokens ) && \T_INLINE_HTML === $tokens [ 0 ][ 0 ]) {
2018-02-01 20:01:12 +00:00
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 ));
}
2022-03-10 11:54:29 +00:00
$nsTokens = [ \T_NS_SEPARATOR => true , \T_STRING => true ];
if ( \defined ( 'T_NAME_QUALIFIED' )) {
$nsTokens [ \T_NAME_QUALIFIED ] = true ;
}
2018-02-01 20:01:12 +00:00
for ( $i = 0 ; isset ( $tokens [ $i ]); ++ $i ) {
$token = $tokens [ $i ];
if ( ! isset ( $token [ 1 ])) {
continue ;
}
2022-03-10 11:54:29 +00:00
if ( true === $class && \T_STRING === $token [ 0 ]) {
2018-02-01 20:01:12 +00:00
return $namespace . '\\' . $token [ 1 ];
}
2022-03-10 11:54:29 +00:00
if ( true === $namespace && isset ( $nsTokens [ $token [ 0 ]])) {
2018-02-01 20:01:12 +00:00
$namespace = $token [ 1 ];
2022-03-10 11:54:29 +00:00
while ( isset ( $tokens [ ++ $i ][ 1 ], $nsTokens [ $tokens [ $i ][ 0 ]])) {
2018-02-01 20:01:12 +00:00
$namespace .= $tokens [ $i ][ 1 ];
}
$token = $tokens [ $i ];
}
2022-03-10 11:54:29 +00:00
if ( \T_CLASS === $token [ 0 ]) {
2018-02-01 20:01:12 +00:00
// Skip usage of ::class constant and anonymous classes
$skipClassToken = false ;
for ( $j = $i - 1 ; $j > 0 ; -- $j ) {
if ( ! isset ( $tokens [ $j ][ 1 ])) {
2022-03-10 11:54:29 +00:00
if ( '(' === $tokens [ $j ] || ',' === $tokens [ $j ]) {
$skipClassToken = true ;
}
2018-02-01 20:01:12 +00:00
break ;
}
2022-03-10 11:54:29 +00:00
if ( \T_DOUBLE_COLON === $tokens [ $j ][ 0 ] || \T_NEW === $tokens [ $j ][ 0 ]) {
2018-02-01 20:01:12 +00:00
$skipClassToken = true ;
break ;
2022-03-10 11:54:29 +00:00
} elseif ( ! \in_array ( $tokens [ $j ][ 0 ], [ \T_WHITESPACE , \T_DOC_COMMENT , \T_COMMENT ])) {
2018-02-01 20:01:12 +00:00
break ;
}
}
if ( ! $skipClassToken ) {
$class = true ;
}
}
2022-03-10 11:54:29 +00:00
if ( \T_NAMESPACE === $token [ 0 ]) {
2018-02-01 20:01:12 +00:00
$namespace = true ;
}
}
return false ;
}
}