Skip to content
Advertisement

The correct implementation option is using the magic method __call()

There is a Controller class that contains the magic __call() method, which, when passing an argument, gets a name and creates an instance of the corresponding name. The properties $this of the Controller class are passed to the constructor of this class in order to use the req() function.

For example, I recreated the situation from production using examples of 3 classes.

class One
{
    public function __construct(
        protected Controller $controller
    ) {}

    public function oneTestFunc(): void {
        echo $this->controller->req([__FUNCTION__, __CLASS__]);
    }
}

class Two
{
    public function __construct(
        protected Controller $controller
    ) {}

    public function twoTestFunc(): void {
        echo $this->controller->req([__FUNCTION__, __CLASS__]);
    }
}

In PHPDoc, I have defined the methods that can be called.

/**
 * Class Controller
 * @method One one()
 * @method Two two()
 */
class Controller
{
    public function __call(string $name, array $args): mixed {
        $name = ucfirst($name);
        return new $name($this);
    }

    public function req(array $data): string {
        return sprintf("Call function %s (class: %s)n", ...$data);
    }
}

All this is correct. HOWEVER, when creating an instance of a class, you can call the req() function, and this is only allowed in predefined classes from PHPDoc.

Good:

$x = new Controller();
$x->one()->oneTestFunc(); // Good: Call function oneTestFunc (class: One)
$x->two()->twoTestFunc(); // Cood: Call function twoTestFunc (class: Two)

// Unacceptable! Not directly possible, the req() function is only for predefined classes.
$x->req(['click', 'clack']); 

Q: How do I avoid this? Create a trait for this to use in predefined classes, or make a wrapper over the Controller class, and inherit from it already? Comrades, what do you recommend in this situation?

Advertisement

Answer

You can use traits. Traits are exclusively for code reuse and control without any kind of class inheritance. Instead of you extending a class and collecting all its features (some may be unnecessary), traits can help you control this flow of functions, as well as collect information from the class that extends the class you use trait.

Trait example:

namespace App;

trait Request
{
    public function req(Array $data): string
    {
        return sprintf("Call function %s (class: %s)n", ...$data);
    }
}

Use trait in classes One and Two, it should work:

use AppRequest;

class One
{
   use Request;
}

class Two
{
   use Request;
}

You can also require the trait file if you are not using autoload.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement