'PHP Subclass needs another subclass [duplicate]

I have two subclasses and I want to call a method in one subclass from another. Is the only solution to start chaining my classes in order of dependence? Sorry for the noob PHP question. Basically it's a common scenario to have multiple "base" classes such as an email class in which other subclasses will need access to. So do you just starting chaining them?

// BaseClass.php

require_once("./services/checkout.php");
require_once("./services/email.php");
class Base {
    // ...
}
// checkout.php
    
class checkout extends Base {
    public function onCheckout() {
        // Call send email
        $this->sendEmail($email); // <- How to do this?
    }
}
// email.php

class email extends Base {
    public function sendEmail($email) {
        // Send email
    }
}


Solution 1:[1]

I think it's common to have BaseAction or BaseController but not something as generic as BaseClass. It feels more intuitive to have something like:

class Checkout
{
    public function onCheckout(Mailer $mailer)
    {
        $mailer->sendEmail($email);
    }
}

class Mailer
{
    public function sendEmail($email)
    {
        // Send email
    }
}

This is still very rough. You most likely want an interface, injected into checkout constructor, perhaps automagically with some dependency injection library that implements autowiring:

interface MessengerInterface
{
    public function send(string $text): bool;
}
class Checkout
{
    
    public function __construct(
       private MessengerInterface $messenger
    ) {
    }

    public function onCheckout()
    {
        $this->messenger->send('Your order blah blah');
    }
}
class Mailer implements MessengerInterface
{
    public function send(string $body): bool
    {
        // Send email
    }
}

Solution 2:[2]

The key phrase here is "composition vs inheritance".

Inheritance represents an "is-a" relationship. You might say that an HTMLEMail is an Email - it's the same kind of thing, but with extra or different behaviour. But it doesn't make sense to say Checkout is an Emailer - they're two different things, neither is a type of the other.

Composition represents a "has-a" relationship. A Checkout might have an Emailer, which it uses to send e-mails. So they are different classes that you "compose" together. That basically means that rather than $this->sendEmail(), you would write $emailer->sendEmail(), where $emailer is another object that's come from somewhere.

The cleanest way to implement "composition" is via "dependency injection", which is mostly a fancy way of saying "passing objects to other objects".

That might be passing it into the individual method:

class Checkout extends Base {
    public function doCheckout($emailer) {
        // Call send email
        $emailer->sendEmail('[email protected]', 'Hello');
    }
}

More flexibly, since you might want to use in multiple methods, passing it into the constructor and storing it on a property:

class Checkout extends Base {
    private Emailer $emailer;

    public function __construct(Emailer $emailer) {
        $this->emailer = $emailer;
    }

    public function doCheckout() {
        // Call send email
        $this->emailer->sendEmail('[email protected]', 'Hello');
    }
}

This kind of setting of properties in the constructor is so common that PHP 8 has a special short-hand for it called "Constructor Property Promotion":

class Checkout extends Base {
    // note the "private" keyword next to the argument
    public function __construct(private Emailer $emailer) {
    }

    public function doCheckout() {
        // Call send email
        $this->emailer->sendEmail('[email protected]', 'Hello');
    }
}

More flexibly still, you can depend on an interface, which means "anything that has the right behaviour can be passed in here, I'll decide on the exact implementation later".

The idea is that you can write the Checkout class without knowing what the inside of the Emailer class is going to look like. You just need to know that sendEmail requires certain arguments, and returns certain things when it's done. Then you can have one implementation that uses a username and password to talk to an SMTP server, one implementation that uses an API key to use a web service, and a third that just logs to a local file for test purposes. The Checkout class doesn't need to know how to create each one, or when to use them, it just waits for one to be provided.

Somewhere else, you have some co-ordinating code that actually knows how to create all these objects. The fancy way to do this in a larger project is with a library like PHP-DI that uses configuration and "auto-wiring" to work out what's needed when, but a simple version just looks like this:

$config = parse_config_file('config.ini');
if ( $config['mode'] === 'debug' ) {
    $emailer = new DebugEmailer($config['logfiles']['email_debug']);
}
else {
    $emailer = new SMTPEmailer($config['smtp']['username'], $config['smtp']['password']);
}
$checkout = new Checkout($emailer);
$checkout->doCheckout();

Solution 3:[3]

Please read Traits in Php. It seem that would be the right thing for your goal:

PHP-Traits (php.org)

PHP-Traits (W3Schools - easier to understand)

I would suggest to reconsider your inheritance, whether it makes sense that E-Mail class and Checkout-Class inherit from the same base class. They should do total different and independent things by their own. If you want to send an e-mail from the checkout-class then try to implement an e-mail class and inject an instance of it to the checkout object.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Will B.
Solution 2
Solution 3 Selim Acar