public function __construct(\PHPParser_Node $rootNode, Scope $parent = null, PhpType $thisType = null) { $this->rootNode = $rootNode; $this->parent = $parent; $this->thisType = $thisType; $this->depth = null === $parent ? 0 : $parent->getDepth() + 1; }
public function testDeclareVar() { $scope = new Scope($this->getMock('PHPParser_Node')); $this->assertNull($scope->getVar('x')); $scope->declareVar('x', $this->registry->getNativeType('integer')); $this->assertNotNull($var = $scope->getVar('x')); $this->assertSame($this->registry->getNativeType('integer'), $var->getType()); $this->assertTrue($var->isTypeInferred()); }
public function testGetSlotFromFunctionScope() { $functionScope = new Scope($this->getMock('PHPParser_Node')); $functionScope->declareVar('x', $this->registry->getNativeType('integer')); $flowScope = LinkedFlowScope::createLatticeElement($functionScope); $this->assertNotNull($var = $flowScope->getSlot('x')); $this->assertSame($this->registry->getNativeType('integer'), $var->getType()); $flowScope = $flowScope->createChildFlowScope(); $this->assertNotNull($var = $flowScope->getSlot('x')); $this->assertSame($this->registry->getNativeType('integer'), $var->getType()); }
public function getSlot($name) { if (isset($this->symbols[$name])) { return $this->symbols[$name]; } return $this->functionScope->getVar($name); }
private function createVar(LinkedFlowScope $scope, $name, $type) { $type = $this->registry->resolveType($type); $this->functionScope->declareVar($name)->setNameNode($n = new \PHPParser_Node_Expr_Variable($name)); $scope->inferSlotType($name, $type); $n->setAttribute('type', $type); return $n; }
private function removeFromUseIfLocal($name, UseLattice $uses) { $var = $this->scope->getVar($name); if (null === $var) { return; } unset($uses[$var]); }
/** * Declares a variable. * * @param string $name * @param array $types * @param boolean $typesInferred */ private function declareVar($name, PhpType $type = null, $typeInferred = true, \PHPParser_Node $nameNode = null) { if ($this->scope->isDeclared($name)) { return $this->scope->getVar($name); } $var = $this->scope->declareVar($name, $type, $typeInferred); if ($nameNode) { $var->setNameNode($nameNode); } return $var; }
private function getThisType(\PHPParser_Node $node, Scope $parentScope = null) { $parent = $node; while (null !== $parent) { // As of PHP 5.4, closures inherit the this type of their parent scope. Since there is currently no way to // configure the PHP version that we build against, we will simply always infer the ThisType. Other passes, // can then perform checks whether ``$this`` may be accessed, allowing us to produce better error messages. if ($parent instanceof \PHPParser_Node_Expr_Closure) { if (null === $parentScope) { return null; } return $parentScope->getTypeOfThis(); } if (NodeUtil::isMethodContainer($parent)) { $name = implode("\\", $parent->namespacedName->parts); return $this->typeRegistry->getClassOrCreate($name); } $parent = $parent->getAttribute('parent'); } return null; }
private function getPropertyType(PhpType $objType = null, $propName, \PHPParser_Node $n, LinkedFlowScope $scope) { // Check whether the scope contains inferred type information about the property, // or fallback to the wider property type if not available. Scopes could // contain information about properties if we have reverse interpreted // the property previously. $qualifiedName = self::getQualifiedName($n); $var = $scope->getSlot($qualifiedName); if (null !== $var && null !== ($varType = $var->getType())) { if ($varType->equals($this->typeRegistry->getNativeType('unknown')) && $var !== $this->syntacticScope->getSlot($qualifiedName)) { // If the type of this qualified name has been checked in this scope, // then use CHECKED_UNKNOWN_TYPE instead to indicate that. // TODO: The checked unknown type has not really proved useful in // practice. Consider removing it entirely. return $this->typeRegistry->getNativeType('unknown_checked'); } return $varType; } $propertyType = null; if (null !== $objType && null !== ($objType = $objType->toMaybeObjectType()) && $objType instanceof Clazz && $objType->hasProperty($propName)) { $propertyType = $objType->getProperty($propName)->getPhpType(); } return $propertyType ?: $this->typeRegistry->getNativeType('unknown'); }
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; } }
/** * Completes the given scope with type information inferred from the local flow where necessary. */ public function completeScope(Scope $scope) { foreach ($scope->getVars() as $name => $var) { if ($var->isTypeInferred()) { $type = $var->getType(); if (null === $type) { $flowType = $this->getSlot($name)->getType(); $var->setType($flowType); } } } }