Circular Reference
ID |
php.circular_reference |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Reliability |
Language |
Php |
Tags |
inheritance, reliability |
Description
Reports inheritance cycles between classes (or interfaces) across the whole project: a chain
of extends relationships that loops back on itself, such as A extends B, B extends C,
C extends A. The check is project-wide — the classes that form the cycle may be declared in
different files and namespaces.
Rationale
A class hierarchy must be a tree (or, for interfaces, a directed acyclic graph): every type has at most one parent and that chain of parents must eventually terminate. When the chain loops back on itself, the type can never be resolved or instantiated. In practice a cycle is a fatal error in PHP and signals one of two mistakes: two classes that were refactored until each references the other, or a copy-paste that names the wrong parent. Surfacing the cycle pinpoints the declaration that closes the loop.
<?php
// file model/a.php
namespace App\Model;
class A extends B {} // OK - this edge alone does not close a cycle
// file model/b.php
namespace App\Model;
class B extends A {} // FLAW - closes the inheritance cycle App\Model\A -> App\Model\B -> App\Model\A
// file model/chain.php
namespace App\Linear;
class Base {}
class Middle extends Base {} // OK - linear chain, terminates at Base
class Leaf extends Middle {} // OK - linear chain, terminates at Base
Remediation
Break the cycle. Decide which class is genuinely the more general one and keep only that
extends direction; extract the shared behaviour the two classes were trying to reach for into
a common base class or an interface, or replace the inheritance with composition so neither type
needs to extend the other.