public function persist(MethodContainer $container, $packageVersionId) { switch (true) { case $container instanceof Clazz: $type = 'class'; $superClass = $container->getSuperClass(); if ($superClasses = $container->getSuperClasses()) { $superClasses = implode(',', $superClasses); } else { $superClasses = null; } if ($implementedInterfaces = $container->getImplementedInterfaces()) { $implementedInterfaces = implode(',', $implementedInterfaces); } else { $implementedInterfaces = null; } $modifier = $container->getModifier(); $extendedInterfaces = null; break; case $container instanceof InterfaceC: $type = 'interface'; $superClass = null; $superClasses = null; $implementedInterfaces = null; $modifier = 0; if ($extendedInterfaces = $container->getExtendedInterfaces()) { $extendedInterfaces = implode(',', $extendedInterfaces); } else { $extendedInterfaces = null; } break; case $container instanceof TraitC: $type = 'trait'; $superClass = null; $superClasses = null; $implementedInterfaces = null; $modifier = 0; $extendedInterfaces = null; break; default: throw new \InvalidArgumentException('Unknown MethodContainer ' . get_class($container)); } $this->insertStmt->bindValue(1, $container->getName()); $this->insertStmt->bindValue(2, $container->isNormalized() ? 1 : 0, \PDO::PARAM_INT); $this->insertStmt->bindValue(3, $packageVersionId, \PDO::PARAM_INT); $this->insertStmt->bindValue(4, $type); $this->insertStmt->bindValue(5, $superClass, null === $superClass ? \PDO::PARAM_NULL : \PDO::PARAM_STR); $this->insertStmt->bindValue(6, $superClasses, null === $superClasses ? \PDO::PARAM_NULL : \PDO::PARAM_STR); $this->insertStmt->bindValue(7, $implementedInterfaces, null === $implementedInterfaces ? \PDO::PARAM_NULL : \PDO::PARAM_STR); $this->insertStmt->bindValue(8, $modifier, \PDO::PARAM_INT); $this->insertStmt->bindValue(9, $extendedInterfaces, null === $extendedInterfaces ? \PDO::PARAM_NULL : \PDO::PARAM_STR); $this->insertStmt->execute(); $containerId = $this->con->lastInsertId(); foreach ($container->getMethods() as $method) { $this->methodPersister->persist($method, $packageVersionId, $containerId); } switch (true) { case $container instanceof Clazz: foreach ($container->getProperties() as $property) { $this->propertyPersister->persist($property, $packageVersionId, $containerId); } foreach ($container->getConstants() as $constant) { $this->constantPersister->persist($constant, $packageVersionId, $containerId); } break; case $container instanceof InterfaceC: foreach ($container->getConstants() as $constant) { $this->constantPersister->persist($constant, $packageVersionId, $containerId); } break; case $container instanceof TraitC: break; default: throw new \InvalidArgumentException('Unknown MethodContainer ' . get_class($container)); } }
public function registerClass(MethodContainer $class) { $class->setTypeRegistry($this); $this->classes[strtolower($class->getName())] = $class; }
public function setPackageVersion(PackageVersion $packageVersion) { parent::setPackageVersion($packageVersion); foreach ($this->constants as $constant) { if ($constant->isInherited()) { continue; } $constant->setPackageVersion($packageVersion); } }
public function addContainer(MethodContainer $container) { $container->setPackageVersion($this); $this->containers->set($container->getName(), $container); }
/** * Returns the inferred return type for the given function. * * If we cannot infer any type NO type is returned, this is also returned if we can only infer * ALL type, or UNKNOWN type. * * We use two characteristics for inferring a return type. First, we are looking at how the * return value of the function is used in the places from where it is called. Currently, we * are only looking at whether it is passed to other functions/methods and what their expected * parameter types are. * * Second, we also take a look at the exit points of the CFG of the function itself to see * whether there are some specific types which we can infer. For example, a function might return * NULL type, or ALL type in which case its return type would normally be set to ALL type. * However, for our analysis here, we just ignore the ALL type, and add the NULL type to the * list of allowed types. * * @param AbstractFunction $function * * @return PhpType */ private function inferReturnTypeForFunction(AbstractFunction $function, MethodContainer $container = null) { $types = array(); if ($container instanceof \Scrutinizer\PhpAnalyzer\Model\InterfaceC) { foreach ($container->getImplementingClasses() as $class) { if (null === ($implementedFunction = $class->getMethod($function->getName()))) { continue; } $returnType = $implementedFunction->getReturnType(); if ($returnType->isUnknownType() || $returnType->isAllType()) { continue; } $types[] = $returnType; } } if (!$types) { foreach ($function->getInCallSites() as $site) { // We can only analyze call sites which are part of the code which is currently being // scanned. Otherwise, we would need to persist the possible types to the database. // This could be a future improvement though if we deem it necessary. if (null === ($node = $site->getAstNode())) { continue; } if (null === ($parent = $node->getAttribute('parent'))) { continue; } switch (true) { case $parent instanceof \PHPParser_Node_Arg: if (null === ($callNode = $parent->getAttribute('parent'))) { break; } $paramType = $this->getSpecificParamTypeForArg($callNode, $parent); if ($paramType) { $types[] = $paramType; } break; case $parent instanceof \PHPParser_Node_Expr_Assign: case $parent instanceof \PHPParser_Node_Expr_AssignRef: foreach ($parent->var->getAttribute('maybe_using_vars', array()) as $varNode) { if (null === ($argNode = $varNode->getAttribute('parent'))) { continue; } if (!$argNode instanceof \PHPParser_Node_Arg) { continue; } $callNode = $argNode->getAttribute('parent'); $paramType = $this->getSpecificParamTypeForArg($callNode, $argNode); if (null !== $paramType) { $types[] = $paramType; } } break; } } } $cfa = new \Scrutinizer\PhpAnalyzer\ControlFlow\ControlFlowAnalysis(); $cfa->process($function->getAstNode()); $cfg = $cfa->getGraph(); foreach ($cfg->getDirectedSuccNodes($cfg->getImplicitReturn()) as $exitGraphNode) { $exitNode = $exitGraphNode->getAstNode(); if (!$exitNode instanceof \PHPParser_Node_Stmt_Return || null === $exitNode->expr) { $types[] = $this->registry->getNativeType('null'); continue; } $returnType = $exitNode->expr->getAttribute('type'); if ($returnType && !$returnType->isUnknownType() && !$returnType->isAllType()) { $types[] = $returnType; } } return $this->refineTypeForAnnotation($this->registry->createUnionType($types), true); }
public function addClass(MethodContainer $container) { $this->classes[strtolower($container->getName())] = $container; }
public function __construct($name) { parent::__construct($name); $this->methods = new ArrayCollection(); $this->properties = new ArrayCollection(); }
private function scanMethod(\PHPParser_Node_Stmt_ClassMethod $stmt, MethodContainer $class) { $method = new Method($stmt->name); $method->setAstNode($stmt); $method->setReturnByRef($stmt->byRef); // convert PHPParser modifier to our modifier $modifier = 0; if (\PHPParser_Node_Stmt_Class::MODIFIER_ABSTRACT === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_ABSTRACT)) { $modifier |= Method::MODIFIER_ABSTRACT; } if (\PHPParser_Node_Stmt_Class::MODIFIER_FINAL === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_FINAL)) { $modifier |= Method::MODIFIER_FINAL; } if (\PHPParser_Node_Stmt_Class::MODIFIER_STATIC === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_STATIC)) { $modifier |= Method::MODIFIER_STATIC; } // All interface methods are automatically abstract without being declared as such. if ($class instanceof InterfaceC) { $modifier |= Method::MODIFIER_ABSTRACT; } $method->setModifier($modifier); $method->setVisibility(\PHPParser_Node_Stmt_Class::MODIFIER_PRIVATE === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_PRIVATE) ? Method::VISIBILITY_PRIVATE : (\PHPParser_Node_Stmt_Class::MODIFIER_PROTECTED === ($stmt->type & \PHPParser_Node_Stmt_Class::MODIFIER_PROTECTED) ? Method::VISIBILITY_PROTECTED : Method::VISIBILITY_PUBLIC)); foreach ($this->paramParser->parse($stmt->params, 'Scrutinizer\\PhpAnalyzer\\Model\\MethodParameter') as $param) { $method->addParameter($param); } foreach ($stmt->params as $i => $param) { if (null !== ($docType = $this->commentParser->getTypeFromParamAnnotation($param, $param->name, true))) { $method->setParamDocType($i, $docType); } } if (null !== ($returnDocType = $this->commentParser->getTypeFromReturnAnnotation($stmt, true))) { $method->setReturnDocType($returnDocType); } $class->addMethod($method); }
private function copyMethods(MethodContainer $a, MethodContainer $b) { // merge in methods from $b into $a foreach ($b->getMethods() as $containerMethod) { // method already exists on $a if ($a->hasMethod($containerMethod->getName())) { // TODO: Check types for the method in $a, and if not complete // copy over all available types from $b continue; } $method = $containerMethod->getMethod(); // We do copy over all methods, even private ones. See above for properties. $a->addMethod($method, $containerMethod->getDeclaringClass()); } }