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')); }