public function leaveNode(\PHPParser_Node $node) { if (isset($node->stmts)) { $node->stmts = new BlockNode($node->stmts, $node->getLine()); } if ($node instanceof \PHPParser_Node_Stmt_Namespace) { $node->setAttribute('imports', $this->imports); } }
public function setInput($code, \PHPParser_Node $ast = null) { $this->astStream->setAst($ast ?: PhpParser\ParseUtils::parse($code)); $this->tokenStream->setCode($code); $lastNode = null; while ($this->moveNext()) { if ($lastNode !== $this->node) { $this->node->setAttribute('start_token', $this->token); if (null !== $lastNode) { $lastNode->setAttribute('end_token', $this->token->getPreviousToken()->get()); } } $lastNode = $this->node; } $this->reset(); }
private function attachLiteralTypes(\PHPParser_Node $node, \PHPParser_Node $parent = null) { switch (true) { case $node instanceof \PHPParser_Node_Name_FullyQualified: $node->setAttribute('type', $this->typeRegistry->getClassOrCreate(implode("\\", $node->parts))); break; case $node instanceof \PHPParser_Node_Name: if ($parent instanceof \PHPParser_Node_Expr_New || $parent instanceof \PHPParser_Node_Expr_StaticPropertyFetch || $parent instanceof \PHPParser_Node_Expr_StaticCall || $parent instanceof \PHPParser_Node_Expr_Instanceof || $parent instanceof \PHPParser_Node_Stmt_Catch) { $name = implode("\\", $node->parts); $lowerName = strtolower($name); if ('static' === $name) { $node->setAttribute('type', $this->typeRegistry->getThisType($this->scope->getTypeOfThis())); } else { if ('self' === $name) { $node->setAttribute('type', $this->scope->getTypeOfThis()); } else { if ('parent' === $name) { $thisType = $this->scope->getTypeOfThis()->toMaybeObjectType(); if (null === $thisType || !$thisType->isClass() || null === $thisType->getSuperClassType()) { $node->setAttribute('type', $this->typeRegistry->getNativeType('unknown')); } else { $node->setAttribute('type', $thisType->getSuperClassType()); } } else { $node->setAttribute('type', $this->typeRegistry->getClassOrCreate($name)); } } } } break; case $node instanceof \PHPParser_Node_Expr_Array: case $node instanceof \PHPParser_Node_Expr_Cast_Array: // We only do attach the generic array type on the first build. // For subsequent builds, other passes likely have already made // the array type more specific. if (null === $node->getAttribute('type')) { $node->setAttribute('type', $this->typeRegistry->getNativeType('array')); } break; case $node instanceof \PHPParser_Node_Expr_UnaryMinus: case $node instanceof \PHPParser_Node_Expr_UnaryPlus: case $node instanceof \PHPParser_Node_Scalar_LNumber: case $node instanceof \PHPParser_Node_Scalar_LineConst: $node->setAttribute('type', $this->typeRegistry->getNativeType('integer')); break; case $node instanceof \PHPParser_Node_Scalar_DNumber: $node->setAttribute('type', $this->typeRegistry->getNativeType('double')); break; case $node instanceof \PHPParser_Node_Scalar_String: case $node instanceof \PHPParser_Node_Scalar_FileConst: case $node instanceof \PHPParser_Node_Scalar_DirConst: $node->setAttribute('type', $this->typeRegistry->getNativeType('string')); break; case $node instanceof \PHPParser_Node_Expr_ClassConstFetch: if ($node->class instanceof \PHPParser_Node_Name && in_array($node->class->parts[0], array('self', 'static')) && null !== ($thisType = $this->scope->getTypeOfThis()) && null !== ($objType = $thisType->toMaybeObjectType()) && ($objType->isClass() || $objType->isInterface()) && $objType->hasConstant($node->name)) { $node->setAttribute('type', $objType->getConstant($node->name)->getPhpType()); } break; case $node instanceof \PHPParser_Node_Expr_ConstFetch: $nameParts = $node->name->parts; if (1 === count($nameParts)) { $name = strtolower($nameParts[0]); if ('true' === $name) { $node->setAttribute('type', $this->typeRegistry->getNativeType('boolean')); } else { if ('false' === $name) { $node->setAttribute('type', $this->typeRegistry->getNativeType('false')); } else { if ('null' === $name) { $node->setAttribute('type', $this->typeRegistry->getNativeType('null')); } } } } break; } }
/** * This method will stop traversal depending on the parent node. * * Principally, we are only interested in adding edges between nodes that change control flow. Notable ones are * loops (WHILE, FOR, etc.) and IF-ELSE statements; other statements typically transfer control to their next sibling. * * With regard to expression trees, we currently do not perform any sort of control flow within them, even if there * are short circuiting operators or conditionals. Instead, we synthesize lattices up when performing data flow * analysis by finding the meet at each expression node. * * @param NodeTraversal $t * @param \PHPParser_Node $node * @param \PHPParser_Node $parent * * @return boolean */ public function shouldTraverse(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null) { $node->setAttribute('position', $this->astPositionCounter++); if ($node instanceof \PHPParser_Node_Stmt_TryCatch) { $this->exceptionHandlers->push($node); return true; } if (null !== $parent) { // Do not traverse into classes, interfaces, or traits as control flow never gets passed in. if (NodeUtil::isMethodContainer($parent)) { return false; } // Generally, the above also applies to closures except when they are the scope's root. if (NodeUtil::isScopeCreator($parent) && $parent !== $this->root) { return false; } // Skip conditions. if ($parent instanceof \PHPParser_Node_Stmt_For || $parent instanceof \PHPParser_Node_Stmt_Foreach) { return $parent->stmts === $node; } // Skip conditions. if ($parent instanceof \PHPParser_Node_Stmt_If || $parent instanceof \PHPParser_Node_Stmt_ElseIf || $parent instanceof \PHPParser_Node_Stmt_While || $parent instanceof \PHPParser_Node_Stmt_Do || $parent instanceof \PHPParser_Node_Stmt_Switch || $parent instanceof \PHPParser_Node_Stmt_Case) { return $parent->cond !== $node; } // Skip exception type. if ($parent instanceof \PHPParser_Node_Stmt_Catch) { return $parent->stmts === $node; } // Skip expressions, see above. if ($parent instanceof \PHPParser_Node_Expr && !$parent instanceof \PHPParser_Node_Expr_Closure) { return false; } if ($parent instanceof \PHPParser_Node_Stmt_Continue || $parent instanceof \PHPParser_Node_Stmt_Break || $parent instanceof \PHPParser_Node_Stmt_Return || $parent instanceof \PHPParser_Node_Stmt_Echo || $parent instanceof \PHPParser_Node_Stmt_Use || $parent instanceof \PHPParser_Node_Stmt_Unset || $parent instanceof \PHPParser_Node_Stmt_Declare || $parent instanceof \PHPParser_Node_Stmt_Global || $parent instanceof \PHPParser_Node_Stmt_Static || $parent instanceof \PHPParser_Node_Stmt_StaticVar || $parent instanceof \PHPParser_Node_Stmt_Throw) { return false; } // Skip parameters. if (NodeUtil::isScopeCreator($parent) && $node !== $parent->stmts) { return false; } // If we are reaching the CATCH, or FINALLY node, they current exception handler cannot catch anymore // exceptions, and we therefore can remove it. // TODO: Add Support for FINALLY (PHP 5.5) if ($parent instanceof \PHPParser_Node_Stmt_TryCatch && $parent->catches[0] === $node && $parent === $this->exceptionHandlers->top()) { $this->exceptionHandlers->pop(); } } return true; }
private function traverseShortCircuitingBinOp(\PHPParser_Node $node, LinkedFlowScope $scope, $condition) { $left = $node->left; $right = $node->right; // Type the left node. $leftLiterals = $this->traverseWithinShortCircuitingBinOp($left, $scope->createChildFlowScope()); $leftType = $left->getAttribute('type'); // As these operations are short circuiting, we can reverse interpreter the left node as we know the // outcome to its evaluation if we are ever going to reach the right node. $rightScope = $this->reverseInterpreter->getPreciserScopeKnowingConditionOutcome($left, $leftLiterals->getOutcomeFlowScope($left, $condition), $condition); // Now, type the right node in the updated scope. $rightLiterals = $this->traverseWithinShortCircuitingBinOp($right, $rightScope->createChildFlowScope()); $rightType = $right->getAttribute('type'); if (null === $leftType || null === $rightType) { return new BooleanOutcomePair($this, array(true, false), array(true, false), $leftLiterals->getJoinedFlowScope(), $rightLiterals->getJoinedFlowScope()); } // In PHP, binary operations are always of boolean type. $node->setAttribute('type', $this->typeRegistry->getNativeType('boolean')); if ($leftLiterals->toBooleanOutcomes === array(!$condition)) { // Use the restricted left type, since the right side never gets evaluated. return $leftLiterals; } // Use the join of the restricted left type knowing the outcome of the // ToBoolean predicate of the right type. return BooleanOutcomePair::fromPairs($this, $leftLiterals, $rightLiterals, $condition); }
private function addDefToVarNodes($name, \PHPParser_Node $defNode, \PHPParser_Node $defExpr, UseLattice $uses) { if (null === ($var = $this->scope->getVar($name))) { return; } $maybeUsingVarNodes = $uses->getUsingVarNodes($var); $defNode->setAttribute('maybe_using_vars', $maybeUsingVarNodes); foreach ($maybeUsingVarNodes as $varNode) { $maybeDefiningExprs = $varNode->getAttribute('maybe_defining_exprs', array()); if (in_array($defExpr, $maybeDefiningExprs, true)) { continue; } $maybeDefiningExprs[] = $defExpr; $varNode->setAttribute('maybe_defining_exprs', $maybeDefiningExprs); } }