mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 21:38:22 +01:00
drop EventDispatcher, add prioritied NodeTraversers
This commit is contained in:
parent
40c7d5366e
commit
261f9aa8f2
@ -11,8 +11,7 @@
|
||||
"symfony/console": "^3.3",
|
||||
"symfony/dependency-injection": "^3.3",
|
||||
"nikic/php-parser": "4.0.x-dev as 3.0.2",
|
||||
"nette/utils": "^2.4",
|
||||
"symfony/event-dispatcher": "^3.3"
|
||||
"nette/utils": "^2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.2",
|
||||
|
@ -11,8 +11,7 @@ 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
|
||||
final class ClassLikeTypeResolver extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var TypeContext
|
||||
@ -45,11 +44,13 @@ final class TypeResolvingNodeVisitor extends NodeVisitorAbstract
|
||||
$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);
|
||||
$parentNode = $node->getAttribute('parent');
|
||||
if ($parentNode instanceof Assign) {
|
||||
if ($parentNode->expr instanceof New_) {
|
||||
$variableType = $parentNode->expr->class->toString();
|
||||
$variableName = $parentNode->var->name;
|
||||
$this->typeContext->addLocalVariable($variableName, $variableType);
|
||||
}
|
||||
} else {
|
||||
$variableType = $this->typeContext->getTypeForVariable((string) $node->name);
|
||||
}
|
@ -24,10 +24,16 @@ final class TypeContext
|
||||
*/
|
||||
private $classLikeNode;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isInClass = false;
|
||||
|
||||
public function startFile(): void
|
||||
{
|
||||
$this->types = [];
|
||||
$this->classLikeNode = [];
|
||||
$this->isInClass = false;
|
||||
}
|
||||
|
||||
public function addLocalVariable(string $variableName, string $variableType): void
|
||||
@ -38,6 +44,8 @@ final class TypeContext
|
||||
public function enterClass(ClassLike $classLikeNode): void
|
||||
{
|
||||
$this->classLikeNode = $classLikeNode;
|
||||
$this->isInClass = true;
|
||||
// @todo: add properties
|
||||
}
|
||||
|
||||
public function enterFunction(FunctionLike $functionLikeNode): void
|
||||
@ -45,8 +53,10 @@ final class TypeContext
|
||||
$this->localTypes = [];
|
||||
|
||||
$functionReflection = $this->getFunctionReflection($functionLikeNode);
|
||||
foreach ($functionReflection->getParameters() as $parameterReflection) {
|
||||
$this->localTypes[$parameterReflection->getName()] = $parameterReflection->getType();
|
||||
if ($functionReflection) {
|
||||
foreach ($functionReflection->getParameters() as $parameterReflection) {
|
||||
$this->localTypes[$parameterReflection->getName()] = $parameterReflection->getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,12 +72,16 @@ final class TypeContext
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionFunction|ReflectionMethod
|
||||
* @return ReflectionFunction|ReflectionMethod|null
|
||||
*/
|
||||
private function getFunctionReflection(FunctionLike $functionLikeNode)
|
||||
{
|
||||
if ($this->classLikeNode) {
|
||||
$className = $this->classLikeNode->namespacedName->toString();
|
||||
if (! class_exists($className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = (string) $functionLikeNode->name;
|
||||
|
||||
return new ReflectionMethod($className, $methodName);
|
||||
|
@ -5,7 +5,3 @@ services:
|
||||
# PSR-4 autodiscovery
|
||||
Rector\NodeTypeResolver\:
|
||||
resource: '../../src'
|
||||
|
||||
PhpParser\NodeTraverser:
|
||||
calls:
|
||||
- ['addVisitor', ['@Rector\NodeTypeResolver\NodeVisitor\TypeResolvingNodeVisitor']]
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace Rector\NodeTypeResolver\Tests;
|
||||
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
@ -14,25 +13,15 @@ final class NodeTypeResolverTest extends AbstractContainerAwareTestCase
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->parser = $this->container->get(ParserInterface::class);
|
||||
$this->nodeTraverser = $this->container->get(NodeTraverser::class);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
// @todo: consider using event in parseFile() method
|
||||
$nodes = $this->parser->parseFile(__DIR__ . '/NodeTypeResolverSource/VariableType.php');
|
||||
|
||||
// run basic traverser here
|
||||
$this->nodeTraverser->traverse($nodes);
|
||||
|
||||
/** @var Variable $htmlVariableNode */
|
||||
$htmlVariableNode = $nodes[1]->stmts[1]->stmts[0]->stmts[0]->expr->var;
|
||||
|
||||
|
@ -63,6 +63,7 @@ final class FileProcessor
|
||||
public function processFile(SplFileInfo $file): void
|
||||
{
|
||||
$oldStmts = $this->parser->parseFile($file->getRealPath());
|
||||
|
||||
if ($oldStmts === null) {
|
||||
return;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ final class ReconstructCommand extends Command
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const NAME = 'reconstruct';
|
||||
private const NAME = 'processClass';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -23,10 +23,8 @@ final class AppKernel extends Kernel
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||
{
|
||||
// they are merged from bottom up; so to keep desired order (load basic config first, then add others)
|
||||
// they have to be inversed here
|
||||
$loader->load(__DIR__ . '/../../packages/NodeTypeResolver/src/config/services.yml');
|
||||
$loader->load(__DIR__ . '/../config/services.yml');
|
||||
$loader->load(__DIR__ . '/../../packages/NodeTypeResolver/src/config/services.yml');
|
||||
|
||||
if ($this->config) {
|
||||
$loader->load($this->config);
|
||||
|
@ -1,33 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Event;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
final class AfterParseEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
public function __construct(array $nodes)
|
||||
{
|
||||
$this->nodes = $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]
|
||||
*/
|
||||
public function getNodes(): array
|
||||
{
|
||||
return $this->nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
public function changeNodes(array $nodes): void
|
||||
{
|
||||
$this->nodes = $nodes;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\EventDispatcher;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
final class ClassBasedEventDispatcher
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function dispatch(Event $event): Event
|
||||
{
|
||||
$eventClass = get_class($event);
|
||||
return $this->eventDispatcher->dispatch($eventClass, $event);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\EventSubscriber;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Event\AfterParseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
final class TraverseAfterParsingEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
|
||||
public function __construct(NodeTraverser $nodeTraverser)
|
||||
{
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [AfterParseEvent::class => 'afterParse'];
|
||||
}
|
||||
|
||||
public function afterParse(AfterParseEvent $afterParseEvent): void
|
||||
{
|
||||
$nodes = $afterParseEvent->getNodes();
|
||||
$nodes = $this->nodeTraverser->traverse($nodes);
|
||||
$afterParseEvent->changeNodes($nodes);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ final class CloningNodeTraverser extends NodeTraverser
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// note: probably have to be recreated to clear cache
|
||||
$this->visitors[] = new CloningVisitor;
|
||||
}
|
||||
}
|
||||
|
9
src/NodeTraverser/MainNodeTraverser.php
Normal file
9
src/NodeTraverser/MainNodeTraverser.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTraverser;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
|
||||
final class MainNodeTraverser extends NodeTraverser
|
||||
{
|
||||
}
|
14
src/NodeTraverser/ShutdownNodeTraverser.php
Normal file
14
src/NodeTraverser/ShutdownNodeTraverser.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTraverser;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\NodeVisitor\PropertyToClassAdder;
|
||||
|
||||
final class ShutdownNodeTraverser extends NodeTraverser
|
||||
{
|
||||
public function __construct(PropertyToClassAdder $propertyToClassAdder)
|
||||
{
|
||||
$this->addVisitor($propertyToClassAdder);
|
||||
}
|
||||
}
|
25
src/NodeTraverser/StartupNodeTraverser.php
Normal file
25
src/NodeTraverser/StartupNodeTraverser.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTraverser;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\ClassLikeTypeResolver;
|
||||
use Rector\NodeVisitor\NodeConnector;
|
||||
|
||||
final class StartupNodeTraverser extends NodeTraverser
|
||||
{
|
||||
/**
|
||||
* NameResolver adds $namespacedName
|
||||
* @see https://github.com/nikic/PHP-Parser/blob/7b36ca3b6cc1b99210c6699074d6091061e73eea/lib/PhpParser/Node/Stmt/ClassLike.php#L8
|
||||
*/
|
||||
public function __construct(
|
||||
NameResolver $nameResolver,
|
||||
NodeConnector $nodeConnector,
|
||||
ClassLikeTypeResolver $classLikeTypeResolver
|
||||
) {
|
||||
$this->addVisitor($nameResolver);
|
||||
$this->addVisitor($nodeConnector);
|
||||
$this->addVisitor($classLikeTypeResolver);
|
||||
}
|
||||
}
|
@ -10,7 +10,14 @@ use PhpParser\NodeVisitorAbstract;
|
||||
*/
|
||||
final class NodeConnector extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $stack;
|
||||
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $prev;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeVisitor\DependencyInjection;
|
||||
namespace Rector\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
@ -10,9 +10,9 @@ use Rector\Builder\ConstructorMethodBuilder;
|
||||
use Rector\Builder\PropertyBuilder;
|
||||
|
||||
/**
|
||||
* Add new propertis to class and to contructor.
|
||||
* Adds new propertis to class and to contructor.
|
||||
*/
|
||||
final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||
final class PropertyToClassAdder extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var ConstructorMethodBuilder
|
||||
@ -27,16 +27,16 @@ final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||
/**
|
||||
* @var ClassPropertyCollector
|
||||
*/
|
||||
private $newClassPropertyCollector;
|
||||
private $classPropertyCollector;
|
||||
|
||||
public function __construct(
|
||||
ConstructorMethodBuilder $constructorMethodBuilder,
|
||||
PropertyBuilder $propertyBuilder,
|
||||
ClassPropertyCollector $newClassPropertyCollector
|
||||
ClassPropertyCollector $classPropertyCollector
|
||||
) {
|
||||
$this->constructorMethodBuilder = $constructorMethodBuilder;
|
||||
$this->propertyBuilder = $propertyBuilder;
|
||||
$this->newClassPropertyCollector = $newClassPropertyCollector;
|
||||
$this->classPropertyCollector = $classPropertyCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,7 +47,7 @@ final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
foreach ($nodes as $key => $node) {
|
||||
if ($node instanceof Class_) {
|
||||
$nodes[$key] = $this->reconstruct($node, (string) $node->name);
|
||||
$nodes[$key] = $this->processClass($node, (string) $node->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -55,9 +55,9 @@ final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
private function reconstruct(Class_ $classNode, string $className): Class_
|
||||
private function processClass(Class_ $classNode, string $className): Class_
|
||||
{
|
||||
$propertiesForClass = $this->newClassPropertyCollector->getPropertiesforClass($className);
|
||||
$propertiesForClass = $this->classPropertyCollector->getPropertiesforClass($className);
|
||||
|
||||
foreach ($propertiesForClass as $propertyType => $propertyName) {
|
||||
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
|
@ -5,8 +5,6 @@ namespace Rector\Parser;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Parser as NikicParser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
use Rector\Event\AfterParseEvent;
|
||||
use Rector\EventDispatcher\ClassBasedEventDispatcher;
|
||||
|
||||
final class Parser implements ParserInterface
|
||||
{
|
||||
@ -20,15 +18,9 @@ final class Parser implements ParserInterface
|
||||
*/
|
||||
private $nodesByFile = [];
|
||||
|
||||
/**
|
||||
* @var ClassBasedEventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(NikicParser $nikicParser, ClassBasedEventDispatcher $eventDispatcher)
|
||||
public function __construct(NikicParser $nikicParser)
|
||||
{
|
||||
$this->nikicParser = $nikicParser;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,23 +33,8 @@ final class Parser implements ParserInterface
|
||||
}
|
||||
|
||||
$fileContent = file_get_contents($filePath);
|
||||
|
||||
$nodes = $this->nikicParser->parse($fileContent);
|
||||
|
||||
$this->nodesByFile[$filePath] = $this->processByEventDisptacher($nodes);
|
||||
$this->nodesByFile[$filePath] = $this->nikicParser->parse($fileContent);
|
||||
|
||||
return $this->nodesByFile[$filePath];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
* @return Node[]
|
||||
*/
|
||||
private function processByEventDisptacher(array $nodes): array
|
||||
{
|
||||
$afterParseEvent = new AfterParseEvent($nodes);
|
||||
$this->eventDispatcher->dispatch($afterParseEvent);
|
||||
|
||||
return $afterParseEvent->getNodes();
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,10 @@
|
||||
namespace Rector\Testing\Application;
|
||||
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitor;
|
||||
use PhpParser\Parser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
use Rector\NodeTraverser\CloningNodeTraverser;
|
||||
use Rector\NodeTraverser\MainNodeTraverser;
|
||||
use Rector\Printer\FormatPerservingPrinter;
|
||||
use Rector\Rector\RectorCollector;
|
||||
use SplFileInfo;
|
||||
@ -30,9 +29,9 @@ final class FileProcessor
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
* @var MainNodeTraverser
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
private $mainNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var RectorCollector
|
||||
@ -49,13 +48,13 @@ final class FileProcessor
|
||||
ParserInterface $parser,
|
||||
FormatPerservingPrinter $codeStyledPrinter,
|
||||
Lexer $lexer,
|
||||
NodeTraverser $nodeTraverser,
|
||||
MainNodeTraverser $mainNodeTraverser,
|
||||
RectorCollector $rectorCollector
|
||||
) {
|
||||
$this->parser = $parser;
|
||||
$this->codeStyledPrinter = $codeStyledPrinter;
|
||||
$this->lexer = $lexer;
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
$this->mainNodeTraverser = $mainNodeTraverser;
|
||||
$this->rectorCollector = $rectorCollector;
|
||||
$this->cloningNodeTraverser = $cloningNodeTraverser;
|
||||
}
|
||||
@ -68,7 +67,7 @@ final class FileProcessor
|
||||
foreach ($rectorClasses as $rectorClass) {
|
||||
/** @var NodeVisitor $rector */
|
||||
$rector = $this->rectorCollector->getRector($rectorClass);
|
||||
$this->nodeTraverser->addVisitor($rector);
|
||||
$this->mainNodeTraverser->addVisitor($rector);
|
||||
}
|
||||
|
||||
return $this->processFile($file);
|
||||
@ -83,7 +82,9 @@ final class FileProcessor
|
||||
$oldTokens = $this->lexer->getTokens();
|
||||
$newStmts = $this->cloningNodeTraverser->traverse($oldStmts);
|
||||
|
||||
$newStmts = $this->nodeTraverser->traverse($newStmts);
|
||||
// @todo: first clone, then startup, then main
|
||||
|
||||
$newStmts = $this->mainNodeTraverser->traverse($newStmts);
|
||||
|
||||
return $this->codeStyledPrinter->printToString($newStmts, $oldStmts, $oldTokens);
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ services:
|
||||
# PSR-4 autodiscovery
|
||||
Rector\:
|
||||
resource: '../../src'
|
||||
exclude: '../../src/{Event}'
|
||||
|
||||
# autowire by interface
|
||||
Rector\Contract\Parser\ParserInterface:
|
||||
@ -21,26 +20,16 @@ services:
|
||||
arguments:
|
||||
$name: "Rector"
|
||||
|
||||
# Event Distpatcher
|
||||
Symfony\Component\EventDispatcher\EventDispatcher: ~
|
||||
|
||||
# PhpParser - Parser
|
||||
PhpParser\Parser:
|
||||
factory: ['@Rector\Parser\ParserFactory', 'create']
|
||||
PhpParser\Lexer:
|
||||
factory: ['@Rector\Parser\LexerFactory', 'create']
|
||||
PhpParser\ParserFactory: ~
|
||||
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']]
|
||||
- ['addVisitor', ['@Rector\NodeVisitor\NodeConnector']]
|
||||
PhpParser\ParserFactory: ~
|
||||
PhpParser\NodeTraverser: ~
|
||||
|
||||
# PhpParser - Printer
|
||||
PhpParser\PrettyPrinter\Standard: ~
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\Tests\Rector\Contrib\Nette\InjectPropertyRector;
|
||||
|
||||
use Rector\NodeVisitor\DependencyInjection\AddPropertiesToClassNodeVisitor;
|
||||
use Rector\Rector\Contrib\Nette\InjectPropertyRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user