* @license MIT */ class NestedExpressionFixer extends Plugin { /** * 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); } /** * Recursively extract bottom ArrayDimFetch. * * @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; } public function leave(Expr $expr, Context $context): ?Expr { /** @var array, true>> */ $subNodes = $this->getConfig($class = \get_class($expr), false); if (!$subNodes) { return null; } foreach ($subNodes as $key => $types) { /** @var Expr $value */ $value = &$expr->{$key}; if (!isset($types[\get_class($value)])) { $workVar = $this->extractWorkVar($value); if (!isset($types[\get_class($workVar)])) { continue; } } switch ($class) { case ArrayDimFetch::class: case PropertyFetch::class: case MethodCall::class: case Instanceof_::class: if ($expr instanceof Instanceof_ && $key === 'class') { return self::callPoly('instanceOf', $expr->expr, $expr->class); } $value = self::callPoly('returnMe', $value); break; case New_::class: case ClassConstFetch::class: $valueCopy = $value; return new Ternary( new BooleanOr( new Assign($value = $context->getVariable(), $valueCopy), new LNumber(1) ), $expr, new LNumber(0) ); // For all the following expressions, wrapping in a ternary breaks return-by-ref, // so for now wrap in a hack and fix once the expression bubbler is ready (READY NOW, done) case StaticCall::class: case StaticPropertyFetch::class: case FuncCall::class: $this->traverser->traverseAst($expr); $valueCopy = $value; $context->insertBefore($expr, new Assign($value = $context->getVariable(), $valueCopy)); /* return new ErrorSuppress( new MethodCall( self::callPoly( "returnMe", new Closure([ 'byRef' => true, 'uses' => \array_keys($this->finderPlugin->getFound()), 'stmts' => [ new Assign($value = $context->getVariable(), $valueCopy), new Return_($expr) ] ]) ), '__invoke' ) );*/ } } } /** * 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; } /** * Check if a is instance of b. * * @param class-string|object $a * @param class-string|object $b * * @return boolean */ public static function instanceOf($a, $b): bool { return $a instanceof $b; } }