正确的实现选项是使用魔术方法 __call()

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

有一个Controller class 包含神奇的__call() 方法,它在传递参数时获取名称并创建相应名称的实例。 Controller class 的属性 $this 传递给此 class 的构造函数,以便使用 req() 函数。

例如,我使用 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__]);
    }
}

在PHPDoc中,我已经定义了可以调用的方法。

/**
 * 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);
    }
}

这一切都是正确的。但是,当创建 class 的实例时,您可以调用 req() 函数,这仅在 PHPDoc 的预定义 class 中允许。

好:

$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']); 

问:如何避免这种情况?为此创建一个特征以在预定义的 classes 中使用,或者对 Controller class 进行包装并从中继承?战友们,这种情况有什么建议呢?

你可以使用特质。 Traits 专门用于代码重用和控制,没有任何类型的 class 继承。无需扩展 class 并收集其所有功能(有些可能是不必要的),特征可以帮助您控制此功能流,以及从扩展 class 的 class 收集信息=] 你使用了特征。

特征示例:

namespace App;

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

在 classes 一和二中使用特征,它应该有效:

use App\Request;

class One
{
   use Request;
}

class Two
{
   use Request;
}

如果您不使用自动加载,您也可以要求特征文件。