如何在方法链中调用第一个带有“::”而其余部分为“->”的方法

how to call method with "::" in the first and "->" for the rest in method chaining

我想制作自己的 class 以用于与数据库交互, 如果我使用方法链,我认为它会很容易和可读。 但是我在静态调用第一个方法时遇到问题。

代码如下:

<?php

class Crud
{

    protected static $action;
    protected static $instance = null;
    protected static $columns  = [];
    protected $data;
    protected $db;
    protected $query;
    protected $table;

    public function __construct()
    {
        $this->db = new mysqli('localhost', 'root', '', 'bahan_belajar');
        if (!$this->db) {

            echo "error";
        }
        return $this;
    }

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    public function select()
    {
        if (empty(func_get_args())) {
            // $this->columns = "*";
            self::$columns = "*";
        } else {
            if (is_array(func_get_args())) {
                // self::columns = join(', ', func_get_args());
                self::$columns = join(', ', func_get_args());
            } else {
                // self::columns = func_get_args();
                self::$columns = func_get_args();
            }
        }

        self::$action = "SELECT";
        return $this;
    }

    public function from($tableName)
    {
        $this->table = ' FROM ' . $tableName;
        return $this;
    }

    public function get($getName = 'object')
    {
        $this->query = self::$action . ' ' . self::$columns . ' ' . $this->table;
        switch ($getName) {
            case 'object':
                $this->data = $this->db->query($this->query)->fetch_object();
                break;
            case 'array':
                $this->data = $this->db->query($this->query)->fetch_array();
                break;
            case 'count':
                $this->data = $this->db->query($this->query)->num_rows;
                break;
        }
        return $this->data;
    }
}

$chat = Crud::getInstance()->select('nama', 'teks')->from('chat')->get();


echo '<pre>';
print_r($chat);
echo '</pre>';

实际上,如果我首先使用 getInstance() 方法,这段代码可以正常工作,如上所示。但是,当我直接调用 select() 方法作为静态方法时,我该如何使它工作,例如:

$chat = Crud::select('nama', 'teks')->from('chat')->get();

如果我运行上面的代码我会得到一个错误,例如:

Fatal error: Uncaught Error: Using $this when not in object context in C:\xampp\htdocs\bahan_belajar\chat\classes.php:47 Stack trace: #0 C:\xampp\htdocs\bahan_belajar\chat\classes.php(74): Crud::select('nama', 'teks') #1 {main} thrown in C:\xampp\htdocs\bahan_belajar\chat\classes.php on line 47

我知道 select() 方法在可以用 :: 调用之前应该是一个静态方法(我认为),但是我怎样才能使它成为静态方法?

您只能将 :: 用于静态方法,将 -> 用于实例方法。这给您留下了 2 个选择:

选项 1:在链接之前显式获取实例

你需要用你的静态方法获取实例,然后使用->链接,像这样:

Crud::getInstance()->select('nama', 'teks')->from('chat')->get();

选项 2:使用魔法方法

您的另一个选择是使用 magic methods. If you make your methods protected or private, you can intercept calls to those methods from outside the class with the __call() and __callStatic() magic methods. When you're in __callStatic(),您可以通过调用 self::getInstance() 切换到使用实例。

这种方法可以让你做类似的事情

Crud::select('nama', 'teks')->from('chat')->get();

这里有一些非常简化的示例代码来演示这个想法 (demo on 3v4l):

class Test
{
    public static $instance;
    protected $myVar = 'foo';

    // This intercepts instance calls ($testObj->whatever()) and handles them
    public function __call($name, $args)
    {
        return call_user_func_array(array($this, $name), $args);
    }

    // This intercepts instance calls ($testObj->whatever()) and handles them
    // The use of self::getInstance() lets us force static methods to act like instance methods
    public static function __callStatic($name, $args)
    {
        return call_user_func_array(array(self::getInstance(), $name), $args);
    }

    public static function getInstance()
    {
        return self::$instance ? : new self;
    }

    protected function getMyVar()
    {
        echo $this->myVar;
    }

    protected function setMyVar($value)
    {
        $this->myVar = $value;
        return $this;
    }
}

echo Test::setMyVar(15)->getMyVar(); // successfully echoes 15

对所有这一切的重要说明:您所做的看起来很像重新发明 Eloquent, the ORM that ships with Laravel。从以前构建过自己的 ORM 的人那里获取:你最好使用像 Eloquent 这样的现有系统,它已经被编写并经过全面测试(并且可以在不需要你使用整个 Laravel框架)。构建 ORM 比看起来要难得多,而且,一旦开始,兔子洞就会越来越深。