Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\Exception;
class AggregateDirectoryScanner extends DirectoryScanner
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @param bool $returnScannerClass
* @todo not implemented
*/
public function getNamespaces($returnScannerClass = false)
{
// @todo
}
public function getIncludes($returnScannerClass = false)
{
}
public function getClasses($returnScannerClass = false, $returnDerivedScannerClass = false)
{
$classes = [];
foreach ($this->directories as $scanner) {
$classes += $scanner->getClasses();
}
if ($returnScannerClass) {
foreach ($classes as $index => $class) {
$classes[$index] = $this->getClass($class, $returnScannerClass, $returnDerivedScannerClass);
}
}
return $classes;
}
/**
* @param string $class
* @return bool
*/
public function hasClass($class)
{
foreach ($this->directories as $scanner) {
if ($scanner->hasClass($class)) {
break;
} else {
unset($scanner);
}
}
return isset($scanner);
}
/**
* @param string $class
* @param bool $returnScannerClass
* @param bool $returnDerivedScannerClass
* @return ClassScanner|DerivedClassScanner
* @throws Exception\RuntimeException
*/
public function getClass($class, $returnScannerClass = true, $returnDerivedScannerClass = false)
{
foreach ($this->directories as $scanner) {
if ($scanner->hasClass($class)) {
break;
} else {
unset($scanner);
}
}
if (! isset($scanner)) {
throw new Exception\RuntimeException('Class by that name was not found.');
}
$classScanner = $scanner->getClass($class);
return new DerivedClassScanner($classScanner, $this);
}
/**
* @param bool $returnScannerClass
*/
public function getFunctions($returnScannerClass = false)
{
$this->scan();
if (! $returnScannerClass) {
$functions = [];
foreach ($this->infos as $info) {
if ($info['type'] == 'function') {
$functions[] = $info['name'];
}
}
return $functions;
}
$scannerClass = new FunctionScanner();
// @todo
}
}

View File

@@ -0,0 +1,379 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationCollection;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\NameInformation;
use function array_pop;
use function current;
use function in_array;
use function is_string;
use function next;
use function preg_match;
use function reset;
use function strlen;
use function strpos;
use function substr;
use function substr_count;
class AnnotationScanner extends AnnotationCollection implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var AnnotationManager
*/
protected $annotationManager;
/**
* @var array
*/
protected $annotations = [];
/**
* @param AnnotationManager $annotationManager
* @param string $docComment
* @param NameInformation $nameInformation
* @return AnnotationScanner
*/
public function __construct(
AnnotationManager $annotationManager,
$docComment,
NameInformation $nameInformation = null
) {
$this->annotationManager = $annotationManager;
$this->docComment = $docComment;
$this->nameInformation = $nameInformation;
$this->scan($this->tokenize());
}
/**
* @param NameInformation $nameInformation
*/
public function setNameInformation(NameInformation $nameInformation)
{
$this->nameInformation = $nameInformation;
}
/**
* @param array $tokens
*/
protected function scan(array $tokens)
{
$annotations = [];
$annotationIndex = -1;
$contentEnd = false;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
switch ($token[0]) {
case 'ANNOTATION_CLASS':
$contentEnd = false;
$annotationIndex++;
$class = substr($token[1], 1);
$class = $this->nameInformation->resolveName($class);
$annotations[$annotationIndex] = [$class, null];
goto SCANNER_CONTINUE;
// goto no break needed
case 'ANNOTATION_CONTENT_START':
$annotations[$annotationIndex][1] = '';
// fall-through
case 'ANNOTATION_CONTENT_END':
case 'ANNOTATION_CONTENT':
case 'ANNOTATION_WHITESPACE':
case 'ANNOTATION_NEWLINE':
if (! $contentEnd
&& isset($annotations[$annotationIndex])
&& is_string($annotations[$annotationIndex][1])
) {
$annotations[$annotationIndex][1] .= $token[1];
}
if ($token[0] === 'ANNOTATION_CONTENT_END') {
$contentEnd = true;
}
goto SCANNER_CONTINUE;
// goto no break needed
}
SCANNER_CONTINUE:
if (next($tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
foreach ($annotations as $annotation) {
$annotation[] = '@' . $annotation[0] . $annotation[1];
$annotationObject = $this->annotationManager->createAnnotation($annotation);
if ($annotationObject) {
$this->append($annotationObject);
}
}
}
/**
* @return array
*/
protected function tokenize()
{
static $CONTEXT_DOCBLOCK = 0x01;
static $CONTEXT_ASTERISK = 0x02;
static $CONTEXT_CLASS = 0x04;
static $CONTEXT_CONTENT = 0x08;
$context = 0x00;
$stream = $this->docComment;
$streamIndex = null;
$tokens = [];
$tokenIndex = null;
$currentChar = null;
$currentWord = null;
$currentLine = null;
$annotationParentCount = 0;
$MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (
&$stream,
&$streamIndex,
&$currentChar,
&$currentWord,
&$currentLine
) {
$positionsForward = $positionsForward > 0 ? $positionsForward : 1;
$streamIndex = $streamIndex === null ? 0 : $streamIndex + $positionsForward;
if (! isset($stream[$streamIndex])) {
$currentChar = false;
return false;
}
$currentChar = $stream[$streamIndex];
$matches = [];
$currentLine = preg_match('#(.*?)(?:\n|\r\n?)#', $stream, $matches, null, $streamIndex) === 1
? $matches[1]
: substr($stream, $streamIndex);
if ($currentChar === ' ') {
$currentWord = preg_match('#( +)#', $currentLine, $matches) === 1 ? $matches[1] : $currentLine;
} else {
$currentWord = ($matches = strpos($currentLine, ' ')) !== false
? substr($currentLine, 0, $matches)
: $currentLine;
}
return $currentChar;
};
$MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
};
$MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
};
$MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
$tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1;
$tokens[$tokenIndex] = ['ANNOTATION_UNKNOWN', ''];
};
$MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
$tokens[$tokenIndex][0] = $type;
};
$MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentChar;
};
$MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentWord;
};
$MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentLine;
};
$MACRO_HAS_CONTEXT = function ($which) use (&$context) {
return ($context & $which) === $which;
};
$MACRO_STREAM_ADVANCE_CHAR();
$MACRO_TOKEN_ADVANCE();
TOKENIZER_TOP:
if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTSTART');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context |= $CONTEXT_DOCBLOCK;
$context |= $CONTEXT_ASTERISK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_CLASS)) {
if (in_array($currentChar, [' ', '(', "\n", "\r"])) {
$context &= ~$CONTEXT_CLASS;
$MACRO_TOKEN_ADVANCE();
} else {
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
}
// Since we don't know what line endings are used in the file, we check for all scenarios. If we find a
// carriage return (\r), we check the next character for a line feed (\n). If so we consume it and act as
// if the carriage return was a line feed.
$lineEnded = $currentChar === "\n";
if ($currentChar === "\r") {
$lineEnded = true;
$nextChar = $MACRO_STREAM_ADVANCE_CHAR();
if ($nextChar !== "\n") {
$streamIndex--;
}
$currentChar = "\n";
}
if ($lineEnded) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_NEWLINE');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_ASTERISK;
$context &= ~$CONTEXT_CLASS;
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === ' ') {
$MACRO_TOKEN_SET_TYPE(
$MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)
? 'ANNOTATION_WHITESPACE'
: 'ANNOTATION_WHITESPACE_INDENT'
);
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_CONTENT) && $MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT');
$annotationParentCount += substr_count($currentWord, '(');
$annotationParentCount -= substr_count($currentWord, ')');
if ($annotationParentCount === 0) {
$context &= ~$CONTEXT_CONTENT;
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_END');
}
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '(' && $tokens[$tokenIndex - 1][0] === 'ANNOTATION_CLASS') {
$context |= $CONTEXT_CONTENT;
$annotationParentCount = 1;
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_START');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && $currentWord === '*/') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTEND');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_DOCBLOCK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '*') {
if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && ($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK))) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
} else {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_ASTERISK');
$context |= $CONTEXT_ASTERISK;
}
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '@') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CLASS');
$context |= $CONTEXT_CLASS;
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
TOKENIZER_CONTINUE:
if ($context && $CONTEXT_CONTENT) {
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
} else {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
$MACRO_TOKEN_APPEND_LINE();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_LINE() === false) {
goto TOKENIZER_END;
}
}
goto TOKENIZER_TOP;
TOKENIZER_END:
array_pop($tokens);
return $tokens;
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
use function file_exists;
use function md5;
use function realpath;
use function spl_object_hash;
use function sprintf;
class CachingFileScanner extends FileScanner
{
/**
* @var array
*/
protected static $cache = [];
/**
* @var null|FileScanner
*/
protected $fileScanner;
/**
* @param string $file
* @param AnnotationManager $annotationManager
* @throws Exception\InvalidArgumentException
*/
public function __construct($file, AnnotationManager $annotationManager = null)
{
if (! file_exists($file)) {
throw new Exception\InvalidArgumentException(sprintf(
'File "%s" not found',
$file
));
}
$file = realpath($file);
$cacheId = md5($file) . '/' . (isset($annotationManager)
? spl_object_hash($annotationManager)
: 'no-annotation');
if (isset(static::$cache[$cacheId])) {
$this->fileScanner = static::$cache[$cacheId];
} else {
$this->fileScanner = new FileScanner($file, $annotationManager);
static::$cache[$cacheId] = $this->fileScanner;
}
}
/**
* @return void
*/
public static function clearCache()
{
static::$cache = [];
}
/**
* @return AnnotationManager
*/
public function getAnnotationManager()
{
return $this->fileScanner->getAnnotationManager();
}
/**
* @return array|null|string
*/
public function getFile()
{
return $this->fileScanner->getFile();
}
/**
* @return null|string
*/
public function getDocComment()
{
return $this->fileScanner->getDocComment();
}
/**
* @return array
*/
public function getNamespaces()
{
return $this->fileScanner->getNamespaces();
}
/**
* @param null|string $namespace
* @return array|null
*/
public function getUses($namespace = null)
{
return $this->fileScanner->getUses($namespace);
}
/**
* @return array
*/
public function getIncludes()
{
return $this->fileScanner->getIncludes();
}
/**
* @return array
*/
public function getClassNames()
{
return $this->fileScanner->getClassNames();
}
/**
* @return array
*/
public function getClasses()
{
return $this->fileScanner->getClasses();
}
/**
* @param int|string $className
* @return ClassScanner
*/
public function getClass($className)
{
return $this->fileScanner->getClass($className);
}
/**
* @param string $className
* @return bool|null|NameInformation
*/
public function getClassNameInformation($className)
{
return $this->fileScanner->getClassNameInformation($className);
}
/**
* @return array
*/
public function getFunctionNames()
{
return $this->fileScanner->getFunctionNames();
}
/**
* @return array
*/
public function getFunctions()
{
return $this->fileScanner->getFunctions();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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;
use function current;
use function is_string;
use function next;
use function reset;
use function strtolower;
use function substr;
use function strpos;
use function var_export;
class ConstantScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var array
*/
protected $tokens;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var string
*/
protected $class;
/**
* @var ClassScanner
*/
protected $scannerClass;
/**
* @var int
*/
protected $lineStart;
/**
* @var string
*/
protected $docComment;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* Constructor
*
* @param array $constantTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $constantTokens, NameInformation $nameInformation = null)
{
$this->tokens = $constantTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* @param ClassScanner $scannerClass
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
}
/**
* @return ClassScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return string
*/
public function getValue()
{
$this->scan();
return $this->value;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param Annotation\AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(Annotation\AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return string
*/
public function __toString()
{
$this->scan();
return var_export($this, true);
}
/**
* Scan tokens
*
* @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;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
if (! is_string($token)) {
list($tokenType, $tokenContent, $tokenLine) = $token;
switch ($tokenType) {
case T_DOC_COMMENT:
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
goto SCANNER_CONTINUE;
// fall-through
case T_STRING:
$string = is_string($token) ? $token : $tokenContent;
if (null === $this->name) {
$this->name = $string;
} else {
if ('self' == strtolower($string)) {
list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens);
if ('::' == $tokenNextContent) {
list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens);
if ($this->getClassScanner()->getConstant($tokenNextContent)) {
$this->value = $this->getClassScanner()->getConstant($tokenNextContent)->getValue();
}
}
}
}
goto SCANNER_CONTINUE;
// fall-through
case T_CONSTANT_ENCAPSED_STRING:
case T_DNUMBER:
case T_LNUMBER:
$string = is_string($token) ? $token : $tokenContent;
if (0 === strpos($string, '"') || 0 === strpos($string, "'")) {
$this->value = substr($string, 1, -1); // Remove quotes
} else {
$this->value = $string;
}
goto SCANNER_CONTINUE;
// fall-trough
default:
goto SCANNER_CONTINUE;
}
}
SCANNER_CONTINUE:
if (next($this->tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
$this->isScanned = true;
}
}

View File

@@ -0,0 +1,386 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\Exception;
use function array_keys;
use function array_merge;
use function sprintf;
use function trigger_error;
class DerivedClassScanner extends ClassScanner
{
/**
* @var DirectoryScanner
*/
protected $directoryScanner;
/**
* @var ClassScanner
*/
protected $classScanner;
/**
* @var array
*/
protected $parentClassScanners = [];
/**
* @var array
*/
protected $interfaceClassScanners = [];
/**
* @param ClassScanner $classScanner
* @param DirectoryScanner $directoryScanner
*/
public function __construct(ClassScanner $classScanner, DirectoryScanner $directoryScanner)
{
$this->classScanner = $classScanner;
$this->directoryScanner = $directoryScanner;
$currentScannerClass = $classScanner;
while ($currentScannerClass && $currentScannerClass->hasParentClass()) {
$currentParentClassName = $currentScannerClass->getParentClass();
if ($directoryScanner->hasClass($currentParentClassName)) {
$currentParentClass = $directoryScanner->getClass($currentParentClassName);
$this->parentClassScanners[$currentParentClassName] = $currentParentClass;
$currentScannerClass = $currentParentClass;
} else {
$currentScannerClass = false;
}
}
foreach ($interfaces = $this->classScanner->getInterfaces() as $iName) {
if ($directoryScanner->hasClass($iName)) {
$this->interfaceClassScanners[$iName] = $directoryScanner->getClass($iName);
}
}
}
/**
* @return null|string
*/
public function getName()
{
return $this->classScanner->getName();
}
/**
* @return null|string
*/
public function getShortName()
{
return $this->classScanner->getShortName();
}
/**
* @return bool
*/
public function isInstantiable()
{
return $this->classScanner->isInstantiable();
}
/**
* @return bool
*/
public function isFinal()
{
return $this->classScanner->isFinal();
}
/**
* @return bool
*/
public function isAbstract()
{
return $this->classScanner->isAbstract();
}
/**
* @return bool
*/
public function isInterface()
{
return $this->classScanner->isInterface();
}
/**
* @return array
*/
public function getParentClasses()
{
return array_keys($this->parentClassScanners);
}
/**
* @return bool
*/
public function hasParentClass()
{
return $this->classScanner->getParentClass() !== null;
}
/**
* @return null|string
*/
public function getParentClass()
{
return $this->classScanner->getParentClass();
}
/**
* @param bool $returnClassScanners
* @return array
*/
public function getInterfaces($returnClassScanners = false)
{
if ($returnClassScanners) {
return $this->interfaceClassScanners;
}
$interfaces = $this->classScanner->getInterfaces();
foreach ($this->parentClassScanners as $pClassScanner) {
$interfaces = array_merge($interfaces, $pClassScanner->getInterfaces());
}
return $interfaces;
}
/**
* Return a list of constant names
*
* @return array
*/
public function getConstantNames()
{
$constants = $this->classScanner->getConstantNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$constants = array_merge($constants, $pClassScanner->getConstantNames());
}
return $constants;
}
/**
* 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();
}
$constants = $this->classScanner->getConstants();
foreach ($this->parentClassScanners as $pClassScanner) {
$constants = array_merge($constants, $pClassScanner->getConstants($namesOnly));
}
return $constants;
}
/**
* 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)
{
if ($this->classScanner->hasConstant($constantNameOrInfoIndex)) {
return $this->classScanner->getConstant($constantNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasConstant($constantNameOrInfoIndex)) {
return $pClassScanner->getConstant($constantNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Constant %s not found in %s',
$constantNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has constant
*
* @param string $name
* @return bool
*/
public function hasConstant($name)
{
if ($this->classScanner->hasConstant($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasConstant($name)) {
return true;
}
}
return false;
}
/**
* Return a list of property names
*
* @return array
*/
public function getPropertyNames()
{
$properties = $this->classScanner->getPropertyNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$properties = array_merge($properties, $pClassScanner->getPropertyNames());
}
return $properties;
}
/**
* @param bool $returnScannerProperty
* @return array
*/
public function getProperties($returnScannerProperty = false)
{
$properties = $this->classScanner->getProperties($returnScannerProperty);
foreach ($this->parentClassScanners as $pClassScanner) {
$properties = array_merge($properties, $pClassScanner->getProperties($returnScannerProperty));
}
return $properties;
}
/**
* 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)
{
if ($this->classScanner->hasProperty($propertyNameOrInfoIndex)) {
return $this->classScanner->getProperty($propertyNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasProperty($propertyNameOrInfoIndex)) {
return $pClassScanner->getProperty($propertyNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Property %s not found in %s',
$propertyNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has property
*
* @param string $name
* @return bool
*/
public function hasProperty($name)
{
if ($this->classScanner->hasProperty($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasProperty($name)) {
return true;
}
}
return false;
}
/**
* @return array
*/
public function getMethodNames()
{
$methods = $this->classScanner->getMethodNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$methods = array_merge($methods, $pClassScanner->getMethodNames());
}
return $methods;
}
/**
* @return MethodScanner[]
*/
public function getMethods()
{
$methods = $this->classScanner->getMethods();
foreach ($this->parentClassScanners as $pClassScanner) {
$methods = array_merge($methods, $pClassScanner->getMethods());
}
return $methods;
}
/**
* @param int|string $methodNameOrInfoIndex
* @return MethodScanner
* @throws Exception\InvalidArgumentException
*/
public function getMethod($methodNameOrInfoIndex)
{
if ($this->classScanner->hasMethod($methodNameOrInfoIndex)) {
return $this->classScanner->getMethod($methodNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasMethod($methodNameOrInfoIndex)) {
return $pClassScanner->getMethod($methodNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Method %s not found in %s',
$methodNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has method by given name
*
* @param string $name
* @return bool
*/
public function hasMethod($name)
{
if ($this->classScanner->hasMethod($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasMethod($name)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,282 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Zend\Code\Exception;
use function array_keys;
use function array_merge;
use function is_array;
use function is_dir;
use function is_string;
use function pathinfo;
use function realpath;
use function sprintf;
class DirectoryScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string[]|DirectoryScanner[]
*/
protected $directories = [];
/**
* @var FileScanner[]
*/
protected $fileScanners = [];
/**
* @var array
*/
protected $classToFileScanner;
/**
* @param null|string|array $directory
*/
public function __construct($directory = null)
{
if ($directory) {
if (is_string($directory)) {
$this->addDirectory($directory);
} elseif (is_array($directory)) {
foreach ($directory as $d) {
$this->addDirectory($d);
}
}
}
}
/**
* @param DirectoryScanner|string $directory
* @return void
* @throws Exception\InvalidArgumentException
*/
public function addDirectory($directory)
{
if ($directory instanceof DirectoryScanner) {
$this->directories[] = $directory;
} elseif (is_string($directory)) {
$realDir = realpath($directory);
if (! $realDir || ! is_dir($realDir)) {
throw new Exception\InvalidArgumentException(sprintf(
'Directory "%s" does not exist',
$realDir
));
}
$this->directories[] = $realDir;
} else {
throw new Exception\InvalidArgumentException(
'The argument provided was neither a DirectoryScanner or directory path'
);
}
}
/**
* @param DirectoryScanner $directoryScanner
* @return void
*/
public function addDirectoryScanner(DirectoryScanner $directoryScanner)
{
$this->addDirectory($directoryScanner);
}
/**
* @param FileScanner $fileScanner
* @return void
*/
public function addFileScanner(FileScanner $fileScanner)
{
$this->fileScanners[] = $fileScanner;
}
/**
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
// iterate directories creating file scanners
foreach ($this->directories as $directory) {
if ($directory instanceof DirectoryScanner) {
$directory->scan();
if ($directory->fileScanners) {
$this->fileScanners = array_merge($this->fileScanners, $directory->fileScanners);
}
} else {
$rdi = new RecursiveDirectoryIterator($directory);
foreach (new RecursiveIteratorIterator($rdi) as $item) {
if ($item->isFile() && pathinfo($item->getRealPath(), PATHINFO_EXTENSION) == 'php') {
$this->fileScanners[] = new FileScanner($item->getRealPath());
}
}
}
}
$this->isScanned = true;
}
/**
* @todo implement method
*/
public function getNamespaces()
{
// @todo
}
/**
* @param bool $returnFileScanners
* @return array
*/
public function getFiles($returnFileScanners = false)
{
$this->scan();
$return = [];
foreach ($this->fileScanners as $fileScanner) {
$return[] = $returnFileScanners ? $fileScanner : $fileScanner->getFile();
}
return $return;
}
/**
* @return array
*/
public function getClassNames()
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
return array_keys($this->classToFileScanner);
}
/**
* @param bool $returnDerivedScannerClass
* @return array
*/
public function getClasses($returnDerivedScannerClass = false)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
$returnClasses = [];
foreach ($this->classToFileScanner as $className => $fsIndex) {
$classScanner = $this->fileScanners[$fsIndex]->getClass($className);
if ($returnDerivedScannerClass) {
$classScanner = new DerivedClassScanner($classScanner, $this);
}
$returnClasses[] = $classScanner;
}
return $returnClasses;
}
/**
* @param string $class
* @return bool
*/
public function hasClass($class)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
return isset($this->classToFileScanner[$class]);
}
/**
* @param string $class
* @param bool $returnDerivedScannerClass
* @return ClassScanner|DerivedClassScanner
* @throws Exception\InvalidArgumentException
*/
public function getClass($class, $returnDerivedScannerClass = false)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
if (! isset($this->classToFileScanner[$class])) {
throw new Exception\InvalidArgumentException('Class not found.');
}
/** @var FileScanner $fs */
$fs = $this->fileScanners[$this->classToFileScanner[$class]];
$returnClass = $fs->getClass($class);
if ($returnClass instanceof ClassScanner && $returnDerivedScannerClass) {
return new DerivedClassScanner($returnClass, $this);
}
return $returnClass;
}
/**
* Create class to file scanner cache
*
* @return void
*/
protected function createClassToFileScannerCache()
{
if ($this->classToFileScanner !== null) {
return;
}
$this->classToFileScanner = [];
/** @var FileScanner $fileScanner */
foreach ($this->fileScanners as $fsIndex => $fileScanner) {
$fsClasses = $fileScanner->getClassNames();
foreach ($fsClasses as $fsClassName) {
$this->classToFileScanner[$fsClassName] = $fsIndex;
}
}
}
/**
* Export
*
* @todo implement method
*/
public static function export()
{
// @todo
}
/**
* __ToString
*
* @todo implement method
*/
public function __toString()
{
// @todo
}
}

View File

@@ -0,0 +1,356 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationManager;
use Zend\Code\NameInformation;
use function array_pop;
use function array_push;
use function current;
use function end;
use function key;
use function next;
use function preg_match;
use function reset;
use function strlen;
use function strpos;
use function substr;
use function trim;
class DocBlockScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var AnnotationManager
*/
protected $annotationManager;
/**
* @var string
*/
protected $shortDescription;
/**
* @var string
*/
protected $longDescription = '';
/**
* @var array
*/
protected $tags = [];
/**
* @var array
*/
protected $annotations = [];
/**
* @param string $docComment
* @param null|NameInformation $nameInformation
*/
public function __construct($docComment, NameInformation $nameInformation = null)
{
$this->docComment = $docComment;
$this->nameInformation = $nameInformation;
}
/**
* @return string
*/
public function getShortDescription()
{
$this->scan();
return $this->shortDescription;
}
/**
* @return string
*/
public function getLongDescription()
{
$this->scan();
return $this->longDescription;
}
/**
* @return array
*/
public function getTags()
{
$this->scan();
return $this->tags;
}
/**
* @return array
*/
public function getAnnotations()
{
$this->scan();
return $this->annotations;
}
/**
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
$mode = 1;
$tokens = $this->tokenize();
$tagIndex = null;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
switch ($token[0]) {
case 'DOCBLOCK_NEWLINE':
if ($this->shortDescription != '' && $tagIndex === null) {
$mode = 2;
} else {
$this->longDescription .= $token[1];
}
goto SCANNER_CONTINUE;
//goto no break needed
case 'DOCBLOCK_WHITESPACE':
case 'DOCBLOCK_TEXT':
if ($tagIndex !== null) {
$this->tags[$tagIndex]['value'] .= $this->tags[$tagIndex]['value'] == ''
? $token[1]
: ' ' . $token[1];
goto SCANNER_CONTINUE;
} elseif ($mode <= 2) {
if ($mode == 1) {
$this->shortDescription .= $token[1];
} else {
$this->longDescription .= $token[1];
}
goto SCANNER_CONTINUE;
}
//gotos no break needed
case 'DOCBLOCK_TAG':
array_push($this->tags, [
'name' => $token[1],
'value' => '',
]);
end($this->tags);
$tagIndex = key($this->tags);
$mode = 3;
goto SCANNER_CONTINUE;
//goto no break needed
case 'DOCBLOCK_COMMENTEND':
goto SCANNER_END;
}
SCANNER_CONTINUE:
if (next($tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
$this->shortDescription = trim($this->shortDescription);
$this->longDescription = trim($this->longDescription);
$this->isScanned = true;
}
/**
* @return array
*/
protected function tokenize()
{
static $CONTEXT_INSIDE_DOCBLOCK = 0x01;
static $CONTEXT_INSIDE_ASTERISK = 0x02;
$context = 0x00;
$stream = $this->docComment;
$streamIndex = null;
$tokens = [];
$tokenIndex = null;
$currentChar = null;
$currentWord = null;
$currentLine = null;
$MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (
&$stream,
&$streamIndex,
&$currentChar,
&$currentWord,
&$currentLine
) {
$positionsForward = $positionsForward > 0 ? $positionsForward : 1;
$streamIndex = $streamIndex === null ? 0 : $streamIndex + $positionsForward;
if (! isset($stream[$streamIndex])) {
$currentChar = false;
return false;
}
$currentChar = $stream[$streamIndex];
$matches = [];
$currentLine = preg_match('#(.*?)\r?\n#', $stream, $matches, null, $streamIndex) === 1
? $matches[1]
: substr($stream, $streamIndex);
if ($currentChar === ' ') {
$currentWord = preg_match('#( +)#', $currentLine, $matches) === 1 ? $matches[1] : $currentLine;
} else {
$currentWord = ($matches = strpos($currentLine, ' ')) !== false
? substr($currentLine, 0, $matches)
: $currentLine;
}
return $currentChar;
};
$MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
};
$MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
};
$MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
$tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1;
$tokens[$tokenIndex] = ['DOCBLOCK_UNKNOWN', ''];
};
$MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
$tokens[$tokenIndex][0] = $type;
};
$MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentChar;
};
$MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentWord;
};
$MACRO_TOKEN_APPEND_WORD_PARTIAL = function ($length) use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= substr($currentWord, 0, $length);
};
$MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentLine;
};
$MACRO_STREAM_ADVANCE_CHAR();
$MACRO_TOKEN_ADVANCE();
TOKENIZER_TOP:
if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTSTART');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context |= $CONTEXT_INSIDE_DOCBLOCK;
$context |= $CONTEXT_INSIDE_ASTERISK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($context & $CONTEXT_INSIDE_DOCBLOCK && $currentWord === '*/') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTEND');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_INSIDE_DOCBLOCK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === ' ' || $currentChar === "\t") {
$MACRO_TOKEN_SET_TYPE(
$context & $CONTEXT_INSIDE_ASTERISK
? 'DOCBLOCK_WHITESPACE'
: 'DOCBLOCK_WHITESPACE_INDENT'
);
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '*') {
if (($context & $CONTEXT_INSIDE_DOCBLOCK) && ($context & $CONTEXT_INSIDE_ASTERISK)) {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
} else {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_ASTERISK');
$context |= $CONTEXT_INSIDE_ASTERISK;
}
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '@') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TAG');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === "\n") {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_NEWLINE');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_INSIDE_ASTERISK;
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
$MACRO_TOKEN_APPEND_LINE();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_LINE() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
TOKENIZER_END:
array_pop($tokens);
return $tokens;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationManager;
use Zend\Code\Exception;
use function file_exists;
use function file_get_contents;
use function sprintf;
use function token_get_all;
class FileScanner extends TokenArrayScanner implements ScannerInterface
{
/**
* @var string
*/
protected $file;
/**
* @param string $file
* @param null|AnnotationManager $annotationManager
* @throws Exception\InvalidArgumentException
*/
public function __construct($file, AnnotationManager $annotationManager = null)
{
$this->file = $file;
if (! file_exists($file)) {
throw new Exception\InvalidArgumentException(sprintf(
'File "%s" not found',
$file
));
}
parent::__construct(token_get_all(file_get_contents($file)), $annotationManager);
}
/**
* @return string
*/
public function getFile()
{
return $this->file;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
class FunctionScanner
{
// @todo
// Should this extend something similar to MethodScanner? Similar to ReflectionFunctionAbstract
}

View File

@@ -0,0 +1,618 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
use function array_slice;
use function count;
use function is_int;
use function is_string;
use function ltrim;
use function strtolower;
use function substr_count;
use function var_export;
class MethodScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment;
/**
* @var ClassScanner
*/
protected $scannerClass;
/**
* @var string
*/
protected $class;
/**
* @var string
*/
protected $name;
/**
* @var int
*/
protected $lineStart;
/**
* @var int
*/
protected $lineEnd;
/**
* @var bool
*/
protected $isFinal = false;
/**
* @var bool
*/
protected $isAbstract = false;
/**
* @var bool
*/
protected $isPublic = true;
/**
* @var bool
*/
protected $isProtected = false;
/**
* @var bool
*/
protected $isPrivate = false;
/**
* @var bool
*/
protected $isStatic = false;
/**
* @var string
*/
protected $body = '';
/**
* @var array
*/
protected $tokens = [];
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var array
*/
protected $infos = [];
/**
* @param array $methodTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $methodTokens, NameInformation $nameInformation = null)
{
$this->tokens = $methodTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
* @return MethodScanner
*/
public function setClass($class)
{
$this->class = (string) $class;
return $this;
}
/**
* @param ClassScanner $scannerClass
* @return MethodScanner
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
return $this;
}
/**
* @return ClassScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return int
*/
public function getLineStart()
{
$this->scan();
return $this->lineStart;
}
/**
* @return int
*/
public function getLineEnd()
{
$this->scan();
return $this->lineEnd;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param AnnotationManager $annotationManager
* @return AnnotationScanner|false
*/
public function getAnnotations(AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return bool
*/
public function isFinal()
{
$this->scan();
return $this->isFinal;
}
/**
* @return bool
*/
public function isAbstract()
{
$this->scan();
return $this->isAbstract;
}
/**
* @return bool
*/
public function isPublic()
{
$this->scan();
return $this->isPublic;
}
/**
* @return bool
*/
public function isProtected()
{
$this->scan();
return $this->isProtected;
}
/**
* @return bool
*/
public function isPrivate()
{
$this->scan();
return $this->isPrivate;
}
/**
* @return bool
*/
public function isStatic()
{
$this->scan();
return $this->isStatic;
}
/**
* Override the given name for a method, this is necessary to
* support traits.
*
* @param string $name
* @return self
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Visibility must be of T_PUBLIC, T_PRIVATE or T_PROTECTED
* Needed to support traits
*
* @param int $visibility T_PUBLIC | T_PRIVATE | T_PROTECTED
* @return self
* @throws \Zend\Code\Exception\InvalidArgumentException
*/
public function setVisibility($visibility)
{
switch ($visibility) {
case T_PUBLIC:
$this->isPublic = true;
$this->isPrivate = false;
$this->isProtected = false;
break;
case T_PRIVATE:
$this->isPublic = false;
$this->isPrivate = true;
$this->isProtected = false;
break;
case T_PROTECTED:
$this->isPublic = false;
$this->isPrivate = false;
$this->isProtected = true;
break;
default:
throw new Exception\InvalidArgumentException('Invalid visibility argument passed to setVisibility.');
}
return $this;
}
/**
* @return int
*/
public function getNumberOfParameters()
{
return count($this->getParameters());
}
/**
* @param bool $returnScanner
* @return array
*/
public function getParameters($returnScanner = false)
{
$this->scan();
$return = [];
foreach ($this->infos as $info) {
if ($info['type'] != 'parameter') {
continue;
}
if (! $returnScanner) {
$return[] = $info['name'];
} else {
$return[] = $this->getParameter($info['name']);
}
}
return $return;
}
/**
* @param int|string $parameterNameOrInfoIndex
* @return ParameterScanner
* @throws Exception\InvalidArgumentException
*/
public function getParameter($parameterNameOrInfoIndex)
{
$this->scan();
if (is_int($parameterNameOrInfoIndex)) {
$info = $this->infos[$parameterNameOrInfoIndex];
if ($info['type'] != 'parameter') {
throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter');
}
} elseif (is_string($parameterNameOrInfoIndex)) {
foreach ($this->infos as $info) {
if ($info['type'] === 'parameter' && $info['name'] === $parameterNameOrInfoIndex) {
break;
}
unset($info);
}
if (! isset($info)) {
throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter');
}
}
$p = new ParameterScanner(
array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart']),
$this->nameInformation
);
$p->setDeclaringFunction($this->name);
$p->setDeclaringScannerFunction($this);
$p->setDeclaringClass($this->class);
$p->setDeclaringScannerClass($this->scannerClass);
$p->setPosition($info['position']);
return $p;
}
/**
* @return string
*/
public function getBody()
{
$this->scan();
return $this->body;
}
public static function export()
{
// @todo
}
public function __toString()
{
$this->scan();
return var_export($this, true);
}
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;
$infoIndex = 0;
$parentCount = 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 += substr_count(
$lastTokenArray[1] ?? '',
"\n"
); // adjust token line by last known newline count
} else {
$lastTokenArray = $token;
[$tokenType, $tokenContent, $tokenLine] = $token;
}
return $tokenIndex;
};
$MACRO_INFO_START = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
$infos[$infoIndex] = [
'type' => 'parameter',
'tokenStart' => $tokenIndex,
'tokenEnd' => null,
'lineStart' => $tokenLine,
'lineEnd' => $tokenLine,
'name' => null,
'position' => $infoIndex + 1, // position is +1 of infoIndex
];
};
$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:
$this->lineStart = $this->lineStart ? : $tokenLine;
switch ($tokenType) {
case T_DOC_COMMENT:
$this->lineStart = null;
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_FINAL:
$this->isFinal = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_ABSTRACT:
$this->isAbstract = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PUBLIC:
// use defaults
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PROTECTED:
$this->setVisibility(T_PROTECTED);
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PRIVATE:
$this->setVisibility(T_PRIVATE);
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_STATIC:
$this->isStatic = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_NS_SEPARATOR:
if (! isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_VARIABLE:
case T_STRING:
if ($tokenType === T_STRING && $parentCount === 0) {
$this->name = $tokenContent;
}
if ($parentCount === 1) {
if (! isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
if ($tokenType === T_VARIABLE) {
$infos[$infoIndex]['name'] = ltrim($tokenContent, '$');
}
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case null:
switch ($tokenContent) {
case '&':
if (! isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case '(':
$parentCount++;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case ')':
$parentCount--;
if ($parentCount > 0) {
goto SCANNER_CONTINUE_SIGNATURE;
}
if ($parentCount === 0) {
if ($infos) {
$MACRO_INFO_ADVANCE();
}
$context = 'body';
}
goto SCANNER_CONTINUE_BODY;
// goto (no break needed);
case ',':
if ($parentCount === 1) {
$MACRO_INFO_ADVANCE();
}
goto SCANNER_CONTINUE_SIGNATURE;
}
}
SCANNER_CONTINUE_SIGNATURE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_CONTINUE_BODY:
$braceCount = 0;
while ($MACRO_TOKEN_ADVANCE() !== false) {
if ($tokenContent == '}') {
$braceCount--;
}
if ($braceCount > 0) {
$this->body .= $tokenContent;
}
if ($tokenContent == '{') {
$braceCount++;
}
$this->lineEnd = $tokenLine;
}
SCANNER_END:
$this->isScanned = true;
}
}

View File

@@ -0,0 +1,359 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\NameInformation;
use function current;
use function is_string;
use function ltrim;
use function next;
use function reset;
use function trim;
class ParameterScanner
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var null|ClassScanner
*/
protected $declaringScannerClass;
/**
* @var null|string
*/
protected $declaringClass;
/**
* @var null|MethodScanner
*/
protected $declaringScannerFunction;
/**
* @var null|string
*/
protected $declaringFunction;
/**
* @var null|string
*/
protected $defaultValue;
/**
* @var null|string
*/
protected $class;
/**
* @var null|string
*/
protected $name;
/**
* @var null|int
*/
protected $position;
/**
* @var bool
*/
protected $isArray = false;
/**
* @var bool
*/
protected $isDefaultValueAvailable = false;
/**
* @var bool
*/
protected $isOptional = false;
/**
* @var bool
*/
protected $isPassedByReference = false;
/**
* @var array|null
*/
protected $tokens;
/**
* @var null|NameInformation
*/
protected $nameInformation;
/**
* @param array $parameterTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $parameterTokens, NameInformation $nameInformation = null)
{
$this->tokens = $parameterTokens;
$this->nameInformation = $nameInformation;
}
/**
* Set declaring class
*
* @param string $class
* @return void
*/
public function setDeclaringClass($class)
{
$this->declaringClass = (string) $class;
}
/**
* Set declaring scanner class
*
* @param ClassScanner $scannerClass
* @return void
*/
public function setDeclaringScannerClass(ClassScanner $scannerClass)
{
$this->declaringScannerClass = $scannerClass;
}
/**
* Set declaring function
*
* @param string $function
* @return void
*/
public function setDeclaringFunction($function)
{
$this->declaringFunction = $function;
}
/**
* Set declaring scanner function
*
* @param MethodScanner $scannerFunction
* @return void
*/
public function setDeclaringScannerFunction(MethodScanner $scannerFunction)
{
$this->declaringScannerFunction = $scannerFunction;
}
/**
* Set position
*
* @param int $position
* @return void
*/
public function setPosition($position)
{
$this->position = $position;
}
/**
* Scan
*
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
$tokens = &$this->tokens;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
if (is_string($token)) {
// check pass by ref
if ($token === '&') {
$this->isPassedByReference = true;
goto SCANNER_CONTINUE;
}
if ($token === '=') {
$this->isOptional = true;
$this->isDefaultValueAvailable = true;
goto SCANNER_CONTINUE;
}
} else {
if ($this->name === null && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
$this->class .= $token[1];
goto SCANNER_CONTINUE;
}
if ($token[0] === T_VARIABLE) {
$this->name = ltrim($token[1], '$');
goto SCANNER_CONTINUE;
}
}
if ($this->name !== null) {
$this->defaultValue .= trim(is_string($token) ? $token : $token[1]);
}
SCANNER_CONTINUE:
if (next($this->tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
if ($this->class && $this->nameInformation) {
$this->class = $this->nameInformation->resolveName($this->class);
}
$this->isScanned = true;
}
/**
* Get declaring scanner class
*
* @return ClassScanner
*/
public function getDeclaringScannerClass()
{
return $this->declaringScannerClass;
}
/**
* Get declaring class
*
* @return string
*/
public function getDeclaringClass()
{
return $this->declaringClass;
}
/**
* Get declaring scanner function
*
* @return MethodScanner
*/
public function getDeclaringScannerFunction()
{
return $this->declaringScannerFunction;
}
/**
* Get declaring function
*
* @return string
*/
public function getDeclaringFunction()
{
return $this->declaringFunction;
}
/**
* Get default value
*
* @return string
*/
public function getDefaultValue()
{
$this->scan();
return $this->defaultValue;
}
/**
* Get class
*
* @return string
*/
public function getClass()
{
$this->scan();
return $this->class;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* Get position
*
* @return int
*/
public function getPosition()
{
$this->scan();
return $this->position;
}
/**
* Check if is array
*
* @return bool
*/
public function isArray()
{
$this->scan();
return $this->isArray;
}
/**
* Check if default value is available
*
* @return bool
*/
public function isDefaultValueAvailable()
{
$this->scan();
return $this->isDefaultValueAvailable;
}
/**
* Check if is optional
*
* @return bool
*/
public function isOptional()
{
$this->scan();
return $this->isOptional;
}
/**
* Check if is passed by reference
*
* @return bool
*/
public function isPassedByReference()
{
$this->scan();
return $this->isPassedByReference;
}
}

View File

@@ -0,0 +1,327 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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;
use function is_array;
use function is_numeric;
use function is_string;
use function ltrim;
use function reset;
use function strpos;
use function substr;
use function trim;
use function var_export;
class PropertyScanner implements ScannerInterface
{
const T_BOOLEAN = 'boolean';
const T_INTEGER = 'int';
const T_STRING = 'string';
const T_ARRAY = 'array';
const T_UNKNOWN = 'unknown';
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var array
*/
protected $tokens;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var string
*/
protected $class;
/**
* @var ClassScanner
*/
protected $scannerClass;
/**
* @var int
*/
protected $lineStart;
/**
* @var bool
*/
protected $isProtected = false;
/**
* @var bool
*/
protected $isPublic = true;
/**
* @var bool
*/
protected $isPrivate = false;
/**
* @var bool
*/
protected $isStatic = false;
/**
* @var string
*/
protected $docComment;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* @var string
*/
protected $valueType;
/**
* Constructor
*
* @param array $propertyTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $propertyTokens, NameInformation $nameInformation = null)
{
$this->tokens = $propertyTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* @param ClassScanner $scannerClass
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
}
/**
* @return ClassScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return string
*/
public function getValueType()
{
$this->scan();
return $this->valueType;
}
/**
* @return bool
*/
public function isPublic()
{
$this->scan();
return $this->isPublic;
}
/**
* @return bool
*/
public function isPrivate()
{
$this->scan();
return $this->isPrivate;
}
/**
* @return bool
*/
public function isProtected()
{
$this->scan();
return $this->isProtected;
}
/**
* @return bool
*/
public function isStatic()
{
$this->scan();
return $this->isStatic;
}
/**
* @return string
*/
public function getValue()
{
$this->scan();
return $this->value;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param Annotation\AnnotationManager $annotationManager
* @return AnnotationScanner|false
*/
public function getAnnotations(Annotation\AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return string
*/
public function __toString()
{
$this->scan();
return var_export($this, true);
}
/**
* Scan tokens
*
* @throws \Zend\Code\Exception\RuntimeException
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
if (! $this->tokens) {
throw new Exception\RuntimeException('No tokens were provided');
}
/**
* Variables & Setup
*/
$value = '';
$concatenateValue = false;
$tokens = &$this->tokens;
reset($tokens);
foreach ($tokens as $token) {
$tempValue = $token;
if (! is_string($token)) {
list($tokenType, $tokenContent, $tokenLine) = $token;
switch ($tokenType) {
case T_DOC_COMMENT:
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
break;
case T_VARIABLE:
$this->name = ltrim($tokenContent, '$');
break;
case T_PUBLIC:
// use defaults
break;
case T_PROTECTED:
$this->isProtected = true;
$this->isPublic = false;
break;
case T_PRIVATE:
$this->isPrivate = true;
$this->isPublic = false;
break;
case T_STATIC:
$this->isStatic = true;
break;
default:
$tempValue = trim($tokenContent);
break;
}
}
//end value concatenation
if (! is_array($token) && trim($token) == ';') {
$concatenateValue = false;
}
if (true === $concatenateValue) {
$value .= $tempValue;
}
//start value concatenation
if (! is_array($token) && trim($token) == '=') {
$concatenateValue = true;
}
}
$this->valueType = self::T_UNKNOWN;
if ($value == 'false' || $value == 'true') {
$this->valueType = self::T_BOOLEAN;
} elseif (is_numeric($value)) {
$this->valueType = self::T_INTEGER;
} elseif (0 === strpos($value, 'array') || 0 === strpos($value, '[')) {
$this->valueType = self::T_ARRAY;
} elseif (0 === strpos($value, '"') || 0 === strpos($value, "'")) {
$value = substr($value, 1, -1); // Remove quotes
$this->valueType = self::T_STRING;
}
$this->value = empty($value) ? null : $value;
$this->isScanned = true;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
interface ScannerInterface
{
/* public static function export($tokens); */
/* public function toString(); */
}

View File

@@ -0,0 +1,700 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 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\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
use function array_shift;
use function array_slice;
use function in_array;
use function is_array;
use function is_int;
use function is_string;
class TokenArrayScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var array
*/
protected $tokens = [];
/**
* @var null
*/
protected $docComment;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var array
*/
protected $infos = [];
/**
* @var AnnotationManager
*/
protected $annotationManager;
/**
* @param null|array $tokens
* @param null|AnnotationManager $annotationManager
*/
public function __construct($tokens, AnnotationManager $annotationManager = null)
{
$this->tokens = $tokens;
$this->annotationManager = $annotationManager;
}
/**
* @return AnnotationManager
*/
public function getAnnotationManager()
{
return $this->annotationManager;
}
/**
* Get doc comment
*
* @todo Assignment of $this->docComment should probably be done in scan()
* and then $this->getDocComment() just retrieves it.
*
* @return string|null
*/
public function getDocComment()
{
foreach ($this->tokens as $token) {
$type = $token[0];
$value = $token[1];
if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
continue;
} elseif ($type == T_DOC_COMMENT) {
$this->docComment = $value;
return $this->docComment;
} else {
// Only whitespace is allowed before file docblocks
return;
}
}
}
/**
* @return array
*/
public function getNamespaces()
{
$this->scan();
$namespaces = [];
foreach ($this->infos as $info) {
if ($info['type'] == 'namespace') {
$namespaces[] = $info['namespace'];
}
}
return $namespaces;
}
/**
* @param null|string $namespace
* @return array|null
*/
public function getUses($namespace = null)
{
$this->scan();
return $this->getUsesNoScan($namespace);
}
/**
* @return void
*/
public function getIncludes()
{
$this->scan();
// @todo Implement getIncludes() in TokenArrayScanner
}
/**
* @return array
*/
public function getClassNames()
{
$this->scan();
$return = [];
foreach ($this->infos as $info) {
if ($info['type'] != 'class') {
continue;
}
$return[] = $info['name'];
}
return $return;
}
/**
* @return ClassScanner[]
*/
public function getClasses()
{
$this->scan();
$return = [];
foreach ($this->infos as $info) {
if ($info['type'] != 'class') {
continue;
}
$return[] = $this->getClass($info['name']);
}
return $return;
}
/**
* Return the class object from this scanner
*
* @param string|int $name
* @throws Exception\InvalidArgumentException
* @return ClassScanner|false
*/
public function getClass($name)
{
$this->scan();
if (is_int($name)) {
$info = $this->infos[$name];
if ($info['type'] != 'class') {
throw new Exception\InvalidArgumentException('Index of info offset is not about a class');
}
} elseif (is_string($name)) {
$classFound = false;
foreach ($this->infos as $info) {
if ($info['type'] === 'class' && $info['name'] === $name) {
$classFound = true;
break;
}
}
if (! $classFound) {
return false;
}
}
return new ClassScanner(
array_slice(
$this->tokens,
$info['tokenStart'],
$info['tokenEnd'] - $info['tokenStart'] + 1
), // zero indexed array
new NameInformation($info['namespace'], $info['uses'])
);
}
/**
* @param string $className
* @return bool|null|NameInformation
*/
public function getClassNameInformation($className)
{
$this->scan();
$classFound = false;
foreach ($this->infos as $info) {
if ($info['type'] === 'class' && $info['name'] === $className) {
$classFound = true;
break;
}
}
if (! $classFound) {
return false;
}
if (! isset($info)) {
return;
}
return new NameInformation($info['namespace'], $info['uses']);
}
/**
* @return array
*/
public function getFunctionNames()
{
$this->scan();
$functionNames = [];
foreach ($this->infos as $info) {
if ($info['type'] == 'function') {
$functionNames[] = $info['name'];
}
}
return $functionNames;
}
/**
* @return array
*/
public function getFunctions()
{
$this->scan();
$functions = [];
// foreach ($this->infos as $info) {
// if ($info['type'] == 'function') {
// // @todo $functions[] = new FunctionScanner($info['name']);
// }
// }
return $functions;
}
/**
* Export
*
* @param mixed $tokens
*/
public static function export($tokens)
{
// @todo
}
public function __toString()
{
// @todo
}
/**
* Scan
*
* @todo: $this->docComment should be assigned for valid docblock during
* the scan instead of $this->getDocComment() (starting with
* T_DOC_COMMENT case)
*
* @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;
$docCommentIndex = false;
$infoIndex = 0;
/*
* MACRO creation
*/
$MACRO_TOKEN_ADVANCE = function () use (
&$tokens,
&$tokenIndex,
&$token,
&$tokenType,
&$tokenContent,
&$tokenLine
) {
$tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1;
if (! isset($tokens[$tokenIndex])) {
$token = false;
$tokenContent = false;
$tokenType = false;
$tokenLine = false;
return false;
}
if (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"') {
do {
$tokenIndex++;
} while (! (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"'));
}
$token = $tokens[$tokenIndex];
if (is_array($token)) {
list($tokenType, $tokenContent, $tokenLine) = $token;
} else {
$tokenType = null;
$tokenContent = $token;
}
return $tokenIndex;
};
$MACRO_TOKEN_LOGICAL_START_INDEX = function () use (&$tokenIndex, &$docCommentIndex) {
return $docCommentIndex === false ? $tokenIndex : $docCommentIndex;
};
$MACRO_DOC_COMMENT_START = function () use (&$tokenIndex, &$docCommentIndex) {
$docCommentIndex = $tokenIndex;
return $docCommentIndex;
};
$MACRO_DOC_COMMENT_VALIDATE = function () use (&$tokenType, &$docCommentIndex) {
static $validTrailingTokens = null;
if ($validTrailingTokens === null) {
$validTrailingTokens = [T_WHITESPACE, T_FINAL, T_ABSTRACT, T_INTERFACE, T_CLASS, T_FUNCTION];
}
if ($docCommentIndex !== false && ! in_array($tokenType, $validTrailingTokens)) {
$docCommentIndex = false;
}
return $docCommentIndex;
};
$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:
if ($token === false) {
goto SCANNER_END;
}
// Validate current doc comment index
$MACRO_DOC_COMMENT_VALIDATE();
switch ($tokenType) {
case T_DOC_COMMENT:
$MACRO_DOC_COMMENT_START();
goto SCANNER_CONTINUE;
// goto no break needed
case T_NAMESPACE:
$infos[$infoIndex] = [
'type' => 'namespace',
'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
'tokenEnd' => null,
'lineStart' => $token[2],
'lineEnd' => null,
'namespace' => null,
];
// start processing with next token
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
SCANNER_NAMESPACE_TOP:
if (($tokenType === null && $tokenContent === ';') || $tokenContent === '{') {
goto SCANNER_NAMESPACE_END;
}
if ($tokenType === T_WHITESPACE) {
goto SCANNER_NAMESPACE_CONTINUE;
}
if ($tokenType === T_NS_SEPARATOR || $tokenType === T_STRING) {
$infos[$infoIndex]['namespace'] .= $tokenContent;
}
SCANNER_NAMESPACE_CONTINUE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_NAMESPACE_TOP;
SCANNER_NAMESPACE_END:
$namespace = $infos[$infoIndex]['namespace'];
$MACRO_INFO_ADVANCE();
goto SCANNER_CONTINUE;
// goto no break needed
case T_USE:
$infos[$infoIndex] = [
'type' => 'use',
'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
'tokenEnd' => null,
'lineStart' => $tokens[$tokenIndex][2],
'lineEnd' => null,
'namespace' => $namespace,
'statements' => [
0 => [
'use' => null,
'as' => null,
],
],
];
$useStatementIndex = 0;
$useAsContext = false;
// start processing with next token
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
SCANNER_USE_TOP:
if ($tokenType === null) {
if ($tokenContent === ';') {
goto SCANNER_USE_END;
} elseif ($tokenContent === ',') {
$useAsContext = false;
$useStatementIndex++;
$infos[$infoIndex]['statements'][$useStatementIndex] = [
'use' => null,
'as' => null,
];
}
}
// ANALYZE
if ($tokenType !== null) {
if ($tokenType == T_AS) {
$useAsContext = true;
goto SCANNER_USE_CONTINUE;
}
if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) {
if ($useAsContext == false) {
$infos[$infoIndex]['statements'][$useStatementIndex]['use'] .= $tokenContent;
} else {
$infos[$infoIndex]['statements'][$useStatementIndex]['as'] = $tokenContent;
}
}
}
SCANNER_USE_CONTINUE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_USE_TOP;
SCANNER_USE_END:
$MACRO_INFO_ADVANCE();
goto SCANNER_CONTINUE;
// goto no break needed
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_REQUIRE:
case T_REQUIRE_ONCE:
// Static for performance
static $includeTypes = [
T_INCLUDE => 'include',
T_INCLUDE_ONCE => 'include_once',
T_REQUIRE => 'require',
T_REQUIRE_ONCE => 'require_once',
];
$infos[$infoIndex] = [
'type' => 'include',
'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
'tokenEnd' => null,
'lineStart' => $tokens[$tokenIndex][2],
'lineEnd' => null,
'includeType' => $includeTypes[$tokens[$tokenIndex][0]],
'path' => '',
];
// start processing with next token
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
SCANNER_INCLUDE_TOP:
if ($tokenType === null && $tokenContent === ';') {
goto SCANNER_INCLUDE_END;
}
$infos[$infoIndex]['path'] .= $tokenContent;
SCANNER_INCLUDE_CONTINUE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_INCLUDE_TOP;
SCANNER_INCLUDE_END:
$MACRO_INFO_ADVANCE();
goto SCANNER_CONTINUE;
// goto no break needed
case T_FUNCTION:
case T_FINAL:
case T_ABSTRACT:
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
$infos[$infoIndex] = [
'type' => $tokenType === T_FUNCTION ? 'function' : 'class',
'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
'tokenEnd' => null,
'lineStart' => $tokens[$tokenIndex][2],
'lineEnd' => null,
'namespace' => $namespace,
'uses' => $this->getUsesNoScan($namespace),
'name' => null,
'shortName' => null,
];
$classBraceCount = 0;
// start processing with current token
SCANNER_CLASS_TOP:
// process the name
if ($infos[$infoIndex]['shortName'] == ''
&& (($tokenType === T_CLASS
|| $tokenType === T_INTERFACE
|| $tokenType === T_TRAIT)
&& $infos[$infoIndex]['type'] === 'class'
|| ($tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function'))
) {
$infos[$infoIndex]['shortName'] = is_array($tokens[$tokenIndex + 2])
? $tokens[$tokenIndex + 2][1]
: $tokens[$tokenIndex + 2];
$infos[$infoIndex]['name'] = ($namespace !== null
? $namespace . '\\'
: '') . $infos[$infoIndex]['shortName'];
}
if ($tokenType === null) {
if ($tokenContent == '{') {
$classBraceCount++;
}
if ($tokenContent == '}') {
$classBraceCount--;
if ($classBraceCount === 0) {
goto SCANNER_CLASS_END;
}
}
}
SCANNER_CLASS_CONTINUE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_CLASS_TOP;
SCANNER_CLASS_END:
$MACRO_INFO_ADVANCE();
goto SCANNER_CONTINUE;
// goto no break needed
}
SCANNER_CONTINUE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
/**
* END FINITE STATE MACHINE FOR SCANNING TOKENS
*/
$this->isScanned = true;
}
/**
* Check for namespace
*
* @param string $namespace
* @return bool
*/
public function hasNamespace($namespace)
{
$this->scan();
foreach ($this->infos as $info) {
if ($info['type'] == 'namespace' && $info['namespace'] == $namespace) {
return true;
}
}
return false;
}
/**
* @param string $namespace
* @return null|array
* @throws Exception\InvalidArgumentException
*/
protected function getUsesNoScan($namespace)
{
$namespaces = [];
foreach ($this->infos as $info) {
if ($info['type'] == 'namespace') {
$namespaces[] = $info['namespace'];
}
}
if ($namespace === null) {
$namespace = array_shift($namespaces);
} elseif (! is_string($namespace)) {
throw new Exception\InvalidArgumentException('Invalid namespace provided');
} elseif (! in_array($namespace, $namespaces)) {
return;
}
$uses = [];
foreach ($this->infos as $info) {
if ($info['type'] !== 'use') {
continue;
}
foreach ($info['statements'] as $statement) {
if ($info['namespace'] == $namespace) {
$uses[] = $statement;
}
}
}
return $uses;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use stdClass;
use Zend\Code\Exception;
use function array_key_exists;
use function is_object;
use function ltrim;
use function property_exists;
use function sprintf;
use function strlen;
use function strpos;
use function substr;
use function substr_replace;
/**
* Shared utility methods used by scanners
*/
class Util
{
/**
* Resolve imports
*
* @param string $value
* @param null|string $key
* @param \stdClass $data
* @return void
* @throws Exception\InvalidArgumentException
*/
public static function resolveImports(&$value, $key = null, stdClass $data = null)
{
if (! is_object($data)
|| ! property_exists($data, 'uses')
|| ! property_exists($data, 'namespace')
) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a data object containing "uses" and "namespace" properties; on or both missing',
__METHOD__
));
}
if ($data->namespace && ! $data->uses && strlen($value) > 0 && $value[0] != '\\') {
$value = $data->namespace . '\\' . $value;
return;
}
if (! $data->uses || strlen($value) <= 0 || $value[0] == '\\') {
$value = ltrim($value, '\\');
return;
}
if ($data->namespace || $data->uses) {
$firstPart = $value;
if (($firstPartEnd = strpos($firstPart, '\\')) !== false) {
$firstPart = substr($firstPart, 0, $firstPartEnd);
} else {
$firstPartEnd = strlen($firstPart);
}
if (array_key_exists($firstPart, $data->uses)) {
$value = substr_replace($value, $data->uses[$firstPart], 0, $firstPartEnd);
return;
}
if ($data->namespace) {
$value = $data->namespace . '\\' . $value;
return;
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
class ValueScanner
{
// @todo
}