/** * Check to see if the given Clazz is a duplicate * * @return null */ public static function analyzeParentConstructorCalled(CodeBase $code_base, Clazz $clazz) { // Only look at classes configured to require a call // to its parent constructor if (!in_array($clazz->getName(), Config::get()->parent_constructor_required)) { return; } // Don't worry about internal classes if ($clazz->isInternal()) { return; } // Don't worry if there's no parent class if (!$clazz->hasParentClassFQSEN()) { return; } if (!$code_base->hasClassWithFQSEN($clazz->getParentClassFQSEN())) { // This is an error, but its caught elsewhere. We'll // just roll through looking for other errors return; } $parent_clazz = $code_base->getClassByFQSEN($clazz->getParentClassFQSEN()); if (!$parent_clazz->isAbstract() && !$clazz->getIsParentConstructorCalled()) { Issue::emit(Issue::TypeParentConstructorCalled, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $clazz->getFQSEN(), (string) $parent_clazz->getFQSEN()); } }
/** * @return bool * True if the FQSEN exists. If not, a log line is emitted */ private static function fqsenExistsForClass(FQSEN $fqsen, CodeBase $code_base, Clazz $clazz, string $issue_type) : bool { if (!$code_base->hasClassWithFQSEN($fqsen)) { Issue::emit($issue_type, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $fqsen); return false; } return true; }
/** * Check to see if the given Clazz is a duplicate * * @return null */ public static function analyzeDuplicateClass(CodeBase $code_base, Clazz $clazz) { // Determine if its a duplicate by looking to see if // the FQSEN is suffixed with an alternate ID. if (!$clazz->getFQSEN()->isAlternate()) { return; } $original_fqsen = $clazz->getFQSEN()->getCanonicalFQSEN(); if (!$code_base->hasClassWithFQSEN($original_fqsen)) { // If there's a missing class we'll catch that // elsewhere return; } // Get the original class $original_class = $code_base->getClassByFQSEN($original_fqsen); // Check to see if the original definition was from // an internal class if ($original_class->isInternal()) { Issue::emit(Issue::RedefineClassInternal, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $clazz, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $original_class); // Otherwise, print the coordinates of the original // definition } else { Issue::emit(Issue::RedefineClass, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $clazz, $clazz->getFileRef()->getFile(), $clazz->getFileRef()->getLineNumberStart(), (string) $original_class, $original_class->getFileRef()->getFile(), $original_class->getFileRef()->getLineNumberStart()); } return; }
/** * Check to see if signatures match * * @return void */ public static function analyzeComposition(CodeBase $code_base, Clazz $class) { // Get the Class's FQSEN $fqsen = $class->getFQSEN(); // Get the list of all inherited classes. $inherited_class_list = $class->getInheritedClassList($code_base); // No chance of failed composition if we don't inherit from // lots of stuff. if (count($inherited_class_list) < 2) { return; } // For each property, find out every inherited class that defines it // and check to see if the types line up. foreach ($class->getPropertyList($code_base) as $property) { try { $property_union_type = $property->getUnionType(); } catch (IssueException $exception) { $property_union_type = new UnionType(); } // Check for that property on each inherited // class/trait/interface foreach ($inherited_class_list as $inherited_class) { // Skip any classes/traits/interfaces not defining that // property if (!$inherited_class->hasPropertyWithName($code_base, $property->getName())) { continue; } // We don't call `getProperty` because that will create // them in some circumstances. $inherited_property_map = $inherited_class->getPropertyMap($code_base); if (!isset($inherited_property_map[$property->getName()])) { continue; } // Get the inherited property $inherited_property = $inherited_property_map[$property->getName()]; // Figure out if this property type can cast to the // inherited definition's type. $can_cast = $property_union_type->canCastToExpandedUnionType($inherited_property->getUnionType(), $code_base); if ($can_cast) { continue; } // Don't emit an issue if the property suppresses the issue if ($property->hasSuppressIssue(Issue::IncompatibleCompositionProp)) { continue; } Issue::maybeEmit($code_base, $property->getContext(), Issue::IncompatibleCompositionProp, $property->getFileRef()->getLineNumberStart(), (string) $class->getFQSEN(), (string) $inherited_class->getFQSEN(), $property->getName(), (string) $class->getFQSEN(), $class->getFileRef()->getFile(), $class->getFileRef()->getLineNumberStart()); } } // TODO: This has too much overlap with PhanParamSignatureMismatch // and we should figure out how to merge it. /* $method_map = $code_base->getMethodMapByFullyQualifiedClassName($fqsen); // For each method, find out every inherited class that defines it // and check to see if the types line up. foreach ($method_map as $i => $method) { $method_union_type = $method->getUnionType(); // We don't need to analyze constructors for signature // compatibility if ($method->getName() == '__construct') { continue; } // Get the method parameter list // Check for that method on each inherited // class/trait/interface foreach ($inherited_class_list as $inherited_class) { // Skip anything that doesn't define this method if (!$inherited_class->hasMethodWithName($code_base, $method->getName())) { continue; } $inherited_method = $inherited_class->getMethodByName($code_base, $method->getName()); if ($method == $inherited_method) { continue; } // Figure out if this method return type can cast to the // inherited definition's return type. $is_compatible = $method_union_type->canCastToExpandedUnionType( $inherited_method->getUnionType(), $code_base ); $inherited_method_parameter_map = $inherited_method->getParameterList(); // Figure out if all of the parameter types line up foreach ($method->getParameterList() as $i => $parameter) { $is_compatible = ( $is_compatible && isset($inherited_method_parameter_map[$i]) && $parameter->getUnionType()->canCastToExpandedUnionType( ($inherited_method_parameter_map[$i])->getUnionType(), $code_base ) ); } if ($is_compatible) { continue; } // Don't emit an issue if the method suppresses the issue if ($method->hasSuppressIssue(Issue::IncompatibleCompositionMethod)) { continue; } Issue::maybeEmit( $code_base, $method->getContext(), Issue::IncompatibleCompositionMethod, $method->getFileRef()->getLineNumberStart(), (string)$method, (string)$inherited_method, $inherited_method->getFileRef()->getFile(), $inherited_method->getFileRef()->getLineNumberStart() ); } } */ }
/** * Add a class to the code base * * @return null */ public function addClass(Clazz $class) { $this->class_map[$class->getFQSEN()] = $class; // For classes that aren't internal PHP classes if (!$class->isInternal()) { // Associate the class with the file it was found in $this->getFileByPath($class->getFileRef()->getFile())->addClassFQSEN($class->getFQSEN()); } }