/** * @param string[] $variableTypes indexed by variable name * * @return bool */ public function canCauseSideEffects(array $variableTypes) : bool { if (self::GLOBAL_VAR === $this->variable->name) { return true; } $lastOperation = $this->getLastOperation(); // @todo any of the following operations on array sub-keys is also to be banished, so we need to consider // any operation on any array key as on "mixed" and re-run this check. if (null === $lastOperation) { return false; } $isScalar = $this->isScalarType($variableTypes); if ($lastOperation instanceof Node\Expr\Cast\String_ && !$isScalar) { return true; } if ($lastOperation instanceof Node\Expr\Cast) { return false; } if ($lastOperation instanceof Node\Expr\Cast\String_ && $isScalar) { return false; } if ($lastOperation instanceof Node\Expr\ArrayDimFetch && $isScalar) { // checking parent operation, since we can't have any assumptions on any of the types // of the array keys, we have to check the operations applied to that array key for side-effects $parentCheck = new self(new Variable(uniqid('surrogateVariableNameForTheArrayDimFetch', true)), array_slice($this->parentOperations, 0, count($this->parentOperations) - 1)); return $parentCheck->canCauseSideEffects($variableTypes); } if (($lastOperation instanceof Node\Expr\BinaryOp\Concat || $lastOperation instanceof Node\Expr\AssignOp\Concat) && !$isScalar) { return true; } if ($lastOperation instanceof Node\Expr\BinaryOp || $lastOperation instanceof Node\Expr\AssignOp) { return false; } return !($lastOperation instanceof Return_ || $lastOperation instanceof Node\Expr\Assign); }