mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 13:28:18 +01:00
use simple NodeTypeResolver according to NodeVisitor example
This commit is contained in:
parent
5a0ce3b7dc
commit
738ac001f9
@ -17,7 +17,8 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.2",
|
||||
"tracy/tracy": "^2.4",
|
||||
"symplify/easy-coding-standard": "^2.2"
|
||||
"symplify/easy-coding-standard": "^2.2",
|
||||
"slam/php-cs-fixer-extensions": "^1.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -27,7 +28,8 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Rector\\Tests\\": "tests"
|
||||
"Rector\\Tests\\": "tests",
|
||||
"Rector\\NodeTypeResolver\\Tests\\": "packages/NodeTypeResolver/tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -20,6 +20,9 @@ checkers:
|
||||
PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\NestingLevelSniff:
|
||||
absoluteNestingLevel: 3
|
||||
|
||||
# Class should be Final or Abstract
|
||||
- SlamCsFixer\FinalInternalClassFixer
|
||||
|
||||
parameters:
|
||||
exclude_checkers:
|
||||
# Excluded from symfony-checkers.neon
|
||||
|
@ -1,95 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Broker;
|
||||
|
||||
use PHPStan\Broker\Broker;
|
||||
use PHPStan\Broker\BrokerFactory as OriginalBrokerFactory;
|
||||
use PHPStan\Reflection\Annotations\AnnotationsMethodsClassReflectionExtension;
|
||||
use PHPStan\Reflection\Annotations\AnnotationsPropertiesClassReflectionExtension;
|
||||
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
|
||||
use PHPStan\Reflection\PhpDefect\PhpDefectClassReflectionExtension;
|
||||
use PHPStan\Type\FileTypeMapper;
|
||||
use Rector\NodeTypeResolver\Reflection\FunctionReflectionFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Only mimics original @see \PHPStan\Broker\BrokerFactory with Symfony DI Container.
|
||||
*/
|
||||
final class BrokerFactory
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface|ContainerBuilder
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @var FunctionReflectionFactory
|
||||
*/
|
||||
private $functionReflectionFactory;
|
||||
|
||||
/**
|
||||
* @var FileTypeMapper
|
||||
*/
|
||||
private $fileTypeMapper;
|
||||
|
||||
public function __construct(
|
||||
ContainerInterface $container,
|
||||
FunctionReflectionFactory $functionReflectionFactory,
|
||||
FileTypeMapper $fileTypeMapper
|
||||
) {
|
||||
$this->container = $container;
|
||||
$this->functionReflectionFactory = $functionReflectionFactory;
|
||||
$this->fileTypeMapper = $fileTypeMapper;
|
||||
}
|
||||
|
||||
public function create(): Broker
|
||||
{
|
||||
$tagToService = function (array $tags) {
|
||||
return array_map(function (string $serviceName) {
|
||||
return $this->container->get($serviceName);
|
||||
}, array_keys($tags));
|
||||
};
|
||||
|
||||
// @todo: require in ctor
|
||||
$phpClassReflectionExtension = $this->container->get(PhpClassReflectionExtension::class);
|
||||
$annotationsPropertiesClassReflectionExtension = $this->container->get(AnnotationsPropertiesClassReflectionExtension::class);
|
||||
$annotationsMethodsClassReflectionExtension = $this->container->get(AnnotationsMethodsClassReflectionExtension::class);
|
||||
$phpDefectClassReflectionExtension = $this->container->get(PhpDefectClassReflectionExtension::class);
|
||||
|
||||
dump('EEA');
|
||||
die;
|
||||
|
||||
dump($tagToService($this->container->findTaggedServiceIds(OriginalBrokerFactory::PROPERTIES_CLASS_REFLECTION_EXTENSION_TAG)));
|
||||
|
||||
die;
|
||||
|
||||
|
||||
$propertiesClassReflectionExtensions = array_merge(
|
||||
[$phpClassReflectionExtension, $phpDefectClassReflectionExtension],
|
||||
$tagToService($this->container->findTaggedServiceIds(OriginalBrokerFactory::PROPERTIES_CLASS_REFLECTION_EXTENSION_TAG)),
|
||||
[$annotationsPropertiesClassReflectionExtension]
|
||||
);
|
||||
|
||||
$methodsClassReflectionExtensions = array_merge(
|
||||
[$phpClassReflectionExtension],
|
||||
$tagToService($this->container->findTaggedServiceIds(OriginalBrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG)),
|
||||
[$annotationsMethodsClassReflectionExtension]
|
||||
);
|
||||
|
||||
$dynamicMethodReturnTypeExtensions = $tagToService(
|
||||
$this->container->findTaggedServiceIds(OriginalBrokerFactory::DYNAMIC_METHOD_RETURN_TYPE_EXTENSION_TAG));
|
||||
$dynamicStaticMethodReturnTypeExtensions = $tagToService($this->container->findTaggedServiceIds(OriginalBrokerFactory::DYNAMIC_STATIC_METHOD_RETURN_TYPE_EXTENSION_TAG)
|
||||
);
|
||||
|
||||
|
||||
return new Broker(
|
||||
$propertiesClassReflectionExtensions,
|
||||
$methodsClassReflectionExtensions,
|
||||
$dynamicMethodReturnTypeExtensions,
|
||||
$dynamicStaticMethodReturnTypeExtensions,
|
||||
$this->functionReflectionFactory,
|
||||
$this->fileTypeMapper
|
||||
);
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Broker;
|
||||
|
||||
use PHPStan\Broker\Broker;
|
||||
use PHPStan\Type\FileTypeMapper;
|
||||
use Rector\NodeTypeResolver\Reflection\FunctionReflectionFactory;
|
||||
|
||||
final class DummyBroker extends Broker
|
||||
{
|
||||
public function __construct(FunctionReflectionFactory $functionReflection, FileTypeMapper $fileTypeMapper)
|
||||
{
|
||||
parent::__construct([], [], [], [], $functionReflection, $fileTypeMapper);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeTraverser;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\TypeResolvingNodeVisitor;
|
||||
|
||||
// @todo: move to normal NodeTraverser as last one? try after setting up tests
|
||||
final class TypeDetectingNodeTraverser extends NodeTraverser
|
||||
{
|
||||
public function __construct(TypeResolvingNodeVisitor $typeResolvingNodeVisitor)
|
||||
{
|
||||
$this->visitors[] = $typeResolvingNodeVisitor;
|
||||
}
|
||||
}
|
@ -3,87 +3,26 @@
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\PrettyPrinter\Standard;
|
||||
use PHPStan\Analyser\NodeScopeResolver;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Analyser\TypeSpecifier;
|
||||
use PHPStan\Broker\Broker;
|
||||
use SplObjectStorage;
|
||||
use Rector\NodeTypeResolver\NodeTraverser\TypeDetectingNodeTraverser;
|
||||
|
||||
final class NodeTypeResolver
|
||||
{
|
||||
/**
|
||||
* @var NodeScopeResolver
|
||||
* @var TypeDetectingNodeTraverser
|
||||
*/
|
||||
private $nodeScopeResolver;
|
||||
private $typeDetectingNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var Standard
|
||||
*/
|
||||
private $prettyPrinter;
|
||||
|
||||
/**
|
||||
* @var TypeSpecifier
|
||||
*/
|
||||
private $typeSpecifier;
|
||||
|
||||
/**
|
||||
* @var SplObjectStorage|Scope[]
|
||||
*/
|
||||
private $nodeWithScope = [];
|
||||
|
||||
/**
|
||||
* @var Broker
|
||||
*/
|
||||
private $broker;
|
||||
|
||||
public function __construct(
|
||||
NodeScopeResolver $nodeScopeResolver,
|
||||
Standard $prettyPrinter,
|
||||
TypeSpecifier $typeSpecifier
|
||||
) {
|
||||
$this->nodeScopeResolver = $nodeScopeResolver;
|
||||
$this->prettyPrinter = $prettyPrinter;
|
||||
$this->typeSpecifier = $typeSpecifier;
|
||||
$this->nodeWithScope = new SplObjectStorage;
|
||||
}
|
||||
|
||||
public function setBroker(Broker $broker): void
|
||||
public function __construct(TypeDetectingNodeTraverser $typeDetectingNodeTraverser)
|
||||
{
|
||||
$this->broker = $broker;
|
||||
$this->typeDetectingNodeTraverser = $typeDetectingNodeTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
public function getTypeForNode(Node $node, array $nodes): Scope
|
||||
public function getTypeForNode(Node $activeNode, array $nodes): void
|
||||
{
|
||||
if (! isset($this->nodeWithScope[$node])) {
|
||||
$this->prepareForNodes($nodes);
|
||||
}
|
||||
|
||||
// variable
|
||||
$scope = $this->nodeWithScope[$node];
|
||||
dump($scope->getType($node));
|
||||
|
||||
// should be 'Nette\Utils\Html'
|
||||
die;
|
||||
|
||||
return $this->nodeWithScope[$node];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
private function prepareForNodes(array $nodes): void
|
||||
{
|
||||
$this->nodeScopeResolver->processNodes($nodes, $this->createScope(), function (Node $node, Scope $scope) {
|
||||
$this->nodeWithScope[$node] = $scope;
|
||||
});
|
||||
}
|
||||
|
||||
private function createScope(): Scope
|
||||
{
|
||||
return new Scope($this->broker, $this->prettyPrinter, $this->typeSpecifier, '');
|
||||
$this->typeDetectingNodeTraverser->traverse($nodes);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeTypeResolver\TypeContext;
|
||||
|
||||
// @todo: rename to ClassLikeType, noother types are here
|
||||
final class TypeResolvingNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var TypeContext
|
||||
*/
|
||||
private $typeContext;
|
||||
|
||||
public function __construct(TypeContext $typeContext)
|
||||
{
|
||||
$this->typeContext = $typeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes): void
|
||||
{
|
||||
$this->typeContext->startFile();
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): void
|
||||
{
|
||||
if ($node instanceof ClassLike) {
|
||||
$this->typeContext->enterClass($node);
|
||||
}
|
||||
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->typeContext->enterFunction($node);
|
||||
}
|
||||
|
||||
$variableType = null;
|
||||
|
||||
if ($node instanceof Variable) {
|
||||
$nextNode = $node->getAttribute('next');
|
||||
if ($nextNode instanceof New_) {
|
||||
$variableType = $nextNode->class->toString();
|
||||
$variableName = $node->name;
|
||||
$this->typeContext->addLocalVariable($variableName, $variableType);
|
||||
} else {
|
||||
$variableType = $this->typeContext->getTypeForVariable((string) $node->name);
|
||||
}
|
||||
}
|
||||
|
||||
if ($variableType) {
|
||||
$node->setAttribute('type', $variableType);
|
||||
}
|
||||
|
||||
if ($node instanceof Assign && $node->var instanceof Variable && $node->expr instanceof Variable) {
|
||||
$this->typeContext->addAssign($node->var->name, $node->expr->name);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Reflection;
|
||||
|
||||
use PHPStan\Reflection\FunctionReflection;
|
||||
use PHPStan\Reflection\FunctionReflectionFactory as FunctionReflectionFactoryInterface;
|
||||
use PHPStan\Type\Type;
|
||||
use ReflectionFunction;
|
||||
|
||||
final class FunctionReflectionFactory implements FunctionReflectionFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $phpDocParameterTypes
|
||||
*/
|
||||
public function create(
|
||||
ReflectionFunction $reflection,
|
||||
array $phpDocParameterTypes,
|
||||
?Type $phpDocReturnType = null
|
||||
): FunctionReflection {
|
||||
return new FunctionReflection($reflection, $phpDocParameterTypes, $phpDocReturnType);
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Reflection\Php;
|
||||
|
||||
use PHPStan\Broker\Broker;
|
||||
use PHPStan\Cache\Cache;
|
||||
use PHPStan\Parser\FunctionCallStatementFinder;
|
||||
use PHPStan\Parser\Parser;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflectionFactory as PhpMethodReflectionFactoryInterface;
|
||||
use PHPStan\Type\Type;
|
||||
use ReflectionMethod;
|
||||
|
||||
final class PhpMethodReflectionFactory implements PhpMethodReflectionFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @var Broker
|
||||
*/
|
||||
private $broker;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var FunctionCallStatementFinder
|
||||
*/
|
||||
private $functionCallStatementFinder;
|
||||
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
public function __construct(
|
||||
Parser $parser,
|
||||
FunctionCallStatementFinder $functionCallStatementFinder,
|
||||
Cache $cache
|
||||
) {
|
||||
$this->parser = $parser;
|
||||
$this->functionCallStatementFinder = $functionCallStatementFinder;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function setBroker(Broker $broker): void
|
||||
{
|
||||
$this->broker = $broker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Type[] $phpDocParameterTypes
|
||||
*/
|
||||
public function create(
|
||||
ClassReflection $declaringClass,
|
||||
ReflectionMethod $reflectionMethod,
|
||||
array $phpDocParameterTypes,
|
||||
?Type $phpDocReturnType = null
|
||||
): PhpMethodReflection {
|
||||
return new PhpMethodReflection(
|
||||
$declaringClass,
|
||||
$reflectionMethod,
|
||||
$this->broker,
|
||||
$this->parser,
|
||||
$this->functionCallStatementFinder,
|
||||
$this->cache,
|
||||
$phpDocParameterTypes,
|
||||
$phpDocReturnType
|
||||
);
|
||||
}
|
||||
}
|
78
packages/NodeTypeResolver/src/TypeContext.php
Normal file
78
packages/NodeTypeResolver/src/TypeContext.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
final class TypeContext
|
||||
{
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $types = [];
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $localTypes;
|
||||
|
||||
/**
|
||||
* @var ClassLike|null
|
||||
*/
|
||||
private $classLikeNode;
|
||||
|
||||
public function startFile(): void
|
||||
{
|
||||
$this->types = [];
|
||||
$this->classLikeNode = [];
|
||||
}
|
||||
|
||||
public function addLocalVariable(string $variableName, string $variableType): void
|
||||
{
|
||||
$this->localTypes[$variableName] = $variableType;
|
||||
}
|
||||
|
||||
public function enterClass(ClassLike $classLikeNode): void
|
||||
{
|
||||
$this->classLikeNode = $classLikeNode;
|
||||
}
|
||||
|
||||
public function enterFunction(FunctionLike $functionLikeNode): void
|
||||
{
|
||||
$this->localTypes = [];
|
||||
|
||||
$functionReflection = $this->getFunctionReflection($functionLikeNode);
|
||||
foreach ($functionReflection->getParameters() as $parameterReflection) {
|
||||
$this->localTypes[$parameterReflection->getName()] = $parameterReflection->getType();
|
||||
}
|
||||
}
|
||||
|
||||
public function getTypeForVariable(string $name): string
|
||||
{
|
||||
return $this->localTypes[$name] ?? '';
|
||||
}
|
||||
|
||||
public function addAssign(string $newVariable, string $oldVariable): void
|
||||
{
|
||||
$type = $this->getTypeForVariable($oldVariable);
|
||||
$this->addLocalVariable($newVariable, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionFunction|ReflectionMethod
|
||||
*/
|
||||
private function getFunctionReflection(FunctionLike $functionLikeNode)
|
||||
{
|
||||
if ($this->classLikeNode) {
|
||||
$className = $this->classLikeNode->namespacedName->toString();
|
||||
$methodName = (string) $functionLikeNode->name;
|
||||
|
||||
return new ReflectionMethod($className, $methodName);
|
||||
}
|
||||
|
||||
return new ReflectionFunction((string) $functionLikeNode->name);
|
||||
}
|
||||
}
|
@ -4,54 +4,3 @@ services:
|
||||
|
||||
Rector\NodeTypeResolver\:
|
||||
resource: '../../src'
|
||||
|
||||
# PHPStan
|
||||
# PHPStan\Analyser\TypeSpecifier: ~
|
||||
# PHPStan\Analyser\NodeScopeResolver:
|
||||
# arguments:
|
||||
# $polluteScopeWithLoopInitialAssignments: false
|
||||
# $polluteCatchScopeWithTryAssignments: false
|
||||
# $earlyTerminatingMethodCalls: []
|
||||
|
||||
# Broker
|
||||
# Rector\NodeTypeResolver\Broker\BrokerFactory: ~
|
||||
# PHPStan\Broker\Broker:
|
||||
# factory: ['@Rector\NodeTypeResolver\Broker\BrokerFactory', 'create']
|
||||
|
||||
# required by Broker
|
||||
# this causes circular referene issue
|
||||
# PHPStan\Reflection\Php\PhpClassReflectionExtension:
|
||||
# calls:
|
||||
# # prevents circular reference
|
||||
# - ['setBroker', ['@PHPStan\Broker\Broker']]
|
||||
|
||||
# PHPStan\Parser\FunctionCallStatementFinder: ~
|
||||
#
|
||||
# Rector\NodeTypeResolver\NodeTypeResolver:
|
||||
# calls:
|
||||
# # prevents circular reference
|
||||
# - ['setBroker', ['@PHPStan\Broker\Broker']]
|
||||
#
|
||||
# Rector\NodeTypeResolver\Reflection\Php\PhpMethodReflectionFactory:
|
||||
# calls:
|
||||
# # prevents circular reference
|
||||
# - ['setBroker', ['@PHPStan\Broker\Broker']]
|
||||
#
|
||||
# # Parser
|
||||
# PHPStan\Parser\DirectParser: ~
|
||||
# PHPStan\Parser\CachedParser:
|
||||
# arguments:
|
||||
# - '@PHPStan\Parser\DirectParser'
|
||||
#
|
||||
# # prefered service for interface
|
||||
# PHPStan\Parser\Parser:
|
||||
# alias: PHPStan\Parser\CachedParser
|
||||
#
|
||||
# PHPStan\Cache\Cache: ~
|
||||
# PHPStan\Cache\MemoryCacheStorage: ~
|
||||
# PHPStan\File\FileHelper:
|
||||
# arguments:
|
||||
# $workingDirectory: %kernel.root_dir%/../../
|
||||
#
|
||||
# # required for Broker
|
||||
# PHPStan\Type\FileTypeMapper: ~
|
||||
|
@ -1,15 +1,12 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Tests;
|
||||
namespace Rector\NodeTypeResolver\Tests\NodeTypeResolverSource;
|
||||
|
||||
use Nette\Utils\Html;
|
||||
|
||||
final class VariableType
|
||||
{
|
||||
/**
|
||||
* @return Html
|
||||
*/
|
||||
public function prepare(): \Nette\Utils\Html
|
||||
public function prepare(): Html
|
||||
{
|
||||
$html = new Html;
|
||||
$assignedHtml = $html;
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
namespace Rector\NodeTypeResolver\Tests;
|
||||
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
use Rector\NodeTypeResolver\NodeTraverser\TypeDetectingNodeTraverser;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
@ -14,27 +17,42 @@ final class NodeTypeResolverTest extends AbstractContainerAwareTestCase
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
* @var ParserInterface
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var TypeDetectingNodeTraverser
|
||||
*/
|
||||
private $typeDetectingNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->nodeTypeResolver = $this->container->get(NodeTypeResolver::class);
|
||||
$this->parser = $this->container->get(Parser::class);
|
||||
$this->parser = $this->container->get(ParserInterface::class);
|
||||
$this->typeDetectingNodeTraverser = $this->container->get(TypeDetectingNodeTraverser::class);
|
||||
$this->nodeTraverser = $this->container->get(NodeTraverser::class);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$code = file_get_contents(__DIR__ . '/NodeTypeResolverSource/VariableType.php');
|
||||
$nodes = $this->parser->parse($code);
|
||||
$nodes = $this->parser->parseFile(__DIR__ . '/NodeTypeResolverSource/VariableType.php');
|
||||
|
||||
$variableNode = $nodes[1]->stmts[1]->stmts[0]->stmts[0]->expr->var;
|
||||
$resolvedType = $this->nodeTypeResolver->getTypeForNode($variableNode, $nodes);
|
||||
$this->assertSame('Nette\Utils\Html', $resolvedType);
|
||||
// run basic traverser here
|
||||
$this->nodeTraverser->traverse($nodes);
|
||||
$this->typeDetectingNodeTraverser->traverse($nodes);
|
||||
|
||||
// $assignedVariableNode = $nodes[1]->stmts[1]->stmts[0]->stmts[2]->expr;
|
||||
// $resolvedType = $this->nodeTypeResolver->getTypeForNode($assignedVariableNode, $nodes);
|
||||
// $this->assertSame('Nette\Utils\Html', $resolvedType);
|
||||
/** @var Variable $htmlVariableNode */
|
||||
$htmlVariableNode = $nodes[1]->stmts[1]->stmts[0]->stmts[0]->expr->var;
|
||||
|
||||
$this->assertSame(
|
||||
'Nette\Utils\Html',
|
||||
$htmlVariableNode->getAttribute('type')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
13
src/Contract/Parser/ParserInterface.php
Normal file
13
src/Contract/Parser/ParserInterface.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Contract\Parser;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
interface ParserInterface
|
||||
{
|
||||
/**
|
||||
* @return Node[]
|
||||
*/
|
||||
public function parseFile(string $filePath): array;
|
||||
}
|
@ -6,5 +6,4 @@ use Exception;
|
||||
|
||||
final class FileNotFoundException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
43
src/NodeVisitor/NodeConnector.php
Normal file
43
src/NodeVisitor/NodeConnector.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
/**
|
||||
* See https://github.com/nikic/PHP-Parser/blob/master/doc/5_FAQ.markdown#how-can-the-nextprevious-sibling-of-a-node-be-obtained
|
||||
*/
|
||||
final class NodeConnector extends NodeVisitorAbstract
|
||||
{
|
||||
private $stack;
|
||||
private $prev;
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->stack = [];
|
||||
$this->prev = null;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (!empty($this->stack)) {
|
||||
$node->setAttribute('parent', $this->stack[count($this->stack)-1]);
|
||||
}
|
||||
|
||||
if ($this->prev && $this->prev->getAttribute('parent') == $node->getAttribute('parent')) {
|
||||
$node->setAttribute('prev', $this->prev);
|
||||
$this->prev->setAttribute('next', $node);
|
||||
}
|
||||
$this->stack[] = $node;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
$this->prev = $node;
|
||||
array_pop($this->stack);
|
||||
}
|
||||
}
|
41
src/Parser/Parser.php
Normal file
41
src/Parser/Parser.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Parser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Parser as NikicParser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
|
||||
final class Parser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* @var NikicParser
|
||||
*/
|
||||
private $nikicParser;
|
||||
|
||||
/**
|
||||
* @var Node[][]
|
||||
*/
|
||||
private $nodesByFile = [];
|
||||
|
||||
public function __construct(NikicParser $nikicParser)
|
||||
{
|
||||
$this->nikicParser = $nikicParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]
|
||||
*/
|
||||
public function parseFile(string $filePath): array
|
||||
{
|
||||
if (isset($this->nodesByFile[$filePath])) {
|
||||
return $this->nodesByFile[$filePath];
|
||||
}
|
||||
|
||||
$fileContent = file_get_contents($filePath);
|
||||
$this->nodesByFile[$filePath] = $this->nikicParser->parse($fileContent);
|
||||
|
||||
|
||||
return $this->nodesByFile[$filePath];
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
namespace Rector\Parser;
|
||||
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\Parser as NikicParser;
|
||||
use PhpParser\ParserFactory as NikicParserFactory;
|
||||
|
||||
final class ParserFactory
|
||||
@ -24,7 +24,7 @@ final class ParserFactory
|
||||
$this->nikicParserFactory = $nikicParserFactory;
|
||||
}
|
||||
|
||||
public function create(): Parser
|
||||
public function create(): NikicParser
|
||||
{
|
||||
return $this->nikicParserFactory->create(NikicParserFactory::PREFER_PHP7, $this->lexer, [
|
||||
'useIdentifierNodes' => true,
|
||||
|
@ -113,7 +113,17 @@ final class HtmlAddMethodRector extends AbstractRector
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $this->nodeTypeResolver->getTypeForNode($node->var, $this->fileNodes);
|
||||
|
||||
$this->nodeTypeResolver->getTypeForNode($node->var, $this->fileNodes);
|
||||
|
||||
dump($node->var);
|
||||
die;
|
||||
|
||||
|
||||
dump($type);
|
||||
die;
|
||||
|
||||
|
||||
dump($type->getType($node->var));
|
||||
die;
|
||||
|
||||
|
@ -47,7 +47,7 @@ abstract class AbstractRectorTestCase extends TestCase
|
||||
|
||||
protected function ensureFileExists(string $file): void
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
if (! file_exists($file)) {
|
||||
throw new FileNotFoundException(sprintf(
|
||||
'File "%s" not found in "%s".',
|
||||
$file,
|
||||
|
@ -10,26 +10,32 @@ services:
|
||||
Rector\:
|
||||
resource: '../../src'
|
||||
|
||||
# autowire by interface
|
||||
Rector\Contract\Parser\ParserInterface:
|
||||
alias: Rector\Parser\Parser
|
||||
|
||||
# 3rd party services
|
||||
Symfony\Component\Console\Application:
|
||||
arguments:
|
||||
$name: "Rector"
|
||||
|
||||
# PhpParser - Parser
|
||||
PhpParser\Parser:
|
||||
factory: ['@Rector\Parser\ParserFactory', 'create']
|
||||
PhpParser\Lexer:
|
||||
factory: ['@Rector\Parser\LexerFactory', 'create']
|
||||
PhpParser\BuilderFactory: ~
|
||||
|
||||
# PhpParser - NodeTraverser
|
||||
# to allow $namespacedName
|
||||
# see https://github.com/nikic/PHP-Parser/blob/7b36ca3b6cc1b99210c6699074d6091061e73eea/lib/PhpParser/Node/Stmt/ClassLike.php#L8
|
||||
PhpParser\NodeVisitor\NameResolver: ~
|
||||
|
||||
PhpParser\NodeTraverser:
|
||||
calls:
|
||||
- ['addVisitor', ['@PhpParser\NodeVisitor\NameResolver']]
|
||||
- ['addVisitor', ['@Rector\NodeVisitor\DependencyInjection\AddPropertiesToClassNodeVisitor']]
|
||||
|
||||
# Traverser
|
||||
- ['addVisitor', ['@Rector\NodeVisitor\NodeConnector']]
|
||||
PhpParser\ParserFactory: ~
|
||||
# Printer
|
||||
|
||||
# PhpParser - Printer
|
||||
PhpParser\PrettyPrinter\Standard: ~
|
||||
|
@ -20,7 +20,7 @@ abstract class AbstractContainerAwareTestCase extends TestCase
|
||||
* @param mixed[] $data
|
||||
* @param string $dataName
|
||||
*/
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
public function __construct(string $name = null, array $data = [], string $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
<?php declare (strict_types=1);
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Contrib\Nette\HtmlAddMethodRector\Correct;
|
||||
|
||||
use Nette\Utils\Html;
|
||||
|
||||
class SomeClass
|
||||
final class SomeClass
|
||||
{
|
||||
private function createHtml()
|
||||
private function createHtml(): void
|
||||
{
|
||||
$html = new Html();
|
||||
$html = new Html;
|
||||
$anotherHtml = $html;
|
||||
$anotherHtml->addHtml('someContent');
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
<?php declare (strict_types=1);
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Contrib\Nette\HtmlAddMethodRector\Wrong;
|
||||
|
||||
use Nette\Utils\Html;
|
||||
|
||||
class SomeClass
|
||||
final class SomeClass
|
||||
{
|
||||
private function createHtml()
|
||||
private function createHtml(): void
|
||||
{
|
||||
$html = new Html();
|
||||
$html = new Html;
|
||||
$anotherHtml = $html;
|
||||
$anotherHtml->add('someContent');
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user