/** * @param mixed $expr * @return mixed * @throws \Exception */ protected function evl($expr) { if (is_string($expr)) { return $expr; } if (is_array($expr)) { return $expr; } $className = get_class($expr); switch ($className) { case "PhpParser\\Node\\Scalar\\DNumber": case "PhpParser\\Node\\Scalar\\LNumber": case "PhpParser\\Node\\Scalar\\String_": return $this->debug($expr, $expr->value); // Arithmetic Operators // Arithmetic Operators case "PhpParser\\Node\\Expr\\UnaryMinus": return $this->debug($expr, -$this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\BinaryOp\\Plus": return $this->debug($expr, $this->evl($expr->left) + $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Minus": return $this->debug($expr, $this->evl($expr->left) - $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Mul": return $this->debug($expr, $this->evl($expr->left) * $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Div": return $this->debug($expr, $this->evl($expr->left) / $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Mod": return $this->debug($expr, $this->evl($expr->left) % $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Pow": // Operator ** Introduced in PHP 5.6 return $this->debug($expr, pow($this->evl($expr->left), $this->evl($expr->right))); // Bitwise Operators // Bitwise Operators case "PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd": return $this->debug($expr, $this->evl($expr->left) & $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr": return $this->debug($expr, $this->evl($expr->left) | $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor": return $this->debug($expr, $this->evl($expr->left) ^ $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BitwiseNot": return $this->debug($expr, ~$this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft": return $this->debug($expr, $this->evl($expr->left) << $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight": return $this->debug($expr, $this->evl($expr->left) >> $this->evl($expr->right)); // Comparison Operators // Comparison Operators case "PhpParser\\Node\\Expr\\BinaryOp\\Equal": return $this->debug($expr, $this->evl($expr->left) == $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Identical": return $this->debug($expr, $this->evl($expr->left) === $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\NotEqual": return $this->debug($expr, $this->evl($expr->left) != $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical": return $this->debug($expr, $this->evl($expr->left) !== $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Smaller": return $this->debug($expr, $this->evl($expr->left) < $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Greater": return $this->debug($expr, $this->evl($expr->left) > $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual": return $this->debug($expr, $this->evl($expr->left) <= $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual": return $this->debug($expr, $this->evl($expr->left) >= $this->evl($expr->right)); // Logical Operators // Logical Operators case "PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd": return $this->debug($expr, $this->evl($expr->left) and $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr": return $this->debug($expr, $this->evl($expr->left) or $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor": return $this->debug($expr, $this->evl($expr->left) xor $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BooleanNot": return $this->debug($expr, !$this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd": return $this->debug($expr, $this->evl($expr->left) && $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr": return $this->debug($expr, $this->evl($expr->left) || $this->evl($expr->right)); // Casting // Casting case "PhpParser\\Node\\Expr\\Cast\\String_": return $this->debug($expr, (string) $this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\Cast\\Int_": return $this->debug($expr, (int) $this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\Cast\\Bool_": return $this->debug($expr, (bool) $this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\Cast\\Double": return $this->debug($expr, (double) $this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\Cast\\Object_": return $this->debug($expr, (object) $this->evl($expr->expr)); case "PhpParser\\Node\\Expr\\Cast\\Array_": return $this->debug($expr, (array) $this->evl($expr->expr)); // String Operators // String Operators case "PhpParser\\Node\\Expr\\BinaryOp\\Concat": return $this->debug($expr, $this->evl($expr->left) . $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\BinaryOp\\Coalesce": // Operator ?? Introduced in PHP 7 try { $left = $this->evl($expr->left); } catch (\OutOfBoundsException $e) { $left = null; } return $this->debug($expr, null !== $left ? $left : $this->evl($expr->right)); case "PhpParser\\Node\\Expr\\Ternary": return $this->debug($expr, $this->evl($expr->cond) ? $this->evl($expr->if) : $this->evl($expr->else)); case "PhpParser\\Node\\Expr\\Isset_": try { $result = $this->evl($expr->vars[0]); } catch (\OutOfBoundsException $e) { $result = null; } return $this->debug($expr, $result !== null); case "PhpParser\\Node\\Expr\\MethodCall": return $this->evaluateMethodCall($expr); case "PhpParser\\Node\\Expr\\ArrayDimFetch": $propertyName = $this->evl($expr->dim); $variable = $this->evl($expr->var); if ($variable instanceof \PhpParser\Node\Expr\ArrayItem) { $variable = $this->evl($variable->value); } if ($variable instanceof \PhpParser\Node\Expr\Array_) { $variable = $this->evl($variable); } // var_export($variable); if (!is_array($variable)) { $variableName = isset($expr->var->name) ? $expr->var->name : ''; return $this->error("Unsupported ArrayDimFetch expression" . " - Variable \${$variableName} is not an array", $expr); } elseif (is_array($variable) && isset($variable[$propertyName])) { return $this->debug($expr, $variable[$propertyName]); } elseif (is_array($variable) && !isset($variable[$propertyName])) { $this->debug($expr, null); throw new \OutOfBoundsException("Undefined index: {$propertyName}", $this::UNDEFINED_INDEX); } return $this->error("Unsupported ArrayDimFetch expression", $expr); case "PhpParser\\Node\\Expr\\StaticPropertyFetch": $className = $this->evl($expr->class); //if ($className !== 'R') { if (true) { // StaticPropertyFetch is forbidden return $this->error("Unsupported StaticPropertyFetch expression", $expr); } // echo "var: R . {$propertyName}; \n\n" ; $propertyName = $this->evl($expr->name); $result = $this->registry->getGlobal($propertyName); // var_export($this->registry->getKeys()); // echo "= " . var_export(is_object($result) ? get_class($result) : $result, true) . "; \n\n" ; return $this->debug($expr, $result); case "PhpParser\\Node\\Expr\\PropertyFetch": return $this->evaluatePropertyFetch($expr); case "PhpParser\\Node\\Expr\\Variable": return $this->debug($expr, $this->registry->get($expr->name)); case "PhpParser\\Node\\Expr\\Array_": $items = []; foreach ($expr->items as $item) { $value = $this->evl($item->value); if (isset($item->key)) { $items[$this->evl($item->key)] = $value; } else { $items[] = $value; } } return $this->debug($expr, $items); case "PhpParser\\Node\\Expr\\ConstFetch": return $this->debug($expr, constant($expr->name->parts[0])); case "PhpParser\\Node\\Expr\\FuncCall": return $this->evaluateFuncCall($expr); case "PhpParser\\Node\\Expr\\Closure": return $this->debug($expr, $this->closure($expr)); case "PhpParser\\Node\\Stmt\\Return_": return $this->debug($expr, $this->evl($expr->expr)); case "PhpParser\\Node\\Stmt\\Global_": foreach ($expr->vars as $var) { $variableName = $var->name; $value = $this->registry->getGlobal($variableName); $this->registry->register($variableName, $value, true); } return $this->debug($expr, null); case "PhpParser\\Node\\Expr\\Assign": if (!isset($expr->var->name) || !isset($expr->expr) || !$expr->var instanceof \PhpParser\Node\Expr\Variable) { return $this->error("Unsupported Assign expression", $expr); } $variableName = $expr->var->name; $value = $this->evl($expr->expr); $this->registry->register($variableName, $value, true); return $this->debug($expr, $value); case "PhpParser\\Node\\Stmt\\If_": $cond = $this->evl($expr->cond); if ($cond) { return $this->debug($expr, $this->evaluateStmts($expr->stmts), $wrap = false); } if (isset($expr->elseifs)) { foreach ($expr->elseifs as $elseif) { $cond = $this->evl($elseif->cond); if ($cond) { return $this->debug($expr, $this->evaluateStmts($elseif->stmts), $wrap = false); } } } if (isset($expr->else)) { return $this->debug($expr, $this->evaluateStmts($expr->else->stmts), $wrap = false); } return $this->debug($expr, null); case "PhpParser\\Node\\Stmt\\Foreach_": $exp = $this->evl($expr->expr); $valueVar = $this->evl($expr->valueVar->name); $keyVar = $expr->keyVar ? $this->evl($expr->keyVar->name) : null; if (!is_array($exp)) { return $this->error("Unsupported Foreach_ expression - Undefined variable", $expr); } foreach ($exp as $key => $value) { $this->registry->register($valueVar, $this->wrap($value), true); if ($keyVar) { $this->registry->register($keyVar, $this->wrap($key), true); } $result = $this->evaluateStmts($expr->stmts); if ($result instanceof \PhpParser\Node\Stmt\Return_) { return $this->debug($expr, $result); } } return $this->debug($expr, null); /* * case "PhpParser\\Node\\Stmt\\Echo_": * foreach ($expr->exprs as $expr) { * echo $this->evl($expr); * return null; * } * return $this->debug($expr, $expr->parts[0]); */ /* * case "PhpParser\\Node\\Stmt\\Echo_": * foreach ($expr->exprs as $expr) { * echo $this->evl($expr); * return null; * } * return $this->debug($expr, $expr->parts[0]); */ case "PhpParser\\Node\\Name": if (!isset($expr->parts) || count($expr->parts) != 1) { return $this->error("Unsupported Name expression", $expr); } return $this->debug($expr, $expr->parts[0]); default: return $this->error("Unsupported expression {$className}", $expr); } }