public function testPersist()
 {
     $this->packageVersion->addContainer($foo = new Clazz('Foo'));
     $foo->addImplementedInterface('Iterator');
     $foo->addMethod($fooMethod = new Method('foo'));
     $this->packageVersion->addFunction($count_foo = new GlobalFunction('count_foo'));
     CallSite::create($fooMethod, $count_foo);
     $this->persister->persist($this->packageVersion);
     $reloadedFoo = $this->em->createQuery('SELECT c FROM Scrutinizer\\PhpAnalyzer\\Model\\Clazz c WHERE c.name = :name')->setParameter('name', 'Foo')->getSingleResult();
     assert($reloadedFoo instanceof Clazz);
     $this->assertEquals(array('Iterator'), $reloadedFoo->getImplementedInterfaces());
     $this->assertTrue($reloadedFoo->hasMethod('foo'));
     $reloadedCountFoo = $this->em->createQuery('SELECT f FROM Scrutinizer\\PhpAnalyzer\\Model\\GlobalFunction f WHERE f.name = :name')->setParameter('name', 'count_foo')->getSingleResult();
     $this->assertCount(1, $inSites = $reloadedCountFoo->getInCallSites());
     $this->assertSame($reloadedFoo->getMethod('foo')->getMethod(), $inSites[0]->getSource());
 }
 public function testIsSubtypeWithNamedTypes()
 {
     $bar = new \Scrutinizer\PhpAnalyzer\Model\InterfaceC('Bar');
     $bar->setNormalized(true);
     $foo = new Clazz('Foo');
     $foo->setNormalized(true);
     $this->assertFalse($foo->isSubtypeOf($bar));
     $foo->addImplementedInterface('Bar');
     $namedBar = NamedType::createResolved($this->registry, $bar);
     $this->assertTrue($foo->isSubTypeOf($namedBar));
     $this->assertTrue($foo->isSubTypeOf($bar));
     $namedFoo = NamedType::createResolved($this->registry, $foo);
     $this->assertTrue($namedFoo->isSubtypeOf($namedBar));
     $this->assertTrue($namedFoo->isSubtypeOf($bar));
     $unresolvedNamedBar = new NamedType($this->registry, 'Bar');
     $this->assertTrue($foo->isSubtypeOf($unresolvedNamedBar));
     $this->assertTrue($namedFoo->isSubtypeOf($unresolvedNamedBar));
     $baz = new InterfaceC('Baz');
     $baz->setNormalized(true);
     $this->assertFalse($baz->isSubtypeOf($bar));
     $this->assertFalse($baz->isSubtypeOf($namedBar));
     $this->assertFalse($baz->isSubtypeOf($unresolvedNamedBar));
     $baz->addExtendedInterface('Bar');
     $this->assertTrue($baz->isSubtypeOf($bar));
     $this->assertTrue($baz->isSubtypeOf($namedBar));
     $this->assertTrue($baz->isSubtypeOf($unresolvedNamedBar));
 }
 public function parse(\PHPParser_Node $node)
 {
     if ($node instanceof \PHPParser_Node_Stmt_Class) {
         $class = new Clazz(implode("\\", $node->namespacedName->parts));
         // convert PHPParser modifier to our modifier
         $modifier = 0;
         if (\PHPParser_Node_Stmt_Class::MODIFIER_FINAL === ($node->type & \PHPParser_Node_Stmt_Class::MODIFIER_FINAL)) {
             $modifier |= Clazz::MODIFIER_FINAL;
         }
         if (\PHPParser_Node_Stmt_Class::MODIFIER_ABSTRACT === ($node->type & \PHPParser_Node_Stmt_Class::MODIFIER_ABSTRACT)) {
             $modifier |= Clazz::MODIFIER_ABSTRACT;
         }
         $class->setModifier($modifier);
         if (null !== $node->extends) {
             $class->setSuperClass(implode("\\", $node->extends->parts));
         }
         foreach ($node->implements as $iface) {
             $class->addImplementedInterface(implode("\\", $iface->parts));
         }
     } else {
         if ($node instanceof \PHPParser_Node_Stmt_Interface) {
             $class = new InterfaceC(implode("\\", $node->namespacedName->parts));
             foreach ($node->extends as $interface) {
                 $class->addExtendedInterface(implode("\\", $interface->parts));
             }
         } else {
             if ($node instanceof \PHPParser_Node_Stmt_Trait) {
                 $class = new TraitC(implode("\\", $node->namespacedName->parts));
             } else {
                 throw new \LogicException(sprintf('The other statements were exhaustive. The node "%s" is not supported.', get_class($node)));
             }
         }
     }
     $class->setImportedNamespaces($this->importedNamespaces);
     $class->setAstNode($node);
     // add methods, properties, and constants
     foreach ($node->stmts as $stmt) {
         if ($stmt instanceof \PHPParser_Node_Stmt_ClassMethod) {
             $this->scanMethod($stmt, $class);
         } else {
             if ($stmt instanceof \PHPParser_Node_Stmt_Property) {
                 $visibility = \PHPParser_Node_Stmt_Class::MODIFIER_PUBLIC === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_PUBLIC) ? Property::VISIBILITY_PUBLIC : (\PHPParser_Node_Stmt_Class::MODIFIER_PROTECTED === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_PROTECTED) ? Property::VISIBILITY_PROTECTED : Property::VISIBILITY_PRIVATE);
                 foreach ($stmt->props as $propNode) {
                     assert($propNode instanceof \PHPParser_Node_Stmt_PropertyProperty);
                     // This is a PHP error which we flag in a later pass. Here, we can just ignore the property definition.
                     if ($class->isInterface()) {
                         continue;
                     }
                     assert($class instanceof Clazz || $class instanceof TraitC);
                     $property = new Property($propNode->name);
                     $property->setAstNode($propNode);
                     $property->setVisibility($visibility);
                     $class->addProperty($property);
                 }
             } else {
                 if ($stmt instanceof \PHPParser_Node_Stmt_ClassConst) {
                     foreach ($stmt->consts as $constNode) {
                         assert($constNode instanceof \PHPParser_Node_Const);
                         $constant = new Constant($constNode->name);
                         $constant->setAstNode($constNode);
                         if (null !== ($type = $constNode->value->getAttribute('type'))) {
                             $constant->setPhpType($type);
                         }
                         $class->addConstant($constant);
                     }
                 }
             }
         }
     }
     // Add magic properties denoted by @property, @property-read, @property-write.
     if ($node instanceof \PHPParser_Node_Stmt_Class && preg_match_all('#@(?:property|property-read|property-write)\\s+[^\\s]+\\s+\\$?([^\\s]+)#', $node->getDocComment(), $matches)) {
         for ($i = 0, $c = count($matches[0]); $i < $c; $i++) {
             // If there is already an explicitly declared property of the same
             // name, it has precedence for us.
             if ($class->hasProperty($matches[1][$i])) {
                 $this->logger->warning(sprintf('The property "%s" is already defined in code; ignoring @property tag.', $matches[1][$i]));
                 continue;
             }
             $property = new Property($matches[1][$i]);
             $property->setAstNode($node);
             $class->addProperty($property);
         }
     }
     // Add magic methods denoted by @method.
     if ($node instanceof \PHPParser_Node_Stmt_Class && preg_match_all('#@method\\s+([^@]+)#s', $node->getDocComment(), $matches)) {
         foreach ($matches[1] as $methodDef) {
             if (null !== ($method = $this->parseCommentMethodDef($methodDef))) {
                 // An explicitly defined method has precedence.
                 if ($class->hasMethod($method->getName())) {
                     $this->logger->warning(sprintf('The method "%s" is already defined in code; ignoring @method tag.', $method->getName()));
                     continue;
                 }
                 $class->addMethod($method);
             }
         }
     }
     return $class;
 }
 /**
  * @group foreach
  */
 public function testForeach3()
 {
     $this->registry->registerClass($foo = new Clazz('Foo'));
     $foo->setTypeRegistry($this->registry);
     $foo->addImplementedInterface('Iterator');
     $foo->addMethod($current = new Method('current'));
     $current->setReturnType($this->registry->getClassOrCreate('Bar'));
     $this->assuming('y', 'object<Foo>');
     $this->inMethod('$x = $i = null; foreach ($y as $i => $x);');
     $this->verify('x', $this->createNullableType('object<Bar>'));
     $this->verify('i', $this->createNullableType('integer|string'));
 }