- <?php
- /**
- * Zend Framework (http://framework.zend.com/)
- *
- * @link http://github.com/zendframework/zf2 for the canonical source repository
- * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
-
- namespace Zend\Code\Scanner;
-
- use Zend\Code\Annotation;
- use Zend\Code\Exception;
- use Zend\Code\NameInformation;
-
- class ClassScanner implements ScannerInterface
- {
- /**
- * @var bool
- */
- protected $isScanned = false;
-
- /**
- * @var string
- */
- protected $docComment = null;
-
- /**
- * @var string
- */
- protected $name = null;
-
- /**
- * @var string
- */
- protected $shortName = null;
-
- /**
- * @var int
- */
- protected $lineStart = null;
-
- /**
- * @var int
- */
- protected $lineEnd = null;
-
- /**
- * @var bool
- */
- protected $isFinal = false;
-
- /**
- * @var bool
- */
- protected $isAbstract = false;
-
- /**
- * @var bool
- */
- protected $isInterface = false;
-
- /**
- * @var string
- */
- protected $parentClass = null;
-
- /**
- * @var string
- */
- protected $shortParentClass = null;
-
- /**
- * @var array
- */
- protected $interfaces = array();
-
- /**
- * @var array
- */
- protected $shortInterfaces = array();
-
- /**
- * @var array
- */
- protected $tokens = array();
-
- /**
- * @var NameInformation
- */
- protected $nameInformation = null;
-
- /**
- * @var array
- */
- protected $infos = array();
-
- /**
- * @param array $classTokens
- * @param NameInformation|null $nameInformation
- * @return ClassScanner
- */
- public function __construct(array $classTokens, NameInformation $nameInformation = null)
- {
- $this->tokens = $classTokens;
- $this->nameInformation = $nameInformation;
- }
-
- /**
- * Get annotations
- *
- * @param Annotation\AnnotationManager $annotationManager
- * @return Annotation\AnnotationCollection
- */
- public function getAnnotations(Annotation\AnnotationManager $annotationManager)
- {
- if (($docComment = $this->getDocComment()) == '') {
- return false;
- }
-
- return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
- }
-
- /**
- * Return documentation comment
- *
- * @return null|string
- */
- public function getDocComment()
- {
- $this->scan();
-
- return $this->docComment;
- }
-
- /**
- * Return documentation block
- *
- * @return false|DocBlockScanner
- */
- public function getDocBlock()
- {
- if (!$docComment = $this->getDocComment()) {
- return false;
- }
-
- return new DocBlockScanner($docComment);
- }
-
- /**
- * Return a name of class
- *
- * @return null|string
- */
- public function getName()
- {
- $this->scan();
- return $this->name;
- }
-
- /**
- * Return short name of class
- *
- * @return null|string
- */
- public function getShortName()
- {
- $this->scan();
- return $this->shortName;
- }
-
- /**
- * Return number of first line
- *
- * @return int|null
- */
- public function getLineStart()
- {
- $this->scan();
- return $this->lineStart;
- }
-
- /**
- * Return number of last line
- *
- * @return int|null
- */
- public function getLineEnd()
- {
- $this->scan();
- return $this->lineEnd;
- }
-
- /**
- * Verify if class is final
- *
- * @return bool
- */
- public function isFinal()
- {
- $this->scan();
- return $this->isFinal;
- }
-
- /**
- * Verify if class is instantiable
- *
- * @return bool
- */
- public function isInstantiable()
- {
- $this->scan();
- return (!$this->isAbstract && !$this->isInterface);
- }
-
- /**
- * Verify if class is an abstract class
- *
- * @return bool
- */
- public function isAbstract()
- {
- $this->scan();
- return $this->isAbstract;
- }
-
- /**
- * Verify if class is an interface
- *
- * @return bool
- */
- public function isInterface()
- {
- $this->scan();
- return $this->isInterface;
- }
-
- /**
- * Verify if class has parent
- *
- * @return bool
- */
- public function hasParentClass()
- {
- $this->scan();
- return ($this->parentClass != null);
- }
-
- /**
- * Return a name of parent class
- *
- * @return null|string
- */
- public function getParentClass()
- {
- $this->scan();
- return $this->parentClass;
- }
-
- /**
- * Return a list of interface names
- *
- * @return array
- */
- public function getInterfaces()
- {
- $this->scan();
- return $this->interfaces;
- }
-
- /**
- * Return a list of constant names
- *
- * @return array
- */
- public function getConstantNames()
- {
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'constant') {
- continue;
- }
-
- $return[] = $info['name'];
- }
-
- return $return;
- }
-
- /**
- * Return a list of constants
- *
- * @param bool $namesOnly Set false to return instances of ConstantScanner
- * @return array|ConstantScanner[]
- */
- public function getConstants($namesOnly = true)
- {
- if (true === $namesOnly) {
- trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED);
- return $this->getConstantNames();
- }
-
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'constant') {
- continue;
- }
-
- $return[] = $this->getConstant($info['name']);
- }
-
- return $return;
- }
-
- /**
- * Return a single constant by given name or index of info
- *
- * @param string|int $constantNameOrInfoIndex
- * @throws Exception\InvalidArgumentException
- * @return bool|ConstantScanner
- */
- public function getConstant($constantNameOrInfoIndex)
- {
- $this->scan();
-
- if (is_int($constantNameOrInfoIndex)) {
- $info = $this->infos[$constantNameOrInfoIndex];
- if ($info['type'] != 'constant') {
- throw new Exception\InvalidArgumentException('Index of info offset is not about a constant');
- }
- } elseif (is_string($constantNameOrInfoIndex)) {
- $constantFound = false;
- foreach ($this->infos as $info) {
- if ($info['type'] === 'constant' && $info['name'] === $constantNameOrInfoIndex) {
- $constantFound = true;
- break;
- }
- }
- if (!$constantFound) {
- return false;
- }
- } else {
- throw new Exception\InvalidArgumentException('Invalid constant name of info index type. Must be of type int or string');
- }
- if (!isset($info)) {
- return false;
- }
- $p = new ConstantScanner(
- array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1),
- $this->nameInformation
- );
- $p->setClass($this->name);
- $p->setScannerClass($this);
- return $p;
- }
-
- /**
- * Verify if class has constant
- *
- * @param string $name
- * @return bool
- */
- public function hasConstant($name)
- {
- $this->scan();
-
- foreach ($this->infos as $info) {
- if ($info['type'] === 'constant' && $info['name'] === $name) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Return a list of property names
- *
- * @return array
- */
- public function getPropertyNames()
- {
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'property') {
- continue;
- }
-
- $return[] = $info['name'];
- }
-
- return $return;
- }
-
- /**
- * Return a list of properties
- *
- * @return PropertyScanner
- */
- public function getProperties()
- {
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'property') {
- continue;
- }
-
- $return[] = $this->getProperty($info['name']);
- }
-
- return $return;
- }
-
- /**
- * Return a single property by given name or index of info
- *
- * @param string|int $propertyNameOrInfoIndex
- * @throws Exception\InvalidArgumentException
- * @return bool|PropertyScanner
- */
- public function getProperty($propertyNameOrInfoIndex)
- {
- $this->scan();
-
- if (is_int($propertyNameOrInfoIndex)) {
- $info = $this->infos[$propertyNameOrInfoIndex];
- if ($info['type'] != 'property') {
- throw new Exception\InvalidArgumentException('Index of info offset is not about a property');
- }
- } elseif (is_string($propertyNameOrInfoIndex)) {
- $propertyFound = false;
- foreach ($this->infos as $info) {
- if ($info['type'] === 'property' && $info['name'] === $propertyNameOrInfoIndex) {
- $propertyFound = true;
- break;
- }
- }
- if (!$propertyFound) {
- return false;
- }
- } else {
- throw new Exception\InvalidArgumentException('Invalid property name of info index type. Must be of type int or string');
- }
- if (!isset($info)) {
- return false;
- }
- $p = new PropertyScanner(
- array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1),
- $this->nameInformation
- );
- $p->setClass($this->name);
- $p->setScannerClass($this);
- return $p;
- }
-
- /**
- * Verify if class has property
- *
- * @param string $name
- * @return bool
- */
- public function hasProperty($name)
- {
- $this->scan();
-
- foreach ($this->infos as $info) {
- if ($info['type'] === 'property' && $info['name'] === $name) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Return a list of method names
- *
- * @return array
- */
- public function getMethodNames()
- {
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'method') {
- continue;
- }
-
- $return[] = $info['name'];
- }
-
- return $return;
- }
-
- /**
- * Return a list of methods
- *
- * @return MethodScanner[]
- */
- public function getMethods()
- {
- $this->scan();
-
- $return = array();
- foreach ($this->infos as $info) {
- if ($info['type'] != 'method') {
- continue;
- }
-
- $return[] = $this->getMethod($info['name']);
- }
-
- return $return;
- }
-
- /**
- * Return a single method by given name or index of info
- *
- * @param string|int $methodNameOrInfoIndex
- * @throws Exception\InvalidArgumentException
- * @return MethodScanner
- */
- public function getMethod($methodNameOrInfoIndex)
- {
- $this->scan();
-
- if (is_int($methodNameOrInfoIndex)) {
- $info = $this->infos[$methodNameOrInfoIndex];
- if ($info['type'] != 'method') {
- throw new Exception\InvalidArgumentException('Index of info offset is not about a method');
- }
- } elseif (is_string($methodNameOrInfoIndex)) {
- $methodFound = false;
- foreach ($this->infos as $info) {
- if ($info['type'] === 'method' && $info['name'] === $methodNameOrInfoIndex) {
- $methodFound = true;
- break;
- }
- }
- if (!$methodFound) {
- return false;
- }
- }
- if (!isset($info)) {
- // @todo find a way to test this
- die('Massive Failure, test this');
- }
- $m = new MethodScanner(
- array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1),
- $this->nameInformation
- );
- $m->setClass($this->name);
- $m->setScannerClass($this);
-
- return $m;
- }
-
- /**
- * Verify if class has method by given name
- *
- * @param string $name
- * @return bool
- */
- public function hasMethod($name)
- {
- $this->scan();
-
- foreach ($this->infos as $info) {
- if ($info['type'] === 'method' && $info['name'] === $name) {
- return true;
- }
- }
-
- return false;
- }
-
- public static function export()
- {
- // @todo
- }
-
- public function __toString()
- {
- // @todo
- }
-
- /**
- * Scan tokens
- *
- * @return void
- * @throws Exception\RuntimeException
- */
- protected function scan()
- {
- if ($this->isScanned) {
- return;
- }
-
- if (!$this->tokens) {
- throw new Exception\RuntimeException('No tokens were provided');
- }
-
- /**
- * Variables & Setup
- */
-
- $tokens = &$this->tokens; // localize
- $infos = &$this->infos; // localize
- $tokenIndex = null;
- $token = null;
- $tokenType = null;
- $tokenContent = null;
- $tokenLine = null;
- $namespace = null;
- $infoIndex = 0;
- $braceCount = 0;
-
- /*
- * MACRO creation
- */
- $MACRO_TOKEN_ADVANCE = function () use (&$tokens, &$tokenIndex, &$token, &$tokenType, &$tokenContent, &$tokenLine) {
- static $lastTokenArray = null;
- $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1;
- if (!isset($tokens[$tokenIndex])) {
- $token = false;
- $tokenContent = false;
- $tokenType = false;
- $tokenLine = false;
-
- return false;
- }
- $token = $tokens[$tokenIndex];
-
- if (is_string($token)) {
- $tokenType = null;
- $tokenContent = $token;
- $tokenLine = $tokenLine + substr_count($lastTokenArray[1],
- "\n"); // adjust token line by last known newline count
- } else {
- $lastTokenArray = $token;
- list($tokenType, $tokenContent, $tokenLine) = $token;
- }
-
- return $tokenIndex;
- };
- $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
- $infos[$infoIndex]['tokenEnd'] = $tokenIndex;
- $infos[$infoIndex]['lineEnd'] = $tokenLine;
- $infoIndex++;
-
- return $infoIndex;
- };
-
- /**
- * START FINITE STATE MACHINE FOR SCANNING TOKENS
- */
-
- // Initialize token
- $MACRO_TOKEN_ADVANCE();
-
- SCANNER_TOP:
-
- switch ($tokenType) {
-
- case T_DOC_COMMENT:
-
- $this->docComment = $tokenContent;
- goto SCANNER_CONTINUE;
-
- case T_FINAL:
- case T_ABSTRACT:
- case T_CLASS:
- case T_INTERFACE:
-
- // CLASS INFORMATION
-
- $classContext = null;
- $classInterfaceIndex = 0;
-
- SCANNER_CLASS_INFO_TOP:
-
- if (is_string($tokens[$tokenIndex + 1]) && $tokens[$tokenIndex + 1] === '{') {
- goto SCANNER_CLASS_INFO_END;
- }
-
- $this->lineStart = $tokenLine;
-
- switch ($tokenType) {
-
- case T_FINAL:
- $this->isFinal = true;
- goto SCANNER_CLASS_INFO_CONTINUE;
-
- case T_ABSTRACT:
- $this->isAbstract = true;
- goto SCANNER_CLASS_INFO_CONTINUE;
-
- case T_INTERFACE:
- $this->isInterface = true;
- case T_CLASS:
- $this->shortName = $tokens[$tokenIndex + 2][1];
- if ($this->nameInformation && $this->nameInformation->hasNamespace()) {
- $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName;
- } else {
- $this->name = $this->shortName;
- }
- goto SCANNER_CLASS_INFO_CONTINUE;
-
- case T_NS_SEPARATOR:
- case T_STRING:
- switch ($classContext) {
- case T_EXTENDS:
- $this->shortParentClass .= $tokenContent;
- break;
- case T_IMPLEMENTS:
- $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent;
- break;
- }
- goto SCANNER_CLASS_INFO_CONTINUE;
-
- case T_EXTENDS:
- case T_IMPLEMENTS:
- $classContext = $tokenType;
- if (($this->isInterface && $classContext === T_EXTENDS) || $classContext === T_IMPLEMENTS) {
- $this->shortInterfaces[$classInterfaceIndex] = '';
- } elseif (!$this->isInterface && $classContext === T_EXTENDS) {
- $this->shortParentClass = '';
- }
- goto SCANNER_CLASS_INFO_CONTINUE;
-
- case null:
- if ($classContext == T_IMPLEMENTS && $tokenContent == ',') {
- $classInterfaceIndex++;
- $this->shortInterfaces[$classInterfaceIndex] = '';
- }
-
- }
-
- SCANNER_CLASS_INFO_CONTINUE:
-
- if ($MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_END;
- }
- goto SCANNER_CLASS_INFO_TOP;
-
- SCANNER_CLASS_INFO_END:
-
- goto SCANNER_CONTINUE;
-
- }
-
- if ($tokenType === null && $tokenContent === '{' && $braceCount === 0) {
-
- $braceCount++;
- if ($MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_END;
- }
-
- SCANNER_CLASS_BODY_TOP:
-
- if ($braceCount === 0) {
- goto SCANNER_CLASS_BODY_END;
- }
-
- switch ($tokenType) {
-
- case T_CONST:
-
- $infos[$infoIndex] = array(
- 'type' => 'constant',
- 'tokenStart' => $tokenIndex,
- 'tokenEnd' => null,
- 'lineStart' => $tokenLine,
- 'lineEnd' => null,
- 'name' => null,
- 'value' => null,
- );
-
- SCANNER_CLASS_BODY_CONST_TOP:
-
- if ($tokenContent === ';') {
- goto SCANNER_CLASS_BODY_CONST_END;
- }
-
- if ($tokenType === T_STRING && null === $infos[$infoIndex]['name']) {
- $infos[$infoIndex]['name'] = $tokenContent;
- }
-
- SCANNER_CLASS_BODY_CONST_CONTINUE:
-
- if ($MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_END;
- }
- goto SCANNER_CLASS_BODY_CONST_TOP;
-
- SCANNER_CLASS_BODY_CONST_END:
-
- $MACRO_INFO_ADVANCE();
- goto SCANNER_CLASS_BODY_CONTINUE;
-
- case T_DOC_COMMENT:
- case T_PUBLIC:
- case T_PROTECTED:
- case T_PRIVATE:
- case T_ABSTRACT:
- case T_FINAL:
- case T_VAR:
- case T_FUNCTION:
-
- $infos[$infoIndex] = array(
- 'type' => null,
- 'tokenStart' => $tokenIndex,
- 'tokenEnd' => null,
- 'lineStart' => $tokenLine,
- 'lineEnd' => null,
- 'name' => null,
- );
-
- $memberContext = null;
- $methodBodyStarted = false;
-
- SCANNER_CLASS_BODY_MEMBER_TOP:
-
- if ($memberContext === 'method') {
- switch ($tokenContent) {
- case '{':
- $methodBodyStarted = true;
- $braceCount++;
- goto SCANNER_CLASS_BODY_MEMBER_CONTINUE;
- case '}':
- $braceCount--;
- goto SCANNER_CLASS_BODY_MEMBER_CONTINUE;
- }
- }
-
- if ($memberContext !== null) {
- if (
- ($memberContext === 'property' && $tokenContent === ';')
- || ($memberContext === 'method' && $methodBodyStarted && $braceCount === 1)
- || ($memberContext === 'method' && $this->isInterface && $tokenContent === ';')
- ) {
- goto SCANNER_CLASS_BODY_MEMBER_END;
- }
- }
-
-
- switch ($tokenType) {
-
- case T_CONST:
- $memberContext = 'constant';
- $infos[$infoIndex]['type'] = 'constant';
- goto SCANNER_CLASS_BODY_CONST_CONTINUE;
-
- case T_VARIABLE:
- if ($memberContext === null) {
- $memberContext = 'property';
- $infos[$infoIndex]['type'] = 'property';
- $infos[$infoIndex]['name'] = ltrim($tokenContent, '$');
- }
- goto SCANNER_CLASS_BODY_MEMBER_CONTINUE;
-
- case T_FUNCTION:
- $memberContext = 'method';
- $infos[$infoIndex]['type'] = 'method';
- goto SCANNER_CLASS_BODY_MEMBER_CONTINUE;
-
- case T_STRING:
- if ($memberContext === 'method' && null === $infos[$infoIndex]['name']) {
- $infos[$infoIndex]['name'] = $tokenContent;
- }
- goto SCANNER_CLASS_BODY_MEMBER_CONTINUE;
- }
-
- SCANNER_CLASS_BODY_MEMBER_CONTINUE:
-
- if ($MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_END;
- }
- goto SCANNER_CLASS_BODY_MEMBER_TOP;
-
- SCANNER_CLASS_BODY_MEMBER_END:
-
- $memberContext = null;
- $MACRO_INFO_ADVANCE();
- goto SCANNER_CLASS_BODY_CONTINUE;
-
- case null: // no type, is a string
-
- switch ($tokenContent) {
- case '{':
- $braceCount++;
- goto SCANNER_CLASS_BODY_CONTINUE;
- case '}':
- $braceCount--;
- goto SCANNER_CLASS_BODY_CONTINUE;
- }
- }
-
- SCANNER_CLASS_BODY_CONTINUE:
-
- if ($braceCount === 0 || $MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_CONTINUE;
- }
- goto SCANNER_CLASS_BODY_TOP;
-
- SCANNER_CLASS_BODY_END:
-
- goto SCANNER_CONTINUE;
-
- }
-
- SCANNER_CONTINUE:
-
- if ($tokenContent === '}') {
- $this->lineEnd = $tokenLine;
- }
-
- if ($MACRO_TOKEN_ADVANCE() === false) {
- goto SCANNER_END;
- }
- goto SCANNER_TOP;
-
- SCANNER_END:
-
- // process short names
- if ($this->nameInformation) {
- if ($this->shortParentClass) {
- $this->parentClass = $this->nameInformation->resolveName($this->shortParentClass);
- }
- if ($this->shortInterfaces) {
- foreach ($this->shortInterfaces as $siIndex => $si) {
- $this->interfaces[$siIndex] = $this->nameInformation->resolveName($si);
- }
- }
- } else {
- $this->parentClass = $this->shortParentClass;
- $this->interfaces = $this->shortInterfaces;
- }
-
- $this->isScanned = true;
-
- return;
- }
- }
# |
Change |
User |
Description |
Committed |
|
#1
|
18334 |
Liz Lam |
initial add of jambox |
9 years ago
|
|