.gitignore 0000644 00000000027 14476011130 0006530 0 ustar 00 /vendor/
composer.lock
.scrutinizer.yml 0000644 00000001517 14476011130 0007727 0 ustar 00 before_commands:
- "composer install --no-dev --prefer-source"
tools:
external_code_coverage:
timeout: 1200
php_code_coverage:
enabled: true
php_code_sniffer:
enabled: true
config:
standard: PSR2
filter:
paths: ["src/*"]
php_cpd:
enabled: true
excluded_dirs: ["docs", "examples", "tests", "vendor"]
php_cs_fixer:
enabled: true
config:
level: all
filter:
paths: ["src/*"]
php_loc:
enabled: true
excluded_dirs: ["docs", "examples", "tests", "vendor"]
php_pdepend:
enabled: true
excluded_dirs: ["docs", "examples", "tests", "vendor"]
php_analyzer:
enabled: true
filter:
paths: ["src/*"]
sensiolabs_security_checker: true
.travis.coverage.sh 0000755 00000000260 14476011130 0010256 0 ustar 00 set -x
if [ "$TRAVIS_PHP_VERSION" = '7.0' ] ; then
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover ./clover.xml
fi
.travis.yml 0000644 00000000417 14476011130 0006654 0 ustar 00 language: php
php:
- 5.5
- 5.6
- 7.0
- hhvm
before_script:
- composer install
script:
- vendor/bin/phpunit --verbose --coverage-text --coverage-clover=clover.xml --colors
after_script:
- sh .travis.coverage.sh
matrix:
allow_failures:
- php: hhvm
README.md 0000644 00000006140 14476011130 0006021 0 ustar 00 Parser Reflection API Library
-----------------
Parser Reflection API library provides a set of classes that extend original internal Reflection classes, but powered by [PHP-Parser](https://github.com/nikic/PHP-Parser) library thus allowing to create a reflection instance without loading classes into the memory.
This library can be used for analysing the source code, automatic proxy creation and much more.
[](https://scrutinizer-ci.com/g/goaop/parser-reflection/build-status/master)
[](https://scrutinizer-ci.com/g/goaop/parser-reflection/?branch=master)
[](https://scrutinizer-ci.com/g/goaop/parser-reflection/?branch=master)
[](https://insight.sensiolabs.com/projects/1fdfee9c-839a-4209-a2f2-42dadc859621)[](https://php.net/)
[](https://packagist.org/packages/goaop/parser-reflection)
Installation
------------
Library can be installed with composer. Installation is quite easy:
```bash
$ composer require goaop/parser-reflection
```
Composer will install the library to your project's `vendor/goaop/parser-reflection` directory.
Usage
------------
Just use `Go\ParserReflection` package reflection classes like traditional ones:
```php
$parsedClass = new \Go\ParserReflection\ReflectionClass(SomeClass::class);
var_dump($parsedClass->getMethods());
```
How it works?
------------
To understand how library works let's look at what happens during the call to the `new \Go\ParserReflection\ReflectionClass(SomeClass::class)`
* `\Go\ParserReflection\ReflectionClass` asks reflection engine to give an AST node for the given class name
* Reflection engine asks a locator to locate a filename for the given class
* `ComposerLocator` instance asks the Composer to find a filename for the given class and returns this result back to the reflection engine
* Reflection engine loads the content of file and passes it to the [PHP-Parser](https://github.com/nikic/PHP-Parser) for tokenization and processing
* PHP-Parser returns an AST (Abstract Syntax Tree)
* Reflection engine then analyse this AST to extract specific nodes an wrap them into corresponding reflection classes.
Compatibility
------------
All parser reflection classes extend PHP internal reflection classes, this means that you can use `\Go\ParserReflection\ReflectionClass` instance in any place that asks for `\ReflectionClass` instance. All reflection methods should be compatible with original ones, providing an except methods that requires object manipulation, such as `invoke()`, `invokeArgs()`, `setAccessible()`, etc. These methods will trigger the autoloading of class and switching to the internal reflection.
composer.json 0000644 00000001215 14476011130 0007262 0 ustar 00 {
"name": "goaop/parser-reflection",
"description": "Provides reflection information, based on raw source",
"license": "MIT",
"authors": [
{
"name": "Alexander Lisachenko",
"email": "lisachenko.it@gmail.com"
}
],
"minimum-stability": "stable",
"autoload": {
"psr-4": {
"Go\\ParserReflection\\": "src"
},
"files": ["src/bootstrap.php"],
"exclude-from-classmap": ["/tests/"]
},
"autoload-dev": {
"psr-4": {
"Go\\ParserReflection\\": "tests"
}
},
"require": {
"php": ">=5.5.0",
"nikic/php-parser": "^1.2|^2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
}
}
phpunit.xml.dist 0000644 00000001725 14476011130 0007721 0 ustar 00
./tests/
./src/
performance
src/Locator/ComposerLocator.php 0000644 00000002672 14476011130 0012566 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\Locator;
use Composer\Autoload\ClassLoader;
use Go\ParserReflection\LocatorInterface;
use Go\ParserReflection\ReflectionException;
/**
* Locator, that can find a file for the given class name by asking composer
*/
class ComposerLocator implements LocatorInterface
{
/**
* @var ClassLoader
*/
private $loader;
public function __construct(ClassLoader $loader = null)
{
if (!$loader) {
$loaders = spl_autoload_functions();
foreach ($loaders as $loader) {
if (is_array($loader) && $loader[0] instanceof ClassLoader) {
$loader = $loader[0];
break;
}
}
if (!$loader) {
throw new ReflectionException("Can not found a correct composer loader");
}
}
$this->loader = $loader;
}
/**
* Returns a path to the file for given class name
*
* @param string $className Name of the class
*
* @return string|false Path to the file with given class or false if not found
*/
public function locateClass($className)
{
return $this->loader->findFile($className);
}
}
src/LocatorInterface.php 0000644 00000001207 14476011130 0011265 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
/**
* Locator is responsible to return a file name for given item, typically class
*/
interface LocatorInterface
{
/**
* Returns a path to the file for given class name
*
* @param string $className Name of the class
*
* @return string|false Path to the file with given class or false if not found
*/
public function locateClass($className);
}
src/NodeVisitor/GeneratorDetector.php 0000644 00000002114 14476011130 0013724 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
/**
* Visitor to check if the method body
*/
class GeneratorDetector extends NodeVisitorAbstract
{
private $isGenerator = false;
/**
* {@inheritDoc}
*/
public function enterNode(Node $node)
{
// There may be internal generators in closures, we do not need to look at them
if ($node instanceof Node\Expr\Closure) {
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}
if ($node instanceof Node\Expr\Yield_ || $node instanceof Node\Expr\YieldFrom) {
$this->isGenerator = true;
}
return null;
}
/**
* Returns the
*
* @return bool
*/
public function isGenerator()
{
return $this->isGenerator;
}
}
src/NodeVisitor/StaticVariablesCollector.php 0000644 00000004067 14476011130 0015244 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\NodeVisitor;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
/**
* Visitor to collect static variables in the method/function body and resove them
*/
class StaticVariablesCollector extends NodeVisitorAbstract
{
/**
* Reflection context, eg. ReflectionClass, ReflectionMethod, etc
*
* @var mixed
*/
private $context;
private $staticVariables = [];
/**
* Default constructor
*
* @param mixed $context Reflection context, eg. ReflectionClass, ReflectionMethod, etc
*/
public function __construct($context)
{
$this->context = $context;
}
/**
* {@inheritDoc}
*/
public function enterNode(Node $node)
{
// There may be internal closures, we do not need to look at them
if ($node instanceof Node\Expr\Closure) {
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}
if ($node instanceof Node\Stmt\Static_) {
$expressionSolver = new NodeExpressionResolver($this->context);
$staticVariables = $node->vars;
foreach ($staticVariables as $staticVariable) {
$expr = $staticVariable->default;
if ($expr) {
$expressionSolver->process($expr);
$value = $expressionSolver->getValue();
} else {
$value = null;
}
$this->staticVariables[$staticVariable->name] = $value;
}
}
return null;
}
/**
* Returns an associative map of static variables in the method/function body
*
* @return array
*/
public function getStaticVariables()
{
return $this->staticVariables;
}
}
src/ReflectionClass.php 0000644 00000010372 14476011130 0011124 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionClassLikeTrait;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\TraitUse;
use ReflectionClass as InternalReflectionClass;
/**
* AST-based reflection class
*/
class ReflectionClass extends InternalReflectionClass
{
use ReflectionClassLikeTrait, InternalPropertiesEmulationTrait;
/**
* Initializes reflection instance
*
* @param string|object $argument Class name or instance of object
* @param ClassLike $classLikeNode AST node for class
*/
public function __construct($argument, ClassLike $classLikeNode = null)
{
$fullClassName = is_object($argument) ? get_class($argument) : $argument;
$namespaceParts = explode('\\', $fullClassName);
$this->className = array_pop($namespaceParts);
// Let's unset original read-only property to have a control over it via __get
unset($this->name);
$this->namespaceName = join('\\', $namespaceParts);
$this->classLikeNode = $classLikeNode ?: ReflectionEngine::parseClass($fullClassName);
}
/**
* Parses interfaces from the concrete class node
*
* @param ClassLike $classLikeNode Class-like node
*
* @return array|\ReflectionClass[] List of reflections of interfaces
*/
public static function collectInterfacesFromClassNode(ClassLike $classLikeNode)
{
$interfaces = [];
$isInterface = $classLikeNode instanceof Interface_;
$interfaceField = $isInterface ? 'extends' : 'implements';
$hasInterfaces = in_array($interfaceField, $classLikeNode->getSubNodeNames());
$implementsList = $hasInterfaces ? $classLikeNode->$interfaceField : array();
if ($implementsList) {
foreach ($implementsList as $implementNode) {
if ($implementNode instanceof FullyQualified) {
$implementName = $implementNode->toString();
$interface = interface_exists($implementName, false)
? new parent($implementName)
: new static($implementName);
$interfaces[$implementName] = $interface;
}
}
}
return $interfaces;
}
/**
* Parses traits from the concrete class node
*
* @param ClassLike $classLikeNode Class-like node
* @param array $traitAdaptations List of method adaptations
*
* @return array|\ReflectionClass[] List of reflections of traits
*/
public static function collectTraitsFromClassNode(ClassLike $classLikeNode, array &$traitAdaptations)
{
$traits = [];
if (!empty($classLikeNode->stmts)) {
foreach ($classLikeNode->stmts as $classLevelNode) {
if ($classLevelNode instanceof TraitUse) {
foreach ($classLevelNode->traits as $classTraitName) {
if ($classTraitName instanceof FullyQualified) {
$traitName = $classTraitName->toString();
$trait = trait_exists($traitName, false)
? new parent($traitName)
: new static($traitName);
$traits[$traitName] = $trait;
}
}
$traitAdaptations = $classLevelNode->adaptations;
}
}
}
return $traits;
}
/**
* Emulating original behaviour of reflection
*/
public function __debugInfo()
{
return array(
'name' => $this->getName()
);
}
/**
* Implementation of internal reflection initialization
*
* @return void
*/
protected function __initialize()
{
parent::__construct($this->getName());
}
}
src/ReflectionEngine.php 0000644 00000014637 14476011130 0011274 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser;
use PhpParser\ParserFactory;
/**
* AST-based reflection engine, powered by PHP-Parser
*/
class ReflectionEngine
{
/**
* @var null|LocatorInterface
*/
protected static $locator = null;
/**
* @var array|Node[]
*/
protected static $parsedFiles = array();
/**
* @var null|Parser
*/
protected static $parser = null;
/**
* @var null|NodeTraverser
*/
protected static $traverser = null;
private function __construct() {}
public static function init(LocatorInterface $locator)
{
$refParser = new \ReflectionClass(Parser::class);
$isNewParser = $refParser->isInterface();
if (!$isNewParser) {
self::$parser = new Parser(new Lexer(['usedAttributes' => [
'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', 'startFilePos', 'endFilePos'
]]));
} else {
self::$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
}
self::$traverser = $traverser = new NodeTraverser();
$traverser->addVisitor(new NameResolver());
self::$locator = $locator;
}
/**
* Locates a file name for class
*
* @param string $fullClassName Full name of the class
*
* @return string
*/
public static function locateClassFile($fullClassName)
{
if (class_exists($fullClassName, false)
|| interface_exists($fullClassName, false)
|| trait_exists($fullClassName, false)
) {
$refClass = new \ReflectionClass($fullClassName);
$classFileName = $refClass->getFileName();
} else {
$classFileName = self::$locator->locateClass($fullClassName);
}
if (!$classFileName) {
throw new \InvalidArgumentException("Class $fullClassName was not found by locator");
}
return $classFileName;
}
/**
* Tries to parse a class by name using LocatorInterface
*
* @param string $fullClassName Class name to load
*
* @return ClassLike
*/
public static function parseClass($fullClassName)
{
$classFileName = self::locateClassFile($fullClassName);
$namespaceParts = explode('\\', $fullClassName);
$className = array_pop($namespaceParts);
$namespaceName = join('\\', $namespaceParts);
if ($namespaceName) {
// we have a namespace nodes somewhere
$namespace = self::parseFileNamespace($classFileName, $namespaceName);
$namespaceNodes = $namespace->stmts;
} else {
// global namespace
$namespaceNodes = self::parseFile($classFileName);
}
foreach ($namespaceNodes as $namespaceLevelNode) {
if ($namespaceLevelNode instanceof ClassLike && $namespaceLevelNode->name == $className) {
return $namespaceLevelNode;
}
}
throw new \InvalidArgumentException("Class $fullClassName was not found in the $classFileName");
}
/**
* Parses class method
*
* @param string $fullClassName Name of the class
* @param string $methodName Name of the method
*
* @return ClassMethod
*/
public static function parseClassMethod($fullClassName, $methodName)
{
$class = self::parseClass($fullClassName);
$classNodes = $class->stmts;
foreach ($classNodes as $classLevelNode) {
if ($classLevelNode instanceof ClassMethod && $classLevelNode->name == $methodName) {
return $classLevelNode;
}
}
throw new \InvalidArgumentException("Method $methodName was not found in the $fullClassName");
}
/**
* Parses class property
*
* @param string $fullClassName Name of the class
* @param string $propertyName Name of the property
*
* @return array Pair of [Property and PropertyProperty] nodes
*/
public static function parseClassProperty($fullClassName, $propertyName)
{
$class = self::parseClass($fullClassName);
$classNodes = $class->stmts;
foreach ($classNodes as $classLevelNode) {
if ($classLevelNode instanceof Property) {
foreach ($classLevelNode->props as $classProperty) {
if ($classProperty->name == $propertyName) {
return [$classLevelNode, $classProperty];
}
}
}
}
throw new \InvalidArgumentException("Property $propertyName was not found in the $fullClassName");
}
/**
* Parses a file and returns an AST for it
*
* @param string $fileName Name of the file
*
* @return Node[]
*/
public static function parseFile($fileName)
{
if (isset(self::$parsedFiles[$fileName])) {
return self::$parsedFiles[$fileName];
}
$fileContent = file_get_contents($fileName);
$treeNode = self::$parser->parse($fileContent);
$treeNode = self::$traverser->traverse($treeNode);
self::$parsedFiles[$fileName] = $treeNode;
return $treeNode;
}
/**
* Parses a file namespace and returns an AST for it
*
* @param string $fileName Name of the file
* @param string $namespaceName Namespace name
*
* @return Namespace_
*/
public static function parseFileNamespace($fileName, $namespaceName)
{
$topLevelNodes = self::parseFile($fileName);
// namespaces can be only top-level nodes, so we can scan them directly
foreach ($topLevelNodes as $topLevelNode) {
if ($topLevelNode instanceof Namespace_ && ($topLevelNode->name->toString() == $namespaceName)) {
return $topLevelNode;
}
}
throw new ReflectionException("Namespace $namespaceName was not found in the file $fileName");
}
}
src/ReflectionException.php 0000644 00000000633 14476011130 0012014 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use ReflectionException as BaseException;
/**
* General parser-level reflection exception
*/
class ReflectionException extends BaseException
{
}
src/ReflectionFile.php 0000644 00000006671 14476011130 0010745 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Namespace_;
/**
* AST-based reflector for the source file
*/
class ReflectionFile
{
/**
* Name of the file for reflectino
*
* @var string
*/
protected $fileName;
/**
* List of namespaces in the file
*
* @var ReflectionFileNamespace[]|array
*/
protected $fileNamespaces;
/**
* Top-level nodes for the file
*
* @var Node[]
*/
private $topLevelNodes;
/**
* ReflectionFile constructor.
*
* @param string $fileName Name of the file to reflect
* @param null|array|Node[] $topLevelNodes Optional corresponding list of AST nodes for that file
*/
public function __construct($fileName, $topLevelNodes = null)
{
$this->fileName = $fileName;
$this->topLevelNodes = $topLevelNodes ?: ReflectionEngine::parseFile($fileName);
}
/**
* Returns a namespace from the file or false if no such a namespace
*
* @param string $namespaceName
*
* @return bool|ReflectionFileNamespace
*/
public function getFileNamespace($namespaceName)
{
if ($this->hasFileNamespace($namespaceName)) {
return $this->fileNamespaces[$namespaceName];
}
return false;
}
/**
* Gets the list of namespaces in the file
*
* @return array|ReflectionFileNamespace[]
*/
public function getFileNamespaces()
{
if (!isset($this->fileNamespaces)) {
$this->fileNamespaces = $this->findFileNamespaces();
}
return $this->fileNamespaces;
}
/**
* Returns the name of current reflected file
*
* @return string
*/
public function getName()
{
return $this->fileName;
}
/**
* Returns the presence of namespace in the file
*
* @param string $namespaceName Namespace to check
*
* @return bool
*/
public function hasFileNamespace($namespaceName)
{
$namespaces = $this->getFileNamespaces();
return isset($namespaces[$namespaceName]);
}
/**
* Searches for file namespaces in the given AST
*
* @return array|ReflectionFileNamespace[]
*/
private function findFileNamespaces()
{
$namespaces = array();
// namespaces can be only top-level nodes, so we can scan them directly
foreach ($this->topLevelNodes as $topLevelNode) {
if ($topLevelNode instanceof Namespace_) {
$namespaceName = $topLevelNode->name ? $topLevelNode->name->toString() : '\\';
$namespaces[$namespaceName] = new ReflectionFileNamespace(
$this->fileName,
$namespaceName,
$topLevelNode
);
}
}
if (!$namespaces) {
// if we don't have a namespaces at all, this is global namespace
$globalNamespaceNode = new Namespace_(new FullyQualified(''), $this->topLevelNodes);
$namespaces['\\'] = new ReflectionFileNamespace($this->fileName, '\\', $globalNamespaceNode);
}
return $namespaces;
}
}
src/ReflectionFileNamespace.php 0000644 00000022656 14476011130 0012563 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Const_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
/**
* AST-based reflection for the concrete namespace in the file
*/
class ReflectionFileNamespace
{
/**
* List of classes in the namespace
*
* @var array|ReflectionClass[]
*/
protected $fileClasses;
/**
* List of functions in the namespace
*
* @var array|ReflectionFunction[]
*/
protected $fileFunctions;
/**
* List of constants in the namespace
*
* @var array
*/
protected $fileConstants;
/**
* List of imported namespaces (aliases)
*
* @var array
*/
protected $fileNamespaceAliases;
/**
* Namespace node
*
* @var Namespace_
*/
private $namespaceNode;
/**
* Name of the file
*
* @var string
*/
private $fileName;
/**
* File namespace constructor
*
* @param string $fileName Name of the file
* @param string $namespaceName Name of the namespace
* @param Namespace_|null $namespaceNode Optional AST-node for this namespace block
*/
public function __construct($fileName, $namespaceName, Namespace_ $namespaceNode = null)
{
if (!$namespaceNode) {
$namespaceNode = ReflectionEngine::parseFileNamespace($fileName, $namespaceName);
}
$this->namespaceNode = $namespaceNode;
$this->fileName = $fileName;
}
/**
* Returns the concrete class from the file namespace or false if there is no class
*
* @param string $className
*
* @return bool|ReflectionClass
*/
public function getClass($className)
{
if ($this->hasClass($className)) {
return $this->fileClasses[$className];
}
return false;
}
/**
* Gets list of classes in the namespace
*
* @return ReflectionClass[]|array
*/
public function getClasses()
{
if (!isset($this->fileClasses)) {
$this->fileClasses = $this->findClasses();
}
return $this->fileClasses;
}
/**
* Returns a value for the constant
*
* @param string $constantName name of the constant to fetch
*
* @return bool|mixed
*/
public function getConstant($constantName)
{
if ($this->hasConstant($constantName)) {
return $this->fileConstants[$constantName];
}
return false;
}
/**
* Returns a list of defined constants in the namespace
*
* @return array
*/
public function getConstants()
{
if (!isset($this->fileConstants)) {
$this->fileConstants = $this->findConstants();
}
return $this->fileConstants;
}
/**
* Gets doc comments from a class.
*
* @return string|false The doc comment if it exists, otherwise "false"
*/
public function getDocComment()
{
$docComment = false;
$comments = $this->namespaceNode->getAttribute('comments');
if ($comments) {
$docComment = (string) $comments[0];
}
return $docComment;
}
/**
* Gets starting line number
*
* @return integer
*/
public function getEndLine()
{
return $this->namespaceNode->getAttribute('endLine');
}
/**
* Returns the name of file
*
* @return string
*/
public function getFileName()
{
return $this->fileName;
}
/**
* Returns the concrete function from the file namespace or false if there is no function
*
* @param string $functionName
*
* @return bool|ReflectionFunction
*/
public function getFunction($functionName)
{
if ($this->hasFunction($functionName)) {
return $this->fileFunctions[$functionName];
}
return false;
}
/**
* Gets list of functions in the namespace
*
* @return ReflectionFunction[]|array
*/
public function getFunctions()
{
if (!isset($this->fileFunctions)) {
$this->fileFunctions = $this->findFunctions();
}
return $this->fileFunctions;
}
/**
* Gets namespace name
*
* @return string
*/
public function getName()
{
$nameNode = $this->namespaceNode->name;
return $nameNode ? $nameNode->toString() : '';
}
/**
* Returns a list of namespace aliases
*
* @return array
*/
public function getNamespaceAliases()
{
if (!isset($this->fileNamespaceAliases)) {
$this->fileNamespaceAliases = $this->findNamespaceAliases();
}
return $this->fileNamespaceAliases;
}
/**
* Gets starting line number
*
* @return integer
*/
public function getStartLine()
{
return $this->namespaceNode->getAttribute('startLine');
}
/**
* Checks if the given class is present in this filenamespace
*
* @param string $className
*
* @return bool
*/
public function hasClass($className)
{
$classes = $this->getClasses();
return isset($classes[$className]);
}
/**
* Checks if the given constant is present in this filenamespace
*
* @param string $constantName
*
* @return bool
*/
public function hasConstant($constantName)
{
$constants = $this->getConstants();
return isset($constants[$constantName]);
}
/**
* Checks if the given function is present in this filenamespace
*
* @param string $functionName
*
* @return bool
*/
public function hasFunction($functionName)
{
$functions = $this->getFunctions();
return isset($functions[$functionName]);
}
/**
* Searches for classes in the given AST
*
* @return array|ReflectionClass[]
*/
private function findClasses()
{
$classes = array();
$namespaceName = $this->getName();
// classes can be only top-level nodes in the namespace, so we can scan them directly
foreach ($this->namespaceNode->stmts as $namespaceLevelNode) {
if ($namespaceLevelNode instanceof ClassLike) {
$classShortName = $namespaceLevelNode->name;
$className = $namespaceName ? $namespaceName .'\\' . $classShortName : $classShortName;
$namespaceLevelNode->setAttribute('fileName', $this->fileName);
$classes[$className] = new ReflectionClass($className, $namespaceLevelNode);
}
}
return $classes;
}
/**
* Searches for functions in the given AST
*
* @return array
*/
private function findFunctions()
{
$functions = array();
$namespaceName = $this->getName();
// functions can be only top-level nodes in the namespace, so we can scan them directly
foreach ($this->namespaceNode->stmts as $namespaceLevelNode) {
if ($namespaceLevelNode instanceof Function_) {
$funcShortName = $namespaceLevelNode->name;
$functionName = $namespaceName ? $namespaceName .'\\' . $funcShortName : $funcShortName;
$namespaceLevelNode->setAttribute('fileName', $this->fileName);
$functions[$funcShortName] = new ReflectionFunction($functionName, $namespaceLevelNode);
}
}
return $functions;
}
/**
* Searches for constants in the given AST
*
* @return array
*/
private function findConstants()
{
$constants = array();
$expressionSolver = new NodeExpressionResolver($this);
// constants can be only top-level nodes in the namespace, so we can scan them directly
foreach ($this->namespaceNode->stmts as $namespaceLevelNode) {
if ($namespaceLevelNode instanceof Const_) {
$nodeConstants = $namespaceLevelNode->consts;
if (!empty($nodeConstants)) {
foreach ($nodeConstants as $nodeConstant) {
$expressionSolver->process($nodeConstant->value);
$constants[$nodeConstant->name] = $expressionSolver->getValue();
}
}
}
}
return $constants;
}
/**
* Searchse for namespace aliases for the current block
*
* @return array
*/
private function findNamespaceAliases()
{
$namespaceAliases = [];
// aliases can be only top-level nodes in the namespace, so we can scan them directly
foreach ($this->namespaceNode->stmts as $namespaceLevelNode) {
if ($namespaceLevelNode instanceof Use_) {
$useAliases = $namespaceLevelNode->uses;
if (!empty($useAliases)) {
foreach ($useAliases as $useNode) {
$namespaceAliases[$useNode->name->toString()] = $useNode->alias;
}
}
}
}
return $namespaceAliases;
}
}
src/ReflectionFunction.php 0000644 00000006532 14476011130 0011647 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionFunctionLikeTrait;
use PhpParser\Node\Stmt\Function_;
use ReflectionFunction as BaseReflectionFunction;
/**
* AST-based reflection for function
*/
class ReflectionFunction extends BaseReflectionFunction
{
use ReflectionFunctionLikeTrait, InternalPropertiesEmulationTrait;
/**
* Initializes reflection instance for given AST-node
*
* @param string|\Closure $functionName The name of the function to reflect or a closure.
* @param Function_|null $functionNode Function node AST
*/
public function __construct($functionName, Function_ $functionNode)
{
$namespaceParts = explode('\\', $functionName);
// Remove the last one part with function name
array_pop($namespaceParts);
$this->namespaceName = join('\\', $namespaceParts);
$this->functionLikeNode = $functionNode;
unset($this->name);
}
/**
* Emulating original behaviour of reflection
*/
public function __debugInfo()
{
$nodeName = 'unknown';
if ($this->functionLikeNode instanceof Function_) {
$nodeName = $this->functionLikeNode->name;
}
return ['name' => $nodeName];
}
/**
* {@inheritDoc}
*/
public function getClosure()
{
$this->initializeInternalReflection();
return parent::getClosure();
}
/**
* {@inheritDoc}
*/
public function invoke($args = null)
{
$this->initializeInternalReflection();
return call_user_func_array('parent::invoke', func_get_args());
}
/**
* {@inheritDoc}
*/
public function invokeArgs(array $args)
{
$this->initializeInternalReflection();
return parent::invokeArgs($args);
}
/**
* Checks if function is disabled
*
* Only internal functions can be disabled using disable_functions directive.
* User-defined functions are unaffected.
*/
public function isDisabled()
{
return false;
}
/**
* Returns textual representation of function
*
* @return string
*/
public function __toString()
{
$paramFormat = ($this->getNumberOfParameters() > 0) ? "\n\n - Parameters [%d] {%s\n }" : '';
$reflectionFormat = "%sFunction [ function %s ] {\n @@ %s %d - %d{$paramFormat}\n}\n";
return sprintf(
$reflectionFormat,
$this->getDocComment() ? $this->getDocComment() . "\n" : '',
$this->getName(),
$this->getFileName(),
$this->getStartLine(),
$this->getEndLine(),
count($this->getParameters()),
array_reduce($this->getParameters(), function ($str, ReflectionParameter $param) {
return $str . "\n " . $param;
}, '')
);
}
/**
* Implementation of internal reflection initialization
*
* @return void
*/
protected function __initialize()
{
parent::__construct($this->getName());
}
}
src/ReflectionMethod.php 0000644 00000017413 14476011130 0011302 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionFunctionLikeTrait;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use ReflectionMethod as BaseReflectionMethod;
/**
* AST-based reflection for the method in a class
*/
class ReflectionMethod extends BaseReflectionMethod
{
use ReflectionFunctionLikeTrait, InternalPropertiesEmulationTrait;
/**
* Name of the class
*
* @var string
*/
private $className;
/**
* Initializes reflection instance for the method node
*
* @param string $className Name of the class
* @param string $methodName Name of the method
* @param ClassMethod $classMethodNode AST-node for method
*/
public function __construct($className, $methodName, ClassMethod $classMethodNode = null)
{
//for some reason, ReflectionMethod->getNamespaceName in php always returns '', so we shouldn't use it too
$this->className = $className;
$this->functionLikeNode = $classMethodNode ?: ReflectionEngine::parseClassMethod($className, $methodName);
// Let's unset original read-only properties to have a control over them via __get
unset($this->name, $this->class);
}
/**
* Emulating original behaviour of reflection
*/
public function __debugInfo()
{
return [
'name' => $this->getClassMethodNode()->name,
'class' => $this->className
];
}
/**
* Returns the string representation of the Reflection method object.
*
* @link http://php.net/manual/en/reflectionmethod.tostring.php
*
* @return string
*/
public function __toString()
{
$paramFormat = ($this->getNumberOfParameters() > 0) ? "\n\n - Parameters [%d] {%s\n }" : '';
$methodParameters = $this->getParameters();
try {
$prototype = $this->getPrototype();
} catch (\ReflectionException $e) {
$prototype = null;
}
$prototypeClass = $prototype ? $prototype->getDeclaringClass()->name : '';
$paramString = '';
$identation = str_repeat(' ', 4);
foreach ($methodParameters as $methodParameter) {
$paramString .= "\n{$identation}" . $methodParameter;
}
return sprintf(
"%sMethod [ %s%s%s %s method %s ] {\n @@ %s %d - %d{$paramFormat}\n}\n",
$this->getDocComment() ? $this->getDocComment() . "\n" : '',
$prototype ? ", overwrites {$prototypeClass}, prototype {$prototypeClass}" : '',
$this->isConstructor() ? ', ctor' : '',
$this->isDestructor() ? ', dtor' : '',
$this->isFinal() ? ' final' : '',
$this->isStatic() ? ' static' : '',
$this->isAbstract() ? ' abstract' : '',
join(' ', \Reflection::getModifierNames($this->getModifiers() & 1792)),
$this->getName(),
$this->getFileName(),
$this->getStartLine(),
$this->getEndLine(),
count($methodParameters),
$paramString
);
}
/**
* {@inheritDoc}
*/
public function getClosure($object)
{
$this->initializeInternalReflection();
return parent::getClosure($object);
}
/**
* {@inheritDoc}
*/
public function getDeclaringClass()
{
return new ReflectionClass($this->className);
}
/**
* {@inheritDoc}
*/
public function getModifiers()
{
$modifiers = 0;
if ($this->isPublic()) {
$modifiers += self::IS_PUBLIC;
}
if ($this->isProtected()) {
$modifiers += self::IS_PROTECTED;
}
if ($this->isPrivate()) {
$modifiers += self::IS_PRIVATE;
}
if ($this->isAbstract()) {
$modifiers += self::IS_ABSTRACT;
}
if ($this->isFinal()) {
$modifiers += self::IS_FINAL;
}
if ($this->isStatic()) {
$modifiers += self::IS_STATIC;
}
return $modifiers;
}
/**
* {@inheritDoc}
*/
public function getPrototype()
{
$parent = $this->getDeclaringClass()->getParentClass();
if (!$parent) {
throw new ReflectionException("No prototype");
}
$prototypeMethod = $parent->getMethod($this->getName());
if (!$prototypeMethod) {
throw new ReflectionException("No prototype");
}
return $prototypeMethod;
}
/**
* {@inheritDoc}
*/
public function invoke($object, $args = null)
{
$this->initializeInternalReflection();
return call_user_func_array('parent::invoke', func_get_args());
}
/**
* {@inheritDoc}
*/
public function invokeArgs($object, array $args)
{
$this->initializeInternalReflection();
return parent::invokeArgs($object, $args);
}
/**
* {@inheritDoc}
*/
public function isAbstract()
{
return $this->getClassMethodNode()->isAbstract();
}
/**
* {@inheritDoc}
*/
public function isConstructor()
{
return $this->getClassMethodNode()->name == '__construct';
}
/**
* {@inheritDoc}
*/
public function isDestructor()
{
return $this->getClassMethodNode()->name == '__destruct';
}
/**
* {@inheritDoc}
*/
public function isFinal()
{
return $this->getClassMethodNode()->isFinal();
}
/**
* {@inheritDoc}
*/
public function isPrivate()
{
return $this->getClassMethodNode()->isPrivate();
}
/**
* {@inheritDoc}
*/
public function isProtected()
{
return $this->getClassMethodNode()->isProtected();
}
/**
* {@inheritDoc}
*/
public function isPublic()
{
return $this->getClassMethodNode()->isPublic();
}
/**
* {@inheritDoc}
*/
public function isStatic()
{
return $this->getClassMethodNode()->isStatic();
}
/**
* {@inheritDoc}
*/
public function setAccessible($accessible)
{
$this->initializeInternalReflection();
parent::setAccessible($accessible);
}
/**
* Parses methods from the concrete class node
*
* @param ClassLike $classLikeNode Class-like node
* @param string $fullClassName FQN of the class
*
* @return array|ReflectionMethod[]
*/
public static function collectFromClassNode(ClassLike $classLikeNode, $fullClassName)
{
$methods = [];
foreach ($classLikeNode->stmts as $classLevelNode) {
if ($classLevelNode instanceof ClassMethod) {
$classLevelNode->setAttribute('fileName', $classLikeNode->getAttribute('fileName'));
$methodName = $classLevelNode->name;
$methods[$methodName] = new ReflectionMethod(
$fullClassName,
$methodName,
$classLevelNode
);
}
}
return $methods;
}
/**
* Implementation of internal reflection initialization
*
* @return void
*/
protected function __initialize()
{
parent::__construct($this->className, $this->getName());
}
/**
* Returns ClassMethod node to prevent all possible type checks with instanceof
*
* @return ClassMethod
*/
private function getClassMethodNode()
{
return $this->functionLikeNode;
}
}
src/ReflectionParameter.php 0000644 00000021724 14476011130 0012002 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use ReflectionParameter as BaseReflectionParameter;
/**
* AST-based reflection for method/function parameter
*/
class ReflectionParameter extends BaseReflectionParameter
{
use InternalPropertiesEmulationTrait;
/**
* Reflection function or method
*
* @var \ReflectionFunctionAbstract
*/
private $declaringFunction;
/**
* Stores the default value for node (if present)
*
* @var mixed
*/
private $defaultValue = null;
/**
* Whether or not default value is constant
*
* @var bool
*/
private $isDefaultValueConstant = false;
/**
* Name of the constant of default value
*
* @var string
*/
private $defaultValueConstantName;
/**
* Index of parameter in the list
*
* @var int
*/
private $parameterIndex = 0;
/**
* Concrete parameter node
*
* @var Param
*/
private $parameterNode;
/**
* Initializes a reflection for the property
*
* @param string|array $unusedFunctionName Name of the function/method
* @param string $parameterName Name of the parameter to reflect
* @param Param $parameterNode Parameter definition node
* @param int $parameterIndex Index of parameter
* @param \ReflectionFunctionAbstract $declaringFunction
*/
public function __construct(
$unusedFunctionName,
$parameterName,
Param $parameterNode = null,
$parameterIndex = 0,
\ReflectionFunctionAbstract $declaringFunction = null
) {
// Let's unset original read-only property to have a control over it via __get
unset($this->name);
$this->parameterNode = $parameterNode;
$this->parameterIndex = $parameterIndex;
$this->declaringFunction = $declaringFunction;
if ($this->isDefaultValueAvailable()) {
if ($declaringFunction instanceof \ReflectionMethod) {
$context = $declaringFunction->getDeclaringClass();
} else {
$context = $declaringFunction;
};
$expressionSolver = new NodeExpressionResolver($context);
$expressionSolver->process($this->parameterNode->default);
$this->defaultValue = $expressionSolver->getValue();
$this->isDefaultValueConstant = $expressionSolver->isConstant();
$this->defaultValueConstantName = $expressionSolver->getConstantName();
}
}
/**
* Emulating original behaviour of reflection
*/
public function __debugInfo()
{
return array(
'name' => $this->parameterNode->name,
);
}
/**
* Returns string representation of this parameter.
*
* @return string
*/
public function __toString()
{
$parameterType = $this->parameterNode->type;
if (is_object($parameterType)) {
$parameterType = $parameterType->toString();
}
$isNullableParam = !empty($parameterType) && $this->allowsNull();
$isOptional = $this->isOptional();
$defaultValue = '';
if ($isOptional) {
$defaultValue = $this->getDefaultValue();
if (is_string($defaultValue) && strlen($defaultValue) > 15) {
$defaultValue = substr($defaultValue, 0, 15) . '...';
}
$defaultValue = str_replace('\\\\', '\\', var_export($defaultValue, true));
}
return sprintf(
'Parameter #%d [ %s %s%s%s%s$%s%s ]',
$this->parameterIndex,
($this->isVariadic() || $isOptional) ? '' : '',
$parameterType ? ltrim($parameterType, '\\') . ' ' : '',
$isNullableParam ? 'or NULL ' : '',
$this->isVariadic() ? '...' : '',
$this->isPassedByReference() ? '&' : '',
$this->getName(),
$isOptional ? (' = ' . $defaultValue) : ''
);
}
/**
* {@inheritDoc}
*/
public function allowsNull()
{
$hasDefaultNull = $this->isDefaultValueAvailable() && $this->getDefaultValue() === null;
if ($hasDefaultNull) {
return true;
}
return !isset($this->parameterNode->type);
}
/**
* {@inheritDoc}
*/
public function canBePassedByValue()
{
return !$this->isPassedByReference();
}
/**
* @inheritDoc
*/
public function getClass()
{
$parameterType = $this->parameterNode->type;
if ($parameterType instanceof Name) {
if (!$parameterType instanceof Name\FullyQualified) {
throw new ReflectionException("Can not resolve a class name for parameter");
}
$className = $parameterType->toString();
$classExists = class_exists($className, false);
return $classExists ? new \ReflectionClass($className) : new ReflectionClass($className);
}
return null;
}
/**
* {@inheritDoc}
*/
public function getDeclaringClass()
{
if ($this->declaringFunction instanceof \ReflectionMethod) {
return $this->declaringFunction->getDeclaringClass();
};
return null;
}
/**
* {@inheritDoc}
*/
public function getDeclaringFunction()
{
return $this->declaringFunction;
}
/**
* {@inheritDoc}
*/
public function getDefaultValue()
{
if (!$this->isDefaultValueAvailable()) {
throw new ReflectionException('Internal error: Failed to retrieve the default value');
}
return $this->defaultValue;
}
/**
* {@inheritDoc}
*/
public function getDefaultValueConstantName()
{
if (!$this->isDefaultValueAvailable()) {
throw new ReflectionException('Internal error: Failed to retrieve the default value');
}
return $this->defaultValueConstantName;
}
/**
* @inheritDoc
*/
public function getName()
{
return $this->parameterNode->name;
}
/**
* {@inheritDoc}
*/
public function getPosition()
{
return $this->parameterIndex;
}
/**
* @inheritDoc
*/
public function getType()
{
$isBuiltin = false;
$allowsNull = $this->allowsNull();
$parameterType = $this->parameterNode->type;
if (is_object($parameterType)) {
$parameterType = $parameterType->toString();
} elseif (is_string($parameterType)) {
$isBuiltin = true;
}
return new ReflectionType($parameterType, $allowsNull, $isBuiltin);
}
/**
* @inheritDoc
*/
public function hasType()
{
$hasType = isset($this->parameterNode->type);
return $hasType;
}
/**
* @inheritDoc
*/
public function isArray()
{
return 'array' === $this->parameterNode->type;
}
/**
* @inheritDoc
*/
public function isCallable()
{
return 'callable' === $this->parameterNode->type;
}
/**
* @inheritDoc
*/
public function isDefaultValueAvailable()
{
return isset($this->parameterNode->default);
}
/**
* {@inheritDoc}
*/
public function isDefaultValueConstant()
{
return $this->isDefaultValueConstant;
}
/**
* {@inheritDoc}
*/
public function isOptional()
{
return $this->isDefaultValueAvailable() && $this->haveSiblingsDefalutValues();
}
/**
* @inheritDoc
*/
public function isPassedByReference()
{
return (bool) $this->parameterNode->byRef;
}
/**
* @inheritDoc
*/
public function isVariadic()
{
return (bool) $this->parameterNode->variadic;
}
/**
* Returns if all following parameters have a default value definition.
*
* @return bool
* @throws ReflectionException If could not fetch declaring function reflection
*/
protected function haveSiblingsDefalutValues()
{
$function = $this->getDeclaringFunction();
if (null === $function) {
throw new ReflectionException('Could not get the declaring function reflection.');
}
/** @var \ReflectionParameter[] $remainingParameters */
$remainingParameters = array_slice($function->getParameters(), $this->parameterIndex + 1);
foreach ($remainingParameters as $reflectionParameter) {
if (!$reflectionParameter->isDefaultValueAvailable()) {
return false;
}
}
return true;
}
}
src/ReflectionProperty.php 0000644 00000014447 14476011130 0011712 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use Go\ParserReflection\Traits\InitializationTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\PropertyProperty;
use ReflectionProperty as BaseReflectionProperty;
/**
* AST-based reflection for class property
*/
class ReflectionProperty extends BaseReflectionProperty
{
use InitializationTrait, InternalPropertiesEmulationTrait;
/**
* Type of property node
*
* @var Property
*/
private $propertyTypeNode;
/**
* Concrete property node
*
* @var PropertyProperty
*/
private $propertyNode;
/**
* Name of the class
*
* @var string
*/
private $className;
/**
* Initializes a reflection for the property
*
* @param string $className Name of the class with properties
* @param string $propertyName Name of the property to reflect
* @param Property $propertyType Property type definition node
* @param PropertyProperty $propertyNode Concrete property definition (value, name)
*/
public function __construct(
$className,
$propertyName,
Property $propertyType = null,
PropertyProperty $propertyNode = null
) {
$this->className = $className;
if (!$propertyType || !$propertyNode) {
list ($propertyType, $propertyNode) = ReflectionEngine::parseClassProperty($className, $propertyName);
}
$this->propertyTypeNode = $propertyType;
$this->propertyNode = $propertyNode;
// Let's unset original read-only properties to have a control over them via __get
unset($this->name, $this->class);
}
/**
* Emulating original behaviour of reflection
*/
public function __debugInfo()
{
return array(
'name' => isset($this->propertyNode) ? $this->propertyNode->name : 'unknown',
'class' => $this->className
);
}
/**
* Return string representation of this little old property.
*
* @return string
*/
public function __toString()
{
return sprintf(
"Property [%s %s $%s ]\n",
$this->isStatic() ? '' : ($this->isDefault() ? ' ' : ' '),
join(' ', \Reflection::getModifierNames($this->getModifiers())),
$this->getName()
);
}
/**
* {@inheritDoc}
*/
public function getDeclaringClass()
{
return new ReflectionClass($this->className);
}
/**
* @inheritDoc
*/
public function getDocComment()
{
$docBlock = $this->propertyTypeNode->getDocComment();
return $docBlock ? $docBlock->getText() : false;
}
/**
* {@inheritDoc}
*/
public function getModifiers()
{
$modifiers = 0;
if ($this->isPublic()) {
$modifiers += self::IS_PUBLIC;
}
if ($this->isProtected()) {
$modifiers += self::IS_PROTECTED;
}
if ($this->isPrivate()) {
$modifiers += self::IS_PRIVATE;
}
if ($this->isStatic()) {
$modifiers += self::IS_STATIC;
}
return $modifiers;
}
/**
* @inheritDoc
*/
public function getName()
{
return $this->propertyNode->name;
}
/**
* @inheritDoc
*/
public function getValue($object = null)
{
if (!isset($object)) {
$solver = new NodeExpressionResolver($this->getDeclaringClass());
if (!isset($this->propertyNode->default)) {
return null;
}
$solver->process($this->propertyNode->default);
return $solver->getValue();
}
$this->initializeInternalReflection();
return parent::getValue($object);
}
/**
* @inheritDoc
*/
public function isDefault()
{
return isset($this->propertyNode->default);
}
/**
* {@inheritDoc}
*/
public function isPrivate()
{
return $this->propertyTypeNode->isPrivate();
}
/**
* {@inheritDoc}
*/
public function isProtected()
{
return $this->propertyTypeNode->isProtected();
}
/**
* {@inheritDoc}
*/
public function isPublic()
{
return $this->propertyTypeNode->isPublic();
}
/**
* {@inheritDoc}
*/
public function isStatic()
{
return $this->propertyTypeNode->isStatic();
}
/**
* {@inheritDoc}
*/
public function setAccessible($accessible)
{
$this->initializeInternalReflection();
parent::setAccessible($accessible);
}
/**
* @inheritDoc
*/
public function setValue($object, $value = null)
{
$this->initializeInternalReflection();
parent::setValue($object, $value);
}
/**
* Parses properties from the concrete class node
*
* @param ClassLike $classLikeNode Class-like node
* @param string $fullClassName FQN of the class
*
* @return array|ReflectionProperty[]
*/
public static function collectFromClassNode(ClassLike $classLikeNode, $fullClassName)
{
$properties = [];
foreach ($classLikeNode->stmts as $classLevelNode) {
if ($classLevelNode instanceof Property) {
foreach ($classLevelNode->props as $classPropertyNode) {
$propertyName = $classPropertyNode->name;
$properties[$propertyName] = new static(
$fullClassName,
$propertyName,
$classLevelNode,
$classPropertyNode
);
}
}
}
return $properties;
}
/**
* Implementation of internal reflection initialization
*
* @return void
*/
protected function __initialize()
{
parent::__construct($this->className, $this->getName());
}
}
src/ReflectionType.php 0000644 00000002326 14476011130 0011000 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection;
use ReflectionType as BaseReflectionType;
/**
* ReflectionType implementation
*/
class ReflectionType extends BaseReflectionType
{
/**
* If type allows null or not
*
* @var boolean
*/
private $allowsNull;
/**
* Is type built-in or not
*
* @var
*/
private $isBuiltin;
/**
* @var string Type name
*/
private $type;
/**
* Initializes reflection data
*/
public function __construct($type, $allowsNull, $isBuiltin)
{
$this->type = $type;
$this->allowsNull = $allowsNull;
$this->isBuiltin = $isBuiltin;
}
/**
* @inheritDoc
*/
public function allowsNull()
{
return $this->allowsNull;
}
/**
* @inheritDoc
*/
public function isBuiltin()
{
return $this->isBuiltin;
}
/**
* @inheritDoc
*/
public function __toString()
{
return $this->type;
}
}
src/Traits/InitializationTrait.php 0000644 00000002043 14476011130 0013301 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\Traits;
trait InitializationTrait
{
/**
* Is internal reflection is initialized or not
*
* @var boolean
*/
private $isInitialized = false;
/**
* Initializes internal reflection for calling misc runtime methods
*/
public function initializeInternalReflection()
{
if (!$this->isInitialized) {
$this->__initialize();
$this->isInitialized = true;
}
}
/**
* Returns the status of initialization status for internal object
*
* @return bool
*/
public function isInitialized()
{
return $this->isInitialized;
}
/**
* Implementation of internal reflection initialization
*
* @return void
*/
abstract protected function __initialize();
}
src/Traits/InternalPropertiesEmulationTrait.php 0000644 00000002060 14476011130 0016020 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\Traits;
/**
* Class for emulating internal properties behaviour
*/
trait InternalPropertiesEmulationTrait
{
/**
* Magic method that should be defined to provide an info about internal properties
*
* @return array
*/
abstract public function __debugInfo();
/**
* Magic implementation of properties
*
* @param string $propertyName Name of the property to return
*
* @return null|mixed
*/
public function __get($propertyName)
{
$internalInfo = $this->__debugInfo();
if (!isset($internalInfo[$propertyName])) {
$className = get_class($this);
trigger_error("Undefined property {$className}::\${$propertyName}");
return null;
}
return $internalInfo[$propertyName];
}
}
src/Traits/ReflectionClassLikeTrait.php 0000644 00000063430 14476011130 0014206 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\Traits;
use Go\ParserReflection\ReflectionClass;
use Go\ParserReflection\ReflectionException;
use Go\ParserReflection\ReflectionMethod;
use Go\ParserReflection\ReflectionProperty;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TraitUseAdaptation;
/**
* General class-like reflection
*/
trait ReflectionClassLikeTrait
{
use InitializationTrait;
/**
* @var ClassLike
*/
protected $classLikeNode;
/**
* Short name of the class, without namespace
*
* @var string
*/
protected $className;
/**
* List of all constants from the class
*
* @var array
*/
protected $constants;
/**
* Interfaces, empty array or null if not initialized yet
*
* @var \ReflectionClass[]|array|null
*/
protected $interfaceClasses;
/**
* List of traits, empty array or null if not initialized yet
*
* @var \ReflectionClass[]|array|null
*/
protected $traits;
/**
* Additional list of trait adaptations
*
* @var TraitUseAdaptation[]|array
*/
protected $traitAdaptations;
/**
* @var array|ReflectionMethod[]
*/
protected $methods;
/**
* Namespace name
*
* @var string
*/
protected $namespaceName = '';
/**
* Parent class, or false if not present, null if uninitialized yet
*
* @var \ReflectionClass|false|null
*/
protected $parentClass;
/**
* @var array|ReflectionProperty[]
*/
protected $properties;
/**
* Returns the string representation of the ReflectionClass object.
*
* @return string
*/
public function __toString()
{
$isObject = $this instanceof \ReflectionObject;
$staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
$format = "%s [ %sclass %s%s%s ] {\n";
$format .= " @@ %s %d-%d\n\n";
$format .= " - Constants [%d] {%s\n }\n\n";
$format .= " - Static properties [%d] {%s\n }\n\n";
$format .= " - Static methods [%d] {%s\n }\n\n";
$format .= " - Properties [%d] {%s\n }\n\n";
$format .= ($isObject ? " - Dynamic properties [%d] {%s\n }\n\n" : '%s%s');
$format .= " - Methods [%d] {%s\n }\n";
$format .= "}\n";
foreach ($this->getProperties() as $property) {
if ($property->isStatic()) {
$staticProperties[] = $property;
} elseif ($property->isDefault()) {
$defaultProperties[] = $property;
} else {
$dynamicProperties[] = $property;
}
}
foreach ($this->getMethods() as $method) {
if ($method->isStatic()) {
$staticMethods[] = $method;
} else {
$methods[] = $method;
}
}
$buildString = function (array $items, $indentLevel = 4) {
if (!count($items)) {
return '';
}
$indent = "\n" . str_repeat(' ', $indentLevel);
return $indent . implode($indent, explode("\n", implode("\n", $items)));
};
$buildConstants = function (array $items, $indentLevel = 4) {
$str = '';
foreach ($items as $name => $value) {
$str .= "\n" . str_repeat(' ', $indentLevel);
$str .= sprintf(
'Constant [ %s %s ] { %s }',
gettype($value),
$name,
$value
);
}
return $str;
};
$interfaceNames = $this->getInterfaceNames();
$parentClass = $this->getParentClass();
$modifiers = '';
if ($this->isAbstract()) {
$modifiers = 'abstract ';
} elseif ($this->isFinal()) {
$modifiers = 'final ';
};
$string = sprintf(
$format,
($isObject ? 'Object of class' : 'Class'),
$modifiers,
$this->getName(),
false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
$interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
$this->getFileName(),
$this->getStartLine(),
$this->getEndLine(),
count($this->getConstants()),
$buildConstants($this->getConstants()),
count($staticProperties),
$buildString($staticProperties),
count($staticMethods),
$buildString($staticMethods),
count($defaultProperties),
$buildString($defaultProperties),
$isObject ? count($dynamicProperties) : '',
$isObject ? $buildString($dynamicProperties) : '',
count($methods),
$buildString($methods)
);
return $string;
}
/**
* {@inheritDoc}
*/
public function getConstant($name)
{
if ($this->hasConstant($name)) {
return $this->constants[$name];
}
return false;
}
/**
* {@inheritDoc}
*/
public function getConstants()
{
if (!isset($this->constants)) {
$directConstants = $this->findConstants();
$parentConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
$result += $instance->getConstants();
});
$constants = $directConstants + $parentConstants;
$this->constants = $constants;
}
return $this->constants;
}
/**
* {@inheritDoc}
*/
public function getConstructor()
{
$constructor = $this->getMethod('__construct');
if (!$constructor) {
return null;
}
return $constructor;
}
/**
* Gets default properties from a class (including inherited properties).
*
* @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
*
* @return array An array of default properties, with the key being the name of the property and the value being
* the default value of the property or NULL if the property doesn't have a default value
*/
public function getDefaultProperties()
{
$defaultValues = [];
$properties = $this->getProperties();
$staticOrder = [true, false];
foreach ($staticOrder as $shouldBeStatic) {
foreach ($properties as $property) {
$isStaticProperty = $property->isStatic();
if ($shouldBeStatic !== $isStaticProperty) {
continue;
}
$propertyName = $property->getName();
$isInternalReflection = get_class($property) == \ReflectionProperty::class;
if (!$isInternalReflection || $isStaticProperty) {
$defaultValues[$propertyName] = $property->getValue();
} elseif (!$isStaticProperty) {
// Internal reflection and dynamic property
$classProperties = $property->getDeclaringClass()->getDefaultProperties();
$defaultValues[$propertyName] = $classProperties[$propertyName];
}
}
}
return $defaultValues;
}
/**
* {@inheritDoc}
*/
public function getDocComment()
{
$docComment = $this->classLikeNode->getDocComment();
return $docComment ? $docComment->getText() : false;
}
public function getEndLine()
{
return $this->classLikeNode->getAttribute('endLine');
}
public function getExtension()
{
return null;
}
public function getExtensionName()
{
return false;
}
public function getFileName()
{
return $this->classLikeNode->getAttribute('fileName');
}
/**
* {@inheritDoc}
*/
public function getInterfaceNames()
{
return array_keys($this->getInterfaces());
}
/**
* {@inheritDoc}
*/
public function getInterfaces()
{
if (!isset($this->interfaceClasses)) {
$this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
if ($instance->isInterface()) {
$result[$instance->name] = $instance;
}
$result += $instance->getInterfaces();
});
}
return $this->interfaceClasses;
}
/**
* {@inheritdoc}
*/
public function getMethod($name)
{
$methods = $this->getMethods();
foreach ($methods as $method) {
if ($method->getName() == $name) {
return $method;
}
}
return false;
}
/**
* Returns list of reflection methods
*
* @param null|integer $filter Optional filter
*
* @return array|\ReflectionMethod[]
*/
public function getMethods($filter = null)
{
if (!isset($this->methods)) {
$directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this->getName());
$parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
$reflectionMethods = [];
foreach ($instance->getMethods() as $reflectionMethod) {
if (!$isParent || !$reflectionMethod->isPrivate()) {
$reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
}
}
$result += $reflectionMethods;
});
$methods = $directMethods + $parentMethods;
$this->methods = $methods;
}
if (!isset($filter)) {
return array_values($this->methods);
}
$methods = [];
foreach ($this->methods as $method) {
if (!($filter & $method->getModifiers())) {
continue;
}
$methods[] = $method;
}
return $methods;
}
/**
* Returns a bitfield of the access modifiers for this class.
*
* @link http://php.net/manual/en/reflectionclass.getmodifiers.php
*
* NB: this method is not fully compatible with original value because of hidden internal constants
*
* @return int
*/
public function getModifiers()
{
$modifiers = 0;
if ($this->isFinal()) {
$modifiers += \ReflectionClass::IS_FINAL;
}
if (PHP_VERSION_ID < 70000 && $this->isTrait()) {
$modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
}
if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
$modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
}
if ($this->isInterface()) {
$abstractMethods = $this->getMethods();
} else {
$abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
}
if (!empty($abstractMethods)) {
$modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
}
return $modifiers;
}
/**
* {@inheritDoc}
*/
public function getName()
{
$namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
return $namespaceName . $this->getShortName();
}
/**
* {@inheritDoc}
*/
public function getNamespaceName()
{
return $this->namespaceName;
}
/**
* {@inheritDoc}
*/
public function getParentClass()
{
if (!isset($this->parentClass)) {
static $extendsField = 'extends';
$parentClass = false;
$hasExtends = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
$extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
if ($extendsNode instanceof FullyQualified) {
$extendsName = $extendsNode->toString();
$parentClass = class_exists($extendsName, false) ? new parent($extendsName) : new static($extendsName);
}
$this->parentClass = $parentClass;
}
return $this->parentClass;
}
/**
* Retrieves reflected properties.
*
* @param int $filter The optional filter, for filtering desired property types.
* It's configured using the ReflectionProperty constants, and defaults to all property types.
*
* @return array|\Go\ParserReflection\ReflectionProperty[]
*/
public function getProperties($filter = null)
{
if (!isset($this->properties)) {
$directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
$parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
$reflectionProperties = [];
foreach ($instance->getProperties() as $reflectionProperty) {
if (!$isParent || !$reflectionProperty->isPrivate()) {
$reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
}
}
$result += $reflectionProperties;
});
$properties = $directProperties + $parentProperties;
$this->properties = $properties;
}
// Without filter we can just return the full list
if (!isset($filter)) {
return array_values($this->properties);
}
$properties = [];
foreach ($this->properties as $property) {
if (!($filter & $property->getModifiers())) {
continue;
}
$properties[] = $property;
}
return $properties;
}
/**
* {@inheritdoc}
*/
public function getProperty($name)
{
$properties = $this->getProperties();
foreach ($properties as $property) {
if ($property->getName() == $name) {
return $property;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public function getShortName()
{
return $this->className;
}
public function getStartLine()
{
return $this->classLikeNode->getAttribute('startLine');
}
/**
* Returns an array of trait aliases
*
* @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
*
* @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
* values.
*/
public function getTraitAliases()
{
$aliases = [];
$traits = $this->getTraits();
foreach ($this->traitAdaptations as $adaptation) {
if ($adaptation instanceof TraitUseAdaptation\Alias) {
$methodName = $adaptation->method;
$traitName = null;
foreach ($traits as $trait) {
if ($trait->hasMethod($methodName)) {
$traitName = $trait->getName();
break;
}
}
$aliases[$adaptation->newName] = $traitName . '::'. $methodName;
}
}
return $aliases;
}
/**
* Returns an array of names of traits used by this class
*
* @link http://php.net/manual/en/reflectionclass.gettraitnames.php
*
* @return array
*/
public function getTraitNames()
{
return array_keys($this->getTraits());
}
/**
* Returns an array of traits used by this class
*
* @link http://php.net/manual/en/reflectionclass.gettraits.php
*
* @return array|\ReflectionClass[]
*/
public function getTraits()
{
if (!isset($this->traits)) {
$traitAdaptations = [];
$this->traits = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
$this->traitAdaptations = $traitAdaptations;
}
return $this->traits;
}
/**
* {@inheritDoc}
*/
public function hasConstant($name)
{
$constants = $this->getConstants();
$hasConstant = isset($constants[$name]) || array_key_exists($constants, $name);
return $hasConstant;
}
/**
* {@inheritdoc}
*/
public function hasMethod($name)
{
$methods = $this->getMethods();
foreach ($methods as $method) {
if ($method->getName() == $name) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function hasProperty($name)
{
$properties = $this->getProperties();
foreach ($properties as $property) {
if ($property->getName() == $name) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public function implementsInterface($interfaceName)
{
$allInterfaces = $this->getInterfaces();
return isset($allInterfaces[$interfaceName]);
}
/**
* {@inheritDoc}
*/
public function inNamespace()
{
return !empty($this->namespaceName);
}
/**
* {@inheritDoc}
*/
public function isAbstract()
{
if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
return true;
} elseif ($this->isInterface() && !empty($this->getMethods())) {
return true;
} elseif ($this->isTrait()) {
return PHP_VERSION_ID < 70000 ? true : false;
}
return false;
}
/**
* Currently, anonymous classes aren't supported for parsed reflection
*/
public function isAnonymous()
{
return false;
}
/**
* {@inheritDoc}
*/
public function isCloneable()
{
if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
return false;
}
if ($this->hasMethod('__clone')) {
return $this->getMethod('__clone')->isPublic();
}
return true;
}
/**
* {@inheritDoc}
*/
public function isFinal()
{
$isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
return $isFinal;
}
/**
* {@inheritDoc}
*/
public function isInstance($object)
{
if (!is_object($object)) {
throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
}
$className = $this->getName();
return $className === get_class($object) || is_subclass_of($object, $className);
}
/**
* {@inheritDoc}
*/
public function isInstantiable()
{
if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
return false;
}
if (null === ($constructor = $this->getConstructor())) {
return true;
}
return $constructor->isPublic();
}
/**
* {@inheritDoc}
*/
public function isInterface()
{
return ($this->classLikeNode instanceof Interface_);
}
/**
* {@inheritDoc}
*/
public function isInternal()
{
// never can be an internal method
return false;
}
/**
* {@inheritDoc}
*/
public function isIterateable()
{
return $this->implementsInterface('Traversable');
}
/**
* {@inheritDoc}
*/
public function isSubclassOf($class)
{
if (is_object($class)) {
if ($class instanceof ReflectionClass) {
$class = $class->name;
} else {
$class = get_class($class);
}
}
if (!$this->classLikeNode instanceof Class_) {
return false;
} else {
$extends = $this->classLikeNode->extends;
if ($extends && $extends->toString() == $class) {
return true;
}
}
$parent = $this->getParentClass();
return false === $parent ? false : $parent->isSubclassOf($class);
}
/**
* {@inheritDoc}
*/
public function isTrait()
{
return ($this->classLikeNode instanceof Trait_);
}
/**
* {@inheritDoc}
*/
public function isUserDefined()
{
// always defined by user, because we parse the source code
return true;
}
/**
* Gets static properties
*
* @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
*
* @return array
*/
public function getStaticProperties()
{
// In runtime static properties can be changed in any time
if ($this->isInitialized()) {
return forward_static_call('parent::getStaticProperties');
}
$properties = [];
$reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
foreach ($reflectionProperties as $reflectionProperty) {
if (!$reflectionProperty instanceof ReflectionProperty) {
if (!$reflectionProperty->isPublic()) {
$reflectionProperty->setAccessible(true);
}
}
$properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
}
return $properties;
}
/**
* Gets static property value
*
* @param string $name The name of the static property for which to return a value.
* @param mixed $default A default value to return in case the class does not declare
* a static property with the given name
*
* @return mixed
* @throws ReflectionException If there is no such property and no default value was given
*/
public function getStaticPropertyValue($name, $default = null)
{
$properties = $this->getStaticProperties();
$propertyExists = array_key_exists($name, $properties);
if (!$propertyExists && func_num_args() === 1) {
throw new ReflectionException("Static property does not exist and no default value is given");
}
return $propertyExists ? $properties[$name] : $default;
}
/**
* Creates a new class instance from given arguments.
*
* @link http://php.net/manual/en/reflectionclass.newinstance.php
* @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
*
* @return object
*/
public function newInstance($args = null)
{
$this->initializeInternalReflection();
return call_user_func_array('parent::newInstance', func_get_args());
}
/**
* Creates a new class instance from given arguments.
*
* @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
*
* @param array $args The parameters to be passed to the class constructor as an array.
*
* @return object
*/
public function newInstanceArgs(array $args = [])
{
$function = __FUNCTION__;
$this->initializeInternalReflection();
return parent::$function($args);
}
/**
* Creates a new class instance without invoking the constructor.
*
* @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
*
* @return object
*/
public function newInstanceWithoutConstructor($args = null)
{
$function = __FUNCTION__;
$this->initializeInternalReflection();
return parent::$function($args);
}
/**
* Sets static property value
*
* @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
*
* @param string $name Property name
* @param mixed $value New property value
*/
public function setStaticPropertyValue($name, $value)
{
$this->initializeInternalReflection();
forward_static_call('parent::setStaticPropertyValue', $name, $value);
}
private function recursiveCollect(\Closure $collector)
{
$result = [];
$isParent = true;
$traits = $this->getTraits();
foreach ($traits as $trait) {
$collector($result, $trait, !$isParent);
}
$parentClass = $this->getParentClass();
if ($parentClass) {
$collector($result, $parentClass, $isParent);
}
$interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
foreach ($interfaces as $interface) {
$collector($result, $interface, $isParent);
}
return $result;
}
/**
* Returns list of constants from the class
*
* @return array
*/
private function findConstants()
{
$constants = array();
$expressionSolver = new NodeExpressionResolver($this);
// constants can be only top-level nodes in the class, so we can scan them directly
foreach ($this->classLikeNode->stmts as $classLevelNode) {
if ($classLevelNode instanceof ClassConst) {
$nodeConstants = $classLevelNode->consts;
if (!empty($nodeConstants)) {
foreach ($nodeConstants as $nodeConstant) {
$expressionSolver->process($nodeConstant->value);
$constants[$nodeConstant->name] = $expressionSolver->getValue();
}
}
}
}
return $constants;
}
}
src/Traits/ReflectionFunctionLikeTrait.php 0000644 00000016456 14476011130 0014734 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\Traits;
use Go\ParserReflection\NodeVisitor\GeneratorDetector;
use Go\ParserReflection\NodeVisitor\StaticVariablesCollector;
use Go\ParserReflection\ReflectionFileNamespace;
use Go\ParserReflection\ReflectionParameter;
use Go\ParserReflection\ReflectionType;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeTraverser;
/**
* General trait for all function-like reflections
*/
trait ReflectionFunctionLikeTrait
{
use InitializationTrait;
/**
* @var FunctionLike
*/
protected $functionLikeNode;
/**
* Namespace name
*
* @var string
*/
protected $namespaceName = '';
/**
* @var array|ReflectionParameter[]
*/
protected $parameters;
/**
* {@inheritDoc}
*/
public function getClosureScopeClass()
{
$this->initializeInternalReflection();
return forward_static_call('parent::getClosureScopeClass');
}
/**
* {@inheritDoc}
*/
public function getClosureThis()
{
$this->initializeInternalReflection();
return forward_static_call('parent::getClosureThis');
}
public function getDocComment()
{
$docComment = $this->functionLikeNode->getDocComment();
return $docComment ? $docComment->getText() : false;
}
public function getEndLine()
{
return $this->functionLikeNode->getAttribute('endLine');
}
public function getExtension()
{
return null;
}
public function getExtensionName()
{
return false;
}
public function getFileName()
{
return $this->functionLikeNode->getAttribute('fileName');
}
/**
* {@inheritDoc}
*/
public function getName()
{
if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
$functionName = $this->functionLikeNode->name;
return $this->namespaceName ? $this->namespaceName . '\\' . $functionName : $functionName;
}
return false;
}
/**
* {@inheritDoc}
*/
public function getNamespaceName()
{
return $this->namespaceName;
}
/**
* Get the number of parameters that a function defines, both optional and required.
*
* @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofparameters.php
*
* @return int
*/
public function getNumberOfParameters()
{
return count($this->functionLikeNode->getParams());
}
/**
* Get the number of required parameters that a function defines.
*
* @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofrequiredparameters.php
*
* @return int
*/
public function getNumberOfRequiredParameters()
{
$requiredParameters = 0;
foreach ($this->getParameters() as $parameter) {
if (!$parameter->isOptional()) {
$requiredParameters++;
}
}
return $requiredParameters;
}
/**
* {@inheritDoc}
*/
public function getParameters()
{
if (!isset($this->parameters)) {
$parameters = [];
foreach ($this->functionLikeNode->getParams() as $parameterIndex => $parameterNode) {
$reflectionParameter = new ReflectionParameter(
$this->getName(),
$parameterNode->name,
$parameterNode,
$parameterIndex,
$this
);
$parameters[] = $reflectionParameter;
}
$this->parameters = $parameters;
}
return $this->parameters;
}
/**
* Gets the specified return type of a function
*
* @return \ReflectionType
*
* @link http://php.net/manual/en/reflectionfunctionabstract.getreturntype.php
*/
public function getReturnType()
{
$isBuiltin = false;
$returnType = $this->functionLikeNode->getReturnType();
if (is_object($returnType)) {
$returnType = $returnType->toString();
} elseif (is_string($returnType)) {
$isBuiltin = true;
}
return new ReflectionType($returnType, false, $isBuiltin);
}
/**
* {@inheritDoc}
*/
public function getShortName()
{
if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
return $this->functionLikeNode->name;
}
return false;
}
public function getStartLine()
{
return $this->functionLikeNode->getAttribute('startLine');
}
/**
* {@inheritDoc}
*/
public function getStaticVariables()
{
$nodeTraverser = new NodeTraverser(false);
$variablesCollector = new StaticVariablesCollector($this);
$nodeTraverser->addVisitor($variablesCollector);
/* @see https://github.com/nikic/PHP-Parser/issues/235 */
$nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
return $variablesCollector->getStaticVariables();
}
/**
* Checks if the function has a specified return type
*
* @return bool
*
* @link http://php.net/manual/en/reflectionfunctionabstract.hasreturntype.php
*/
public function hasReturnType()
{
$returnType = $this->functionLikeNode->getReturnType();
return isset($returnType);
}
/**
* {@inheritDoc}
*/
public function inNamespace()
{
return !empty($this->namespaceName);
}
/**
* {@inheritDoc}
*/
public function isClosure()
{
return $this->functionLikeNode instanceof Closure;
}
/**
* {@inheritDoc}
*/
public function isDeprecated()
{
// userland method/function/closure can not be deprecated
return false;
}
/**
* {@inheritDoc}
*/
public function isGenerator()
{
$nodeTraverser = new NodeTraverser(false);
$nodeDetector = new GeneratorDetector();
$nodeTraverser->addVisitor($nodeDetector);
/* @see https://github.com/nikic/PHP-Parser/issues/235 */
$nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
return $nodeDetector->isGenerator();
}
/**
* {@inheritDoc}
*/
public function isInternal()
{
// never can be an internal method
return false;
}
/**
* {@inheritDoc}
*/
public function isUserDefined()
{
// always defined by user, because we parse the source code
return true;
}
/**
* {@inheritDoc}
*/
public function isVariadic()
{
foreach ($this->getParameters() as $parameter) {
if ($parameter->isVariadic()) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public function returnsReference()
{
return $this->functionLikeNode->returnsByRef();
}
}
src/ValueResolver/NodeExpressionResolver.php 0000644 00000021134 14476011130 0015327 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
namespace Go\ParserReflection\ValueResolver;
use Go\ParserReflection\ReflectionClass;
use Go\ParserReflection\ReflectionException;
use Go\ParserReflection\ReflectionFileNamespace;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\MagicConst;
/**
* Tries to resolve expression into value
*/
class NodeExpressionResolver
{
/**
* List of exception for constant fetch
*
* @var array
*/
private static $notConstants = [
'true' => true,
'false' => true,
'null' => true,
];
/**
* Name of the constant (if present)
*
* @var null|string
*/
private $constantName = null;
/**
* Current reflection context for parsing
*
* @var mixed
*/
private $context;
/**
* Flag if expression is constant
*
* @var bool
*/
private $isConstant = false;
/**
* Node resolving level, 1 = top-level
*
* @var int
*/
private $nodeLevel = 0;
/**
* @var mixed Value of expression/constant
*/
private $value;
public function __construct($context)
{
$this->context = $context;
}
public function getConstantName()
{
return $this->constantName;
}
public function getValue()
{
return $this->value;
}
public function isConstant()
{
return $this->isConstant;
}
/**
* {@inheritDoc}
*/
public function process(Node $node)
{
$this->nodeLevel = 0;
$this->isConstant = false;
$this->constantName = null;
$this->value = $this->resolve($node);
}
/**
* Resolves node into valid value
*
* @param Node $node
*
* @return mixed
*/
protected function resolve(Node $node)
{
$value = null;
try {
++$this->nodeLevel;
$nodeType = $node->getType();
$methodName = 'resolve' . str_replace('_', '', $nodeType);
if (method_exists($this, $methodName)) {
$value = $this->$methodName($node);
}
} finally {
--$this->nodeLevel;
}
return $value;
}
protected function resolveScalarDNumber(Scalar\DNumber $node)
{
return $node->value;
}
protected function resolveScalarLNumber(Scalar\LNumber $node)
{
return $node->value;
}
protected function resolveScalarString(Scalar\String_ $node)
{
return $node->value;
}
protected function resolveScalarMagicConstMethod()
{
if ($this->context instanceof \ReflectionMethod) {
$fullName = $this->context->getDeclaringClass()->getName() . '::' . $this->context->getShortName();
return $fullName;
}
return '';
}
protected function resolveScalarMagicConstFunction()
{
if ($this->context instanceof \ReflectionFunctionAbstract) {
return $this->context->getName();
}
return '';
}
protected function resolveScalarMagicConstNamespace()
{
if (method_exists($this->context, 'getNamespaceName')) {
return $this->context->getNamespaceName();
}
if ($this->context instanceof ReflectionFileNamespace) {
return $this->context->getName();
}
return '';
}
protected function resolveScalarMagicConstClass()
{
if ($this->context instanceof \ReflectionClass) {
return $this->context->getName();
}
if (method_exists($this->context, 'getDeclaringClass')) {
$declaringClass = $this->context->getDeclaringClass();
if ($declaringClass instanceof \ReflectionClass) {
return $declaringClass->getName();
}
}
return '';
}
protected function resolveScalarMagicConstDir()
{
if (method_exists($this->context, 'getFileName')) {
return dirname($this->context->getFileName());
}
return '';
}
protected function resolveScalarMagicConstFile()
{
if (method_exists($this->context, 'getFileName')) {
return $this->context->getFileName();
}
return '';
}
protected function resolveScalarMagicConstLine(MagicConst\Line $node)
{
return $node->hasAttribute('startLine') ? $node->getAttribute('startLine') : 0;
}
protected function resolveScalarMagicConstTrait()
{
if ($this->context instanceof \ReflectionClass && $this->context->isTrait()) {
return $this->context->getName();
}
return '';
}
protected function resolveExprConstFetch(Expr\ConstFetch $node)
{
$constantValue = null;
$isResolved = false;
/** @var ReflectionFileNamespace|null $fileNamespace */
$fileNamespace = null;
$isFQNConstant = $node->name instanceof Node\Name\FullyQualified;
$constantName = $node->name->toString();
if (!$isFQNConstant) {
if (method_exists($this->context, 'getFileName')) {
$fileName = $this->context->getFileName();
$namespaceName = $this->context->getNamespaceName();
$fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
if ($fileNamespace->hasConstant($constantName)) {
$constantValue = $fileNamespace->getConstant($constantName);
$constantName = $fileNamespace->getName() . '\\' . $constantName;
$isResolved = true;
}
}
}
if (!$isResolved && defined($constantName)) {
$constantValue = constant($constantName);
}
if ($this->nodeLevel === 1 && !isset(self::$notConstants[$constantName])) {
$this->isConstant = true;
$this->constantName = $constantName;
}
return $constantValue;
}
protected function resolveExprClassConstFetch(Expr\ClassConstFetch $node)
{
$refClass = $this->fetchReflectionClass($node->class);
$constantName = $node->name;
// special handling of ::class constants
if ('class' === $constantName) {
return $refClass->getName();
}
return $refClass->getConstant($constantName);
}
protected function resolveExprArray(Expr\Array_ $node)
{
$result = [];
foreach ($node->items as $itemIndex => $arrayItem) {
$itemValue = $this->resolve($arrayItem->value);
$itemKey = isset($arrayItem->key) ? $this->resolve($arrayItem->key) : $itemIndex;
$result[$itemKey] = $itemValue;
}
return $result;
}
/**
* Utility method to fetch reflection class instance by name
*
* Supports:
* 'self' keyword
* 'parent' keyword
* not-FQN class names
*
* @param Node\Name $node Class name node
*
* @return bool|\ReflectionClass
*
* @throws ReflectionException
*/
private function fetchReflectionClass(Node\Name $node)
{
$className = $node->toString();
$isFQNClass = $node instanceof Node\Name\FullyQualified;
if ($isFQNClass) {
return new ReflectionClass($className);
}
if ('self' === $className) {
if ($this->context instanceof \ReflectionClass) {
return $this->context;
} elseif (method_exists($this->context, 'getDeclaringClass')) {
return $this->context->getDeclaringClass();
}
}
if ('parent' === $className) {
if ($this->context instanceof \ReflectionClass) {
return $this->context->getParentClass();
} elseif (method_exists($this->context, 'getDeclaringClass')) {
return $this->context->getDeclaringClass()->getParentClass();
}
}
if (method_exists($this->context, 'getFileName')) {
/** @var ReflectionFileNamespace|null $fileNamespace */
$fileName = $this->context->getFileName();
$namespaceName = $this->context->getNamespaceName();
$fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
return $fileNamespace->getClass($className);
}
throw new ReflectionException("Can not resolve class $className");
}
}
src/bootstrap.php 0000644 00000000731 14476011130 0010057 0 ustar 00
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
use Go\ParserReflection\Locator\ComposerLocator;
use Go\ParserReflection\ReflectionEngine;
/**
* This file is used for automatic configuration of Go\ParserReflection\ReflectionEngine class
*/
ReflectionEngine::init(new ComposerLocator());
tests/ReflectionClassTest.php 0000644 00000030711 14476011130 0012336 0 ustar 00 getFileNamespace('Go\ParserReflection\Stub');
$this->parsedRefFileNamespace = $parsedFileNamespace;
include_once $fileName;
}
/**
* Tests that names are correct for reflection data
*
* @dataProvider listOfClasses55
*
* @param string $className Class name to test
*/
public function testGeneralInfoGetters($className)
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass($className);
$this->performGeneralMethodComparison($parsedRefClass);
}
/**
* Tests getModifier() method
* NB: value is masked because there are many internal constants that aren't exported in the userland
*
* @dataProvider listOfClasses55
*
* @param string $className Class name to test
*/
public function testGetModifiers($className)
{
$mask =
\ReflectionClass::IS_EXPLICIT_ABSTRACT
+ \ReflectionClass::IS_FINAL
+ \ReflectionClass::IS_IMPLICIT_ABSTRACT;
$parsedRefClass = $this->parsedRefFileNamespace->getClass($className);
$originalRefClass = new \ReflectionClass($className);
$parsedModifiers = $parsedRefClass->getModifiers() & $mask;
$originalModifiers = $originalRefClass->getModifiers() & $mask;
$this->assertEquals($originalModifiers, $parsedModifiers);
}
/**
* Tests getMethods() returns correct number of methods for the class
*
* @dataProvider listOfClasses55
*
* @param string $className Class name to test
*/
public function testGetMethods($className)
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass($className);
$originalRefClass = new \ReflectionClass($className);
$parsedMethods = $parsedRefClass->getMethods();
$originalMethods = $originalRefClass->getMethods();
if ($parsedRefClass->getTraitAliases()) {
$this->markTestIncomplete("Adoptation methods for traits are not supported yet");
}
$this->assertCount(count($originalMethods), $parsedMethods);
}
/**
* Tests getProperties() returns correct number of properties for the class
*
* @dataProvider listOfClasses55
*
* @param string $className Class name to test
*/
public function testGetProperties($className)
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass($className);
$originalRefClass = new \ReflectionClass($className);
$parsedProperties = $parsedRefClass->getProperties();
$originalProperties = $originalRefClass->getProperties();
$this->assertCount(count($originalProperties), $parsedProperties);
}
/**
* Data provider with list of all class names to test for PHP5.5 and upper
*
* @return array
*/
public function listOfClasses55()
{
$classNames = [
ExplicitAbstractClass::class => [ExplicitAbstractClass::class],
ImplicitAbstractClass::class => [ImplicitAbstractClass::class],
FinalClass::class => [FinalClass::class],
ClassWithMethodsAndProperties::class => [ClassWithMethodsAndProperties::class],
SimpleInterface::class => [SimpleInterface::class],
InterfaceWithMethod::class => [InterfaceWithMethod::class],
SimpleTrait::class => [SimpleTrait::class],
SimpleInheritance::class => [SimpleInheritance::class],
SimpleAbstractInheritance::class => [SimpleAbstractInheritance::class],
ClassWithInterface::class => [ClassWithInterface::class],
ClassWithTrait::class => [ClassWithTrait::class],
ClassWithTraitAndInterface::class => [ClassWithTraitAndInterface::class],
NoCloneable::class => [NoCloneable::class],
NoInstantiable::class => [NoInstantiable::class],
AbstractInterface::class => [AbstractInterface::class],
ClassWithScalarConstants::class => [ClassWithScalarConstants::class],
ClassWithMagicConstants::class => [ClassWithMagicConstants::class],
ClassWithConstantsAndInheritance::class => [ClassWithConstantsAndInheritance::class],
TraitWithProperties::class => [TraitWithProperties::class],
ClassWithTraitAndAdaptation::class => [ClassWithTraitAndAdaptation::class],
ClassWithTraitAndConflict::class => [ClassWithTraitAndConflict::class],
];
return $classNames;
}
/**
* Tests specific features of PHP5.6 and newer, for example, array constants, etc
*/
public function testGettersPHP56()
{
if (PHP_VERSION_ID < 50600) {
$this->markTestSkipped("Can not test new features on old version of PHP");
}
$fileName = stream_resolve_include_path(__DIR__ . self::STUB_FILE56);
$fileNode = ReflectionEngine::parseFile($fileName);
$reflectionFile = new ReflectionFile($fileName, $fileNode);
include_once $fileName;
$parsedFileNamespace = $reflectionFile->getFileNamespace('Go\ParserReflection\Stub');
foreach ($parsedFileNamespace->getClasses() as $parsedRefClass) {
$this->performGeneralMethodComparison($parsedRefClass);
}
}
public function testNewInstanceMethod()
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass(FinalClass::class);
$instance = $parsedRefClass->newInstance();
$this->assertInstanceOf(FinalClass::class, $instance);
$this->assertSame([], $instance->args);
}
public function testNewInstanceArgsMethod()
{
$someValueByRef = 5;
$arguments = [1, &$someValueByRef];
$parsedRefClass = $this->parsedRefFileNamespace->getClass(FinalClass::class);
$instance = $parsedRefClass->newInstanceArgs($arguments);
$this->assertInstanceOf(FinalClass::class, $instance);
$this->assertSame($arguments, $instance->args);
}
public function testNewInstanceWithoutConstructorMethod()
{
$arguments = [1, 2];
$parsedRefClass = $this->parsedRefFileNamespace->getClass(FinalClass::class);
$instance = $parsedRefClass->newInstanceWithoutConstructor($arguments);
$this->assertInstanceOf(FinalClass::class, $instance);
$this->assertSame([], $instance->args);
}
public function testSetStaticPropertyValueMethod()
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass(ClassWithConstantsAndInheritance::class);
$originalRefClass = new \ReflectionClass(ClassWithConstantsAndInheritance::class);
$parsedRefClass->setStaticPropertyValue('h', 'test');
$this->assertSame($parsedRefClass->getStaticPropertyValue('h'), $originalRefClass->getStaticPropertyValue('h'));
}
public function testGetMethodsFiltering()
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass(ClassWithMethodsAndProperties::class);
$originalRefClass = new \ReflectionClass(ClassWithMethodsAndProperties::class);
$parsedMethods = $parsedRefClass->getMethods(\ReflectionMethod::IS_PUBLIC);
$originalMethods = $originalRefClass->getMethods(\ReflectionMethod::IS_PUBLIC);
$this->assertCount(count($originalMethods), $parsedMethods);
$parsedMethods = $parsedRefClass->getMethods(\ReflectionMethod::IS_PRIVATE | \ReflectionMethod::IS_STATIC);
$originalMethods = $originalRefClass->getMethods(\ReflectionMethod::IS_PRIVATE | \ReflectionMethod::IS_STATIC);
$this->assertCount(count($originalMethods), $parsedMethods);
}
public function testDirectMethods()
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass(ImplicitAbstractClass::class);
$originalRefClass = new \ReflectionClass(ImplicitAbstractClass::class);
$this->assertEquals($originalRefClass->hasMethod('test'), $parsedRefClass->hasMethod('test'));
$this->assertCount(count($originalRefClass->getMethods()), $parsedRefClass->getMethods());
$originalMethodName = $originalRefClass->getMethod('test')->getName();
$parsedMethodName = $parsedRefClass->getMethod('test')->getName();
$this->assertSame($originalMethodName, $parsedMethodName);
}
public function testInheritedMethods()
{
$parsedRefClass = $this->parsedRefFileNamespace->getClass(SimpleAbstractInheritance::class);
$originalRefClass = new \ReflectionClass(SimpleAbstractInheritance::class);
$this->assertEquals($originalRefClass->hasMethod('test'), $parsedRefClass->hasMethod('test'));
}
public function testCoverAllMethods()
{
$allInternalMethods = get_class_methods(\ReflectionClass::class);
$allMissedMethods = [];
foreach ($allInternalMethods as $internalMethodName) {
if ('export' === $internalMethodName) {
continue;
}
$refMethod = new \ReflectionMethod(ReflectionClass::class, $internalMethodName);
$definerClass = $refMethod->getDeclaringClass()->getName();
if (strpos($definerClass, 'Go\\ParserReflection') !== 0) {
$allMissedMethods[] = $internalMethodName;
}
}
if ($allMissedMethods) {
$this->markTestIncomplete('Methods ' . join($allMissedMethods, ', ') . ' are not implemented');
}
}
/**
* Performs list of common checks on parsed and runtime refelection
*
* @param ReflectionCLass $parsedRefClass
* @param array $allNameGetters Optional list of getters to check
*/
protected function performGeneralMethodComparison(ReflectionCLass $parsedRefClass, array $allNameGetters = [])
{
$allNameGetters = $allNameGetters ?: [
'getStartLine', 'getEndLine', 'getDocComment', 'getExtension', 'getExtensionName',
'getName', 'getNamespaceName', 'getShortName', 'inNamespace',
'isAbstract', 'isCloneable', 'isFinal', 'isInstantiable',
'isInterface', 'isInternal', 'isIterateable', 'isTrait', 'isUserDefined',
'getConstants', 'getTraitNames', 'getInterfaceNames', 'getStaticProperties',
'getDefaultProperties', 'getTraitAliases'
];
$className = $parsedRefClass->getName();
$originalRefClass = new \ReflectionClass($className);
foreach ($allNameGetters as $getterName) {
$expectedValue = $originalRefClass->$getterName();
$actualValue = $parsedRefClass->$getterName();
$this->assertSame(
$expectedValue,
$actualValue,
"$getterName() for class $className should be equal"
);
}
}
}
tests/ReflectionFileNamespaceTest.php 0000644 00000010651 14476011130 0013766 0 ustar 00 getFileNamespace('Go\ParserReflection\Stub');
$this->parsedRefFileNamespace = $parsedFileNamespace;
include_once $fileName;
}
public function testGetClass()
{
$refClass = $this->parsedRefFileNamespace->getClass('Unknown');
$this->assertFalse($refClass);
$refClass = $this->parsedRefFileNamespace->getClass(TestNamespaceClassFoo::class);
$this->assertInstanceOf(\ReflectionClass::class, $refClass);
$this->assertEquals(TestNamespaceClassFoo::class, $refClass->name);
}
public function testGetClasses()
{
$refClasses = $this->parsedRefFileNamespace->getClasses();
$this->assertCount(2, $refClasses);
}
public function testGetConstant()
{
$constValue = $this->parsedRefFileNamespace->getConstant('Unknown');
$this->assertFalse($constValue);
$constValue = $this->parsedRefFileNamespace->getConstant('NAMESPACE_NAME');
$this->assertNotFalse($constValue);
$this->assertEquals(\Go\ParserReflection\Stub\NAMESPACE_NAME, $constValue);
}
public function testGetDocComment()
{
$docComment = $this->parsedRefFileNamespace->getDocComment();
$this->assertNotEmpty($docComment);
}
public function testGetEndLine()
{
$endLine = $this->parsedRefFileNamespace->getEndLine();
$this->assertEquals(\Go\ParserReflection\Stub\END_MARKER + 1, $endLine);
}
public function testGetFileName()
{
$fileName = $this->parsedRefFileNamespace->getFileName();
$this->assertEquals(\Go\ParserReflection\Stub\FILE_NAME, $fileName);
}
public function testGetFunction()
{
$refFunction = $this->parsedRefFileNamespace->getFunction('Unknown');
$this->assertFalse($refFunction);
$refFunction = $this->parsedRefFileNamespace->getFunction('testFunctionBar');
$this->assertInstanceOf(\ReflectionFunction::class, $refFunction);
$this->assertEquals('testFunctionBar', $refFunction->name);
}
public function testGetFunctions()
{
$refFunctions = $this->parsedRefFileNamespace->getFunctions();
$this->assertCount(1, $refFunctions);
}
public function testGetName()
{
$namespaceName = $this->parsedRefFileNamespace->getName();
$this->assertEquals(\Go\ParserReflection\Stub\NAMESPACE_NAME, $namespaceName);
}
public function testGetNamespaceAliases()
{
$expectedAliases = [
'ReflectionClass' => 'UnusedReflectionClass',
'PhpParser\Node' => 'UnusedNode',
'PhpParser\Node\Expr' => 'UnusedNodeExpr'
];
$realAliases = $this->parsedRefFileNamespace->getNamespaceAliases();
$this->assertEquals($expectedAliases, $realAliases);
}
public function testGetStartLine()
{
$startLine = $this->parsedRefFileNamespace->getStartLine();
$this->assertEquals(\Go\ParserReflection\Stub\START_MARKER - 2, $startLine);
}
public function testHasClass()
{
$hasClass = $this->parsedRefFileNamespace->hasClass('Unknown');
$this->assertFalse($hasClass);
$hasClass = $this->parsedRefFileNamespace->hasClass(TestNamespaceClassFoo::class);
$this->assertTrue($hasClass);
}
public function testHasConstant()
{
$hasConstant = $this->parsedRefFileNamespace->hasConstant('Unknown');
$this->assertFalse($hasConstant);
$hasConstant = $this->parsedRefFileNamespace->hasConstant('NAMESPACE_NAME');
$this->assertTrue($hasConstant);
}
public function testHasFunction()
{
$hasFunction = $this->parsedRefFileNamespace->hasFunction('Unknown');
$this->assertFalse($hasFunction);
$hasFunction = $this->parsedRefFileNamespace->hasFunction('testFunctionBar');
$this->assertTrue($hasFunction);
}
}
tests/ReflectionFileTest.php 0000644 00000004015 14476011130 0012146 0 ustar 00 parsedRefFile = $reflectionFile;
}
public function testGetName()
{
$fileName = $this->parsedRefFile->getName();
$expectedName = stream_resolve_include_path(__DIR__ . self::STUB_FILE);
$this->assertEquals($expectedName, $fileName);
}
public function testGetFileNamespaces()
{
$reflectionFileNamespaces = $this->parsedRefFile->getFileNamespaces();
$this->assertCount(3, $reflectionFileNamespaces);
}
public function testGetFileNamespace()
{
$reflectionFileNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$this->assertInstanceOf(ReflectionFileNamespace::class, $reflectionFileNamespace);
$reflectionFileNamespace = $this->parsedRefFile->getFileNamespace('Unknown');
$this->assertFalse($reflectionFileNamespace);
}
public function testHasFileNamespace()
{
$hasFileNamespace = $this->parsedRefFile->hasFileNamespace('Go\ParserReflection\Stub');
$this->assertTrue($hasFileNamespace);
$hasFileNamespace = $this->parsedRefFile->hasFileNamespace('Unknown');
$this->assertFalse($hasFileNamespace);
}
public function testGetGlobalFileNamespace()
{
$fileName = stream_resolve_include_path(__DIR__ . self::STUB_GLOBAL_FILE);
$reflectionFile = new ReflectionFile($fileName);
$reflectionFileNamespace = $reflectionFile->getFileNamespace('\\');
$this->assertInstanceOf(ReflectionFileNamespace::class, $reflectionFileNamespace);
}
}
tests/ReflectionFunctionTest.php 0000644 00000012177 14476011130 0013064 0 ustar 00 parsedRefFile = $reflectionFile;
include_once $fileName;
}
public function testGeneralInfoGetters()
{
$allNameGetters = [
'getStartLine', 'getEndLine', 'getDocComment', 'getExtension', 'getExtensionName',
'getName', 'getNamespaceName', 'getShortName', 'inNamespace', 'getStaticVariables',
'getNumberOfParameters', 'getNumberOfRequiredParameters', '__toString', 'isDisabled',
'returnsReference', 'getClosureScopeClass', 'getClosureThis'
];
if (PHP_VERSION_ID >= 70000) {
$allNameGetters[] = 'hasReturnType';
}
foreach ($this->parsedRefFile->getFileNamespaces() as $fileNamespace) {
foreach ($fileNamespace->getFunctions() as $refFunction) {
$functionName = $refFunction->getName();
$originalRefFunction = new \ReflectionFunction($functionName);
foreach ($allNameGetters as $getterName) {
$expectedValue = $originalRefFunction->$getterName();
$actualValue = $refFunction->$getterName();
$this->assertSame(
$expectedValue,
$actualValue,
"{$getterName}() for function {$functionName} should be equal"
);
}
}
}
}
public function testCoverAllMethods()
{
$allInternalMethods = get_class_methods(\ReflectionFunction::class);
$allMissedMethods = [];
foreach ($allInternalMethods as $internalMethodName) {
if ('export' === $internalMethodName) {
continue;
}
$refMethod = new \ReflectionMethod(ReflectionFunction::class, $internalMethodName);
$definerClass = $refMethod->getDeclaringClass()->getName();
if (strpos($definerClass, 'Go\\ParserReflection') !== 0) {
$allMissedMethods[] = $internalMethodName;
}
}
if ($allMissedMethods) {
$this->markTestIncomplete('Methods ' . join($allMissedMethods, ', ') . ' are not implemented');
}
}
public function testGetClosureMethod()
{
$fileNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$refFunc = $fileNamespace->getFunction('noGeneratorFunc');
$closure = $refFunc->getClosure();
$this->assertInstanceOf(\Closure::class, $closure);
$retValue = $closure();
$this->assertEquals(100, $retValue);
}
public function testInvokeMethod()
{
$fileNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$refFunc = $fileNamespace->getFunction('funcWithReturnArgs');
$retValue = $refFunc->invoke(1, 2, 3);
$this->assertEquals([1, 2, 3], $retValue);
}
public function testInvokeArgsMethod()
{
$fileNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$refFunc = $fileNamespace->getFunction('funcWithReturnArgs');
$retValue = $refFunc->invokeArgs([1, 2, 3]);
$this->assertEquals([1, 2, 3], $retValue);
}
public function testGetReturnTypeMethod()
{
if (PHP_VERSION_ID < 70000) {
$this->markTestSkipped('Test available only for PHP7.0 and newer');
}
$fileName = stream_resolve_include_path(__DIR__ . self::STUB_FILE70);
$reflectionFile = new ReflectionFile($fileName);
include $fileName;
foreach ($reflectionFile->getFileNamespaces() as $fileNamespace) {
foreach ($fileNamespace->getFunctions() as $refFunction) {
$functionName = $refFunction->getName();
$originalRefFunction = new \ReflectionFunction($functionName);
$hasReturnType = $refFunction->hasReturnType();
$this->assertSame(
$originalRefFunction->hasReturnType(),
$hasReturnType,
"Presence of return type for function {$functionName} should be equal"
);
if ($hasReturnType) {
$parsedReturnType = $refFunction->getReturnType();
$originalReturnType = $originalRefFunction->getReturnType();
$this->assertSame($originalReturnType->allowsNull(), $parsedReturnType->allowsNull());
$this->assertSame($originalReturnType->isBuiltin(), $parsedReturnType->isBuiltin());
$this->assertSame($originalReturnType->__toString(), $parsedReturnType->__toString());
}
}
}
}
}
tests/ReflectionMethodTest.php 0000644 00000011670 14476011130 0012514 0 ustar 00 originalRefClass = $refClass = new \ReflectionClass(self::STUB_CLASS);
$fileName = $refClass->getFileName();
$reflectionFile = new ReflectionFile($fileName);
$parsedClass = $reflectionFile->getFileNamespace($refClass->getNamespaceName())->getClass($refClass->getName());
$this->parsedRefClass = $parsedClass;
}
public function testGeneralInfoGetters()
{
$allNameGetters = [
'getStartLine', 'getEndLine', 'getDocComment', 'getExtension', 'getExtensionName',
'getName', 'getNamespaceName', 'getShortName', 'inNamespace', 'getStaticVariables',
'isClosure', 'isDeprecated', 'isInternal', 'isUserDefined',
'isAbstract', 'isConstructor', 'isDestructor', 'isFinal',
'isPrivate', 'isProtected', 'isPublic', 'isStatic', '__toString',
'getNumberOfParameters', 'getNumberOfRequiredParameters', 'returnsReference', 'getClosureScopeClass',
'getClosureThis'
];
if (PHP_VERSION_ID >= 50600) {
$allNameGetters[] = 'isVariadic';
$allNameGetters[] = 'isGenerator';
}
if (PHP_VERSION_ID >= 70000) {
$allNameGetters[] = 'hasReturnType';
}
$allMethods = $this->originalRefClass->getMethods();
foreach ($allMethods as $refMethod) {
$methodName = $refMethod->getName();
$parsedMethod = $this->parsedRefClass->getMethod($methodName);
foreach ($allNameGetters as $getterName) {
$expectedValue = $refMethod->$getterName();
$actualValue = $parsedMethod->$getterName();
$this->assertSame(
$expectedValue,
$actualValue,
"$getterName() for method $methodName should be equal"
);
}
}
}
public function testCoverAllMethods()
{
$allInternalMethods = get_class_methods(\ReflectionMethod::class);
$allMissedMethods = [];
foreach ($allInternalMethods as $internalMethodName) {
if ('export' === $internalMethodName) {
continue;
}
$refMethod = new \ReflectionMethod(ReflectionMethod::class, $internalMethodName);
$definerClass = $refMethod->getDeclaringClass()->getName();
if (strpos($definerClass, 'Go\\ParserReflection') !== 0) {
$allMissedMethods[] = $internalMethodName;
}
}
if ($allMissedMethods) {
$this->markTestIncomplete('Methods ' . join($allMissedMethods, ', ') . ' are not implemented');
}
}
public function testGetClosureMethod()
{
$refMethod = $this->parsedRefClass->getMethod('funcWithDocAndBody');
$closure = $refMethod->getClosure(null);
$this->assertInstanceOf(\Closure::class, $closure);
$retValue = $closure();
$this->assertEquals('hello', $retValue);
}
public function testInvokeMethod()
{
$refMethod = $this->parsedRefClass->getMethod('funcWithReturnArgs');
$retValue = $refMethod->invoke(null, 1, 2, 3);
$this->assertEquals([1, 2, 3], $retValue);
}
public function testInvokeArgsMethod()
{
$refMethod = $this->parsedRefClass->getMethod('funcWithReturnArgs');
$retValue = $refMethod->invokeArgs(null, [1, 2, 3]);
$this->assertEquals([1, 2, 3], $retValue);
}
public function testDebugInfoMethod()
{
$parsedRefMethod = new ReflectionMethod(self::STUB_CLASS, 'funcWithDocAndBody');
$originalRefMethod = new \ReflectionMethod(self::STUB_CLASS, 'funcWithDocAndBody');
$expectedValue = (array) $originalRefMethod;
$this->assertSame($expectedValue, $parsedRefMethod->__debugInfo());
}
public function testSetAccessibleMethod()
{
$refMethod = $this->parsedRefClass->getMethod('protectedStaticFunc');
$refMethod->setAccessible(true);
$retValue = $refMethod->invokeArgs(null, []);
$this->assertEquals(null, $retValue);
}
public function testGetPrototypeMethod()
{
$refMethod = $this->parsedRefClass->getMethod('prototypeMethod');
$retValue = $refMethod->invokeArgs(null, []);
$this->assertEquals(self::STUB_CLASS, $retValue);
$prototype = $refMethod->getPrototype();
$this->assertInstanceOf(\ReflectionMethod::class, $prototype);
$prototype->setAccessible(true);
$retValue = $prototype->invokeArgs(null, []);
$this->assertNotEquals(self::STUB_CLASS, $retValue);
}
}
tests/ReflectionParameterTest.php 0000644 00000021161 14476011130 0013210 0 ustar 00 parsedRefFile = $reflectionFile;
include_once $fileName;
}
public function testGeneralInfoGetters()
{
$allNameGetters = [
'isArray', 'isCallable', 'isOptional', 'isPassedByReference', 'isDefaultValueAvailable',
'getPosition', 'canBePassedByValue', 'allowsNull', 'getDefaultValue', 'getDefaultValueConstantName',
'isDefaultValueConstant', '__toString'
];
$onlyWithDefaultValues = array_flip([
'getDefaultValue', 'getDefaultValueConstantName', 'isDefaultValueConstant'
]);
if (PHP_VERSION_ID >= 50600) {
$allNameGetters[] = 'isVariadic';
}
if (PHP_VERSION_ID >= 70000) {
$allNameGetters[] = 'hasType';
}
foreach ($this->parsedRefFile->getFileNamespaces() as $fileNamespace) {
foreach ($fileNamespace->getFunctions() as $refFunction) {
$functionName = $refFunction->getName();
foreach ($refFunction->getParameters() as $refParameter) {
$parameterName = $refParameter->getName();
$originalRefParameter = new \ReflectionParameter($functionName, $parameterName);
foreach ($allNameGetters as $getterName) {
// skip some methods if there is no default value
$isDefaultValueAvailable = $originalRefParameter->isDefaultValueAvailable();
if (isset($onlyWithDefaultValues[$getterName]) && !$isDefaultValueAvailable) {
continue;
}
$expectedValue = $originalRefParameter->$getterName();
$actualValue = $refParameter->$getterName();
$this->assertSame(
$expectedValue,
$actualValue,
"{$getterName}() for parameter {$functionName}:{$parameterName} should be equal"
);
}
}
}
}
}
public function testGetClassMethod()
{
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$parsedFunction = $parsedNamespace->getFunction('miscParameters');
$parameters = $parsedFunction->getParameters();
$this->assertSame(null, $parameters[0 /* array $arrayParam*/]->getClass());
$this->assertSame(null, $parameters[3 /* callable $callableParam */]->getClass());
$objectParam = $parameters[5 /* \stdClass $objectParam */]->getClass();
$this->assertInstanceOf(\ReflectionClass::class, $objectParam);
$this->assertSame(\stdClass::class, $objectParam->getName());
$typehintedParamWithNs = $parameters[7 /* ReflectionParameter $typehintedParamWithNs */]->getClass();
$this->assertInstanceOf(\ReflectionClass::class, $typehintedParamWithNs);
$this->assertSame(ReflectionParameter::class, $typehintedParamWithNs->getName());
}
public function testGetDeclaringClassMethodReturnsObject()
{
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$parsedClass = $parsedNamespace->getClass(Foo::class);
$parsedFunction = $parsedClass->getMethod('methodParam');
$parameters = $parsedFunction->getParameters();
$this->assertSame($parsedClass->getName(), $parameters[0]->getDeclaringClass()->getName());
}
public function testGetDeclaringClassMethodReturnsNull()
{
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$parsedFunction = $parsedNamespace->getFunction('miscParameters');
$parameters = $parsedFunction->getParameters();
$this->assertNull($parameters[0]->getDeclaringClass());
}
public function testDebugInfoMethod()
{
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$parsedFunction = $parsedNamespace->getFunction('miscParameters');
$parsedRefParameters = $parsedFunction->getParameters();
$parsedRefParameter = $parsedRefParameters[0];
$originalRefParameter = new \ReflectionParameter('Go\ParserReflection\Stub\miscParameters', 'arrayParam');
$expectedValue = (array) $originalRefParameter;
$this->assertSame($expectedValue, $parsedRefParameter->__debugInfo());
}
/**
* @dataProvider listOfDefaultGetters
*
* @param string $getterName Name of the getter to call
*/
public function testGetDefaultValueThrowsAnException($getterName)
{
$originalException = null;
$parsedException = null;
try {
$originalRefParameter = new \ReflectionParameter('Go\ParserReflection\Stub\miscParameters', 'arrayParam');
$originalRefParameter->$getterName();
} catch (\ReflectionException $e) {
$originalException = $e;
}
try {
$parsedNamespace = $this->parsedRefFile->getFileNamespace('Go\ParserReflection\Stub');
$parsedFunction = $parsedNamespace->getFunction('miscParameters');
$parsedRefParameters = $parsedFunction->getParameters();
$parsedRefParameter = $parsedRefParameters[0];
$parsedRefParameter->$getterName();
} catch (\ReflectionException $e) {
$parsedException = $e;
}
$this->assertInstanceOf(\ReflectionException::class, $originalException);
$this->assertInstanceOf(\ReflectionException::class, $parsedException);
$this->assertSame($originalException->getMessage(), $parsedException->getMessage());
}
public function listOfDefaultGetters()
{
return [
['getDefaultValue'],
['getDefaultValueConstantName']
];
}
public function testCoverAllMethods()
{
$allInternalMethods = get_class_methods(\ReflectionParameter::class);
$allMissedMethods = [];
foreach ($allInternalMethods as $internalMethodName) {
if ('export' === $internalMethodName) {
continue;
}
$refMethod = new \ReflectionMethod(ReflectionParameter::class, $internalMethodName);
$definerClass = $refMethod->getDeclaringClass()->getName();
if (strpos($definerClass, 'Go\\ParserReflection') !== 0) {
$allMissedMethods[] = $internalMethodName;
}
}
if ($allMissedMethods) {
$this->markTestIncomplete('Methods ' . join($allMissedMethods, ', ') . ' are not implemented');
}
}
public function testGetTypeMethod()
{
if (PHP_VERSION_ID < 70000) {
$this->markTestSkipped('Test available only for PHP7.0 and newer');
}
foreach ($this->parsedRefFile->getFileNamespaces() as $fileNamespace) {
foreach ($fileNamespace->getFunctions() as $refFunction) {
$functionName = $refFunction->getName();
foreach ($refFunction->getParameters() as $refParameter) {
$parameterName = $refParameter->getName();
$originalRefParameter = new \ReflectionParameter($functionName, $parameterName);
$hasType = $refParameter->hasType();
$this->assertSame(
$originalRefParameter->hasType(),
$hasType,
"Presence of type for parameter {$functionName}:{$parameterName} should be equal"
);
if ($hasType) {
$parsedReturnType = $refParameter->getType();
$originalReturnType = $originalRefParameter->getType();
$this->assertSame($originalReturnType->allowsNull(), $parsedReturnType->allowsNull());
$this->assertSame($originalReturnType->isBuiltin(), $parsedReturnType->isBuiltin());
$this->assertSame($originalReturnType->__toString(), $parsedReturnType->__toString());
}
}
}
}
}
}
tests/ReflectionPropertyTest.php 0000644 00000007632 14476011130 0013123 0 ustar 00 originalRefClass = $refClass = new \ReflectionClass(self::STUB_CLASS);
$fileName = $refClass->getFileName();
$fileNode = ReflectionEngine::parseFile($fileName);
$reflectionFile = new ReflectionFile($fileName, $fileNode);
$parsedClass = $reflectionFile->getFileNamespace($refClass->getNamespaceName())->getClass($refClass->getName());
$this->parsedRefClass = $parsedClass;
}
public function testGeneralInfoGetters()
{
$allNameGetters = [
'isDefault', 'getName', 'getModifiers', 'getDocComment',
'isPrivate', 'isProtected', 'isPublic', 'isStatic', '__toString'
];
$allProperties = $this->originalRefClass->getProperties();
foreach ($allProperties as $refProperty) {
$propertyName = $refProperty->getName();
$parsedProperty = $this->parsedRefClass->getProperty($propertyName);
foreach ($allNameGetters as $getterName) {
$expectedValue = $refProperty->$getterName();
$actualValue = $parsedProperty->$getterName();
$this->assertSame(
$expectedValue,
$actualValue,
"$getterName() for property $propertyName should be equal"
);
}
}
}
public function testSetAccessibleMethod()
{
$parsedProperty = $this->parsedRefClass->getProperty('protectedStaticProperty');
$parsedProperty->setAccessible(true);
$value = $parsedProperty->getValue();
$this->assertSame('foo', $value);
}
public function testGetSetValueForObjectMethods()
{
$parsedProperty = $this->parsedRefClass->getProperty('protectedProperty');
$parsedProperty->setAccessible(true);
$className = self::STUB_CLASS;
$obj = new $className;
$value = $parsedProperty->getValue($obj);
$this->assertSame('a', $value);
$parsedProperty->setValue($obj, 43);
$value = $parsedProperty->getValue($obj);
$this->assertSame(43, $value);
}
public function testCompatibilityWithOriginalConstructor()
{
$parsedRefProperty = new ReflectionProperty(self::STUB_CLASS, 'publicStaticProperty');
$originalValue = $parsedRefProperty->getValue();
$this->assertSame(M_PI, $originalValue);
}
public function testDebugInfoMethod()
{
$parsedRefProperty = new ReflectionProperty(self::STUB_CLASS, 'publicStaticProperty');
$originalRefProperty = new \ReflectionProperty(self::STUB_CLASS, 'publicStaticProperty');
$expectedValue = (array) $originalRefProperty;
$this->assertSame($expectedValue, $parsedRefProperty->__debugInfo());
}
public function testCoverAllMethods()
{
$allInternalMethods = get_class_methods(\ReflectionProperty::class);
$allMissedMethods = [];
foreach ($allInternalMethods as $internalMethodName) {
if ('export' === $internalMethodName) {
continue;
}
$refMethod = new \ReflectionMethod(ReflectionProperty::class, $internalMethodName);
$definerClass = $refMethod->getDeclaringClass()->getName();
if (strpos($definerClass, 'Go\\ParserReflection') !== 0) {
$allMissedMethods[] = $internalMethodName;
}
}
if ($allMissedMethods) {
$this->markTestIncomplete('Methods ' . join($allMissedMethods, ', ') . ' are not implemented');
}
}
}
tests/Stub/AbstractClassWithMethods.php 0000644 00000002761 14476011130 0014250 0 ustar 00 args = array_slice(array($a, &$b), 0, func_num_args());
}
}
abstract class ClassWithMethodsAndProperties
{
public $publicProperty;
protected $protectedProperty;
private $privateProperty;
static public $staticPublicProperty;
static protected $staticProtectedProperty;
static private $staticPrivateProperty;
public function publicMethod() {}
protected function protectedMethod() {}
private function privateMethod() {}
static public function publicStaticMethod() {}
static protected function protectedStaticMethod() {}
static private function privateStaticMethod() {}
abstract public function publicAbstractMethod();
abstract protected function protectedAbstractMethod();
final public function publicFinalMethod() {}
final protected function protectedFinalMethod() {}
final private function privateFinalMethod() {}
}
interface SimpleInterface {}
interface InterfaceWithMethod {
function foo();
}
trait SimpleTrait
{
function foo() { return __CLASS__; }
}
trait ConflictedSimpleTrait
{
function foo() { return 'BAZ'; }
}
class SimpleInheritance extends ExplicitAbstractClass {}
abstract class SimpleAbstractInheritance extends ImplicitAbstractClass
{
public $b = 'bar1';
public $d = 'foobar';
private $e = 'foobaz';
}
class ClassWithInterface implements SimpleInterface {}
class ClassWithTrait
{
use SimpleTrait;
}
class ClassWithTraitAndAdaptation
{
use SimpleTrait {
foo as protected fooBar;
foo as private fooBaz;
}
}
class ClassWithTraitAndConflict
{
use SimpleTrait, ConflictedSimpleTrait {
foo as protected fooBar;
ConflictedSimpleTrait::foo insteadof SimpleTrait;
}
}
class ClassWithTraitAndInterface implements InterfaceWithMethod
{
use SimpleTrait;
}
class NoCloneable
{
private function __clone() {}
}
class NoInstantiable
{
private function __construct() {}
}
interface AbstractInterface
{
public function foo();
public function bar();
}
class ClassWithScalarConstants
{
const A = 10, A1 = 11;
const B = 42.0;
const C = 'foo';
const D = false;
}
class ClassWithMagicConstants
{
const A = __DIR__;
const B = __FILE__;
const C = __NAMESPACE__;
const D = __CLASS__;
const E = __LINE__;
public static $a = self::A;
protected static $b = self::B;
private static $c = self::C;
}
const NS_CONST = 'test';
class ClassWithConstantsAndInheritance extends ClassWithMagicConstants
{
const A = 'overridden';
const H = M_PI;
const J = NS_CONST;
public static $h = self::H;
}
trait TraitWithProperties
{
private $a = 'foo';
protected $b = 'bar';
public $c = 'baz';
private static $as = 1;
protected static $bs = __TRAIT__;
public static $cs = 'foo';
}
tests/Stub/FileWithClasses56.php 0000644 00000000536 14476011130 0012541 0 ustar 00