2015-08-17 17:00:26 -07:00
< ? php
namespace Doctrine\Common\Reflection ;
use Doctrine\Common\Annotations\TokenParser ;
2015-10-08 11:40:12 -07:00
use ReflectionException ;
2018-11-23 12:29:20 +00:00
use const T_CLASS ;
use const T_DOC_COMMENT ;
use const T_EXTENDS ;
use const T_FUNCTION ;
use const T_PAAMAYIM_NEKUDOTAYIM ;
use const T_PRIVATE ;
use const T_PROTECTED ;
use const T_PUBLIC ;
use const T_STRING ;
use const T_USE ;
use const T_VAR ;
use const T_VARIABLE ;
use function array_merge ;
use function file_get_contents ;
use function ltrim ;
use function preg_match ;
use function sprintf ;
use function strpos ;
use function strrpos ;
use function strtolower ;
use function substr ;
2015-08-17 17:00:26 -07:00
/**
* Parses a file for namespaces / use / class declarations .
*/
class StaticReflectionParser implements ReflectionProviderInterface
{
/**
* The fully qualified class name .
*
* @ var string
*/
protected $className ;
/**
* The short class name .
*
* @ var string
*/
protected $shortClassName ;
/**
* Whether the caller only wants class annotations .
*
2018-11-23 12:29:20 +00:00
* @ var bool
2015-08-17 17:00:26 -07:00
*/
protected $classAnnotationOptimize ;
2018-11-23 12:29:20 +00:00
/**
* A ClassFinder object which finds the class .
*
* @ var ClassFinderInterface
*/
protected $finder ;
2015-08-17 17:00:26 -07:00
/**
* Whether the parser has run .
*
2018-11-23 12:29:20 +00:00
* @ var bool
2015-08-17 17:00:26 -07:00
*/
protected $parsed = false ;
/**
* The namespace of the class .
*
* @ var string
*/
protected $namespace = '' ;
/**
* The use statements of the class .
*
2018-11-23 12:29:20 +00:00
* @ var string []
2015-08-17 17:00:26 -07:00
*/
2017-04-13 15:53:35 +01:00
protected $useStatements = [];
2015-08-17 17:00:26 -07:00
/**
* The docComment of the class .
*
2018-11-23 12:29:20 +00:00
* @ var mixed []
2015-08-17 17:00:26 -07:00
*/
2017-04-13 15:53:35 +01:00
protected $docComment = [
2015-08-17 17:00:26 -07:00
'class' => '' ,
2017-04-13 15:53:35 +01:00
'property' => [],
2018-11-23 12:29:20 +00:00
'method' => [],
2017-04-13 15:53:35 +01:00
];
2015-08-17 17:00:26 -07:00
/**
* The name of the class this class extends , if any .
*
* @ var string
*/
protected $parentClassName = '' ;
/**
* The parent PSR - 0 Parser .
*
* @ var \Doctrine\Common\Reflection\StaticReflectionParser
*/
protected $parentStaticReflectionParser ;
/**
* Parses a class residing in a PSR - 0 hierarchy .
*
* @ param string $className The full , namespaced class name .
* @ param ClassFinderInterface $finder A ClassFinder object which finds the class .
2018-11-23 12:29:20 +00:00
* @ param bool $classAnnotationOptimize Only retrieve the class docComment .
* Presumes there is only one statement per line .
2015-08-17 17:00:26 -07:00
*/
public function __construct ( $className , $finder , $classAnnotationOptimize = false )
{
$this -> className = ltrim ( $className , '\\' );
2018-11-23 12:29:20 +00:00
$lastNsPos = strrpos ( $this -> className , '\\' );
2015-08-17 17:00:26 -07:00
if ( $lastNsPos !== false ) {
2018-11-23 12:29:20 +00:00
$this -> namespace = substr ( $this -> className , 0 , $lastNsPos );
2015-08-17 17:00:26 -07:00
$this -> shortClassName = substr ( $this -> className , $lastNsPos + 1 );
} else {
$this -> shortClassName = $this -> className ;
}
2018-11-23 12:29:20 +00:00
$this -> finder = $finder ;
2015-08-17 17:00:26 -07:00
$this -> classAnnotationOptimize = $classAnnotationOptimize ;
}
/**
* @ return void
*/
protected function parse ()
{
2018-11-23 12:29:20 +00:00
$fileName = $this -> finder -> findFile ( $this -> className );
if ( $this -> parsed || ! $fileName ) {
2015-08-17 17:00:26 -07:00
return ;
}
$this -> parsed = true ;
2018-11-23 12:29:20 +00:00
$contents = file_get_contents ( $fileName );
2015-08-17 17:00:26 -07:00
if ( $this -> classAnnotationOptimize ) {
2018-11-23 12:29:20 +00:00
$regex = sprintf ( '/\A.*^\s*((abstract|final)\s+)?class\s+%s\s+/sm' , $this -> shortClassName );
if ( preg_match ( $regex , $contents , $matches )) {
2015-08-17 17:00:26 -07:00
$contents = $matches [ 0 ];
}
}
$tokenParser = new TokenParser ( $contents );
2018-11-23 12:29:20 +00:00
$docComment = '' ;
$last_token = false ;
2015-08-17 17:00:26 -07:00
while ( $token = $tokenParser -> next ( false )) {
2018-11-23 12:29:20 +00:00
switch ( $token [ 0 ]) {
case T_USE :
$this -> useStatements = array_merge ( $this -> useStatements , $tokenParser -> parseUseStatement ());
break ;
case T_DOC_COMMENT :
$docComment = $token [ 1 ];
break ;
case T_CLASS :
if ( $last_token !== T_PAAMAYIM_NEKUDOTAYIM ) {
2015-08-17 17:00:26 -07:00
$this -> docComment [ 'class' ] = $docComment ;
2018-11-23 12:29:20 +00:00
$docComment = '' ;
}
break ;
case T_VAR :
case T_PRIVATE :
case T_PROTECTED :
case T_PUBLIC :
$token = $tokenParser -> next ();
if ( $token [ 0 ] === T_VARIABLE ) {
$propertyName = substr ( $token [ 1 ], 1 );
$this -> docComment [ 'property' ][ $propertyName ] = $docComment ;
continue 2 ;
}
if ( $token [ 0 ] !== T_FUNCTION ) {
// For example, it can be T_FINAL.
continue 2 ;
}
// No break.
case T_FUNCTION :
// The next string after function is the name, but
// there can be & before the function name so find the
// string.
while (( $token = $tokenParser -> next ()) && $token [ 0 ] !== T_STRING ) {
continue ;
}
$methodName = $token [ 1 ];
$this -> docComment [ 'method' ][ $methodName ] = $docComment ;
$docComment = '' ;
break ;
case T_EXTENDS :
$this -> parentClassName = $tokenParser -> parseClass ();
$nsPos = strpos ( $this -> parentClassName , '\\' );
$fullySpecified = false ;
if ( $nsPos === 0 ) {
$fullySpecified = true ;
} else {
if ( $nsPos ) {
$prefix = strtolower ( substr ( $this -> parentClassName , 0 , $nsPos ));
$postfix = substr ( $this -> parentClassName , $nsPos );
2015-08-17 17:00:26 -07:00
} else {
2018-11-23 12:29:20 +00:00
$prefix = strtolower ( $this -> parentClassName );
$postfix = '' ;
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
foreach ( $this -> useStatements as $alias => $use ) {
if ( $alias !== $prefix ) {
continue ;
}
$this -> parentClassName = '\\' . $use . $postfix ;
$fullySpecified = true ;
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
}
if ( ! $fullySpecified ) {
$this -> parentClassName = '\\' . $this -> namespace . '\\' . $this -> parentClassName ;
}
break ;
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
$last_token = $token [ 0 ];
2015-08-17 17:00:26 -07:00
}
}
/**
* @ return StaticReflectionParser
*/
protected function getParentStaticReflectionParser ()
{
if ( empty ( $this -> parentStaticReflectionParser )) {
$this -> parentStaticReflectionParser = new static ( $this -> parentClassName , $this -> finder );
}
return $this -> parentStaticReflectionParser ;
}
/**
* @ return string
*/
public function getClassName ()
{
return $this -> className ;
}
/**
* @ return string
*/
public function getNamespaceName ()
{
return $this -> namespace ;
}
/**
* { @ inheritDoc }
*/
public function getReflectionClass ()
{
return new StaticReflectionClass ( $this );
}
/**
* { @ inheritDoc }
*/
public function getReflectionMethod ( $methodName )
{
return new StaticReflectionMethod ( $this , $methodName );
}
/**
* { @ inheritDoc }
*/
public function getReflectionProperty ( $propertyName )
{
return new StaticReflectionProperty ( $this , $propertyName );
}
/**
* Gets the use statements from this file .
*
2018-11-23 12:29:20 +00:00
* @ return string []
2015-08-17 17:00:26 -07:00
*/
public function getUseStatements ()
{
$this -> parse ();
return $this -> useStatements ;
}
/**
* Gets the doc comment .
*
* @ param string $type The type : 'class' , 'property' or 'method' .
* @ param string $name The name of the property or method , not needed for 'class' .
*
* @ return string The doc comment , empty string if none .
*/
public function getDocComment ( $type = 'class' , $name = '' )
{
$this -> parse ();
return $name ? $this -> docComment [ $type ][ $name ] : $this -> docComment [ $type ];
}
/**
* Gets the PSR - 0 parser for the declaring class .
*
* @ param string $type The type : 'property' or 'method' .
* @ param string $name The name of the property or method .
*
* @ return StaticReflectionParser A static reflection parser for the declaring class .
*
* @ throws ReflectionException
*/
public function getStaticReflectionParserForDeclaringClass ( $type , $name )
{
$this -> parse ();
if ( isset ( $this -> docComment [ $type ][ $name ])) {
return $this ;
}
2018-11-23 12:29:20 +00:00
if ( ! empty ( $this -> parentClassName )) {
2015-08-17 17:00:26 -07:00
return $this -> getParentStaticReflectionParser () -> getStaticReflectionParserForDeclaringClass ( $type , $name );
}
throw new ReflectionException ( 'Invalid ' . $type . ' "' . $name . '"' );
}
}