private function createNamed($name) { $n = new \PHPParser_Node_Name_FullyQualified(explode("\\", $name)); $type = $this->registry->getClassOrCreate($name); $n->setAttribute('type', $type); return $n; }
private function ensurePropertyDefined($left, $resultType) { if ($resultType->isUnknownType()) { return; } $property = $this->typeRegistry->getFetchedPropertyByNode($left); if (null !== $property) { assert($property instanceof ClassProperty); // Specifically ignore anything that is being set in a tearDown() method. // TODO: We should think about introducing a general concept for this which takes // inter procedural control flow into account. $scopeRoot = $this->syntacticScope->getRootNode(); if (!$scopeRoot instanceof BlockNode && !$scopeRoot instanceof \PHPParser_Node_Expr_Closure) { $testCase = $this->typeRegistry->getClassOrCreate('PHPUnit_Framework_TestCase'); $method = $this->typeRegistry->getFunctionByNode($scopeRoot); if ($property->getDeclaringClassType()->isSubtypeOf($testCase) && null !== ($thisType = $this->syntacticScope->getTypeOfThis()) && $thisType->isSubtypeOf($testCase) && null !== $method && strtolower($method->getName()) === 'teardown') { return; } } if (null !== ($type = $property->getPhpType())) { $newType = $this->typeRegistry->createUnionType(array($type, $resultType)); } else { $newType = $resultType; } $property->setPhpType($newType); if ($astNode = $property->getAstNode()) { $astNode->setAttribute('type', $newType); } } }
public function testIsSubtypeWithPartiallyResolvedTypes() { $interface = $this->registry->getClassOrCreate('I'); $interface->setReferencedType(new InterfaceC('I')); $interface->getReferencedType()->setNormalized(true); $class = new Clazz('C'); $this->assertTrue($class->isSubtypeOf($interface), 'Non-normalized types are always subtypes.'); $class->addImplementedInterface('I'); $this->assertTrue($class->isSubtypeOf($interface)); }
private function parseTypeName() { $type = null; if ('\\' === $this->token[0][0]) { $type = $this->typeRegistry->getClassOrCreate(substr($this->token[0], 1)); } else { // First, we check whether the name is a class that we have already scanned $parts = explode("\\", $this->token[0]); if (isset($this->importedNamespaces[$parts[0]])) { $className = $this->importedNamespaces[$parts[0]]; if (count($parts) > 1) { $className .= '\\' . implode("\\", array_slice($parts, 1)); } $type = $this->typeRegistry->getClassOrCreate($className); } else { switch (strtolower($this->token[0])) { case '(': $type = $this->parseGroup(); break; case 'false': $type = $this->typeRegistry->getNativeType('false'); break; case 'bool': case 'boolean': $type = $this->typeRegistry->getNativeType('boolean'); break; case 'int': case 'integer': $type = $this->typeRegistry->getNativeType('integer'); break; case 'float': case 'double': $type = $this->typeRegistry->getNativeType('double'); break; case 'scalar': $type = $this->typeRegistry->getNativeType('scalar'); break; case 'string': $type = $this->typeRegistry->getNativeType('string'); break; case 'array': if ($this->isNextToken('<')) { $this->match('<'); $this->moveNext(); $firstType = $this->parse(); if ($this->isNextToken(',')) { $this->match(','); $this->moveNext(); $arrayType = $this->typeRegistry->getArrayType($this->parse(), $firstType); $this->match('>'); return $arrayType; } $this->match('>'); return $this->typeRegistry->getArrayType($firstType, $this->typeRegistry->getNativeType('integer')); } $type = $this->typeRegistry->getNativeType('array'); break; case 'resource': $type = $this->typeRegistry->getNativeType('resource'); break; case '*': case 'mixed': $type = $this->typeRegistry->getNativeType('all'); break; case 'number': $type = $this->typeRegistry->getNativeType('number'); break; case 'object': $type = $this->typeRegistry->getNativeType('object'); break; case 'void': case 'null': $type = $this->typeRegistry->getNativeType('null'); break; case 'callable': case 'callback': $type = $this->typeRegistry->getNativeType('callable'); break; default: if ($this->isThisReference($this->token[0])) { if (null === $this->currentClassName) { throw new \RuntimeException(sprintf('"%s" is only available from within classes.', $this->token[0])); } $type = $this->typeRegistry->getClassOrCreate($this->currentClassName); } else { if (preg_match('/^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*(?:\\\\[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)*$/', $this->token[0])) { $className = !empty($this->importedNamespaces['']) ? $this->importedNamespaces[''] . '\\' : ''; $className .= $this->token[0]; $type = $this->typeRegistry->getClassOrCreate($className); } else { throw new \RuntimeException(sprintf('Unknown type name "%s" at position %d.', $this->token[0], $this->token[1])); } } } } } if (!$type) { throw new \RuntimeException(sprintf('Internal error for token "%s" at position %d.', $this->token[0], $this->token[1])); } if ($this->isNextToken('[')) { $this->moveNext(); // In the php stubs, there often is an [optional] appended to types. // We just ignore this suffix. if ($this->isNextToken('optional')) { $this->moveNext(); $this->match(']'); } else { $this->match(']'); return $this->typeRegistry->getArrayType($type, $this->typeRegistry->getNativeType('integer')); } } return $type; }