From 19601c0e25626791bdecbd1767b1f40301e687b9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 30 Nov 2020 21:07:11 +0100 Subject: [PATCH] BC BREAK: removed `Scanner` component, removed `FileReflection`, removed `NameInformation` and made `DocBlockScanner` `@internal` The scanners were full of incomplete, untested and generally experimental code that would simply crash when presented with something like PHP 8 code. In addition to that, they were extremely unmaintainable due to the code being written for PHP 5, and never being touched afterwards (too complex). In order to slim down things and keep maintenance cost low, the complete `Laminas\Code\Scanner` component has been trashed, with the exception of `Laminas\Code\Scanner\DocBlockScanner`, which is still used in in some reflection internals. It is advised for end-users to move to more modern tooling, such as [`roave/better-reflection`](https://github.com/Roave/BetterReflection) which provides a safer (and well tested) API to scan source directories and extract reflection information without loading any potentially dangerous PHP files. The following components have been removed from the public API: * `Laminas\Code\Scanner\*` * `Laminas\Code\NameInformation` (unused in this package) * `Laminas\Code\Reflection\FileReflection` (use `roave/better-reflection` instead) * `Laminas\Code\Generic\Prototype\*`, which contains utilities to define custom docblock types: please consider using something like https://github.com/phpDocumentor/ReflectionDocBlock instead. --- docs/book/generator/examples.md | 11 - docs/book/generator/reference.md | 1 - src/Generator/FileGenerator.php | 62 - src/Generator/FileGeneratorRegistry.php | 45 - .../Prototype/PrototypeClassFactory.php | 2 + .../Prototype/PrototypeGenericInterface.php | 1 + src/Generic/Prototype/PrototypeInterface.php | 1 + src/NameInformation.php | 168 -- src/Reflection/ClassReflection.php | 12 - src/Reflection/FileReflection.php | 327 ---- src/Reflection/ReflectionInterface.php | 1 + src/Scanner/AggregateDirectoryScanner.php | 111 -- src/Scanner/CachingFileScanner.php | 155 -- src/Scanner/ClassScanner.php | 1352 ----------------- src/Scanner/ConstantScanner.php | 230 --- src/Scanner/DerivedClassScanner.php | 385 ----- src/Scanner/DirectoryScanner.php | 281 ---- src/Scanner/DocBlockScanner.php | 14 +- src/Scanner/FileScanner.php | 48 - src/Scanner/FunctionScanner.php | 15 - src/Scanner/MethodScanner.php | 605 -------- src/Scanner/ParameterScanner.php | 365 ----- src/Scanner/PropertyScanner.php | 312 ---- src/Scanner/ScannerInterface.php | 15 - src/Scanner/TokenArrayScanner.php | 699 --------- src/Scanner/Util.php | 83 - src/Scanner/ValueScanner.php | 14 - test/Generator/ClassGeneratorTest.php | 8 +- test/Generator/FileGeneratorTest.php | 178 --- test/NameInformationTest.php | 65 - test/Reflection/ClassReflectionTest.php | 7 - test/Reflection/FileReflectionTest.php | 199 --- .../Scanner/AggregateDirectoryScannerTest.php | 19 - test/Scanner/CachingFileScannerTest.php | 73 - test/Scanner/ClassScannerTest.php | 304 ---- test/Scanner/ConstantScannerTest.php | 35 - test/Scanner/DerivedClassScannerTest.php | 26 - test/Scanner/FileScannerTest.php | 23 - test/Scanner/MethodScannerTest.php | 133 -- test/Scanner/ParameterScannerTest.php | 33 - test/Scanner/PropertyScannerTest.php | 131 -- .../TestAsset/MapperExample/DbAdapter.php | 27 - .../TestAsset/MapperExample/EntityA.php | 19 - .../TestAsset/MapperExample/Mapper.php | 30 - .../TestAsset/MapperExample/RepositoryA.php | 42 - .../TestAsset/MapperExample/RepositoryB.php | 19 - .../TestAsset/TestClassWithAliasException.php | 27 - test/Scanner/TokenArrayScannerTest.php | 101 -- 48 files changed, 12 insertions(+), 6802 deletions(-) delete mode 100644 src/Generator/FileGeneratorRegistry.php delete mode 100644 src/NameInformation.php delete mode 100644 src/Reflection/FileReflection.php delete mode 100644 src/Scanner/AggregateDirectoryScanner.php delete mode 100644 src/Scanner/CachingFileScanner.php delete mode 100644 src/Scanner/ClassScanner.php delete mode 100644 src/Scanner/ConstantScanner.php delete mode 100644 src/Scanner/DerivedClassScanner.php delete mode 100644 src/Scanner/DirectoryScanner.php delete mode 100644 src/Scanner/FileScanner.php delete mode 100644 src/Scanner/FunctionScanner.php delete mode 100644 src/Scanner/MethodScanner.php delete mode 100644 src/Scanner/ParameterScanner.php delete mode 100644 src/Scanner/PropertyScanner.php delete mode 100644 src/Scanner/ScannerInterface.php delete mode 100644 src/Scanner/TokenArrayScanner.php delete mode 100644 src/Scanner/Util.php delete mode 100644 src/Scanner/ValueScanner.php delete mode 100644 test/NameInformationTest.php delete mode 100644 test/Reflection/FileReflectionTest.php delete mode 100644 test/Scanner/AggregateDirectoryScannerTest.php delete mode 100644 test/Scanner/CachingFileScannerTest.php delete mode 100644 test/Scanner/ClassScannerTest.php delete mode 100644 test/Scanner/ConstantScannerTest.php delete mode 100644 test/Scanner/DerivedClassScannerTest.php delete mode 100644 test/Scanner/FileScannerTest.php delete mode 100644 test/Scanner/MethodScannerTest.php delete mode 100644 test/Scanner/ParameterScannerTest.php delete mode 100644 test/Scanner/PropertyScannerTest.php delete mode 100644 test/Scanner/TestAsset/MapperExample/DbAdapter.php delete mode 100644 test/Scanner/TestAsset/MapperExample/EntityA.php delete mode 100644 test/Scanner/TestAsset/MapperExample/Mapper.php delete mode 100644 test/Scanner/TestAsset/MapperExample/RepositoryA.php delete mode 100644 test/Scanner/TestAsset/MapperExample/RepositoryB.php delete mode 100644 test/Scanner/TestAsset/TestClassWithAliasException.php delete mode 100644 test/Scanner/TokenArrayScannerTest.php diff --git a/docs/book/generator/examples.md b/docs/book/generator/examples.md index 210763c3..923ca52f 100644 --- a/docs/book/generator/examples.md +++ b/docs/book/generator/examples.md @@ -322,17 +322,6 @@ define('APPLICATION_ENV', 'testing'); ## Add code to existing PHP files and classes -### Seeding PHP file code generation via reflection - -You can add *PHP* code to an existing *PHP* file using the code generator. To do so, you need to -first do reflection on it. The static method `fromReflectedFileName()` allows you to do this. - -```php -$generator = Laminas\Code\Generator\FileGenerator::fromReflectedFileName($path); -$generator->setBody("\$foo->bar();"); -file_put_contents($path, $generator->generate()); -``` - ### Seeding PHP class generation via reflection You may add code to an existing class. To do so, first use the static `fromReflection()` method to diff --git a/docs/book/generator/reference.md b/docs/book/generator/reference.md index 73985650..18f39afd 100644 --- a/docs/book/generator/reference.md +++ b/docs/book/generator/reference.md @@ -268,7 +268,6 @@ class Laminas\Code\Generator\FileGenerator extends Laminas\Code\Generator\Abstra $filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true) - public static function fromReflection(Laminas\Code\Reflection\FileReflection $reflectionFile) public function setDocblock(Laminas\Code\Generator\DocBlockGenerator $docblock) public function getDocblock() public function setRequiredFiles($requiredFiles) diff --git a/src/Generator/FileGenerator.php b/src/Generator/FileGenerator.php index 242aa01c..7239627d 100644 --- a/src/Generator/FileGenerator.php +++ b/src/Generator/FileGenerator.php @@ -11,8 +11,6 @@ use Laminas\Code\DeclareStatement; use Laminas\Code\Exception\InvalidArgumentException; use Laminas\Code\Generator\Exception\ClassNotFoundException; -use Laminas\Code\Reflection\Exception as ReflectionException; -use Laminas\Code\Reflection\FileReflection; use function array_key_exists; use function array_merge; @@ -91,66 +89,6 @@ public function __construct($options = null) } } - /** - * Use this if you intend on generating code generation objects based on the same file. - * This will keep previous changes to the file in tact during the same PHP process - * - * @param string $filePath - * @param bool $includeIfNotAlreadyIncluded - * @throws ReflectionException\InvalidArgumentException If file does not exists - * @throws ReflectionException\RuntimeException If file exists but is not included or required - * @return FileGenerator - */ - public static function fromReflectedFileName($filePath, $includeIfNotAlreadyIncluded = true) - { - $fileReflector = new FileReflection($filePath, $includeIfNotAlreadyIncluded); - $codeGenerator = static::fromReflection($fileReflector); - - return $codeGenerator; - } - - /** - * @param FileReflection $fileReflection - * @return FileGenerator - */ - public static function fromReflection(FileReflection $fileReflection) - { - $file = new static(); - - $file->setSourceContent($fileReflection->getContents()); - $file->setSourceDirty(false); - - $uses = $fileReflection->getUses(); - - foreach ($fileReflection->getClasses() as $class) { - $phpClass = ClassGenerator::fromReflection($class); - $phpClass->setContainingFileGenerator($file); - - foreach ($uses as $fileUse) { - $phpClass->addUse($fileUse['use'], $fileUse['as']); - } - - $file->setClass($phpClass); - } - - $namespace = $fileReflection->getNamespace(); - - if ($namespace != '') { - $file->setNamespace($namespace); - } - - if ($uses) { - $file->setUses($uses); - } - - if ($fileReflection->getDocComment() != '') { - $docBlock = $fileReflection->getDocBlock(); - $file->setDocBlock(DocBlockGenerator::fromReflection($docBlock)); - } - - return $file; - } - /** * @param array $values * @return FileGenerator diff --git a/src/Generator/FileGeneratorRegistry.php b/src/Generator/FileGeneratorRegistry.php deleted file mode 100644 index 3e2eb7fb..00000000 --- a/src/Generator/FileGeneratorRegistry.php +++ /dev/null @@ -1,45 +0,0 @@ -getFilename(); - } - - if ($fileName == '') { - throw new RuntimeException('FileName does not exist.'); - } - - // cannot use realpath since the file might not exist, but we do need to have the index - // in the same DIRECTORY_SEPARATOR that realpath would use: - $fileName = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fileName); - - static::$fileCodeGenerators[$fileName] = $fileCodeGenerator; - } -} diff --git a/src/Generic/Prototype/PrototypeClassFactory.php b/src/Generic/Prototype/PrototypeClassFactory.php index 425441cb..d61d673d 100644 --- a/src/Generic/Prototype/PrototypeClassFactory.php +++ b/src/Generic/Prototype/PrototypeClassFactory.php @@ -22,6 +22,8 @@ * If the factory can not supply the class someone is asking for * it tries to fallback on a generic default prototype, which would * have need to be set before. + * + * @internal this class is not part of the public API of this package */ class PrototypeClassFactory { diff --git a/src/Generic/Prototype/PrototypeGenericInterface.php b/src/Generic/Prototype/PrototypeGenericInterface.php index 88d3fa6c..4d79a7b7 100644 --- a/src/Generic/Prototype/PrototypeGenericInterface.php +++ b/src/Generic/Prototype/PrototypeGenericInterface.php @@ -8,6 +8,7 @@ namespace Laminas\Code\Generic\Prototype; +/** @internal this class is not part of the public API of this package */ interface PrototypeGenericInterface extends PrototypeInterface { /** diff --git a/src/Generic/Prototype/PrototypeInterface.php b/src/Generic/Prototype/PrototypeInterface.php index 45e80455..ebec09f6 100644 --- a/src/Generic/Prototype/PrototypeInterface.php +++ b/src/Generic/Prototype/PrototypeInterface.php @@ -8,6 +8,7 @@ namespace Laminas\Code\Generic\Prototype; +/** @internal this class is not part of the public API of this package */ interface PrototypeInterface { /** diff --git a/src/NameInformation.php b/src/NameInformation.php deleted file mode 100644 index 5fe9c185..00000000 --- a/src/NameInformation.php +++ /dev/null @@ -1,168 +0,0 @@ -setNamespace($namespace); - } - if ($uses) { - $this->setUses($uses); - } - } - - /** - * @param string $namespace - * @return NameInformation - */ - public function setNamespace($namespace) - { - $this->namespace = (string) $namespace; - return $this; - } - - /** - * @return string - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * @return bool - */ - public function hasNamespace() - { - return $this->namespace !== null; - } - - /** - * @param array $uses - * @return NameInformation - */ - public function setUses(array $uses) - { - $this->uses = []; - $this->addUses($uses); - - return $this; - } - - /** - * @param array $uses - * @return NameInformation - */ - public function addUses(array $uses) - { - foreach ($uses as $use => $as) { - if (is_int($use)) { - $this->addUse($as); - } elseif (is_string($use)) { - $this->addUse($use, $as); - } - } - - return $this; - } - - /** - * @param array|string $use - * @param string $as - */ - public function addUse($use, $as = null) - { - if (is_array($use) && array_key_exists('use', $use) && array_key_exists('as', $use)) { - $uses = $use; - $use = $uses['use']; - $as = $uses['as']; - } - - $use = trim($use, '\\'); - if ($as === null) { - $as = trim($use, '\\'); - $nsSeparatorPosition = strrpos($as, '\\'); - if ($nsSeparatorPosition !== false && $nsSeparatorPosition !== 0 && $nsSeparatorPosition != strlen($as)) { - $as = substr($as, $nsSeparatorPosition + 1); - } - } - - $this->uses[$use] = $as; - } - - /** - * @return array - */ - public function getUses() - { - return $this->uses; - } - - /** - * @param string $name - * @return string - */ - public function resolveName($name) - { - if ($this->namespace && ! $this->uses && strlen($name) > 0 && $name[0] != '\\') { - return $this->namespace . '\\' . $name; - } - - if (! $this->uses || strlen($name) <= 0 || $name[0] == '\\') { - return ltrim($name, '\\'); - } - - if ($this->namespace || $this->uses) { - $firstPart = $name; - if (($firstPartEnd = strpos($firstPart, '\\')) !== false) { - $firstPart = substr($firstPart, 0, $firstPartEnd); - } else { - $firstPartEnd = strlen($firstPart); - } - if (($fqns = array_search($firstPart, $this->uses)) !== false) { - return substr_replace($name, $fqns, 0, $firstPartEnd); - } - if ($this->namespace) { - return $this->namespace . '\\' . $name; - } - } - - return $name; - } -} diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 60e363ae..390c0b6e 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -27,18 +27,6 @@ class ClassReflection extends ReflectionClass implements ReflectionInterface */ protected $docBlock; - /** - * Return the reflection file of the declaring file. - * - * @return FileReflection - */ - public function getDeclaringFile() - { - $instance = new FileReflection($this->getFileName()); - - return $instance; - } - /** * Return the classes DocBlock reflection object * diff --git a/src/Reflection/FileReflection.php b/src/Reflection/FileReflection.php deleted file mode 100644 index 89d987cf..00000000 --- a/src/Reflection/FileReflection.php +++ /dev/null @@ -1,327 +0,0 @@ -filePath = $fileRealPath; - $this->reflect(); - } - - /** - * Required by the Reflector interface. - * - * @todo What should this do? - * @return void - */ - public static function export() - { - } - - /** - * Return the file name of the reflected file - * - * @return string - */ - public function getFileName() - { - return basename($this->filePath); - } - - /** - * Get the start line - Always 1, staying consistent with the Reflection API - * - * @return int - */ - public function getStartLine() - { - return $this->startLine; - } - - /** - * Get the end line / number of lines - * - * @return int - */ - public function getEndLine() - { - return $this->endLine; - } - - /** - * @return string - */ - public function getDocComment() - { - return $this->docComment; - } - - /** - * @return DocBlockReflection|false - */ - public function getDocBlock() - { - if (! ($docComment = $this->getDocComment())) { - return false; - } - - $instance = new DocBlockReflection($docComment); - - return $instance; - } - - /** - * @return string[] - */ - public function getNamespaces() - { - return $this->namespaces; - } - - /** - * @return null|string - */ - public function getNamespace() - { - if (count($this->namespaces) == 0) { - return; - } - - return $this->namespaces[0]; - } - - /** - * @return array - */ - public function getUses() - { - return $this->uses; - } - - /** - * Return the reflection classes of the classes found inside this file - * - * @return ClassReflection[] - */ - public function getClasses() - { - $classes = []; - foreach ($this->classes as $class) { - $classes[] = new ClassReflection($class); - } - - return $classes; - } - - /** - * Return the reflection functions of the functions found inside this file - * - * @return FunctionReflection[] - */ - public function getFunctions() - { - $functions = []; - foreach ($this->functions as $function) { - $functions[] = new FunctionReflection($function); - } - - return $functions; - } - - /** - * Retrieve the reflection class of a given class found in this file - * - * @param null|string $name - * @return ClassReflection - * @throws Exception\InvalidArgumentException for invalid class name or invalid reflection class - */ - public function getClass($name = null) - { - if (null === $name) { - reset($this->classes); - $selected = current($this->classes); - - return new ClassReflection($selected); - } - - if (in_array($name, $this->classes)) { - return new ClassReflection($name); - } - - throw new Exception\InvalidArgumentException(sprintf( - 'Class by name %s not found.', - $name - )); - } - - /** - * Return the full contents of file - * - * @return string - */ - public function getContents() - { - return file_get_contents($this->filePath); - } - - public function toString() - { - return ''; // @todo - } - - /** - * Serialize to string - * - * Required by the Reflector interface - * - * @todo What should this serialization look like? - * @return string - */ - public function __toString() - { - return ''; - } - - /** - * This method does the work of "reflecting" the file - * - * Uses Laminas\Code\Scanner\FileScanner to gather file information - * - * @return void - */ - protected function reflect() - { - $scanner = new CachingFileScanner($this->filePath); - $this->docComment = $scanner->getDocComment(); - $this->requiredFiles = $scanner->getIncludes(); - $this->classes = $scanner->getClassNames(); - $this->namespaces = $scanner->getNamespaces(); - $this->uses = $scanner->getUses(); - } - - /** - * Validate / check a file level DocBlock - * - * @param array $tokens Array of tokenizer tokens - * @return void - */ - protected function checkFileDocBlock($tokens) - { - foreach ($tokens as $token) { - $type = $token[0]; - $value = $token[1]; - $lineNum = $token[2]; - if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) { - continue; - } elseif ($type == T_DOC_COMMENT) { - $this->docComment = $value; - $this->startLine = $lineNum + substr_count($value, "\n") + 1; - - return; - } else { - // Only whitespace is allowed before file DocBlocks - return; - } - } - } -} diff --git a/src/Reflection/ReflectionInterface.php b/src/Reflection/ReflectionInterface.php index 01452765..4f191dd3 100644 --- a/src/Reflection/ReflectionInterface.php +++ b/src/Reflection/ReflectionInterface.php @@ -10,6 +10,7 @@ use Reflector; +/** @internal this class is not part of the public API of this package */ interface ReflectionInterface extends Reflector { /** diff --git a/src/Scanner/AggregateDirectoryScanner.php b/src/Scanner/AggregateDirectoryScanner.php deleted file mode 100644 index 5b2bd55a..00000000 --- a/src/Scanner/AggregateDirectoryScanner.php +++ /dev/null @@ -1,111 +0,0 @@ -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 - } -} diff --git a/src/Scanner/CachingFileScanner.php b/src/Scanner/CachingFileScanner.php deleted file mode 100644 index ba1c062c..00000000 --- a/src/Scanner/CachingFileScanner.php +++ /dev/null @@ -1,155 +0,0 @@ -fileScanner = static::$cache[$cacheId]; - } else { - $this->fileScanner = new FileScanner($file); - static::$cache[$cacheId] = $this->fileScanner; - } - } - - /** - * @return void - */ - public static function clearCache() - { - static::$cache = []; - } - - /** - * @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(); - } -} diff --git a/src/Scanner/ClassScanner.php b/src/Scanner/ClassScanner.php deleted file mode 100644 index a86aed06..00000000 --- a/src/Scanner/ClassScanner.php +++ /dev/null @@ -1,1352 +0,0 @@ -tokens = $classTokens; - $this->nameInformation = $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 a trait - * - * @return bool - */ - public function isTrait() - { - $this->scan(); - return $this->isTrait; - } - - /** - * Verify if class is instantiable - * - * @return bool - */ - public function isInstantiable() - { - $this->scan(); - return ! $this->isAbstract && ! $this->isInterface && ! $this->isTrait; - } - - /** - * 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 = []; - 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 = []; - 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 = []; - 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 = []; - 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; - } - - /** - * Retrieve any traits used by the class. - * - * @return ClassScanner[] - */ - public function getTraits() - { - if (! empty($this->traits)) { - return $this->traits; - } - - // get list of trait names - $traitNames = $this->getTraitNames(); - foreach ($traitNames as $traitName) { - $r = new ReflectionClass($traitName); - if (! $r->isTrait()) { - throw new Exception\RuntimeException(sprintf( - 'Non-trait class detected as a trait: %s', - $traitName - )); - } - $fileName = $r->getFileName(); - - $file = new FileScanner($fileName); - $this->traits[] = $file->getClass($traitName); - } - - return $this->traits; - } - - /** - * Retrieve a list of trait names used by this class. - * - * @return array - */ - public function getTraitNames() - { - $this->scan(); - - $return = []; - foreach ($this->infos as $info) { - if ($info['type'] !== 'use') { - continue; - } - - if (is_array($info['use_statements'])) { - foreach ($info['use_statements'] as $trait) { - $traitName = $trait; - if ($this->nameInformation instanceof NameInformation) { - $traitName = $this->nameInformation->resolveName($traitName); - } - $return[] = $traitName; - } - } - } - - return $return; - } - - /** - * Retrieve a list of aliased traits used by the class. - * - * @return array - */ - public function getTraitAliases() - { - $this->scan(); - - $return = []; - foreach ($this->infos as $info) { - if ($info['type'] !== 'use') { - continue; - } - - if (is_array($info['aliases'])) { - foreach ($info['aliases'] as $alias) { - if (null === $alias - || (! empty($alias['type']) && $alias['type'] !== 'as') - ) { - continue; - } - - // attempt to get fqcn - list($trait, $method) = explode('::', $alias['original']); - if ($this->nameInformation instanceof NameInformation) { - $trait = $this->nameInformation->resolveName($trait); - } - - $return[$alias['alias']] = $trait . '::' . $method; - } - } - } - - return $return; - } - - /** - * Retrieve visibility for a given alias. - * - * @param mixed $aliasName - * @return string - */ - protected function getVisibilityForAlias($aliasName) - { - $this->scan(); - - $return = null; - foreach ($this->infos as $info) { - if ($info['type'] !== 'use') { - continue; - } - - if (is_array($info['aliases'])) { - foreach ($info['aliases'] as $alias) { - if (null === $alias - && (! empty($alias['type']) && $alias['type'] !== 'as') - ) { - continue; - } - - if ($alias['alias'] === $aliasName) { - $return = $alias['visibility']; - break 2; - } - } - } - } - - return $return; - } - - /** - * Return an array of key = trait to keep, value = trait::method to ignore - * - * @return array - */ - protected function getBlockedTraitMethods() - { - $this->scan(); - - $return = []; - foreach ($this->infos as $info) { - if ($info['type'] !== 'use') { - continue; - } - - if (is_array($info['aliases'])) { - foreach ($info['aliases'] as $alias) { - if (null === $alias - || (! empty($alias['type']) && $alias['type'] !== 'insteadof') - ) { - continue; - } - - // attempt to get fqcn - list($trait, $method) = explode('::', $alias['original']); - if ($this->nameInformation instanceof NameInformation) { - $trait = $this->nameInformation->resolveName($alias['alias']); - } - - $return[] = $trait . '::' . $method; - } - } - } - - return $return; - } - - /** - * Return a list of method names - * - * @return array - */ - public function getMethodNames() - { - $this->scan(); - - $methods = $this->getMethods(); - $return = []; - foreach ($methods as $method) { - $return[] = $method->getName(); - } - - return $return; - } - - /** - * Return a list of methods - * - * @return MethodScanner[] - */ - public function getMethods() - { - $this->scan(); - - if (! empty($this->methods)) { - return $this->methods; - } - - foreach ($this->infos as $info) { - if ($info['type'] !== 'method' && $info['type'] !== 'use') { - continue; - } - - // Merge in trait methods - if ($info['type'] === 'use') { - $traitMethods = []; - $traits = $this->getTraits(); - $insteadof = $this->getBlockedTraitMethods(); - $aliases = $this->getTraitAliases(); - - foreach ($traits as $trait) { - $tempMethods = $trait->getMethods(); - foreach ($tempMethods as $tempMethod) { - $methodFullName = $trait->getName() . '::' . $tempMethod->getName(); - $methodAlias = array_search($methodFullName, $aliases); - - if (false !== $methodAlias) { - // trait::method is aliased - // clone the tempMethod as we need to change - // the name and possibly the visibility of the - // scanned method. - // - // @todo setName and setVisibility were added to - // MethodScanner to accomplish this, may not be the - // best option, could use ReflectionClass instead? - $newMethod = clone $tempMethod; - $newMethod->setName($methodAlias); - - // if visibility exists, change it on the MethodScanner - $visibility = $this->getVisibilityForAlias($methodAlias); - if (null !== $visibility) { - $newMethod->setVisibility($visibility); - } - $traitMethods[$methodAlias] = $newMethod; - } elseif (in_array($methodFullName, $insteadof)) { - // ignore overridden methods - continue; - } else { - if (array_key_exists($tempMethod->getName(), $traitMethods)) { - throw new Exception\RuntimeException(sprintf( - 'Trait method %s has not been applied because there are' - . ' collisions with other trait methods see: (insteadof OR as)', - $tempMethod->getName() - )); - } - - $traitMethods[$tempMethod->getName()] = $tempMethod; - } - } - } - - $this->methods = array_merge($this->methods, array_values($traitMethods)); - continue; - } - - $m = new MethodScanner( - array_slice( - $this->tokens, - $info['tokenStart'], - $info['tokenEnd'] - $info['tokenStart'] + 1 - ), - $this->nameInformation - ); - $m->setClass($this->name); - $m->setScannerClass($this); - - $this->methods[] = $m; - } - - return $this->methods; - } - - /** - * 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'); - } - $methodNameOrInfoIndex = $info['name']; - } - - $returnMethod = false; - $methods = $this->getMethods(); - foreach ($methods as $method) { - if ($method->getName() === $methodNameOrInfoIndex) { - $returnMethod = $method; - break; - } - } - - return $returnMethod; - } - - /** - * Verify if class has method by given name - * - * @param string $name - * @return bool - */ - public function hasMethod($name) - { - $this->scan(); - - return is_object($this->getMethod($name)); - } - - 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 += substr_count( - $lastTokenArray[1] ?? '', - "\n" - ); // adjust token line by last known newline count - } else { - $lastTokenArray = $token; - [$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; - //goto no break needed - - case T_FINAL: - case T_ABSTRACT: - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - // 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; - // goto no break needed - - case T_ABSTRACT: - $this->isAbstract = true; - goto SCANNER_CLASS_INFO_CONTINUE; - // goto no break needed - - case T_TRAIT: - $this->isTrait = true; - $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; - // goto no break needed - - case T_INTERFACE: - $this->isInterface = true; - // fall-through - 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; - // goto no break needed - - case T_NS_SEPARATOR: - case T_STRING: - case T_NAME_FULLY_QUALIFIED: - case T_NAME_QUALIFIED: - switch ($classContext) { - case T_EXTENDS: - if ($this->isInterface) { - $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent; - } else { - $this->shortParentClass .= $tokenContent; - } - break; - case T_IMPLEMENTS: - $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent; - break; - } - goto SCANNER_CLASS_INFO_CONTINUE; - // goto no break needed - - 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; - // goto no break needed - - case null: - if (($classContext == T_IMPLEMENTS && $tokenContent == ',') - || ($classContext == T_EXTENDS && $tokenContent == ',' && $this->isInterface) - ) { - $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] = [ - '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; - // goto no break needed - - case T_USE: - // ensure php backwards compatibility - if (! defined('T_INSTEADOF')) { - define('T_INSTEADOF', 24000); - } - - $infos[$infoIndex] = [ - 'type' => 'use', - 'tokenStart' => $tokenIndex, - 'tokenEnd' => null, - 'lineStart' => $tokens[$tokenIndex][2], - 'lineEnd' => null, - 'name' => $namespace, - 'use_statements' => [0 => null], - 'aliases' => [0 => null], - ]; - - $isOriginalName = [T_STRING, T_DOUBLE_COLON]; - $isAlias = [T_STRING]; - $isVisibility = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC]; - $isAliasType = [T_AS, T_INSTEADOF]; - $isValidAlias = array_merge($isOriginalName, $isAlias, $isVisibility, $isAliasType); - - $useStatementIndex = 0; - $aliasStatementIndex = 0; - $useAliasContext = false; - $useAsContext = false; - - // start processing with next token - if ($MACRO_TOKEN_ADVANCE() === false) { - goto SCANNER_END; - } - - SCANNER_USE_TOP: - - if ($tokenType === null) { - if ($tokenContent === '{') { - $useStatementIndex = 0; - $useAliasContext = true; - $infos[$infoIndex]['aliases'][$useStatementIndex] = [ - 'original' => null, - 'alias' => null, - 'visibility' => null, - 'type' => 'as', - ]; - } elseif ($tokenContent === '}') { - $useAliasContext = false; - goto SCANNER_USE_END; - } elseif ($tokenContent === ';') { - if ($useAliasContext === true) { - $useStatementIndex++; - $useAsContext = false; - } - // only end if we aren't inside braces - if (false === $useAliasContext) { - goto SCANNER_USE_END; - } - } elseif ($tokenContent === ',') { - $useStatementIndex++; - $infos[$infoIndex]['use_statements'][$useStatementIndex] = ''; - } - } - - // ANALYZE - if ($tokenType !== null) { - // use context - if (false === $useAliasContext) { - if ($tokenType == T_NS_SEPARATOR - || $tokenType == T_STRING - || $tokenType == T_NAME_QUALIFIED - || $tokenType == T_NAME_FULLY_QUALIFIED - ) { - $infos[$infoIndex]['use_statements'][$useStatementIndex] .= $tokenContent; - } - } else { - if (in_array($tokenType, $isValidAlias) - && empty($infos[$infoIndex]['aliases'][$useStatementIndex]) - ) { - $infos[$infoIndex]['aliases'][$useStatementIndex] = [ - 'original' => null, - 'visibility' => null, - 'alias' => null, - 'type' => null, - ]; - } - - if ($tokenType == T_AS || $tokenType == T_INSTEADOF) { - $useAsContext = true; - $infos[$infoIndex]['aliases'][$useStatementIndex]['type'] = $tokenType == T_INSTEADOF - ? 'insteadof' - : 'as'; - goto SCANNER_USE_CONTINUE; - } - - // in alias context - if ($useAsContext === true && in_array($tokenType, $isAlias)) { - $infos[$infoIndex]['aliases'][$useStatementIndex]['alias'] = $tokenContent; - } elseif (in_array($tokenType, $isOriginalName)) { - $infos[$infoIndex]['aliases'][$useStatementIndex]['original'] .= $tokenContent; - } elseif (in_array($tokenType, $isVisibility)) { - //add whitespace (will trim later) - $infos[$infoIndex]['aliases'][$useStatementIndex]['visibility'] = $tokenType; - } - } - } - - SCANNER_USE_CONTINUE: - - if ($MACRO_TOKEN_ADVANCE() === false) { - goto SCANNER_END; - } - goto SCANNER_USE_TOP; - - SCANNER_USE_END: - - $MACRO_INFO_ADVANCE(); - goto SCANNER_CLASS_BODY_CONTINUE; - // goto no break needed - - 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] = [ - '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; - // goto no break needed - - case '}': - $braceCount--; - goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; - // goto no break needed - - case ';': - $infos[$infoIndex]['tokenEnd'] = $tokenIndex; - goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; - // goto no break needed - } - } - - 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; - //goto no break needed - - case T_VARIABLE: - if ($memberContext === null) { - $memberContext = 'property'; - $infos[$infoIndex]['type'] = 'property'; - $infos[$infoIndex]['name'] = ltrim($tokenContent, '$'); - } - goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; - // goto no break needed - - case T_FUNCTION: - $memberContext = 'method'; - $infos[$infoIndex]['type'] = 'method'; - goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; - // goto no break needed - - case T_STRING: - if ($memberContext === 'method' && null === $infos[$infoIndex]['name']) { - $infos[$infoIndex]['name'] = $tokenContent; - } - goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; - // goto no break needed - } - - 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; - // goto no break needed - - case null: // no type, is a string - switch ($tokenContent) { - case '{': - $braceCount++; - goto SCANNER_CLASS_BODY_CONTINUE; - // goto no break needed - - case '}': - $braceCount--; - goto SCANNER_CLASS_BODY_CONTINUE; - // goto no break needed - } - } - - 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; - } -} diff --git a/src/Scanner/ConstantScanner.php b/src/Scanner/ConstantScanner.php deleted file mode 100644 index 181ffa8b..00000000 --- a/src/Scanner/ConstantScanner.php +++ /dev/null @@ -1,230 +0,0 @@ -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; - } - - /** - * @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; - } -} diff --git a/src/Scanner/DerivedClassScanner.php b/src/Scanner/DerivedClassScanner.php deleted file mode 100644 index bfb1fca9..00000000 --- a/src/Scanner/DerivedClassScanner.php +++ /dev/null @@ -1,385 +0,0 @@ -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; - } -} diff --git a/src/Scanner/DirectoryScanner.php b/src/Scanner/DirectoryScanner.php deleted file mode 100644 index c4895329..00000000 --- a/src/Scanner/DirectoryScanner.php +++ /dev/null @@ -1,281 +0,0 @@ -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 - } -} diff --git a/src/Scanner/DocBlockScanner.php b/src/Scanner/DocBlockScanner.php index 9b3385c3..76529bf7 100644 --- a/src/Scanner/DocBlockScanner.php +++ b/src/Scanner/DocBlockScanner.php @@ -8,8 +8,6 @@ namespace Laminas\Code\Scanner; -use Laminas\Code\NameInformation; - use function array_pop; use function array_push; use function current; @@ -23,7 +21,8 @@ use function substr; use function trim; -class DocBlockScanner implements ScannerInterface +/** @internal this class is not part of the public API of this package */ +class DocBlockScanner { /** * @var bool @@ -35,11 +34,6 @@ class DocBlockScanner implements ScannerInterface */ protected $docComment; - /** - * @var NameInformation - */ - protected $nameInformation; - /** * @var string */ @@ -57,12 +51,10 @@ class DocBlockScanner implements ScannerInterface /** * @param string $docComment - * @param null|NameInformation $nameInformation */ - public function __construct($docComment, NameInformation $nameInformation = null) + public function __construct($docComment) { $this->docComment = $docComment; - $this->nameInformation = $nameInformation; } /** diff --git a/src/Scanner/FileScanner.php b/src/Scanner/FileScanner.php deleted file mode 100644 index 1a07a24a..00000000 --- a/src/Scanner/FileScanner.php +++ /dev/null @@ -1,48 +0,0 @@ -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))); - } - - /** - * @return string - */ - public function getFile() - { - return $this->file; - } -} diff --git a/src/Scanner/FunctionScanner.php b/src/Scanner/FunctionScanner.php deleted file mode 100644 index e4188b6a..00000000 --- a/src/Scanner/FunctionScanner.php +++ /dev/null @@ -1,15 +0,0 @@ -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; - } - - /** - * @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 \Laminas\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: - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - 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; - } -} diff --git a/src/Scanner/ParameterScanner.php b/src/Scanner/ParameterScanner.php deleted file mode 100644 index 0aa3df52..00000000 --- a/src/Scanner/ParameterScanner.php +++ /dev/null @@ -1,365 +0,0 @@ -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 - || $token[0] === T_NAME_QUALIFIED - || $token[0] === T_NAME_FULLY_QUALIFIED - ) - ) { - $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; - } -} diff --git a/src/Scanner/PropertyScanner.php b/src/Scanner/PropertyScanner.php deleted file mode 100644 index 760734de..00000000 --- a/src/Scanner/PropertyScanner.php +++ /dev/null @@ -1,312 +0,0 @@ -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; - } - - /** - * @return string - */ - public function __toString() - { - $this->scan(); - return var_export($this, true); - } - - /** - * Scan tokens - * - * @throws \Laminas\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; - } -} diff --git a/src/Scanner/ScannerInterface.php b/src/Scanner/ScannerInterface.php deleted file mode 100644 index f5db5334..00000000 --- a/src/Scanner/ScannerInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -tokens = $tokens; - } - - /** - * 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; - }; - - // ensure php backwards compatibility - if (! defined('T_NAME_QUALIFIED')) { - define('T_NAME_QUALIFIED', 24001); - } - if (! defined('T_NAME_FULLY_QUALIFIED')) { - define('T_NAME_FULLY_QUALIFIED', 24002); - } - - /** - * 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 - || $tokenType === T_NAME_QUALIFIED - || $tokenType === T_NAME_FULLY_QUALIFIED - ) { - $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 - || $tokenType == T_NAME_QUALIFIED - || $tokenType == T_NAME_FULLY_QUALIFIED - ) { - 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; - } -} diff --git a/src/Scanner/Util.php b/src/Scanner/Util.php deleted file mode 100644 index b314e2cb..00000000 --- a/src/Scanner/Util.php +++ /dev/null @@ -1,83 +0,0 @@ -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; - } - } - } -} diff --git a/src/Scanner/ValueScanner.php b/src/Scanner/ValueScanner.php deleted file mode 100644 index 6ebda883..00000000 --- a/src/Scanner/ValueScanner.php +++ /dev/null @@ -1,14 +0,0 @@ -setName('ClassName'); $classGenerator->setNamespaceName('SomeNamespace'); - $classGenerator->addUse(NameInformation::class); - $classGenerator->setExtendedClass(NameInformation::class); - self::assertStringContainsString('class ClassName extends NameInformation', $classGenerator->generate()); + $classGenerator->addUse(FooClass::class); + $classGenerator->setExtendedClass(FooClass::class); + self::assertStringContainsString('class ClassName extends FooClass', $classGenerator->generate()); } /** diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index 06e21b05..1729a911 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -13,7 +13,6 @@ use Laminas\Code\Generator\ClassGenerator; use Laminas\Code\Generator\Exception\ClassNotFoundException; use Laminas\Code\Generator\FileGenerator; -use Laminas\Code\Reflection\FileReflection; use PHPUnit\Framework\TestCase; use function explode; @@ -82,85 +81,6 @@ abstract class SampleClass extends ExtendedClassName implements Iterator, Traver self::assertEquals($expectedOutput, $output, $output); } - public function testFromReflection() - { - $tempFile = tempnam(sys_get_temp_dir(), 'UnitFile'); - - $codeGenFile = FileGenerator::fromArray([ - 'class' => [ - 'name' => 'SampleClass', - ], - ]); - - file_put_contents($tempFile, $codeGenFile->generate()); - - require_once $tempFile; - - $fileGenerator = FileGenerator::fromReflection(new FileReflection($tempFile)); - - unlink($tempFile); - - self::assertEquals(FileGenerator::class, get_class($fileGenerator)); - self::assertCount(1, $fileGenerator->getClasses()); - } - - public function testFromFileReflection() - { - $file = __DIR__ . '/TestAsset/TestSampleSingleClass.php'; - require_once $file; - - $codeGenFileFromDisk = FileGenerator::fromReflection($fileRefl = new FileReflection($file)); - - $codeGenFileFromDisk->getClass()->addMethod('foobar'); - - $expectedOutput = <<<'EOS' - - */ - - -namespace LaminasTest\Code\Generator\TestAsset; - -/** - * class docblock - */ -class TestSampleSingleClass -{ - /** - * Enter description here... - * - * @return bool - */ - public function someMethod() - { - /* test test */ - } - - /** - * Enter description here... - * - * @return bool - */ - protected function withParamsAndReturnType($mixed, array $array, ?callable $callable = null, ?int $int = 0) : bool - { - /* test test */ - return true; - } - - public function foobar() - { - } -} - - -EOS; - - self::assertEquals($expectedOutput, $codeGenFileFromDisk->generate()); - } - public function testClassNotFoundException() { $fileGenerator = new FileGenerator(); @@ -316,104 +236,6 @@ public function testCreateFromArrayWithClassFromArray() self::assertInstanceOf(ClassGenerator::class, $class); } - public function testGeneratingFromAReflectedFileName() - { - $generator = FileGenerator::fromReflectedFileName(__DIR__ . '/TestAsset/OneInterface.php'); - self::assertInstanceOf(FileGenerator::class, $generator); - } - - public function testGeneratedClassesHaveUses() - { - $generator = FileGenerator::fromReflectedFileName(__DIR__ . '/TestAsset/ClassWithUses.php'); - $class = $generator->getClass(); - - $expectedUses = [TestAsset\ClassWithNamespace::class]; - - self::assertEquals($expectedUses, $class->getUses()); - } - - /** - * @group 4747 - */ - public function testIssue4747FileGenerationWithAddedMethodIsCorrectlyFormatted() - { - $g = new FileGenerator(); - $g = $g->fromReflectedFileName(__DIR__ . '/TestAsset/ClassWithUses.php'); - $g->setFilename(sys_get_temp_dir() . '/result_class.php'); - $g->getClass()->addMethod('added'); - $g->write(); - - $expected = <<<'CODE' -fromReflectedFileName(__DIR__ . '/TestAsset/ClassWithUses.php'); - $g->setFilename(sys_get_temp_dir() . '/result_class.php'); - $g->getClass()->addMethod('added'); - $g->setBody('$foo->bar();'); - $g->write(); - - $expected = <<<'CODE' -bar(); -CODE; - $actual = file_get_contents(sys_get_temp_dir() . '/result_class.php'); - self::assertEquals($expected, $actual); - } - public function testSingleDeclareStatement(): void { $generator = FileGenerator::fromArray([ diff --git a/test/NameInformationTest.php b/test/NameInformationTest.php deleted file mode 100644 index 10ab0f4e..00000000 --- a/test/NameInformationTest.php +++ /dev/null @@ -1,65 +0,0 @@ -getNamespace()); - - $nr = new NameInformation(); - $nr->setNamespace('Bar\Baz'); - self::assertEquals('Bar\Baz', $nr->getNamespace()); - } - - public function testNamespaceResolverPersistsUseRules() - { - $nr = new NameInformation('Foo\Bar', ['Aaa\Bbb\Ccc' => 'C']); - self::assertEquals(['Aaa\Bbb\Ccc' => 'C'], $nr->getUses()); - - $nr = new NameInformation(); - $nr->setUses(['Aaa\Bbb\Ccc']); - self::assertEquals(['Aaa\Bbb\Ccc' => 'Ccc'], $nr->getUses()); - - $nr->setUses(['ArrayObject']); - self::assertEquals(['ArrayObject' => 'ArrayObject'], $nr->getUses()); - - $nr->setUses(['ArrayObject' => 'AO']); - self::assertEquals(['ArrayObject' => 'AO'], $nr->getUses()); - - $nr->setUses(['\Aaa\Bbb\Ccc' => 'Ccc']); - self::assertEquals(['Aaa\Bbb\Ccc' => 'Ccc'], $nr->getUses()); - } - - public function testNamespaceResolverCorrectlyResolvesNames() - { - $nr = new NameInformation(); - $nr->setNamespace('Laminas\MagicComponent'); - $nr->setUses([ - 'ArrayObject', - 'Laminas\OtherMagicComponent\Foo', - 'Laminas\SuperMagic' => 'SM', - ]); - - // test against namespace - self::assertEquals('Laminas\MagicComponent\Bar', $nr->resolveName('Bar')); - - // test against uses - self::assertEquals('ArrayObject', $nr->resolveName('ArrayObject')); - self::assertEquals('ArrayObject', $nr->resolveName('\ArrayObject')); - self::assertEquals('Laminas\OtherMagicComponent\Foo', $nr->resolveName('Foo')); - self::assertEquals('Laminas\SuperMagic', $nr->resolveName('SM')); - self::assertEquals('Laminas\SuperMagic\Bar', $nr->resolveName('SM\Bar')); - } -} diff --git a/test/Reflection/ClassReflectionTest.php b/test/Reflection/ClassReflectionTest.php index 43a9b964..fc4aef0f 100644 --- a/test/Reflection/ClassReflectionTest.php +++ b/test/Reflection/ClassReflectionTest.php @@ -12,7 +12,6 @@ use Laminas\Code\Reflection\MethodReflection; use Laminas\Code\Reflection\PropertyReflection; use Laminas\Code\Scanner\FileScanner; -use LaminasTest\Code\Reflection\TestAsset\InjectableClassReflection; use PHPUnit\Framework\TestCase; use function array_shift; @@ -148,12 +147,6 @@ public function testStartLine() self::assertEquals(5, $reflectionClass->getStartLine(true)); } - public function testGetDeclaringFileReturnsFilename() - { - $reflectionClass = new ClassReflection(TestAsset\TestSampleClass2::class); - self::assertStringContainsString('TestSampleClass2.php', $reflectionClass->getDeclaringFile()->getFileName()); - } - public function testGetContentsReturnsEmptyContentsOnEvaldCode() { $className = uniqid('ClassReflectionTestGenerated'); diff --git a/test/Reflection/FileReflectionTest.php b/test/Reflection/FileReflectionTest.php deleted file mode 100644 index dd0d150b..00000000 --- a/test/Reflection/FileReflectionTest.php +++ /dev/null @@ -1,199 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('found'); - new FileReflection($nonExistentFile); - } - - public function testFileConstructorFromAReflectedFilenameInIncludePathWithoutIncludeFlagEnabled() - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('must be required'); - $oldIncludePath = set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__ . '/TestAsset/'); - - try { - new FileReflection('an_empty_file.php', false); - set_include_path($oldIncludePath); - $this->fail('Should throw exception'); - } catch (Exception $e) { - set_include_path($oldIncludePath); - throw $e; - } - } - - public function testFileConstructorFromAReflectedFilenameIncluded() - { - include_once __DIR__ . '/TestAsset/an_empty_file.php'; - - $oldIncludePath = set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__ . '/TestAsset/'); - - try { - $file = new FileReflection('an_empty_file.php', false); - - self::assertSame('an_empty_file.php', $file->getFileName()); - } finally { - set_include_path($oldIncludePath); - } - } - - public function testFileConstructorFromAReflectedFilenameInIncludePath() - { - self::assertNotContains(realpath(__DIR__ . '/TestAsset/a_second_empty_file.php'), get_included_files()); - $oldIncludePath = set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__ . '/TestAsset/'); - - try { - new FileReflection('a_second_empty_file.php', true); - set_include_path($oldIncludePath); - } catch (Exception $e) { - set_include_path($oldIncludePath); - throw $e; - } - } - - public function testFileGetClassReturnsClassReflectionObject() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - self::assertEquals(get_class($reflectionFile), FileReflection::class); - self::assertCount(1, $reflectionFile->getClasses()); - } - - public function testFileGetClassReturnsFirstClassWithNoOptions() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - self::assertEquals(TestAsset\TestSampleClass::class, $reflectionFile->getClass()->getName()); - } - - public function testFileGetClassThrowsExceptionOnNonExistentClassName() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - $nonExistentClass = 'Some_Non_Existent_Class'; - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Class by name Some_Non_Existent_Class not found'); - $reflectionFile->getClass($nonExistentClass); - } - - public function testFileReflectorRequiredFunctionsDoNothing() - { - self::assertNull(FileReflection::export()); - - $reflectionFile = new FileReflection(__FILE__); - self::assertEquals('', $reflectionFile->__toString()); - } - - public function testFileGetFilenameReturnsCorrectFilename() - { - $reflectionFile = new FileReflection(__FILE__); - - self::assertEquals('FileReflectionTest.php', $reflectionFile->getFileName()); - } - - public function testFileGetLineNumbersWorks() - { - $this->markTestIncomplete('Line numbering not implemented yet'); - - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - self::assertEquals(9, $reflectionFile->getStartLine()); - self::assertEquals(24, $reflectionFile->getEndLine()); - } - - public function testFileGetDocBlockReturnsFileDocBlock() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass7.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - - $reflectionDocBlock = $reflectionFile->getDocBlock(); - self::assertInstanceOf(DocBlockReflection::class, $reflectionDocBlock); - - $authorTag = $reflectionDocBlock->getTag('author'); - self::assertEquals('Jeremiah Small', $authorTag->getAuthorName()); - self::assertEquals('jsmall@soliantconsulting.com', $authorTag->getAuthorEmail()); - } - - public function testFileGetFunctionsReturnsFunctions() - { - $this->markTestIncomplete('Function scanning not implemented yet'); - - $fileToRequire = __DIR__ . '/TestAsset/FileOfFunctions.php'; - include_once $fileToRequire; - $reflectionFile = new FileReflection($fileToRequire); - $funcs = $reflectionFile->getFunctions(); - self::assertInstanceOf(FunctionReflection::class, current($funcs)); - } - - public function testFileCanReflectFileWithInterface() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleInterface.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - $class = $reflectionFile->getClass(); - self::assertEquals(TestAsset\TestSampleInterface::class, $class->getName()); - self::assertTrue($class->isInterface()); - } - - public function testFileCanReflectFileWithUses() - { - $fileToReflect = __DIR__ . '/TestAsset/TestSampleClass8.php'; - include_once $fileToReflect; - $reflectionFile = new FileReflection($fileToReflect); - $expected = [ - ['use' => 'Laminas\Config', 'as' => 'LaminasConfig'], - ['use' => 'FooBar\Foo\Bar', 'as' => null], - ['use' => 'One\Two\Three\Four\Five', 'as' => 'ottff'], - ]; - self::assertSame($expected, $reflectionFile->getUses()); - } - - /** - * @group 70 - * @group 43 - */ - public function testFileReflectionShouldNotRaiseNoticesWhenReflectingClosures() - { - require_once __DIR__ . '/TestAsset/issue-70.php'; - $r = new FileReflection(__DIR__ . '/TestAsset/issue-70.php'); - self::assertStringContainsString('spl_autoload_register', $r->getContents()); - self::assertStringContainsString('function ()', $r->getContents()); - } -} diff --git a/test/Scanner/AggregateDirectoryScannerTest.php b/test/Scanner/AggregateDirectoryScannerTest.php deleted file mode 100644 index a58c3a4e..00000000 --- a/test/Scanner/AggregateDirectoryScannerTest.php +++ /dev/null @@ -1,19 +0,0 @@ -markTestIncomplete('This test needs to be filled out'); - } -} diff --git a/test/Scanner/CachingFileScannerTest.php b/test/Scanner/CachingFileScannerTest.php deleted file mode 100644 index b3dcbefb..00000000 --- a/test/Scanner/CachingFileScannerTest.php +++ /dev/null @@ -1,73 +0,0 @@ -getClassNames()); - self::assertEquals(1, $this->getCacheCount($cfs1)); - - // ensure same class is used internally - $cfs2 = new CachingFileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - self::assertEquals(1, $this->getCacheCount($cfs2)); - self::assertSameInternalFileScanner($cfs1, $cfs2); - - // ensure - $cfs3 = new CachingFileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - self::assertEquals(2, $this->getCacheCount($cfs3)); - self::assertDifferentInternalFileScanner($cfs2, $cfs3); - } - - protected function getCacheCount(CachingFileScanner $cfs) - { - $r = new \ReflectionObject($cfs); - $cacheProp = $r->getProperty('cache'); - $cacheProp->setAccessible(true); - return count($cacheProp->getValue($cfs)); - } - - protected function assertSameInternalFileScanner(CachingFileScanner $one, CachingFileScanner $two) - { - $rOne = new \ReflectionObject($one); - $fileScannerPropOne = $rOne->getProperty('fileScanner'); - $fileScannerPropOne->setAccessible(true); - $rTwo = new \ReflectionObject($two); - $fileScannerPropTwo = $rTwo->getProperty('fileScanner'); - $fileScannerPropTwo->setAccessible(true); - self::assertSame($fileScannerPropOne->getValue($one), $fileScannerPropTwo->getValue($two)); - } - - protected function assertDifferentInternalFileScanner(CachingFileScanner $one, CachingFileScanner $two) - { - $rOne = new \ReflectionObject($one); - $fileScannerPropOne = $rOne->getProperty('fileScanner'); - $fileScannerPropOne->setAccessible(true); - $rTwo = new \ReflectionObject($two); - $fileScannerPropTwo = $rTwo->getProperty('fileScanner'); - $fileScannerPropTwo->setAccessible(true); - self::assertNotSame($fileScannerPropOne->getValue($one), $fileScannerPropTwo->getValue($two)); - } -} diff --git a/test/Scanner/ClassScannerTest.php b/test/Scanner/ClassScannerTest.php deleted file mode 100644 index 26b6377d..00000000 --- a/test/Scanner/ClassScannerTest.php +++ /dev/null @@ -1,304 +0,0 @@ -getClass(FooClass::class); - self::assertEquals(FooClass::class, $class->getName()); - self::assertEquals('FooClass', $class->getShortName()); - self::assertFalse($class->isFinal()); - self::assertTrue($class->isAbstract()); - self::assertFalse($class->isInterface()); - $interfaces = $class->getInterfaces(); - self::assertContains('ArrayAccess', $interfaces); - self::assertContains('A\B\C\D\Blarg', $interfaces); - self::assertContains('LaminasTest\Code\TestAsset\Local\SubClass', $interfaces); - $methods = $class->getMethodNames(); - self::assertIsArray($methods); - self::assertContains('fooBarBaz', $methods); - } - - public function testClassScannerHasConstant() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertIsArray($class->getConstantNames()); - self::assertContains('FOO', $class->getConstantNames()); - } - - public function testClassScannerHasProperties() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertContains('bar', $class->getPropertyNames()); - } - - public function testClassScannerHasMethods() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertContains('fooBarBaz', $class->getMethodNames()); - } - - /** - * @todo Remove error handling once we remove deprecation warning from getConstants method - */ - public function testGetConstantsReturnsConstantNames() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - - ErrorHandler::start(E_USER_DEPRECATED); - $constants = $class->getConstants(); - $error = ErrorHandler::stop(); - - self::assertInstanceOf(\ErrorException::class, $error); - self::assertContains('FOO', $constants); - } - - public function testGetConstantsReturnsInstancesOfConstantScanner() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - $constants = $class->getConstants(false); - foreach ($constants as $constant) { - self::assertInstanceOf(ConstantScanner::class, $constant); - } - } - - public function testHasConstant() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertTrue($class->hasConstant('FOO')); - self::assertFalse($class->hasConstant('foo')); - } - - public function testHasProperty() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertTrue($class->hasProperty('foo')); - self::assertFalse($class->hasProperty('FOO')); - self::assertTrue($class->hasProperty('bar')); - } - - public function testHasMethod() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertTrue($class->hasMethod('fooBarBaz')); - self::assertFalse($class->hasMethod('FooBarBaz')); - self::assertFalse($class->hasMethod('bar')); - } - - public function testClassScannerReturnsMethodsWithMethodScanners() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - $methods = $class->getMethods(); - foreach ($methods as $method) { - self::assertInstanceOf(MethodScanner::class, $method); - } - } - - public function testClassScannerReturnsPropertiesWithPropertyScanners() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - $properties = $class->getProperties(); - foreach ($properties as $property) { - self::assertInstanceOf(PropertyScanner::class, $property); - } - } - - public function testClassScannerCanScanInterface() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooInterface.php'); - $class = $file->getClass(FooInterface::class); - self::assertEquals(FooInterface::class, $class->getName()); - } - - public function testClassScannerCanReturnLineNumbers() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertEquals(11, $class->getLineStart()); - self::assertEquals(36, $class->getLineEnd()); - - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - self::assertEquals(10, $class->getLineStart()); - self::assertEquals(42, $class->getLineEnd()); - } - - /** - * @group trait1 - */ - public function testClassScannerCanScanTraits() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarTrait.php'); - $class = $file->getClass(BarTrait::class); - - self::assertTrue($class->isTrait()); - self::assertTrue($class->hasMethod('bar')); - } - - /** - * @group trait2 - */ - public function testClassScannerCanScanClassThatUsesTraits() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/TestClassUsesTraitSimple.php'); - $class = $file->getClass(TestClassUsesTraitSimple::class); - - self::assertFalse($class->isTrait()); - $traitNames = $class->getTraitNames(); - $class->getTraitAliases(); - self::assertContains(BarTrait::class, $traitNames); - self::assertContains(FooTrait::class, $traitNames); - self::assertContains(BazTrait::class, $traitNames); - } - - /** - * @group trait3 - */ - public function testClassScannerCanScanClassAndGetTraitsAliases() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/TestClassWithTraitAliases.php'); - $class = $file->getClass(TestClassWithTraitAliases::class); - - self::assertFalse($class->isTrait()); - - $aliases = $class->getTraitAliases(); - - self::assertCount(1, $aliases); - - self::assertEquals(key($aliases), 'test'); - self::assertEquals(current($aliases), 'LaminasTest\Code\TestAsset\TraitWithSameMethods::foo'); - } - - /** - * @group trait4 - */ - public function testClassScannerCanGetTraitMethodsInGetMethods() - { - //load files or test may fail due to autoload issues - require_once __DIR__ . '/../TestAsset/TraitWithSameMethods.php'; - require_once __DIR__ . '/../TestAsset/BarTrait.php'; - - $file = new FileScanner(__DIR__ . '/../TestAsset/TestClassWithTraitAliases.php'); - - $class = $file->getClass(TestClassWithTraitAliases::class); - - self::assertFalse($class->isTrait()); - - $testMethods = [ - 'fooBarBaz' => 'isPublic', - 'foo' => 'isPublic', - 'bar' => 'isPublic', - 'test' => 'isPrivate', - 'bazFooBar' => 'isPublic', - ]; - - self::assertEquals($class->getMethodNames(), array_keys($testMethods)); - - foreach ($testMethods as $methodName => $testMethod) { - self::assertTrue($class->hasMethod($methodName), sprintf('Cannot find method %s', $methodName)); - - $method = $class->getMethod($methodName); - self::assertInstanceOf(MethodScanner::class, $method, $methodName . ' not found.'); - - self::assertTrue($method->$testMethod()); - - // test that we got the right ::bar method based on declaration - if ($testMethod === 'bar') { - self::assertEquals(trim($method->getBody), 'echo "foo";'); - } - } - } - - /** - * @group trait5 - */ - public function testGetMethodsThrowsExceptionOnDuplicateMethods() - { - $file = new FileScanner(__DIR__ . '/TestAsset/TestClassWithAliasException.php'); - $class = $file->getClass(TestAsset\TestClassWithAliasException::class); - - $this->expectException(RuntimeException::class); - $class->getMethods(); - } - - public function testClassIsInstantiable() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooBarClass.php'); - $class = $file->getClass('LaminasTest_Code_TestAsset_FooBar'); - self::assertFalse($class->isAbstract()); - self::assertTrue($class->isInstantiable()); - } - - public function testAbstractClassIsNotInstantiable() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php'); - $class = $file->getClass(FooClass::class); - self::assertTrue($class->isAbstract()); - self::assertFalse($class->isInstantiable()); - } - - public function testInterfaceIsNotInstantiable() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooInterface.php'); - $class = $file->getClass(FooInterface::class); - self::assertTrue($class->isInterface()); - self::assertFalse($class->isInstantiable()); - } - - public function testTraitIsNotInstantiable() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooTrait.php'); - $class = $file->getClass(FooTrait::class); - self::assertTrue($class->isTrait()); - self::assertFalse($class->isInstantiable()); - } - - public function testGetInterfacesFromInterface() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/FooInterface.php'); - $class = $file->getClass(FooInterface::class); - self::assertTrue($class->isInterface()); - self::assertCount(1, $class->getInterfaces()); - self::assertEquals('ArrayAccess', $class->getInterfaces()[0]); - } -} diff --git a/test/Scanner/ConstantScannerTest.php b/test/Scanner/ConstantScannerTest.php deleted file mode 100644 index 9934de1e..00000000 --- a/test/Scanner/ConstantScannerTest.php +++ /dev/null @@ -1,35 +0,0 @@ -getClass(FooClass::class); - - $constant = $class->getConstant('BAR'); - self::assertEquals('BAR', $constant->getName()); - self::assertEquals(5, $constant->getValue()); - - $constant = $class->getConstant('FOO'); - self::assertEquals('FOO', $constant->getName()); - self::assertEquals(5, $constant->getValue()); - - $constant = $class->getConstant('BAZ'); - self::assertEquals('BAZ', $constant->getName()); - self::assertEquals('baz', $constant->getValue()); - self::assertNotNull('Some comment', $constant->getDocComment()); - } -} diff --git a/test/Scanner/DerivedClassScannerTest.php b/test/Scanner/DerivedClassScannerTest.php deleted file mode 100644 index d6d40881..00000000 --- a/test/Scanner/DerivedClassScannerTest.php +++ /dev/null @@ -1,26 +0,0 @@ -addDirectory(__DIR__ . '/TestAsset'); - $ads = new AggregateDirectoryScanner(); - $ads->addDirectoryScanner($ds); - $c = $ads->getClass(TestAsset\MapperExample\RepositoryB::class); - self::assertEquals(TestAsset\MapperExample\RepositoryB::class, $c->getName()); - } -} diff --git a/test/Scanner/FileScannerTest.php b/test/Scanner/FileScannerTest.php deleted file mode 100644 index 9a8a920f..00000000 --- a/test/Scanner/FileScannerTest.php +++ /dev/null @@ -1,23 +0,0 @@ -getClass(Baz::class)->getName()); - self::assertEquals('Foo', $tokenScanner->getClass('Foo')->getName()); - } -} diff --git a/test/Scanner/MethodScannerTest.php b/test/Scanner/MethodScannerTest.php deleted file mode 100644 index b10a1500..00000000 --- a/test/Scanner/MethodScannerTest.php +++ /dev/null @@ -1,133 +0,0 @@ -getClass(FooClass::class); - $method = $class->getMethod('fooBarBaz'); - self::assertEquals('fooBarBaz', $method->getName()); - self::assertFalse($method->isAbstract()); - self::assertTrue($method->isFinal()); - self::assertTrue($method->isPublic()); - self::assertFalse($method->isProtected()); - self::assertFalse($method->isPrivate()); - self::assertFalse($method->isStatic()); - } - - public function testMethodScannerReturnsParameters() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('three'); - $parameters = $method->getParameters(); - self::assertIsArray($parameters); - } - - public function testMethodScannerReturnsParameterScanner() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('three'); - self::assertEquals(['o', 't', 'bbf'], $method->getParameters()); - $parameter = $method->getParameter('t'); - self::assertInstanceOf(ParameterScanner::class, $parameter); - self::assertEquals('t', $parameter->getName()); - } - - public function testMethodScannerParsesClassNames() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('five'); - self::assertEquals(['a'], $method->getParameters()); - $parameter = $method->getParameter('a'); - self::assertEquals(AbstractClass::class, $parameter->getClass()); - } - - public function testMethodScannerReturnsPropertyWithNoDefault() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BazClass.php'); - $class = $file->getClass('BazClass'); - $method = $class->getMethod('__construct'); - self::assertTrue($method->isPublic()); - } - - public function testMethodScannerReturnsLineNumbersForMethods() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('three'); - self::assertEquals(27, $method->getLineStart()); - self::assertEquals(31, $method->getLineEnd()); - } - - public function testMethodScannerReturnsBodyMethods() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('three'); - $expected = "\n" . ' $x = 5 + 5;' . "\n" . ' $y = \'this string\';' . "\n "; - self::assertEquals($expected, $method->getBody()); - } - - public function testMethodScannerMethodSignatureLatestOptionalParamHasParentheses() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php'); - $class = $file->getClass(BarClass::class); - $method = $class->getMethod('four'); - $paramTwo = $method->getParameter(1); - $optionalValue = $paramTwo->getDefaultValue(); - self::assertEquals('array([array(\'default\')])', $optionalValue); - } - - /** - * @group issue-6893 - */ - public function testMethodScannerWorksWithSingleAbstractFunction() - { - $file = new FileScanner(__DIR__ . '/../TestAsset/AbstractClass.php'); - - $class = $file->getClass(AbstractClass::class); - $method = $class->getMethod('helloWorld'); - - self::assertTrue($method->isAbstract()); - } - - public function testMethodScannerSetVisibilityThrowsInvalidArgumentException() - { - $methodScanner = new MethodScanner([]); - - // make sure test argument is invalid - $invalidArgument = max(T_PUBLIC, T_PROTECTED, T_PRIVATE) + 1; - - $this->expectException('\Laminas\Code\Exception\InvalidArgumentException'); - $methodScanner->setVisibility($invalidArgument); - } - - public function testMethodScannerSetVisibilityAcceptsIntegerTokens() - { - $methodScanner = new MethodScanner([]); - - $this->assertSame($methodScanner->setVisibility(T_PUBLIC), $methodScanner); - $this->assertSame($methodScanner->setVisibility(T_PROTECTED), $methodScanner); - $this->assertSame($methodScanner->setVisibility(T_PRIVATE), $methodScanner); - } -} diff --git a/test/Scanner/ParameterScannerTest.php b/test/Scanner/ParameterScannerTest.php deleted file mode 100644 index 631625b1..00000000 --- a/test/Scanner/ParameterScannerTest.php +++ /dev/null @@ -1,33 +0,0 @@ -getClass(BarClass::class); - $method = $class->getMethod('three'); - $parameter = $method->getParameter('t'); - self::assertEquals(BarClass::class, $parameter->getDeclaringClass()); - self::assertEquals('three', $parameter->getDeclaringFunction()); - self::assertEquals('t', $parameter->getName()); - self::assertEquals(2, $parameter->getPosition()); - self::assertEquals('2', $parameter->getDefaultValue()); - self::assertFalse($parameter->isArray()); - self::assertTrue($parameter->isDefaultValueAvailable()); - self::assertTrue($parameter->isOptional()); - self::assertTrue($parameter->isPassedByReference()); - } -} diff --git a/test/Scanner/PropertyScannerTest.php b/test/Scanner/PropertyScannerTest.php deleted file mode 100644 index 2925657c..00000000 --- a/test/Scanner/PropertyScannerTest.php +++ /dev/null @@ -1,131 +0,0 @@ -getClass(FooClass::class); - - $property = $class->getProperty('bar'); - self::assertEquals('bar', $property->getName()); - self::assertEquals('value', $property->getValue()); - self::assertFalse($property->isPublic()); - self::assertTrue($property->isProtected()); - self::assertFalse($property->isPrivate()); - self::assertTrue($property->isStatic()); - - $property = $class->getProperty('foo'); - self::assertEquals('foo', $property->getName()); - self::assertEquals('value2', $property->getValue()); - self::assertTrue($property->isPublic()); - self::assertFalse($property->isProtected()); - self::assertFalse($property->isPrivate()); - self::assertFalse($property->isStatic()); - - $property = $class->getProperty('baz'); - self::assertEquals('baz', $property->getName()); - self::assertEquals(3, $property->getValue()); - self::assertFalse($property->isPublic()); - self::assertFalse($property->isProtected()); - self::assertTrue($property->isPrivate()); - self::assertFalse($property->isStatic()); - } - - /** - * @group 5384 - */ - public function testPropertyScannerReturnsProperValue() - { - $class = <<<'CLASS' - 2,2); - private $arraynew = ['test' => 2,2]; - private $notarray = "['test' => 2,2]"; - private $status = false; -} -CLASS; - - $tokenScanner = new TokenArrayScanner(token_get_all($class)); - $fooClass = $tokenScanner->getClass('Foo'); - foreach ($fooClass->getProperties() as $property) { - $value = $property->getValue(); - $valueType = $property->getValueType(); - switch ($property->getName()) { - case 'empty': - self::assertNull($value); - self::assertEquals('unknown', $valueType); - break; - case 'string': - self::assertEquals('string', $value); - self::assertEquals('string', $valueType); - break; - case 'int': - self::assertEquals('123', $value); - self::assertEquals('int', $valueType); - break; - case 'array': - self::assertEquals("array('test'=>2,2)", $value); - self::assertEquals('array', $valueType); - break; - case 'arraynew': - self::assertEquals("['test'=>2,2]", $value); - self::assertEquals('array', $valueType); - break; - case 'notarray': - self::assertEquals('string', $valueType); - break; - case 'status': - self::assertEquals('false', $value); - self::assertEquals('boolean', $valueType); - break; - } - } - } - - /** - * @group issue-8 - */ - public function testPropertyScannerReturnsProperValueRegardlessOfOrder() - { - $class = <<<'CLASS' -getClass('Foo'); - - $property = $class->getProperty('string'); - self::assertEquals('string', $property->getValue()); - self::assertEquals('string', $property->getValueType()); - - $property = $class->getProperty('int'); - self::assertEquals('int', $property->getValueType()); - self::assertEquals(123, $property->getValue()); - } -} diff --git a/test/Scanner/TestAsset/MapperExample/DbAdapter.php b/test/Scanner/TestAsset/MapperExample/DbAdapter.php deleted file mode 100644 index 8eaefb68..00000000 --- a/test/Scanner/TestAsset/MapperExample/DbAdapter.php +++ /dev/null @@ -1,27 +0,0 @@ -username = $username; - $this->password = $password; - } - - public function __toString() - { - return 'I am ' . get_class($this) . ' object (hash ' . spl_object_hash($this) . '), with these parameters (username = ' . $this->username . ', password = ' . $this->password . ')'; - } - -} diff --git a/test/Scanner/TestAsset/MapperExample/EntityA.php b/test/Scanner/TestAsset/MapperExample/EntityA.php deleted file mode 100644 index b834c7e1..00000000 --- a/test/Scanner/TestAsset/MapperExample/EntityA.php +++ /dev/null @@ -1,19 +0,0 @@ -dbAdapter = $dbAdapter; - } - - public function __toString() - { - return 'I am a ' . get_class($this) . ' object (hash ' . spl_object_hash($this) . '), using this dbAdapter ' . "\n" . ' ' . $this->dbAdapter; - } - -} diff --git a/test/Scanner/TestAsset/MapperExample/RepositoryA.php b/test/Scanner/TestAsset/MapperExample/RepositoryA.php deleted file mode 100644 index f9fcf15f..00000000 --- a/test/Scanner/TestAsset/MapperExample/RepositoryA.php +++ /dev/null @@ -1,42 +0,0 @@ -mapper = $mapper; - } - - public function find(/* $entityCriterion */) - { - // so something with criterion - /* - $data = $mapper->findByCriterion($entityCriterion); - $entity = new EntityA(); - populate($entity); - return $entity; - */ - return new EntityA; - } - - public function __toString() - { - return 'I am a ' . get_class($this) . ' object (hash ' . spl_object_hash($this) . '), using this mapper object ' . "\n" . ' ' . $this->mapper; - } - -} diff --git a/test/Scanner/TestAsset/MapperExample/RepositoryB.php b/test/Scanner/TestAsset/MapperExample/RepositoryB.php deleted file mode 100644 index 7cbc7503..00000000 --- a/test/Scanner/TestAsset/MapperExample/RepositoryB.php +++ /dev/null @@ -1,19 +0,0 @@ -hasNamespace('LaminasTest\Code\TestAsset')); - $namespaces = $tokenScanner->getNamespaces(); - self::assertIsArray($namespaces); - self::assertContains('LaminasTest\Code\TestAsset', $namespaces); - } - - public function testScannerReturnsNamespacesInNotNamespacedClasses() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/FooBarClass.php') - )); - $uses = $tokenScanner->getUses(); - self::assertIsArray($uses); - $foundUses = []; - foreach ($uses as $use) { - $foundUses[] = $use['use']; - } - self::assertContains('ArrayObject', $foundUses); - } - - public function testScannerReturnsClassNames() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/FooClass.php') - )); - $classes = $tokenScanner->getClassNames(); - self::assertIsArray($classes); - self::assertContains(FooClass::class, $classes); - } - - /** - * @group gh-4989 - */ - public function testScannerReturnsClassNamesForTraits() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/FooTrait.php') - )); - $classes = $tokenScanner->getClassNames(); - self::assertIsArray($classes); - self::assertContains(FooTrait::class, $classes); - } - - public function testScannerReturnsFunctions() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/functions.php') - )); - $functions = $tokenScanner->getFunctionNames(); - self::assertIsArray($functions); - self::assertContains('LaminasTest\Code\TestAsset\foo_bar', $functions); - } - - public function testScannerReturnsClassScanner() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/FooClass.php') - )); - $classes = $tokenScanner->getClasses(); - self::assertIsArray($classes); - foreach ($classes as $class) { - self::assertInstanceOf(ClassScanner::class, $class); - } - } - - public function testScannerCanHandleMultipleNamespaceFile() - { - $tokenScanner = new TokenArrayScanner(token_get_all( - file_get_contents(__DIR__ . '/../TestAsset/MultipleNamespaces.php') - )); - self::assertEquals(Baz::class, $tokenScanner->getClass(Baz::class)->getName()); - self::assertEquals('Foo', $tokenScanner->getClass('Foo')->getName()); - } -}