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