phabel/src/Plugin/NestedExpressionFixer.php

115 lines
3.3 KiB
PHP
Raw Normal View History

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 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-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-08-30 20:55:28 +02:00
class NestedExpressionFixer extends Plugin
{
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:
// For all the following expressions, wrapping in a ternary breaks return-by-ref
case StaticCall::class:
case StaticPropertyFetch::class:
2020-08-30 20:55:28 +02:00
case FuncCall::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-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
}