The Problems with PHP Class Inheritance and What We Can Do About It The PHP community heavily relies on class inheritance to structure and reuse code. However, does this drive new development standards? This article will look at the problems with class inheritance and why we don't think it's a good thing, except with a framework and/or if you are modifying an individual class to include an inheritance-based class structure. We offer alternatives: Actions, DI, and Composition. Problems with Class Inheritance When we inherit a class, our code is tightly coupled to the implementation of that class, producing several issues:- A class is closed for modification but open for extension of the base code violating the Open/Closed Principle ( SOLID principle).- A subclass may inherit methods that it does not need or can use, violating the single responsibility.- Update issues if your code relies on a library or framework base class that has been modified.- Hard makes testing more difficult with units that have high interdependencies.- The diamond problem of being too high in the hierarchy conflicts with methods.
Bad Example: Extending a Course
class FileLogger { public function log($message) { file_put_contents('log.txt', $message . "\n", FILE_APPEND); } } class UserLogger extends FileLogger { public function logUserAction($action) { $this->log("User action: " . $action); } } $logger = new UserLogger(); $logger->logUserAction("Logged in");
In this sense, UserLogger extends FileLogger, thus any modifications to FileLogger will influence UserLogger. Should we wish to change the way data is logged—that is, stored in a database—we have to alter FileLogger, which may have unintended consequences.
Improved substitutes
Dependency Injection
Dependency injection is a better way to divide issues than running seminars.
interface LoggerInterface { public function log($message); } class FileLogger implements LoggerInterface { public function log($message) { file_put_contents('log.txt', $message . "\n", FILE_APPEND); } } class UserLogger { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function logUserAction($action) { $this->logger->log("User action: " . $action); } } $logger = new FileLogger(); $userLogger = new UserLogger($logger); $userLogger->logUserAction("Logged in");
This approach allows easier replacement of the logger without modifying UserLogger's logic.
2. Composition (Using Instead of Inheriting)
Composition allows us to use a class within another instead of inheriting from it.
class Logger { public function log($message) { file_put_contents('log.txt', $message . "\n", FILE_APPEND); } } class UserActivity { private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function track($action) { $this->logger->log("User action: " . $action); } } $logger = new Logger(); $userActivity = new UserActivity($logger); $userActivity->track("User logged in");
Here, UserActivity does not need to inherit Logger but simply uses it as a dependency.
3. Actions (Single-Responsibility Classes)
Actions are small, single-purpose classes that perform specific tasks. Instead of creating a subclass, we can use an action class.
class LogUserAction { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function execute($action) { $this->logger->log("User action: " . $action); } } $logger = new FileLogger(); $logAction = new LogUserAction($logger); $logAction->execute("Logged in");
This method affords better organization and more flexibility without making dependencies of inheritance. Conclusion separate responsibilities with dependency injection, composition, or actions rather than using inheritance is a better solution. This approach creates clearer, more flexible, and easier to maintain code, especially in large PHP applications, because there are many things to consider when building a large application, such as scalability, testability, and so on. Instead of writing subclasses only to reuse code, create better structure and organization by considering other methods.