动态生成 PHP 个静态方法来实例化新对象

Dynamically generate PHP static methods to instantiate new object

我正在尝试建立一个数据库-class 允许方法链接来查询它。我想像 Laravel 的 Eloquent 一样实例化它,方法是将第一个调用设为静态。

看这个例子:

class Database
{
    public function construct($data) {
        $this->data = $data;
    }
    public function filter($criteria) {
        // ...
        return $this;
    }
    public static function filter($data, $criteria) {
        $obj = new Database($data);
        $obj->filter($criteria);
        return $obj;
    }
    public function add($value) {
        // ...
        return $this;
    }
    public static function add($data, $value,) {
        $obj = new Database($data);
        $obj->add($value);
        return $obj;
    }
}

例如,这将允许我:

Database::add($myData, $newValue)->add($anotherValue)->filter('isString');
Database::filter($myData, 'isNumber')->add($thirdValue);

这应该不是真的,但我很好奇是否有办法减少整个静态方法中的重复代码或完全删除它们。 我想到了神奇的方法__callStatic(),但我不确定它是否是最好的实现方法。

如果知道的人可以向我解释一下,大型框架如何处理这种任务,我将不胜感激。

您可以使用对象的静态方法,但不能在该方法中使用 $this。

您不能定义同名的静态和非静态方法。

在大多数情况下,我只需要非静态方法,在极少数情况下,我没有仅为此方法调用实例化的对象实例 - 并调用 non-static 方法。

在极少数情况下,我也会用不同的名称定义静态方法,例如 filterStatic()。这是一种非常罕见的情况,在这种情况下 non-static 方法包装静态方法以避免代码重复。

是的,它确实是通过魔术方法完成的__callStatic()

这里有 Laravel Eloquent 代码片段,这些代码摘自 Model class,还有更多事情正在发生,但这些与你的案例相关:

public static function __callStatic($method, $parameters)
{
    return (new static)->$method(...$parameters); // delegating to __call method
}

public function __call($method, $parameters)
{
    ...
    // self explanatory
    return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}

// newQuery method lead to this method:
public function newModelQuery()
{
    return $this->newEloquentBuilder(
        $this->newBaseQueryBuilder() // deals with getting database connection
    )->setModel($this);
}

// newEloquentBuilder return Builder class which have the "where" etc. methods defined.

在你的情况下简单的例子:

class Database
{
    protected static $connection;
    protected $query; // could be array or better object

    public function __construct() {
        if(!static::$connection){
            static::$connection = new mysqli(...); // for example
        }
    }
    
    public static function __callStatic($method, $parameters)
    {
        return (new static)->$method(...$parameters);
    }
    
    // in case $query is object have these two methods defined there
    // also delegate with __call method to the query object
    
    public function filter($criteria) {
        ... // filling $query variable
        return $this;
    }
  
    public function add($value) {
        ... // filling $query variable
        return $this;
    }
    
    public function fetch_assoc() {
        $stmt = static::$connection->prepare(...);
        ... // fill the prepared statements with $query data
        return $stmt->get_result()->fetch_assoc();
    }
}