为每个方法调用处理声明文件上的 PHP 对象 [重复]

Posted

技术标签:

【中文标题】为每个方法调用处理声明文件上的 PHP 对象 [重复]【英文标题】:Handle PHP objects on declaration file for every method call [duplicate] 【发布时间】:2019-04-02 21:10:20 【问题描述】:

我正在寻找构建处理程序或编辑 php 对象的方法,以下是 mysqli 的示例

我有一个包含许多文件的系统,在变量中使用 mysqli 对象

$my_var=new mysqli($host, $user, $pass, $base);

文件调用它来执行查询并获得如下结果:

$q=$my_var->query("SELECT * FROM table");
$q->fetch_assoc();

如果查询出现错误,$my_var->error 将在第一行填充,并在第二行抛出错误 500。

我正在寻找一种方法来为$my_var->error 构建处理程序并在调用任何 fetch_* 方法之前抛出错误,所以,

有没有办法为$my_var->error 构建处理程序/侦听器?

如果这不可能,有没有办法覆盖mysqli fetch_assoc()、fetch_row() 和fetch_array() 方法来检查$my_var->error 是否为真并在继续之前显示错误?

我知道trycatch()throw new Exceptionor die()方法,但这些方法意味着编辑系统中的每个fetch_*,我只想编辑连接文件。

谢谢!

---编辑---

我认为准备好的陈述不是我想要的。

---编辑2---

不,我不是在寻找如何获取 mysqli 错误。

感谢您的帮助!

---回答最终代码---

class mysqli2
    private $c;
    function __construct(...$args) // open connection
        $this->c=new mysqli(...$args);
        if(!empty($this->c->error)) // check for errors
            echo("<script>console.error(\"MySQL: ".$this->c->error."\");</script>");
        
    
    function query($query, $resultmode = MYSQLI_STORE_RESULT)
        $r=$this->c->query($query, $resultmode); // make query
        if(!empty($this->c->error)) // check for errors
            echo("<script>console.error(\"MySQL: ".$this->c->error."\");</script>");
        
        return $r; // returns query results
    
    function __call($method, $args) // calls others mysqli methods
        return $this->c->$method(...$args);
    
    function __get($name) // get all the properties
        return $this->c->$name;
    
    function __set($name, $value) // set all the properties
        if (property_exists($this->c, $name))$this->c->$name = $value;
    

【问题讨论】:

您需要为您的 mysqli 对象创建一个包装器或数据库抽象层 (DBAL),它可以处理 mysqli 所做的所有方法。我建议在编写自己的库之前查看其他预先存在和维护的库,例如 Doctrine 或 Eloquent。 你想要一个 posabbol;ity 来在一个循环内累积一个异常吗? @fyrye 如果你对此有强烈的感觉,你应该使用@Machavity ping Machavity,就像我在这里为你做的那样。他们会收到 ping,如果他们同意,他们会重新打开。 感谢@FunkFortyNiner - @Machavity 这个问题与标记为重复的任何一个问题都无关。 OP 正在询问如何覆盖 mysqli::query 的默认功能,以便在不显式处理异常的情况下提前强制抛出异常。认为代码块内的 ping 不起作用。 【参考方案1】:

为提出最佳实践建议,在使用 PDO 或 MySQLi 扩展时,建议始终检查任何可用方法的返回结果,然后再转向依赖其结果的方法。

由于mysqli::query 在错误时返回false,所以在使用fetch_assoc 之前应该检查它,而不是假设它不是false。这同样适用于使用fetch_assoc,因为它可以返回NULL

if (!$q = $my_var->query($sql)) 
    //something went wrong - handle the error here.

if ($data = $q->fetch_assoc()) 
   echo $data['column'];


不过,正如我所建议的,您需要创建一个抽象层。

由于错误时$q 将是false,因此您问题中代码的异常是:

Fatal error: Call to a member function fetch_assoc() on boolean

意味着您将无法覆盖 fetch_* 方法,除非覆盖 mysqli::query 以始终返回对象。

请不要在生产代码中使用。

这只是一个示例,说明如何用您自己的方法覆盖 mysqli::query 方法。您还需要覆盖所有其他 mysqli::*** methods,例如 preparecommit 等。

示例https://3v4l.org/PlZEs

class Conn


    private $conn;

    public function __construct(...$args) 
    
        $this->conn = new mysqli(...$args);
    

    public function query($query, $resultmode = \MYSQLI_STORE_RESULT) 
    
       $d = $this->conn->query($query, $resultmode);
       if (!empty($this->conn->error)) 
           throw new \RuntimeException($this->conn->error);
       

       return $d;
    


//Your connection code
$con = 'my_var';
$$con = new Conn('host', 'user', 'pass', 'base');

//example usage of valid query
$q = $my_var->query('Valid Query');
$q->fetch_assoc();

//example use of invalid query throwing an exception before `fetch_assoc`
$q = $my_var->query('This is not valid');
$q->fetch_assoc();

结果

I was successful
-----

Fatal error: Uncaught exception 'RuntimeException' with message 'Expected "Valid Query"' in /in/PlZEs:51
Stack trace:
#0 /in/PlZEs(70): Conn->query('This is not val...')
#1 main
  thrown in /in/PlZEs on line 51

这种方法使用PHP 5.6 argument packing and unpacking,来匹配函数调用参数,避免手动定义它们。

可以对此进行扩展,以将消息写入日志文件、发送电子邮件、触发事件或显示友好的错误消息,而不是引发异常。以及用您自己的 Statement 对象覆盖 mysqli_stmt 响应。

【讨论】:

经过测试并完美运行,我对 mysqli 对象中的每个变量和方法进行了迭代,并使用func_get_args 来获取所有参数,作为一个魅力,构建一个抽象层作为辅助类是一件好事想法 @stramin 如果您使用 PHP 5.6 或更高版本,则使用 splat 运算符,...$args 作为方法参数并将其传递给覆盖的 $this-&gt;conn-&gt;method(...$args),就像我在示例中所做的那样,__construct 应该是比使用func_get_args() 的开销更少。或者,在 PHP 5.5 - 5.3 中,您可以使用 $r = call_user_func_array(array($this-&gt;conn, 'method'), func_get_args()); 要在对象的 new 实例上使用 func_get_args(),您可以使用 ReflectionClass::newInstanceArgs,例如 3v4l.org/5kgkN(兼容 PHP 5.3+) @stramin 如果您不想手动定义每个mysqli_query::method 来覆盖,您可以改用__call magic method。像这样3v4l.org/6U6dt @stramin 是的,您可以更新您的问题以显示您最终从我的回答中采用的内容,但请忽略任何可能使您的应用程序暴露于安全漏洞的敏感信息。这也将有助于重复问题标志。 @stramin 您可以通过替换 call_user_func_array(array($this-&gt;c, $method), $args); 来进一步提高性能 通过使用 $this-&gt;c-&gt;$method(...$args); 此外,您可以使用 __get__set 而不是定义抽象层 (AL) 属性,例如: 3v4l.org/AeZAT 检索或设置所需的值(如果它们在您的 AL 中不存在)。

以上是关于为每个方法调用处理声明文件上的 PHP 对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

php面向对象编程之构造方法__construct()

如何避免在 PHP 7 中的每个文件上重新声明刻度

PHP面向对象(抽象类与抽象方法接口的实现)

final关键字, 自动加载类

PHP验证码类

PHP 面向对象程序设计(oop)学习笔记 - 抽象类对象接口instanceof 和契约式编程