Finalize php5.6 transforms

This commit is contained in:
Daniil Gentili 2020-08-14 14:43:29 +02:00
parent 128977ef7e
commit 9b35d45048
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
4 changed files with 107 additions and 42 deletions

View File

@ -2,9 +2,22 @@
namespace Phabel;
use SplStack;
/**
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Context
{
/**
* Parent nodes stack
*
* @var SplStack<Node>
*/
public SplStack $parents;
public function __construct()
{
$this->parents = new SplStack;
}
}

32
src/RootNode.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace Phabel;
use PhpParser\Node;
use PhpParser\NodeAbstract;
/**
* Root node
*/
class RootNode extends NodeAbstract
{
/**
* Children
*
* @var Node[]
*/
public $stmts = [];
public function __construct(array $stmts, array $attributes = [])
{
$this->stmts = $stmts;
parent::__construct($attributes);
}
public function getSubNodeNames(): array
{
return ['stmts'];
}
public function getType(): string
{
return 'rootNode';
}
}

View File

@ -2,44 +2,71 @@
namespace Phabel\Target\Php70;
use Phabel\Context;
use Phabel\Plugin;
use Phabel\RootNode;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Stmt\Namespace_;
class AnonymousClassReplacer extends Plugin
{
/**
* @var array
* Anonymous class count.
*/
protected $anonymousClassNodes = [];
public static $count = 0;
private int $count = 0;
/**
* {@inheritdoc}
* Current file name hash.
*/
public function leaveNode(Node $node)
{
if (!$node instanceof Node\Expr\New_) {
return;
}
private string $fileName = '';
/**
* {@inheritDoc}
*/
public function shouldRunFile(string $file): bool
{
$this->fileName = \hash('sha256', $file);
return parent::shouldRunFile($file);
}
/**
* Leave new.
*
* @param New_ $node New stmt
* @param Context $ctx Context
*
* @return void
*/
public function leaveNew(New_ $node, Context $ctx): void
{
$classNode = $node->class;
if (!$classNode instanceof Node\Stmt\Class_) {
return;
}
$newClassName = 'AnonymousClass'.(self::$count++);
$classNode->name = 'PhabelAnonymousClass'.$this->fileName.($this->count++);
$classNode->name = $newClassName;
$this->anonymousClassNodes[] = $classNode;
// Generate new code that instantiate our new class
$newNode = new Node\Expr\New_(
new Node\Expr\ConstFetch(
new Node\Name($newClassName)
),
$node->args
$node->class = new Node\Expr\ConstFetch(
new Node\Name($classNode->name)
);
return $newNode;
$prevNode = $node;
foreach ($ctx->parents as $node) {
if ($node instanceof Namespace_ || $node instanceof RootNode) {
$foundIndex = -1;
foreach ($node->stmts as $index => $curNode) {
if ($curNode === $prevNode) {
$foundIndex = $index;
break;
}
}
if ($foundIndex >= 0) {
\array_splice($node->stmts, $foundIndex, 0, [$classNode]);
return;
}
}
$prevNode = $node;
}
throw new \RuntimeException('Could not find hook for inserting anonymous class!');
}
}

View File

@ -94,23 +94,10 @@ class Traverser
} elseif (!$reducedQueue->count()) {
return;
}
$ast = $this->parser->parse(\file_get_contents($file));
$ast = new RootNode($this->parser->parse(\file_get_contents($file)));
$context = new Context;
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);
$this->traverseNode($ast, $queue, $context);
}
}
/**
@ -118,9 +105,11 @@ class Traverser
*
* @param Node &$node Node
* @param SplQueue<Plugin> $plugins Plugins
* @param Context $context Context
*
* @return void
*/
public function traverseNode(Node &$node, SplQueue $plugins): void
public function traverseNode(Node &$node, SplQueue $plugins, Context $context): void
{
foreach ($plugins as $plugin) {
foreach (PluginCache::enterMethods(\get_class($plugin)) as $type => $methods) {
@ -128,7 +117,7 @@ class Traverser
continue;
}
foreach ($methods as $method) {
$result = $plugin->{$method}($node);
$result = $plugin->{$method}($node, $context);
if ($result instanceof Node) {
if (!$result instanceof $node) {
$node = $result;
@ -139,21 +128,25 @@ class Traverser
}
}
}
$context->parents->push($node);
foreach ($node->getSubNodeNames() as $name) {
$subNode = &$node->{$name};
if (\is_array($subNode)) {
$this->traverseArray($subNode, $plugins);
foreach ($subNode as &$subNodeNode) {
$this->traverseNode($subNodeNode, $plugins, $context);
}
} else {
$this->traverseNode($subNode, $plugins);
$this->traverseNode($subNode, $plugins, $context);
}
}
$context->parents->pop();
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);
$result = $plugin->{$method}($node, $context);
if ($result instanceof Node) {
if (!$result instanceof $node) {
$node = $result;