Page MenuHomeWickedGov Phorge

ClassInheritanceAnalyzer.php
No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None

ClassInheritanceAnalyzer.php

<?php
declare(strict_types=1);
namespace Phan\Analysis;
use Phan\CodeBase;
use Phan\Issue;
use Phan\IssueFixSuggester;
use Phan\Language\Element\Clazz;
use Phan\Language\FQSEN\FullyQualifiedClassName;
/**
* A checker for whether the given Clazz(class/trait/interface) properly inherits
* from its classes, traits, and/or interfaces.
*/
class ClassInheritanceAnalyzer
{
/**
* Checks if the given Clazz(class/trait/interface) properly inherits
* from its classes, traits, and/or interfaces
*/
public static function analyzeClassInheritance(
CodeBase $code_base,
Clazz $clazz
): void {
// Don't worry about internal classes
if ($clazz->isPHPInternal()) {
return;
}
if ($clazz->hasParentType()) {
$class_exists = self::fqsenExistsForClass(
$clazz->getParentClassFQSEN(),
$code_base,
$clazz,
Issue::UndeclaredExtendedClass
);
if ($class_exists) {
self::testClassAccess(
$clazz,
$clazz->getParentClass($code_base),
$code_base
);
}
}
foreach ($clazz->getInterfaceFQSENList() as $fqsen) {
$class_exists = self::fqsenExistsForClass(
$fqsen,
$code_base,
$clazz,
Issue::UndeclaredInterface
);
if ($class_exists) {
self::testClassAccess(
$clazz,
$code_base->getClassByFQSEN($fqsen),
$code_base
);
}
}
foreach ($clazz->getTraitFQSENList() as $fqsen) {
$class_exists = self::fqsenExistsForClass(
$fqsen,
$code_base,
$clazz,
Issue::UndeclaredTrait
);
if ($class_exists) {
self::testClassAccess(
$clazz,
$code_base->getClassByFQSEN($fqsen),
$code_base
);
}
}
}
/**
* @return bool
* True if the FQSEN exists. If not, a log line is emitted
*/
private static function fqsenExistsForClass(
FullyQualifiedClassName $fqsen,
CodeBase $code_base,
Clazz $clazz,
string $issue_type
): bool {
if (!$code_base->hasClassWithFQSEN($fqsen)) {
$filter = null;
switch ($issue_type) {
case Issue::UndeclaredExtendedClass:
$filter = IssueFixSuggester::createFQSENFilterForClasslikeCategories($code_base, true, false, false);
break;
case Issue::UndeclaredTrait:
$filter = IssueFixSuggester::createFQSENFilterForClasslikeCategories($code_base, false, true, false);
break;
case Issue::UndeclaredInterface:
$filter = IssueFixSuggester::createFQSENFilterForClasslikeCategories($code_base, false, false, true);
break;
}
$suggestion = IssueFixSuggester::suggestSimilarClass($code_base, $clazz->getContext(), $fqsen, $filter);
Issue::maybeEmitWithParameters(
$code_base,
$clazz->getContext(),
$issue_type,
$clazz->getLinenoOfAncestorReference($fqsen),
[(string)$fqsen],
$suggestion
);
return false;
}
return true;
}
/**
* @param Clazz $source_class
* The class accessing the $target_class
*
* @param Clazz $target_class
* The class being accessed from the $source_class
*
* @param CodeBase $code_base
* The code base in which both classes exist
*/
private static function testClassAccess(
Clazz $source_class,
Clazz $target_class,
CodeBase $code_base
): void {
if ($target_class->isNSInternal($code_base)
&& !$target_class->isNSInternalAccessFromContext(
$code_base,
$source_class->getContext()
)
) {
Issue::maybeEmit(
$code_base,
$source_class->getInternalContext(),
Issue::AccessClassInternal,
$source_class->getFileRef()->getLineNumberStart(),
(string)$target_class,
$target_class->getFileRef()->getFile(),
(string)$target_class->getFileRef()->getLineNumberStart()
);
}
$target_class_fqsen = $target_class->getFQSEN();
if ($target_class->isDeprecated()) {
if ($target_class->isTrait()) {
$issue_type = Issue::DeprecatedTrait;
} elseif ($target_class->isInterface()) {
$issue_type = Issue::DeprecatedInterface;
} else {
$issue_type = Issue::DeprecatedClass;
}
Issue::maybeEmit(
$code_base,
$source_class->getInternalContext(),
$issue_type,
$source_class->getFileRef()->getLineNumberStart(),
$target_class_fqsen,
$target_class->getContext()->getFile(),
$target_class->getContext()->getLineNumberStart(),
$target_class->getDeprecationReason()
);
}
// TODO: Make this also work for classes implementing an interface that extends Serializable.
if (!$source_class->isInterface() &&
$target_class_fqsen->getName() === 'Serializable' && $target_class_fqsen->getNamespace() === '\\') {
// Must define both __serialize and __unserialize to suppress php 8.1's warning.
if (!$source_class->hasMethodWithName($code_base, '__serialize', true) || !$source_class->hasMethodWithName($code_base, '__unserialize', true)) {
Issue::maybeEmit(
$code_base,
$source_class->getInternalContext(),
Issue::CompatibleSerializeInterfaceDeprecated,
$source_class->getFileRef()->getLineNumberStart(),
$source_class->getFQSEN()
);
}
}
if ($target_class->isInterface() && !$source_class->isEnum() && !$source_class->isInterface()) {
if (\in_array(\strtolower($target_class->getFQSEN()->__toString()), ['\unitenum', '\backedenum'], true)) {
Issue::maybeEmit(
$code_base,
$source_class->getInternalContext(),
Issue::EnumCannotImplement,
$source_class->getFileRef()->getLineNumberStart(),
$source_class->getFQSEN(),
$target_class->getFQSEN()
);
}
}
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, May 16, 22:31 (2 h, 5 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8d/35/37f05e094848ada79030a883baa2
Default Alt Text
ClassInheritanceAnalyzer.php (6 KB)

Event Timeline