Skip to content

Commit

Permalink
Merge pull request #471 from kukulich/reflection-constant
Browse files Browse the repository at this point in the history
Added `ReflectionConstant`
  • Loading branch information
Ocramius committed May 27, 2019
2 parents 454e04b + f0c678d commit 38876ee
Show file tree
Hide file tree
Showing 45 changed files with 8,387 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -175,7 +175,7 @@ jobs:
php: 7.2
env: DEPENDENCIES=""
before_script:
- travis_retry composer require --dev --prefer-dist --prefer-stable phpstan/phpstan:^0.11.5
- travis_retry composer require --dev --prefer-dist --prefer-stable phpstan/phpstan:^0.11.7
script: vendor/bin/phpstan analyse

- stage: Static Analysis
Expand Down
66 changes: 56 additions & 10 deletions bin/generate-phpstorm-stubs-map.php
Expand Up @@ -11,17 +11,20 @@
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitorAbstract;
use PhpParser\ParserFactory;
use Roave\BetterReflection\Reflection\Exception\InvalidConstantNode;
use Roave\BetterReflection\SourceLocator\FileChecker;
use Roave\BetterReflection\Util\ConstantNodeChecker;
use function array_map;
use function count;
use function file_get_contents;
use function file_put_contents;
use function in_array;
use function is_file;
use function is_readable;
use function ksort;
use function preg_match;
use function sprintf;
use function str_replace;
use function strlen;
use function strtolower;
use function substr;
use function var_export;

Expand All @@ -40,6 +43,9 @@
/** @var string[] */
private $functionNames = [];

/** @var string[] */
private $constantNames = [];

public function enterNode(Node $node) : ?int
{
if ($node instanceof Node\Stmt\ClassLike) {
Expand All @@ -55,6 +61,35 @@ public function enterNode(Node $node) : ?int
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}

if ($node instanceof Node\Const_) {
/** @psalm-suppress UndefinedPropertyFetch */
$this->constantNames[] = $node->namespacedName->toString();

return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}

if ($node instanceof Node\Expr\FuncCall) {
try {
ConstantNodeChecker::assertValidDefineFunctionCall($node);
} catch (InvalidConstantNode $e) {
return null;
}

/** @var Node\Scalar\String_ $nameNode */
$nameNode = $node->args[0]->value;

if (count($node->args) === 3
&& $node->args[2]->value instanceof Node\Expr\ConstFetch
&& $node->args[2]->value->name->toLowerString() === 'true'
) {
$this->constantNames[] = strtolower($nameNode->value);
}

$this->constantNames[] = $nameNode->value;

return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}

return null;
}

Expand All @@ -74,18 +109,27 @@ public function getFunctionNames() : array
return $this->functionNames;
}

/**
* @return string[]
*/
public function getConstantNames() : array
{
return $this->constantNames;
}

public function clear() : void
{
$this->classNames = [];
$this->functionNames = [];
$this->constantNames = [];
}
};

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new NameResolver());
$nodeTraverser->addVisitor($fileVisitor);

$map = ['classes' => [], 'functions' => []];
$map = ['classes' => [], 'functions' => [], 'constants' => []];

foreach (new DirectoryIterator($phpStormStubsDirectory) as $directoryInfo) {
if ($directoryInfo->isDot()) {
Expand All @@ -109,13 +153,7 @@ public function clear() : void
continue;
}

if (! is_file($fileInfo->getPathname())) {
throw new Exception(sprintf('"%s" is not a file', $fileInfo->getPathname()));
}

if (! is_readable($fileInfo->getPathname())) {
throw new Exception(sprintf('File "%s" is not readable', $fileInfo->getPathname()));
}
FileChecker::assertReadableFile($fileInfo->getPathname());

$ast = $phpParser->parse(file_get_contents($fileInfo->getPathname()));

Expand All @@ -131,6 +169,11 @@ public function clear() : void
$map['functions'][$functionName] = $fileInfo->getPathname();
}

/** @psalm-suppress UndefinedMethod */
foreach ($fileVisitor->getConstantNames() as $constantName) {
$map['constants'][$constantName] = $fileInfo->getPathname();
}

$fileVisitor->clear();
}
}
Expand All @@ -145,6 +188,7 @@ public function clear() : void

$exportedClasses = var_export($mapWithRelativeFilePaths['classes'], true);
$exportedFunctions = var_export($mapWithRelativeFilePaths['functions'], true);
$exportedConstants = var_export($mapWithRelativeFilePaths['constants'], true);

$output = <<<"PHP"
<?php
Expand All @@ -161,6 +205,8 @@ final class PhpStormStubsMap
const CLASSES = {$exportedClasses};

const FUNCTIONS = {$exportedFunctions};

const CONSTANTS = {$exportedConstants};
}
PHP;

Expand Down
19 changes: 16 additions & 3 deletions phpstan.neon
Expand Up @@ -7,7 +7,20 @@ parameters:

ignoreErrors:
# Some parent constructors are explicitly to be ignored
- '#does not call parent constructor#'
- '#Access to an undefined property PhpParser\\Node\\Param::\$isOptional#'
-
message: '#does not call parent constructor#'
paths:
- %currentWorkingDirectory%/src/Reflection/Adapter/*
- %currentWorkingDirectory%/src/Reflection/ReflectionObject.php
-
message: '#Access to an undefined property PhpParser\\Node\\Param::\$isOptional#'
paths:
- %currentWorkingDirectory%/src/Reflection/ReflectionFunctionAbstract.php
- %currentWorkingDirectory%/src/Reflection/ReflectionParameter.php
# Impossible to define type hint for anonymous class
- '#Call to an undefined method PhpParser\\NodeVisitorAbstract::(clearNodes|getClassNodes|getFunctionNodes)\(\)#'
-
message: '#Call to an undefined method PhpParser\\NodeVisitorAbstract::(clearNodes|getClassNodes|getFunctionNodes|getConstantNodes)\(\)#'
path: %currentWorkingDirectory%/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php
-
message: '#Call to an undefined method PhpParser\\NodeVisitorAbstract::(getNode|setConstantName)\(\)#'
path: %currentWorkingDirectory%/src/SourceLocator/Type/AutoloadSourceLocator.php
16 changes: 14 additions & 2 deletions src/BetterReflection.php
Expand Up @@ -8,6 +8,7 @@
use PhpParser\Parser;
use PhpParser\ParserFactory;
use Roave\BetterReflection\Reflector\ClassReflector;
use Roave\BetterReflection\Reflector\ConstantReflector;
use Roave\BetterReflection\Reflector\FunctionReflector;
use Roave\BetterReflection\SourceLocator\Ast\Locator as AstLocator;
use Roave\BetterReflection\SourceLocator\Ast\Parser\MemoizingParser;
Expand All @@ -34,6 +35,9 @@ final class BetterReflection
/** @var FunctionReflector|null */
private $functionReflector;

/** @var ConstantReflector|null */
private $constantReflector;

/** @var Parser|null */
private $phpParser;

Expand All @@ -55,7 +59,7 @@ public function sourceLocator() : SourceLocator
?? $this->sourceLocator = new MemoizingSourceLocator(new AggregateSourceLocator([
new PhpInternalSourceLocator($astLocator, $sourceStubber),
new EvaledCodeSourceLocator($astLocator, $sourceStubber),
new AutoloadSourceLocator($astLocator),
new AutoloadSourceLocator($astLocator, $this->phpParser()),
]));
}

Expand All @@ -71,6 +75,12 @@ public function functionReflector() : FunctionReflector
?? $this->functionReflector = new FunctionReflector($this->sourceLocator(), $this->classReflector());
}

public function constantReflector() : ConstantReflector
{
return $this->constantReflector
?? $this->constantReflector = new ConstantReflector($this->sourceLocator(), $this->classReflector());
}

public function phpParser() : Parser
{
return $this->phpParser
Expand All @@ -84,7 +94,9 @@ public function phpParser() : Parser
public function astLocator() : AstLocator
{
return $this->astLocator
?? $this->astLocator = new AstLocator($this->phpParser());
?? $this->astLocator = new AstLocator($this->phpParser(), function () : FunctionReflector {
return $this->functionReflector();
});
}

public function findReflectionsOnLine() : FindReflectionOnLine
Expand Down
5 changes: 5 additions & 0 deletions src/Identifier/Identifier.php
Expand Up @@ -67,4 +67,9 @@ public function isFunction() : bool
{
return $this->type->isFunction();
}

public function isConstant() : bool
{
return $this->type->isConstant();
}
}
12 changes: 12 additions & 0 deletions src/Identifier/IdentifierType.php
Expand Up @@ -7,6 +7,7 @@
use InvalidArgumentException;
use Roave\BetterReflection\Reflection\Reflection;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionConstant;
use Roave\BetterReflection\Reflection\ReflectionFunction;
use function array_key_exists;
use function sprintf;
Expand All @@ -15,10 +16,12 @@ class IdentifierType
{
public const IDENTIFIER_CLASS = ReflectionClass::class;
public const IDENTIFIER_FUNCTION = ReflectionFunction::class;
public const IDENTIFIER_CONSTANT = ReflectionConstant::class;

private const VALID_TYPES = [
self::IDENTIFIER_CLASS => null,
self::IDENTIFIER_FUNCTION => null,
self::IDENTIFIER_CONSTANT => null,
];

/** @var string */
Expand Down Expand Up @@ -50,6 +53,11 @@ public function isFunction() : bool
return $this->name === self::IDENTIFIER_FUNCTION;
}

public function isConstant() : bool
{
return $this->name === self::IDENTIFIER_CONSTANT;
}

/**
* Check to see if a reflector is of a valid type specified by this identifier.
*/
Expand All @@ -63,6 +71,10 @@ public function isMatchingReflector(Reflection $reflector) : bool
return $reflector instanceof ReflectionFunction;
}

if ($this->name === self::IDENTIFIER_CONSTANT) {
return $reflector instanceof ReflectionConstant;
}

return false;
}
}
22 changes: 22 additions & 0 deletions src/Reflection/Exception/InvalidConstantNode.php
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Roave\BetterReflection\Reflection\Exception;

use PhpParser\Node;
use PhpParser\PrettyPrinter\Standard;
use RuntimeException;
use function sprintf;
use function substr;

class InvalidConstantNode extends RuntimeException
{
public static function create(Node $node) : self
{
return new self(sprintf(
'Invalid constant node (first 50 characters: %s)',
substr((new Standard())->prettyPrint([$node]), 0, 50)
));
}
}

0 comments on commit 38876ee

Please sign in to comment.