2015-08-17 17:00:26 -07: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\Routing\RouteCollection ;
use Symfony\Component\Config\Resource\FileResource ;
use Symfony\Component\Config\Loader\FileLoader ;
use Symfony\Component\Config\FileLocatorInterface ;
/**
* 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 ;
/**
* Constructor .
*
* @ param FileLocatorInterface $locator A FileLocator instance
* @ param AnnotationClassLoader $loader An AnnotationClassLoader instance
*
* @ throws \RuntimeException
*/
public function __construct ( FileLocatorInterface $locator , AnnotationClassLoader $loader )
{
if ( ! function_exists ( 'token_get_all' )) {
throw new \RuntimeException ( 'The Tokenizer extension is required for the routing annotation loaders.' );
}
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
*
* @ return RouteCollection A RouteCollection instance
*
* @ throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
*/
public function load ( $file , $type = null )
{
$path = $this -> locator -> locate ( $file );
$collection = new RouteCollection ();
if ( $class = $this -> findClass ( $path )) {
$collection -> addResource ( new FileResource ( $path ));
$collection -> addCollection ( $this -> loader -> load ( $class , $type ));
}
2016-04-20 09:56:34 -07:00
if ( PHP_VERSION_ID >= 70000 ) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches ();
}
2015-08-17 17:00:26 -07:00
return $collection ;
}
/**
* { @ inheritdoc }
*/
public function supports ( $resource , $type = null )
{
return is_string ( $resource ) && 'php' === pathinfo ( $resource , PATHINFO_EXTENSION ) && ( ! $type || 'annotation' === $type );
}
/**
* Returns the full class name for the first class in the file .
*
* @ param string $file A PHP file path
*
* @ return string | false Full class name if found , false otherwise
*/
protected function findClass ( $file )
{
$class = false ;
$namespace = false ;
$tokens = token_get_all ( file_get_contents ( $file ));
2016-04-20 09:56:34 -07:00
for ( $i = 0 ; isset ( $tokens [ $i ]); ++ $i ) {
2015-08-17 17:00:26 -07:00
$token = $tokens [ $i ];
2016-04-20 09:56:34 -07:00
if ( ! isset ( $token [ 1 ])) {
2015-08-17 17:00:26 -07:00
continue ;
}
if ( true === $class && T_STRING === $token [ 0 ]) {
return $namespace . '\\' . $token [ 1 ];
}
if ( true === $namespace && T_STRING === $token [ 0 ]) {
2016-04-20 09:56:34 -07:00
$namespace = $token [ 1 ];
while ( isset ( $tokens [ ++ $i ][ 1 ]) && in_array ( $tokens [ $i ][ 0 ], array ( T_NS_SEPARATOR , T_STRING ))) {
$namespace .= $tokens [ $i ][ 1 ];
}
$token = $tokens [ $i ];
2015-08-17 17:00:26 -07:00
}
if ( T_CLASS === $token [ 0 ]) {
2017-02-02 16:28:38 -08:00
// Skip usage of ::class constant
$isClassConstant = false ;
for ( $j = $i - 1 ; $j > 0 ; -- $j ) {
if ( ! isset ( $tokens [ $j ][ 1 ])) {
break ;
}
if ( T_DOUBLE_COLON === $tokens [ $j ][ 0 ]) {
$isClassConstant = true ;
break ;
} elseif ( ! in_array ( $tokens [ $j ][ 0 ], array ( T_WHITESPACE , T_DOC_COMMENT , T_COMMENT ))) {
break ;
}
}
if ( ! $isClassConstant ) {
$class = true ;
}
2015-08-17 17:00:26 -07:00
}
if ( T_NAMESPACE === $token [ 0 ]) {
$namespace = true ;
}
}
return false ;
}
}