diff --git a/composer.json b/composer.json index cbf1ad4cc88..b5855d0ee5a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "symfony/console": "^3.3", "symfony/dependency-injection": "^3.3", "nikic/php-parser": "4.0.x-dev as 3.0.2", - "nette/utils": "^2.4" + "nette/utils": "^2.4", + "symfony/event-dispatcher": "^3.3" }, "require-dev": { "phpunit/phpunit": "^6.2", @@ -37,5 +38,8 @@ "check-cs": "ecs check bin src tests", "fix-cs": "ecs check bin src tests --fix", "phpstan": "phpstan analyse bin src tests --level max --configuration phpstan.neon" + }, + "config": { + "sort-packages": true } } diff --git a/src/Application/FileProcessor.php b/src/Application/FileProcessor.php index 3fe63172488..79e667d694e 100644 --- a/src/Application/FileProcessor.php +++ b/src/Application/FileProcessor.php @@ -4,7 +4,7 @@ namespace Rector\Application; use PhpParser\Lexer; use PhpParser\NodeTraverser; -use PhpParser\Parser; +use Rector\Contract\Parser\ParserInterface; use Rector\NodeTraverser\CloningNodeTraverser; use Rector\Printer\FormatPerservingPrinter; use SplFileInfo; @@ -12,7 +12,7 @@ use SplFileInfo; final class FileProcessor { /** - * @var Parser + * @var ParserInterface */ private $parser; @@ -37,7 +37,7 @@ final class FileProcessor private $cloningNodeTraverser; public function __construct( - Parser $parser, + ParserInterface $parser, FormatPerservingPrinter $codeStyledPrinter, Lexer $lexer, NodeTraverser $nodeTraverser, @@ -62,8 +62,7 @@ final class FileProcessor public function processFile(SplFileInfo $file): void { - $fileContent = file_get_contents($file->getRealPath()); - $oldStmts = $this->parser->parse($fileContent); + $oldStmts = $this->parser->parseFile($file->getRealPath()); if ($oldStmts === null) { return; } diff --git a/src/DependencyInjection/CompilerPass/CollectorCompilerPass.php b/src/DependencyInjection/CompilerPass/CollectorCompilerPass.php index 6af54dcfa16..277171c01ea 100644 --- a/src/DependencyInjection/CompilerPass/CollectorCompilerPass.php +++ b/src/DependencyInjection/CompilerPass/CollectorCompilerPass.php @@ -8,6 +8,8 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symplify\PackageBuilder\Adapter\Symfony\DependencyInjection\DefinitionCollector; final class CollectorCompilerPass implements CompilerPassInterface @@ -16,6 +18,7 @@ final class CollectorCompilerPass implements CompilerPassInterface { $this->collectCommandsToConsoleApplication($containerBuilder); $this->collectRectorsToRectorCollector($containerBuilder); + $this->collectEventSubscribersToEventDisptacher($containerBuilder); } private function collectCommandsToConsoleApplication(ContainerBuilder $containerBuilder): void @@ -37,4 +40,14 @@ final class CollectorCompilerPass implements CompilerPassInterface 'addRector' ); } + + private function collectEventSubscribersToEventDisptacher(ContainerBuilder $containerBuilder): void + { + DefinitionCollector::loadCollectorWithType( + $containerBuilder, + EventDispatcherInterface::class, + EventSubscriberInterface::class, + 'addSubscriber' + ); + } } diff --git a/src/Event/AfterParseEvent.php b/src/Event/AfterParseEvent.php new file mode 100644 index 00000000000..7f6c08c3250 --- /dev/null +++ b/src/Event/AfterParseEvent.php @@ -0,0 +1,33 @@ +nodes = $nodes; + } + + /** + * @return Node[] + */ + public function getNodes(): array + { + return $this->nodes; + } + + /** + * @param Node[] $nodes + */ + public function changeNodes(array $nodes): void + { + $this->nodes = $nodes; + } +} diff --git a/src/EventDispatcher/ClassBasedEventDispatcher.php b/src/EventDispatcher/ClassBasedEventDispatcher.php new file mode 100644 index 00000000000..6485b677eea --- /dev/null +++ b/src/EventDispatcher/ClassBasedEventDispatcher.php @@ -0,0 +1,25 @@ +eventDispatcher = $eventDispatcher; + } + + public function dispatch(Event $event): Event + { + $eventClass = get_class($event); + return $this->eventDispatcher->dispatch($eventClass, $event); + } +} diff --git a/src/EventSubscriber/TraverseAfterParsingEventSubscriber.php b/src/EventSubscriber/TraverseAfterParsingEventSubscriber.php new file mode 100644 index 00000000000..5c2bb8a5aba --- /dev/null +++ b/src/EventSubscriber/TraverseAfterParsingEventSubscriber.php @@ -0,0 +1,35 @@ +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); + } +} diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index 00a9656bd17..b8860ffc1e1 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -5,6 +5,8 @@ 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 { @@ -18,9 +20,15 @@ final class Parser implements ParserInterface */ private $nodesByFile = []; - public function __construct(NikicParser $nikicParser) + /** + * @var ClassBasedEventDispatcher + */ + private $eventDispatcher; + + public function __construct(NikicParser $nikicParser, ClassBasedEventDispatcher $eventDispatcher) { $this->nikicParser = $nikicParser; + $this->eventDispatcher = $eventDispatcher; } /** @@ -33,8 +41,23 @@ final class Parser implements ParserInterface } $fileContent = file_get_contents($filePath); - $this->nodesByFile[$filePath] = $this->nikicParser->parse($fileContent); + + $nodes = $this->nikicParser->parse($fileContent); + + $this->nodesByFile[$filePath] = $this->processByEventDisptacher($nodes); 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(); + } } diff --git a/src/Testing/Application/FileProcessor.php b/src/Testing/Application/FileProcessor.php index cfd89161295..262fd141bb5 100644 --- a/src/Testing/Application/FileProcessor.php +++ b/src/Testing/Application/FileProcessor.php @@ -6,6 +6,7 @@ use PhpParser\Lexer; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use PhpParser\Parser; +use Rector\Contract\Parser\ParserInterface; use Rector\NodeTraverser\CloningNodeTraverser; use Rector\Printer\FormatPerservingPrinter; use Rector\Rector\RectorCollector; @@ -14,7 +15,7 @@ use SplFileInfo; final class FileProcessor { /** - * @var Parser + * @var ParserInterface */ private $parser; @@ -45,7 +46,7 @@ final class FileProcessor public function __construct( CloningNodeTraverser $cloningNodeTraverser, - Parser $parser, + ParserInterface $parser, FormatPerservingPrinter $codeStyledPrinter, Lexer $lexer, NodeTraverser $nodeTraverser, @@ -78,9 +79,7 @@ final class FileProcessor */ public function processFile(SplFileInfo $file): string { - $fileContent = $this->getFileContent($file); - - $oldStmts = $this->parser->parse($fileContent); + $oldStmts = $this->parser->parseFile($file->getRealPath()); $oldTokens = $this->lexer->getTokens(); $newStmts = $this->cloningNodeTraverser->traverse($oldStmts); @@ -88,9 +87,4 @@ final class FileProcessor return $this->codeStyledPrinter->printToString($newStmts, $oldStmts, $oldTokens); } - - private function getFileContent(SplFileInfo $file): string - { - return file_get_contents($file->getRealPath()); - } } diff --git a/src/config/services.yml b/src/config/services.yml index 866113c29ef..241be3a2d0b 100644 --- a/src/config/services.yml +++ b/src/config/services.yml @@ -5,10 +5,12 @@ parameters: services: _defaults: autowire: true + autoconfigure: true # PSR-4 autodiscovery Rector\: resource: '../../src' + exclude: '../../src/{Event}' # autowire by interface Rector\Contract\Parser\ParserInterface: @@ -19,6 +21,9 @@ services: arguments: $name: "Rector" + # Event Distpatcher + Symfony\Component\EventDispatcher\EventDispatcher: ~ + # PhpParser - Parser PhpParser\Parser: factory: ['@Rector\Parser\ParserFactory', 'create']