2020-08-03 21:31:32 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Phabel;
|
|
|
|
|
2020-08-13 21:34:59 +02:00
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Parser;
|
|
|
|
use PhpParser\ParserFactory;
|
|
|
|
use SplQueue;
|
|
|
|
|
2020-08-13 18:30:12 +02:00
|
|
|
/**
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
|
|
|
*/
|
2020-08-03 21:31:32 +02:00
|
|
|
class Traverser
|
|
|
|
{
|
2020-08-13 21:34:59 +02:00
|
|
|
/**
|
|
|
|
* Plugin queue.
|
|
|
|
*
|
|
|
|
* @return SplQueue<SplQueue<Plugin>>
|
|
|
|
*/
|
|
|
|
private SplQueue $queue;
|
|
|
|
/**
|
|
|
|
* Parser instance.
|
|
|
|
*/
|
|
|
|
private Parser $parser;
|
|
|
|
/**
|
|
|
|
* Plugin queue for specific package.
|
|
|
|
*
|
|
|
|
* @return SplQueue<SplQueue<Plugin>>
|
|
|
|
*/
|
|
|
|
private SplQueue $packageQueue;
|
|
|
|
/**
|
|
|
|
* AST traverser.
|
|
|
|
*
|
|
|
|
* @return SplQueue<SplQueue<Plugin>> $queue Plugin queue
|
|
|
|
*/
|
|
|
|
public function __construct(SplQueue $queue)
|
|
|
|
{
|
|
|
|
$this->queue = $queue;
|
|
|
|
$this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Set package name.
|
|
|
|
*
|
|
|
|
* @param string $package Package name
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setPackage(string $package): void
|
|
|
|
{
|
|
|
|
$this->packageQueue = new SplQueue;
|
|
|
|
$newQueue = new SplQueue;
|
|
|
|
foreach ($this->queue as $queue) {
|
|
|
|
if ($newQueue->count()) {
|
|
|
|
$this->packageQueue->enqueue($newQueue);
|
|
|
|
$newQueue = new SplQueue;
|
|
|
|
}
|
|
|
|
/** @var Plugin */
|
|
|
|
foreach ($queue as $plugin) {
|
|
|
|
if ($plugin->shouldRun($package)) {
|
|
|
|
$newQueue->enqueue($plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($newQueue->count()) {
|
|
|
|
$this->packageQueue->enqueue($newQueue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Traverse AST of file.
|
|
|
|
*
|
|
|
|
* @param string $file File
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function traverse(string $file): void
|
|
|
|
{
|
|
|
|
/** @var SplQueue<SplQueue<Plugin>> */
|
|
|
|
$reducedQueue = new SplQueue;
|
|
|
|
$newQueue = new SplQueue;
|
|
|
|
foreach ($this->packageQueue ?? $this->queue as $queue) {
|
|
|
|
if ($newQueue->count()) {
|
|
|
|
$reducedQueue->enqueue($newQueue);
|
|
|
|
$newQueue = new SplQueue;
|
|
|
|
}
|
|
|
|
/** @var Plugin */
|
|
|
|
foreach ($queue as $plugin) {
|
|
|
|
if ($plugin->shouldRunFile($file)) {
|
|
|
|
$newQueue->enqueue($plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($newQueue->count()) {
|
|
|
|
$reducedQueue->enqueue($newQueue);
|
|
|
|
} elseif (!$reducedQueue->count()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$ast = $this->parser->parse(\file_get_contents($file));
|
|
|
|
foreach ($reducedQueue as $queue) {
|
|
|
|
$this->traverseArray($ast, $queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Traverse array of nodes.
|
|
|
|
*
|
|
|
|
* @param Node[] $nodes Nodes
|
|
|
|
* @param SplQueue<Plugin> $plugins Plugins
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function traverseArray(array &$nodes, SplQueue $plugins): void
|
|
|
|
{
|
|
|
|
foreach ($nodes as &$node) {
|
|
|
|
$this->traverseNode($node, $plugins);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Traverse node.
|
|
|
|
*
|
|
|
|
* @param Node &$node Node
|
|
|
|
* @param SplQueue<Plugin> $plugins Plugins
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function traverseNode(Node &$node, SplQueue $plugins): void
|
|
|
|
{
|
|
|
|
foreach ($plugins as $plugin) {
|
|
|
|
foreach (PluginCache::enterMethods(\get_class($plugin)) as $type => $methods) {
|
|
|
|
if (!$node instanceof $type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
foreach ($methods as $method) {
|
|
|
|
$result = $plugin->{$method}($node);
|
|
|
|
if ($result instanceof Node) {
|
|
|
|
if (!$result instanceof $node) {
|
|
|
|
$node = $result;
|
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
$node = $result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($node->getSubNodeNames() as $name) {
|
|
|
|
$subNode = &$node->{$name};
|
|
|
|
if (\is_array($subNode)) {
|
|
|
|
$this->traverseArray($subNode, $plugins);
|
|
|
|
} else {
|
|
|
|
$this->traverseNode($subNode, $plugins);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($plugins as $plugin) {
|
|
|
|
foreach (PluginCache::leaveMethods(\get_class($plugin)) as $type => $methods) {
|
|
|
|
if (!$node instanceof $type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
foreach ($methods as $method) {
|
|
|
|
$result = $plugin->{$method}($node);
|
|
|
|
if ($result instanceof Node) {
|
|
|
|
if (!$result instanceof $node) {
|
|
|
|
$node = $result;
|
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
$node = $result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-05 14:52:49 +02:00
|
|
|
}
|