drop EventDispatcher, add prioritied NodeTraversers

This commit is contained in:
TomasVotruba 2017-08-20 15:25:03 +02:00
parent 40c7d5366e
commit 261f9aa8f2
21 changed files with 108 additions and 179 deletions

View File

@ -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",

View File

@ -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);
}

View File

@ -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);

View File

@ -5,7 +5,3 @@ services:
# PSR-4 autodiscovery
Rector\NodeTypeResolver\:
resource: '../../src'
PhpParser\NodeTraverser:
calls:
- ['addVisitor', ['@Rector\NodeTypeResolver\NodeVisitor\TypeResolvingNodeVisitor']]

View File

@ -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;

View File

@ -63,6 +63,7 @@ final class FileProcessor
public function processFile(SplFileInfo $file): void
{
$oldStmts = $this->parser->parseFile($file->getRealPath());
if ($oldStmts === null) {
return;
}

View File

@ -15,7 +15,7 @@ final class ReconstructCommand extends Command
/**
* @var string
*/
private const NAME = 'reconstruct';
private const NAME = 'processClass';
/**
* @var string

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,9 @@
<?php declare(strict_types=1);
namespace Rector\NodeTraverser;
use PhpParser\NodeTraverser;
final class MainNodeTraverser extends NodeTraverser
{
}

View 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);
}
}

View 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);
}
}

View File

@ -10,7 +10,14 @@ use PhpParser\NodeVisitorAbstract;
*/
final class NodeConnector extends NodeVisitorAbstract
{
/**
* @var Node
*/
private $stack;
/**
* @var Node
*/
private $prev;
/**

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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: ~

View File

@ -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;