/** * @param \PhpParser\Node\Expr\BinaryOp\Concat $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $compiler = new Expression($context); $leftExpression = $compiler->compile($expr->left); $rightExpression = $compiler->compile($expr->right); switch ($leftExpression->getType()) { case CompiledExpression::ARR: $context->notice('unsupported-operand-types', 'Unsupported operand types -{array}', $expr); break; } switch ($rightExpression->getType()) { case CompiledExpression::ARR: $context->notice('unsupported-operand-types', 'Unsupported operand types -{array}', $expr); break; } switch ($leftExpression->getType()) { case CompiledExpression::STRING: case CompiledExpression::NUMBER: case CompiledExpression::INTEGER: case CompiledExpression::DOUBLE: switch ($rightExpression->getType()) { case CompiledExpression::STRING: case CompiledExpression::NUMBER: case CompiledExpression::INTEGER: case CompiledExpression::DOUBLE: return new CompiledExpression(CompiledExpression::STRING, $leftExpression->getValue() . $rightExpression->getValue()); break; } break; } return new CompiledExpression(CompiledExpression::NULL); }
/** * @param Expr $expr * @param Context $context * @return bool */ public function pass(Expr $expr, Context $context) { $castType = CompiledExpression::UNKNOWN; switch (get_class($expr)) { case Expr\Cast\Array_::class: $castType = CompiledExpression::ARR; break; case Expr\Cast\Bool_::class: $castType = CompiledExpression::BOOLEAN; break; case Expr\Cast\Int_::class: $castType = CompiledExpression::INTEGER; break; case Expr\Cast\Double::class: $castType = CompiledExpression::DOUBLE; break; case Expr\Cast\Object_::class: $castType = CompiledExpression::OBJECT; break; case Expr\Cast\String_::class: $castType = CompiledExpression::STRING; break; } $compiledExpression = $context->getExpressionCompiler()->compile($expr->expr); $exprType = $compiledExpression->getType(); $typeName = $compiledExpression->getTypeName(); if ($castType === $exprType) { $context->notice('stupid.cast', sprintf("You are trying to cast '%s' to '%s'", $typeName, $typeName), $expr); return true; } elseif (get_class($expr) == Expr\Cast\Unset_::class && $exprType === CompiledExpression::NULL) { $context->notice('stupid.cast', "You are trying to cast 'null' to 'unset' (null)", $expr); return true; } return false; }
/** * {expr}::{expr}(); * * @param \PhpParser\Node\Expr\StaticCall $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->class instanceof \PhpParser\Node\Name) { $scope = $expr->class->parts[0]; $name = $expr->name; if ($scope == 'self') { if ($context->scope instanceof ClassDefinition) { $context->notice('scall-self-not-context', sprintf('No scope. You cannot call from %s out from class scope', $name, $scope), $expr); return new CompiledExpression(); } /** @var ClassDefinition $classDefinition */ $classDefinition = $context->scope; if (!$classDefinition->hasMethod($name, true)) { $context->notice('undefined-scall', sprintf('Static method %s() does not exist in %s scope', $name, $scope), $expr); return new CompiledExpression(); } $method = $classDefinition->getMethod($name); if (!$method->isStatic()) { $context->notice('undefined-scall', sprintf('Method %s() is not static but it was called as static way', $name), $expr); return new CompiledExpression(); } } return new CompiledExpression(); } $context->debug('Unknown static function call'); return new CompiledExpression(); }
/** * classname->property * * @param \PhpParser\Node\Expr\PropertyFetch $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $compiler = $context->getExpressionCompiler(); $propertNameCE = $compiler->compile($expr->name); $scopeExpression = $compiler->compile($expr->var); if ($scopeExpression->isObject()) { $scopeExpressionValue = $scopeExpression->getValue(); if ($scopeExpressionValue instanceof ClassDefinition) { $propertyName = $propertNameCE->isString() ? $propertNameCE->getValue() : false; if ($propertyName) { if ($scopeExpressionValue->hasProperty($propertyName, true)) { $property = $scopeExpressionValue->getProperty($propertyName, true); return $compiler->compile($property); } else { $context->notice('language_error', sprintf('Property %s does not exist in %s scope', $propertyName, $scopeExpressionValue->getName()), $expr); } } } return new CompiledExpression(); } elseif ($scopeExpression->canBeObject()) { return new CompiledExpression(); } $context->notice('language_error', "It's not possible to fetch a property on a non-object", $expr, Check::CHECK_BETA); return new CompiledExpression(); }
/** * @param Context $context * @return boolean|null */ public function compile(Context $context) { $this->compiled = true; $context->scopePointer = $this->getPointer(); if ($this->statement->getDocComment() === null) { $context->notice('missing-docblock', sprintf('Missing docblock for %s() method', $this->name), $this->statement); } /** * It's not needed to compile empty method via it's abstract */ if ($this->isAbstract()) { /** @var ClassDefinition $scope */ $scope = $context->scope; if (!$scope->isAbstract()) { $context->notice('not-abstract-class-with-abstract-method', 'Class must be an abstract', $this->statement); } return true; } if (count($this->statement->stmts) == 0) { return $context->notice('not-implemented-method', sprintf('Method %s() is not implemented', $this->name), $this->statement); } if (count($this->statement->params) > 0) { /** @var Node\Param $parameter */ foreach ($this->statement->params as $parameter) { $context->addSymbol($parameter->name); } } foreach ($this->statement->stmts as $st) { \PHPSA\nodeVisitorFactory($st, $context); } }
/** * {expr}++ * * @param \PhpParser\Node\Expr\PostDec $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->var instanceof \PHPParser\Node\Expr\Variable) { $variableName = $expr->var->name; if ($variableName instanceof Name) { $variableName = $variableName->parts[0]; } $variable = $context->getSymbol($variableName); if ($variable) { $variable->incUse(); switch ($variable->getType()) { case CompiledExpression::LNUMBER: case CompiledExpression::DNUMBER: $variable->dec(); return CompiledExpression::fromZvalValue($variable->getValue()); } $context->notice('postdec.variable.wrong-type', 'You are trying to use post derement operator on variable $' . $variableName . ' with type: ' . $variable->getTypeName(), $expr); } else { $context->notice('postdec.undefined-variable', 'You are trying to use post derement operator on undefined variable: ' . $variableName, $expr); } return new CompiledExpression(CompiledExpression::UNKNOWN); } $expression = new Expression($context); $compiledExpression = $expression->compile($expr->var); switch ($compiledExpression->getType()) { case CompiledExpression::LNUMBER: case CompiledExpression::DNUMBER: $value = $compiledExpression->getValue(); return CompiledExpression::fromZvalValue($value++); } return new CompiledExpression(CompiledExpression::UNKNOWN); }
/** * {expr} / {expr} * * @param \PhpParser\Node\Expr\BinaryOp\Div $expr * @param Context $context * @return CompiledExpression */ public function compile($expr, Context $context) { $expression = new Expression($context); $left = $expression->compile($expr->left); $expression = new Expression($context); $right = $expression->compile($expr->right); switch ($left->getType()) { case CompiledExpression::DNUMBER: if ($left->isEquals(0)) { $context->notice('division-zero', sprintf('You trying to use division from %s/{expr}', $left->getValue()), $expr); return new CompiledExpression(CompiledExpression::DNUMBER, 0.0); } break; case CompiledExpression::LNUMBER: case CompiledExpression::BOOLEAN: if ($left->isEquals(0)) { $context->notice('division-zero', sprintf('You trying to use division from %s/{expr}', $left->getValue()), $expr); switch ($right->getType()) { case CompiledExpression::LNUMBER: case CompiledExpression::BOOLEAN: return new CompiledExpression(CompiledExpression::LNUMBER, 0); case CompiledExpression::DNUMBER: return new CompiledExpression(CompiledExpression::DNUMBER, 0.0); } } break; } switch ($right->getType()) { case CompiledExpression::LNUMBER: case CompiledExpression::DNUMBER: case CompiledExpression::BOOLEAN: if ($right->isEquals(0)) { $context->notice('division-zero', sprintf('You trying to use division on {expr}/%s', $right->getValue()), $expr); return new CompiledExpression(CompiledExpression::UNKNOWN); } } switch ($left->getType()) { case CompiledExpression::LNUMBER: case CompiledExpression::DNUMBER: case CompiledExpression::BOOLEAN: switch ($right->getType()) { case CompiledExpression::BOOLEAN: /** * Boolean is true via isEquals(0) check is not passed before * {int}/1 = {int} * {double}/1 = {double} */ $context->notice('division-on-true', 'You trying to use stupid division {expr}/true ~ {expr}/1 = {expr}', $expr); //no break //no break case CompiledExpression::LNUMBER: case CompiledExpression::DNUMBER: case CompiledExpression::BOOLEAN: return CompiledExpression::fromZvalValue($left->getValue() / $right->getValue()); } break; } return new CompiledExpression(CompiledExpression::UNKNOWN); }
/** * @param \PhpParser\Node\Expr\MethodCall $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { if ($expr->var instanceof Variable) { $symbol = $context->getSymbol($expr->var->name); if ($symbol) { switch ($symbol->getType()) { case CompiledExpression::OBJECT: case CompiledExpression::DYNAMIC: $symbol->incUse(); /** @var ClassDefinition $calledObject */ $calledObject = $symbol->getValue(); if ($calledObject instanceof ClassDefinition) { $methodName = is_string($expr->name) ? $expr->name : false; if ($expr->name instanceof Variable) { /** * @todo implement fetch from symbol table */ //$methodName = $expr->name->name; } if ($methodName) { if (!$calledObject->hasMethod($methodName, true)) { $context->notice('undefined-mcall', sprintf('Method %s() does not exist in %s scope', $methodName, $expr->var->name), $expr); //it's needed to exit return new CompiledExpression(); } $method = $calledObject->getMethod($methodName); if (!$method) { $context->debug('getMethod is not working'); return new CompiledExpression(); } if ($method->isStatic()) { $context->notice('undefined-mcall', sprintf('Method %s() is a static function but called like class method in $%s variable', $methodName, $expr->var->name), $expr); } return new CompiledExpression(); } return new CompiledExpression(); } /** * It's a wrong type or value, maybe it's implemented and We need to fix it in another compilers */ $context->debug('Unknown $calledObject - is ' . gettype($calledObject)); return new CompiledExpression(); } $context->notice('variable-wrongtype.mcall', sprintf('Variable $%s is not object\\callable and cannot be called like this', $expr->var->name), $expr); return new CompiledExpression(); } else { $context->notice('undefined-variable.mcall', sprintf('Variable $%s is not defined in this scope', $expr->var->name), $expr); return new CompiledExpression(); } } $expression = new Expression($context); $expression->compile($expr->var); $context->debug('Unknown method call'); return new CompiledExpression(); }
/** * @param Stmt $stmt * @param Context $context * @return bool */ public function pass(Stmt $stmt, Context $context) { if ($stmt instanceof Label) { $context->notice('goto_usage', 'Do not use labels', $stmt); return true; } elseif ($stmt instanceof Goto_) { $context->notice('goto_usage', 'Do not use goto statements', $stmt); return true; } return false; }
/** * @param Stmt $stmt * @param Context $context * @return bool */ public function pass(Stmt $stmt, Context $context) { // if it is private, protected or public return false if ($stmt->isPrivate() || $stmt->isProtected() || ($stmt->type & Class_::MODIFIER_PUBLIC) !== 0) { return false; } if ($stmt instanceof Property) { $context->notice('missing_visibility', 'Class property was defined with the deprecated var keyword. Use a visibility modifier instead.', $stmt); } elseif ($stmt instanceof ClassMethod) { $context->notice('missing_visibility', 'Class method was defined without a visibility modifier.', $stmt); } return true; }
/** * Compile function to check it * * @param Context $context * @return bool */ public function compile(Context $context) { if ($this->compiled) { return true; } $context->setFilepath($this->filepath); $this->compiled = true; $context->clearSymbols(); $context->scopePointer = $this->getPointer(); $context->setScope(null); if (count($this->statement->stmts) == 0) { return $context->notice('not-implemented-function', sprintf('Closure %s() is not implemented', $this->name), $this->statement); } if (count($this->statement->params) > 0) { /** @var Node\Param $parameter */ foreach ($this->statement->params as $parameter) { $type = CompiledExpression::UNKNOWN; if ($parameter->type) { if (is_string($parameter->type)) { $type = Types::getType($parameter->type); } elseif ($parameter->type instanceof Node\Name) { $type = CompiledExpression::OBJECT; } } $context->addVariable(new Parameter($parameter->name, null, $type, $parameter->byRef)); } } foreach ($this->statement->stmts as $st) { \PHPSA\nodeVisitorFactory($st, $context); } return true; }
/** * $array[1], $array[$var], $array["string"] * * @param \PhpParser\Node\Expr\ArrayDimFetch $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $compiler = $context->getExpressionCompiler(); $var = $compiler->compile($expr->var); $dim = $compiler->compile($expr->dim); if (!$var->isArray()) { $context->notice('language_error', "It's not possible to fetch an array element on a non array", $expr); return new CompiledExpression(); } if (!in_array($dim->getValue(), $var->getValue())) { $context->notice('language_error', "The array does not contain this value", $expr); return new CompiledExpression(); } $resultArray = $var->getValue(); return CompiledExpression::fromZvalValue($resultArray[$dim->getValue()]); }
/** * @param Stmt\Case_ $case * @param Context $context * @return bool */ private function checkCaseStatement(Stmt\Case_ $case, Context $context) { /* * switch(…) { * case 41: * case 42: * case 43: * return 'the truth, or almost.'; * } */ if (!$case->stmts) { return false; } foreach ($case->stmts as $node) { // look for a break statement if ($node instanceof Stmt\Break_) { return false; } // or for a return if ($node instanceof Stmt\Return_) { return false; } } $context->notice('missing_break_statement', 'Missing "break" statement', $case); return true; }
/** * @param Node\Name $expr * @return CompiledExpression */ public function getNodeName(Node\Name $expr) { $nodeString = $expr->toString(); if ($nodeString === 'null') { return new CompiledExpression(CompiledExpression::NULL); } if (in_array($nodeString, ['parent'], true)) { /** @var ClassDefinition $scope */ $scope = $this->context->scope; assert($scope instanceof ClassDefinition); if ($scope->getExtendsClass()) { $definition = $scope->getExtendsClassDefinition(); if ($definition) { return new CompiledExpression(CompiledExpression::OBJECT, $definition); } } else { $this->context->notice('language_error', 'Cannot access parent:: when current class scope has no parent', $expr); } } if (in_array($nodeString, ['self', 'static'], true)) { return CompiledExpression::fromZvalValue($this->context->scope); } if (defined($nodeString)) { return CompiledExpression::fromZvalValue(constant($expr)); } return new CompiledExpression(CompiledExpression::STRING, $expr->toString()); }
/** * @param Context $context * @return boolean|null */ public function compile(Context $context) { $context->getEventManager()->fire(Event\StatementBeforeCompile::EVENT_NAME, new Event\StatementBeforeCompile($this->statement, $context)); $this->compiled = true; $context->scopePointer = $this->getPointer(); /** * It's not needed to compile empty method via it's abstract */ if ($this->isAbstract()) { /** @var ClassDefinition $scope */ $scope = $context->scope; if (!$scope->isAbstract()) { $context->notice('not-abstract-class-with-abstract-method', 'Class must be abstract', $this->statement); } return true; } if ($this->statement->params) { foreach ($this->statement->params as $parameter) { $type = CompiledExpression::UNKNOWN; if ($parameter->type) { if (is_string($parameter->type)) { $type = Types::getType($parameter->type); } elseif ($parameter->type instanceof Node\Name) { $type = CompiledExpression::OBJECT; } } $context->addVariable(new Parameter($parameter->name, null, $type, $parameter->byRef)); } } foreach ($this->statement->stmts as $st) { \PHPSA\nodeVisitorFactory($st, $context); } }
/** * @param Expr $expr * @param Context $context * @return bool */ public function pass(Expr $expr, Context $context) { if (get_class($expr->expr) != get_class($expr)) { $context->notice('stupid_unary_operators', 'Better to use type casting then unary plus.', $expr); return true; } return false; }
/** * @param Expr\Array_ $expr * @param Context $context * @return bool */ public function pass(Expr\Array_ $expr, Context $context) { if ($expr->getAttribute('kind') == Expr\Array_::KIND_LONG) { $context->notice('array.short-syntax', 'Please use [] (short syntax) for array definition.', $expr); return true; } return false; }
/** * @param For_ $stmt * @param Context $context * @return bool */ public function pass(For_ $stmt, Context $context) { if (count($stmt->cond) > 1) { $context->notice('for_condition', 'You should merge the conditions into one with &&', $stmt); return false; } return true; }
/** * @param Expr\Ternary $expr * @param Context $context * @return bool */ public function pass(Expr\Ternary $expr, Context $context) { if ($expr->if instanceof Expr\Ternary || $expr->else instanceof Expr\Ternary) { $context->notice('nested_ternary', 'Nested ternaries are confusing you should use if instead.', $expr); return true; } return false; }
/** * @param Scalar\LNumber $lNum * @param Context $context * @return bool */ public function pass(Scalar\LNumber $lNum, Context $context) { if ($lNum->getAttribute('kind') != Scalar\LNumber::KIND_DEC) { $context->notice('l_number_kind', 'Avoid using octal, hexadecimal or binary', $lNum); return true; } return false; }
/** * @param Expr $expr * @param Context $context * @return bool */ public function pass(Expr $expr, Context $context) { if (get_class($expr->expr) == get_class($expr)) { $context->notice('multiple_unary_operators', "You are using multiple unary operators. This has no effect", $expr); return true; } return false; }
/** * @param Stmt $stmt * @param Context $context * @return bool */ public function pass(Stmt $stmt, Context $context) { if ($stmt->getDocComment() === null) { $context->notice('missing_docblock', 'Missing Docblock', $stmt); return true; } return false; }
/** * @param Property $prop * @param Context $context * @return bool */ public function pass(Property $prop, Context $context) { if (count($prop->props) > 1) { $context->notice('limit.properties', 'Number of properties larger than one.', $prop); return true; } return false; }
/** * @param ClassMethod $methodStmt * @param Context $context * @return bool */ public function pass(ClassMethod $methodStmt, Context $context) { if ($methodStmt->name == '__get') { if (count($methodStmt->params) == 0) { $context->notice('magic_method_parameters', 'Magic method __get must take 1 parameter at least', $methodStmt, Check::CHECK_SAFE); } } if ($methodStmt->name == '__set') { if (count($methodStmt->params) < 2) { $context->notice('magic_method_parameters', 'Magic method __set must take 2 parameters at least', $methodStmt, Check::CHECK_SAFE); } } if ($methodStmt->name == '__clone') { if (count($methodStmt->params) > 0) { $context->notice('magic_method_parameters', 'Magic method __clone cannot accept arguments', $methodStmt, Check::CHECK_SAFE); } } }
/** * @param Context $context * @return bool */ public function compile(Context $context) { if ($this->st->getDocComment() === null) { return $context->notice('missing-docblock', sprintf('Missing docblock for %s() method', $this->name), $this->st); } if (count($this->ast) == 0) { return $context->notice('not-implemented-method', sprintf('Method %s() is not implemented', $this->name), $this->st); } if (count($this->st->params) > 0) { /** @var Node\Param $parameter */ foreach ($this->st->params as $parameter) { $context->addSymbol($parameter->name); } } foreach ($this->ast as $st) { $result = \PHPSA\nodeVisitorFactory($st, $context); } }
/** * @param \PhpParser\Node\Stmt\Break_ $stmt * @param Context $context * @return CompiledExpression */ public function compile($stmt, Context $context) { $compiler = $context->getExpressionCompiler(); if ($stmt->num !== null) { $compiled = $compiler->compile($stmt->num); if (!$stmt->num instanceof LNumber || $compiled->getValue() == 0) { $context->notice('language_error', 'Break only supports positive integers.', $stmt); } } }
public function visitPhpFunctionCall(FuncCall $funcCall, Context $context) { $name = false; if ($funcCall->name instanceof Name && !$funcCall->name->isFullyQualified()) { $name = $funcCall->name->getFirst(); } if ($name && isset($this->map[$name])) { $context->notice('rand.api.migration', sprintf('Function %s() is not recommended, please use mt_%s analog instead of it.', $name, $name), $funcCall); } }
/** * @param BooleanNot $expr * @param Context $context * @return bool */ public function pass(BooleanNot $expr, Context $context) { if (!array_key_exists($expr->expr->getType(), $this->map)) { return false; } list($use, $instead) = $this->map[$expr->expr->getType()]; $msg = sprintf('Use "a %s b" expression instead of "!(a %s b)".', $use, $instead); $context->notice('logic_inversion', $msg, $expr); return true; }
/** * $a * * @param \PhpParser\Node\Expr\Variable $expr * @param Context $context * @return CompiledExpression */ protected function compile($expr, Context $context) { $variable = $context->getSymbol($expr->name); if ($variable) { $variable->incGets(); return new CompiledExpression($variable->getType(), $variable->getValue(), $variable); } $context->notice('undefined-variable', sprintf('You are trying to use an undefined variable $%s', $expr->name), $expr); return new CompiledExpression(); }
/** * @param Node\Expr\Variable $expr * @return CompiledExpression */ protected function passExprVariable(Node\Expr\Variable $expr) { $variable = $this->context->getSymbol($expr->name); if ($variable) { $variable->incGets(); return new CompiledExpression($variable->getType(), $variable->getName()); } $this->context->notice('undefined-variable', sprintf('You trying to use undefined variable $%s', $expr->name), $expr); return new CompiledExpression(); }