2020-08-30 20:55:28 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Phabel\Plugin;
|
|
|
|
|
2020-09-01 12:52:44 +02:00
|
|
|
use Phabel\Context;
|
2020-08-30 20:55:28 +02:00
|
|
|
use Phabel\Plugin;
|
2020-09-01 17:36:13 +02:00
|
|
|
use Phabel\Traverser;
|
2020-09-01 13:53:03 +02:00
|
|
|
use PhpParser\Node;
|
2020-08-30 20:55:28 +02:00
|
|
|
use PhpParser\Node\Expr;
|
|
|
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
2020-09-01 12:52:44 +02:00
|
|
|
use PhpParser\Node\Expr\Assign;
|
|
|
|
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
|
|
|
|
use PhpParser\Node\Expr\ClassConstFetch;
|
2020-09-01 17:36:13 +02:00
|
|
|
use PhpParser\Node\Expr\Closure;
|
|
|
|
use PhpParser\Node\Expr\ErrorSuppress;
|
2020-08-30 20:55:28 +02:00
|
|
|
use PhpParser\Node\Expr\FuncCall;
|
|
|
|
use PhpParser\Node\Expr\Instanceof_;
|
|
|
|
use PhpParser\Node\Expr\MethodCall;
|
|
|
|
use PhpParser\Node\Expr\New_;
|
|
|
|
use PhpParser\Node\Expr\PropertyFetch;
|
2020-09-01 12:52:44 +02:00
|
|
|
use PhpParser\Node\Expr\StaticCall;
|
|
|
|
use PhpParser\Node\Expr\StaticPropertyFetch;
|
|
|
|
use PhpParser\Node\Expr\Ternary;
|
|
|
|
use PhpParser\Node\Scalar\LNumber;
|
2020-09-01 17:36:13 +02:00
|
|
|
use PhpParser\Node\Stmt\Return_;
|
2020-08-30 20:55:28 +02:00
|
|
|
|
2020-09-05 22:35:30 +02:00
|
|
|
/**
|
|
|
|
* Fix nested expressions.
|
|
|
|
*
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
|
|
|
* @license MIT
|
|
|
|
*/
|
2020-08-30 20:55:28 +02:00
|
|
|
class NestedExpressionFixer extends Plugin
|
|
|
|
{
|
2020-09-01 17:36:13 +02:00
|
|
|
/**
|
|
|
|
* Traverser.
|
|
|
|
*/
|
|
|
|
private Traverser $traverser;
|
|
|
|
/**
|
|
|
|
* Finder plugin.
|
|
|
|
*/
|
|
|
|
private ArrowClosureVariableFinder $finderPlugin;
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*/
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
$this->finderPlugin = new ArrowClosureVariableFinder;
|
|
|
|
$this->finderPlugin->setConfig('byRef', true);
|
|
|
|
$this->traverser = Traverser::fromPlugin($this->finderPlugin);
|
|
|
|
}
|
2020-09-01 13:53:03 +02:00
|
|
|
/**
|
2020-09-01 14:31:23 +02:00
|
|
|
* Recursively extract bottom ArrayDimFetch.
|
2020-09-01 13:53:03 +02:00
|
|
|
*
|
|
|
|
* @param Node $var
|
|
|
|
* @return Node
|
|
|
|
*/
|
|
|
|
private static function &extractWorkVar(Expr &$var): Expr
|
|
|
|
{
|
|
|
|
if ($var instanceof ArrayDimFetch && $var->var instanceof ArrayDimFetch) {
|
|
|
|
return self::extractWorkVar($var->var);
|
|
|
|
}
|
|
|
|
return $var;
|
|
|
|
}
|
|
|
|
|
2020-09-01 12:52:44 +02:00
|
|
|
public function leave(Expr $expr, Context $context): ?Expr
|
2020-08-30 20:55:28 +02:00
|
|
|
{
|
|
|
|
/** @var array<string, array<class-string<Expr>, true>> */
|
|
|
|
$subNodes = $this->getConfig($class = \get_class($expr), false);
|
|
|
|
if (!$subNodes) {
|
2020-09-01 12:52:44 +02:00
|
|
|
return null;
|
2020-08-30 20:55:28 +02:00
|
|
|
}
|
|
|
|
foreach ($subNodes as $key => $types) {
|
|
|
|
/** @var Expr $value */
|
|
|
|
$value = &$expr->{$key};
|
|
|
|
if (!isset($types[\get_class($value)])) {
|
2020-09-01 13:53:03 +02:00
|
|
|
$workVar = $this->extractWorkVar($value);
|
|
|
|
if (!isset($types[\get_class($workVar)])) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-30 20:55:28 +02:00
|
|
|
}
|
|
|
|
switch ($class) {
|
|
|
|
case ArrayDimFetch::class:
|
|
|
|
case PropertyFetch::class:
|
|
|
|
case MethodCall::class:
|
|
|
|
case Instanceof_::class:
|
2020-09-01 13:53:03 +02:00
|
|
|
if ($expr instanceof Instanceof_ && $key === 'class') {
|
|
|
|
return self::callPoly('instanceOf', $expr->expr, $expr->class);
|
|
|
|
}
|
2020-08-30 20:55:28 +02:00
|
|
|
$value = self::callPoly('returnMe', $value);
|
|
|
|
break;
|
2020-09-01 13:53:03 +02:00
|
|
|
case New_::class:
|
2020-09-01 12:52:44 +02:00
|
|
|
case ClassConstFetch::class:
|
2020-09-01 13:53:03 +02:00
|
|
|
$valueCopy = $value;
|
2020-09-01 12:52:44 +02:00
|
|
|
return new Ternary(
|
|
|
|
new BooleanOr(
|
2020-09-01 13:53:03 +02:00
|
|
|
new Assign($value = $context->getVariable(), $valueCopy),
|
2020-09-01 12:52:44 +02:00
|
|
|
new LNumber(1)
|
|
|
|
),
|
|
|
|
$expr,
|
|
|
|
new LNumber(0)
|
|
|
|
);
|
2020-09-01 17:36:13 +02:00
|
|
|
// For all the following expressions, wrapping in a ternary breaks return-by-ref,
|
2020-10-05 11:35:11 +02:00
|
|
|
// so for now wrap in a hack and fix once the expression bubbler is ready (READY NOW, done)
|
2020-09-01 17:36:13 +02:00
|
|
|
case StaticCall::class:
|
|
|
|
case StaticPropertyFetch::class:
|
|
|
|
case FuncCall::class:
|
|
|
|
$this->traverser->traverseAst($expr);
|
|
|
|
$valueCopy = $value;
|
2020-10-05 11:35:11 +02:00
|
|
|
$context->insertBefore($expr, new Assign($value = $context->getVariable(), $valueCopy));
|
|
|
|
/*
|
2020-09-01 17:36:13 +02:00
|
|
|
return new ErrorSuppress(
|
|
|
|
new MethodCall(
|
|
|
|
self::callPoly(
|
|
|
|
"returnMe",
|
|
|
|
new Closure([
|
|
|
|
'byRef' => true,
|
2020-09-05 22:35:30 +02:00
|
|
|
'uses' => \array_keys($this->finderPlugin->getFound()),
|
2020-09-01 17:36:13 +02:00
|
|
|
'stmts' => [
|
|
|
|
new Assign($value = $context->getVariable(), $valueCopy),
|
|
|
|
new Return_($expr)
|
|
|
|
]
|
|
|
|
])
|
|
|
|
),
|
|
|
|
'__invoke'
|
|
|
|
)
|
2020-10-05 11:35:11 +02:00
|
|
|
);*/
|
2020-08-30 20:55:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the data provided.
|
|
|
|
*
|
|
|
|
* @param mixed $data Data
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*
|
|
|
|
* @template T
|
|
|
|
*
|
|
|
|
* @psalm-param T $data data
|
|
|
|
*
|
|
|
|
* @psalm-return T
|
|
|
|
*/
|
|
|
|
public static function returnMe($data)
|
|
|
|
{
|
|
|
|
return $data;
|
|
|
|
}
|
2020-09-01 13:53:03 +02:00
|
|
|
|
|
|
|
/**
|
2020-09-01 14:31:23 +02:00
|
|
|
* Check if a is instance of b.
|
2020-09-01 13:53:03 +02:00
|
|
|
*
|
|
|
|
* @param class-string|object $a
|
|
|
|
* @param class-string|object $b
|
2020-09-01 14:31:23 +02:00
|
|
|
*
|
2020-09-01 13:53:03 +02:00
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public static function instanceOf($a, $b): bool
|
|
|
|
{
|
|
|
|
return $a instanceof $b;
|
|
|
|
}
|
2020-08-30 20:55:28 +02:00
|
|
|
}
|