为每个方法调用处理声明文件上的 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 Exception
和or 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,例如 prepare
、commit
等。
示例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->conn->method(...$args)
,就像我在示例中所做的那样,__construct
应该是比使用func_get_args()
的开销更少。或者,在 PHP 5.5 - 5.3 中,您可以使用 $r = call_user_func_array(array($this->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->c, $method), $args);
来进一步提高性能 通过使用 $this->c->$method(...$args);
此外,您可以使用 __get
和 __set
而不是定义抽象层 (AL) 属性,例如: 3v4l.org/AeZAT 检索或设置所需的值(如果它们在您的 AL 中不存在)。以上是关于为每个方法调用处理声明文件上的 PHP 对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章