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);
         }
     }
 }
예제 #3
0
 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;
 }