如何 return 在 PDO 包装器中执行的值
How to return the value of execute in PDO wrapper
下面是我的 PDO 包装器。我希望能够使用 run
方法,但是,我希望能够检查执行是否成功,例如:
if($sth->execute())
{
...
}
但是,正如您在包装器中看到的那样,运行 命令仅 returns prepare
语句,实现此目的的最有效方法是什么?
<?php
class Database {
const hostname = 'localhost';
const user = 'root';
const password = '';
const charset = 'utf8';
const database = 'syn_v2';
protected static $instance;
protected $pdo;
protected function __construct()
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => false
);
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=%s', self::hostname, self::database, self::charset);
$this->pdo = new PDO($dsn, self::user, self::password);
}
public static function instance()
{
if(self::$instance === null)
{
self::$instance = new self;
}
return self::$instance;
}
public function __call($method, $args)
{
return call_user_func_array(array($this->pdo, $method), $args);
}
public function run($sql, $args = [])
{
if(!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
?>
因为 PDOStatement::execute
returns true/false 并且您当前的 run
方法是 returning a PDOStatement
成功和 false .我建议检查 prepare
和 execute
是否为 false 和 return PDOStatement
是否成功,否则为 false,就像 PDO::prepare
和 [=29= 的情况一样].
函数示例https://3v4l.org/OnDdn
/**
* @return PDOStatement|false
*/
public function run($sql, $args = [])
{
if (!$args) {
return $this->pdo->query($sql);
}
if ($stmt = $this->pdo->prepare($sql)) {
if ($stmt->execute($args)) {
return $stmt;
}
}
return false; //either prepare or execute failed
}
$db = Database::instance();
var_dump($db->run('SELECT ?', ['foo', 'bar'])); //false
另一种方法是将最后一个执行值存储在 属性 中以供以后检索。
示例https://3v4l.org/UbM1N
class Database
{
protected $lastExecute;
//...
/**
* @return PDOStatement|false
*/
public function run($sql, $args = [])
{
if (!$args) {
return $this->pdo->query($sql);
}
if ($stmt = $this->pdo->prepare($sql)) {
$this->lastExecute = $stmt->execute($args);
}
return $stmt;
}
/**
* @return null|bool
*/
public function getLastExecute()
{
return $this->lastExecute;
}
}
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
var_dump($db->getLastExecute()); //false
解决以下 best-practices 关于使用异常处理确定 PDO::execute
方法何时从 Database::run
方法中具体失败的问题的评论。
请记住 Best-Practices 没有对错之分,“它们只是推荐的代码编写方法。”指的是专业应用程序开发中通常首选的编程方法。始终使用最适合您、您正在开发的环境和您的应用程序要求的方法。
Generally speaking StackOverlow is not an appropriate place to discuss or evaluate an author's application of
best-practices. Those types of discussions or critiques should be reserved for CodeReview. Whosebug is
intended to answer the author's specific
question, or provide a viable alternative method to accomplish what the user is asking for. Not infer the user has asked the wrong question.
要使用例外,您需要启用 PDO::ERRMODE_EXCEPTION
(见下文 Database
class)。
将 try/catch
与 PDO 包装器方法一起使用的问题是 PDO 只会抛出一个异常对象 PDOException
,这无法让您确定调用哪个 PDO 方法特别失败。留给您阅读 PDOException::getMessage()
or PDOException::getTrace()
,以确定原因。
一个简单的方法是检查 PDOException::trace
中导致异常的函数名称。
函数示例(PHP 5.6+):https://3v4l.org/fDnBI
try {
$db = Database::instance();
var_dump($db->run('SELECT ?', ['foo', 'bar'])->fetch());
} catch(\PDOException $e) {
if ('execute' === $e->getTrace()[0]['function']) {
echo 'PDO::execute() failed';
//Handle the execute exception
}
throw $e;
}
Please see the answer on PDO mysql: How to know if insert was
successful by Your
Common Sense for a more generalized approach to PDOException
handling.
上述方法阻止您在 Database::run
方法中仅处理特定的异常类型,要求您在条件之后使用 throw $e;
,当异常是意外的。
为了解决这个问题,另一种方法是创建自定义异常 classes。您可以通过扩展基础 PDOException
class 以符合其他异常处理方法或捕获其中任何方法来做到这一点。
为了捕获任何 run
特定异常,可以使用一个空接口,然后在扩展 PDOException
classes.
上实现该接口
interface DatabaseRunException{}
然后为您要处理的每个特定 PDO 方法创建一个新异常 class,实现 DatabaseRunException
接口。
class PDOPrepareException extends PDOException implements DatabaseRunException{}
class PDOExecuteException extends PDOException implements DatabaseRunException{}
class PDOQueryException extends PDOException implements DatabaseRunException{}
要使用自定义异常来确定哪个 PDO 方法失败,您需要处理 Database::run()
方法中的 PDOException(s)
和自定义异常之一 throw
。
为简洁起见,我删除了某些部分,注释掉了会改变您当前配置的内容,对 PHP 5.6+ 进行了一些 best-practices 和优化更改。
class Database
{
private const OPTIONS = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
// PDO::ATTR_EMULATE_PREPARES => false
];
//...
protected function __construct()
{
$this->pdo = new PDO($dsn, self::user, self::password, self::OPTIONS);
}
public static function instance()
{
if (null === self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
public function __call($method, $args)
{
//always ensure the desired method is callable!
if (is_callable([$this->pdo, $method])) {
//php 5.6+ variadic optimization (aka splat operator)
return $this->pdo->$method(...$args);
//php <= 5.5
//return call_user_func_array(array($this->pdo, $method), $args);
}
throw new \BadMethodCallException(sprintf('Unknown method PDO::%s called!', $method));
}
public function run($sql, $args = [])
{
if (!$args) {
try {
return $this->query($sql);
} catch(\PDOException $e) {
throw new \PDOQueryException($e->getMessage(), (int) $e->getCode(), $e);
}
}
try {
$stmt = $this->prepare($sql);
} catch(\PDOException $e) {
throw new \PDOPrepareException($e->getMessage(), (int) $e->getCode(), $e);
}
try {
$stmt->execute($args);
return $stmt;
} catch(\PDOException $e) {
throw new \PDOExecuteException($e->getMessage(), (int) $e->getCode(), $e);
}
throw new \LogicException('Unknown error occurred');
}
}
函数示例(PHP 5.6+):https://3v4l.org/8QoRF
您现在可以处理任何特定类型的每个异常。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\PDOExecuteException $e) {
echo 'PDO::execute() failed';
//Handle the execute exception
throw $e;
}
在 PHP 7.1+ 中你可以捕获多个异常。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\PDOQueryException $e) {
//Handle the query exception
throw $e;
} catch(\PDOPrepareException $e) {
//Handle the prepare exception
throw $e;
} catch(\PDOExecuteException $e) {
echo 'PDO::execute() failed';
//Handle the execute exception
throw $e;
}
在PHP <= 7.0 中可以使用DatabaseRunException
接口来捕捉和检查Database::run()
引起的具体异常,用instanceof
来判断到底是哪个异常抛出。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\DatabaseRunException $e) {
if ($e instanceof \PDOQueryException) {
//Handle the query exception
} elseif ($e instanceof \PDOPrepareException) {
//Handle the prepare exception
} elseif ($e instanceof \PDOExecuteException) {
echo 'PDO::execute() failed';
//Handle the execute exception
}
throw $e;
}
如您所见,这会增加代码的复杂性,您可以自行决定什么最适合您的应用程序需求。
需要注意的是,在try
段中声明的变量,如果在声明之前发生异常,则不会被声明。
try {
throw new \Exception('FooBar');
$foo = 'foo';
} catch(\Exception $e) {
var_dump(isset($foo)); //false
}
var_dump(isset($foo)); //false
下面是我的 PDO 包装器。我希望能够使用 run
方法,但是,我希望能够检查执行是否成功,例如:
if($sth->execute())
{
...
}
但是,正如您在包装器中看到的那样,运行 命令仅 returns prepare
语句,实现此目的的最有效方法是什么?
<?php
class Database {
const hostname = 'localhost';
const user = 'root';
const password = '';
const charset = 'utf8';
const database = 'syn_v2';
protected static $instance;
protected $pdo;
protected function __construct()
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => false
);
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=%s', self::hostname, self::database, self::charset);
$this->pdo = new PDO($dsn, self::user, self::password);
}
public static function instance()
{
if(self::$instance === null)
{
self::$instance = new self;
}
return self::$instance;
}
public function __call($method, $args)
{
return call_user_func_array(array($this->pdo, $method), $args);
}
public function run($sql, $args = [])
{
if(!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
?>
因为 PDOStatement::execute
returns true/false 并且您当前的 run
方法是 returning a PDOStatement
成功和 false .我建议检查 prepare
和 execute
是否为 false 和 return PDOStatement
是否成功,否则为 false,就像 PDO::prepare
和 [=29= 的情况一样].
函数示例https://3v4l.org/OnDdn
/**
* @return PDOStatement|false
*/
public function run($sql, $args = [])
{
if (!$args) {
return $this->pdo->query($sql);
}
if ($stmt = $this->pdo->prepare($sql)) {
if ($stmt->execute($args)) {
return $stmt;
}
}
return false; //either prepare or execute failed
}
$db = Database::instance();
var_dump($db->run('SELECT ?', ['foo', 'bar'])); //false
另一种方法是将最后一个执行值存储在 属性 中以供以后检索。
示例https://3v4l.org/UbM1N
class Database
{
protected $lastExecute;
//...
/**
* @return PDOStatement|false
*/
public function run($sql, $args = [])
{
if (!$args) {
return $this->pdo->query($sql);
}
if ($stmt = $this->pdo->prepare($sql)) {
$this->lastExecute = $stmt->execute($args);
}
return $stmt;
}
/**
* @return null|bool
*/
public function getLastExecute()
{
return $this->lastExecute;
}
}
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
var_dump($db->getLastExecute()); //false
解决以下 best-practices 关于使用异常处理确定 PDO::execute
方法何时从 Database::run
方法中具体失败的问题的评论。
请记住 Best-Practices 没有对错之分,“它们只是推荐的代码编写方法。”指的是专业应用程序开发中通常首选的编程方法。始终使用最适合您、您正在开发的环境和您的应用程序要求的方法。
Generally speaking StackOverlow is not an appropriate place to discuss or evaluate an author's application of best-practices. Those types of discussions or critiques should be reserved for CodeReview. Whosebug is intended to answer the author's specific question, or provide a viable alternative method to accomplish what the user is asking for. Not infer the user has asked the wrong question.
要使用例外,您需要启用 PDO::ERRMODE_EXCEPTION
(见下文 Database
class)。
将 try/catch
与 PDO 包装器方法一起使用的问题是 PDO 只会抛出一个异常对象 PDOException
,这无法让您确定调用哪个 PDO 方法特别失败。留给您阅读 PDOException::getMessage()
or PDOException::getTrace()
,以确定原因。
一个简单的方法是检查 PDOException::trace
中导致异常的函数名称。
函数示例(PHP 5.6+):https://3v4l.org/fDnBI
try {
$db = Database::instance();
var_dump($db->run('SELECT ?', ['foo', 'bar'])->fetch());
} catch(\PDOException $e) {
if ('execute' === $e->getTrace()[0]['function']) {
echo 'PDO::execute() failed';
//Handle the execute exception
}
throw $e;
}
Please see the answer on PDO mysql: How to know if insert was successful by Your Common Sense for a more generalized approach to
PDOException
handling.
上述方法阻止您在 Database::run
方法中仅处理特定的异常类型,要求您在条件之后使用 throw $e;
,当异常是意外的。
为了解决这个问题,另一种方法是创建自定义异常 classes。您可以通过扩展基础 PDOException
class 以符合其他异常处理方法或捕获其中任何方法来做到这一点。
为了捕获任何 run
特定异常,可以使用一个空接口,然后在扩展 PDOException
classes.
interface DatabaseRunException{}
然后为您要处理的每个特定 PDO 方法创建一个新异常 class,实现 DatabaseRunException
接口。
class PDOPrepareException extends PDOException implements DatabaseRunException{}
class PDOExecuteException extends PDOException implements DatabaseRunException{}
class PDOQueryException extends PDOException implements DatabaseRunException{}
要使用自定义异常来确定哪个 PDO 方法失败,您需要处理 Database::run()
方法中的 PDOException(s)
和自定义异常之一 throw
。
为简洁起见,我删除了某些部分,注释掉了会改变您当前配置的内容,对 PHP 5.6+ 进行了一些 best-practices 和优化更改。
class Database
{
private const OPTIONS = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
// PDO::ATTR_EMULATE_PREPARES => false
];
//...
protected function __construct()
{
$this->pdo = new PDO($dsn, self::user, self::password, self::OPTIONS);
}
public static function instance()
{
if (null === self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
public function __call($method, $args)
{
//always ensure the desired method is callable!
if (is_callable([$this->pdo, $method])) {
//php 5.6+ variadic optimization (aka splat operator)
return $this->pdo->$method(...$args);
//php <= 5.5
//return call_user_func_array(array($this->pdo, $method), $args);
}
throw new \BadMethodCallException(sprintf('Unknown method PDO::%s called!', $method));
}
public function run($sql, $args = [])
{
if (!$args) {
try {
return $this->query($sql);
} catch(\PDOException $e) {
throw new \PDOQueryException($e->getMessage(), (int) $e->getCode(), $e);
}
}
try {
$stmt = $this->prepare($sql);
} catch(\PDOException $e) {
throw new \PDOPrepareException($e->getMessage(), (int) $e->getCode(), $e);
}
try {
$stmt->execute($args);
return $stmt;
} catch(\PDOException $e) {
throw new \PDOExecuteException($e->getMessage(), (int) $e->getCode(), $e);
}
throw new \LogicException('Unknown error occurred');
}
}
函数示例(PHP 5.6+):https://3v4l.org/8QoRF
您现在可以处理任何特定类型的每个异常。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\PDOExecuteException $e) {
echo 'PDO::execute() failed';
//Handle the execute exception
throw $e;
}
在 PHP 7.1+ 中你可以捕获多个异常。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\PDOQueryException $e) {
//Handle the query exception
throw $e;
} catch(\PDOPrepareException $e) {
//Handle the prepare exception
throw $e;
} catch(\PDOExecuteException $e) {
echo 'PDO::execute() failed';
//Handle the execute exception
throw $e;
}
在PHP <= 7.0 中可以使用DatabaseRunException
接口来捕捉和检查Database::run()
引起的具体异常,用instanceof
来判断到底是哪个异常抛出。
try {
$db = Database::instance();
$db->run('SELECT ?', ['foo', 'bar']);
} catch(\DatabaseRunException $e) {
if ($e instanceof \PDOQueryException) {
//Handle the query exception
} elseif ($e instanceof \PDOPrepareException) {
//Handle the prepare exception
} elseif ($e instanceof \PDOExecuteException) {
echo 'PDO::execute() failed';
//Handle the execute exception
}
throw $e;
}
如您所见,这会增加代码的复杂性,您可以自行决定什么最适合您的应用程序需求。
需要注意的是,在try
段中声明的变量,如果在声明之前发生异常,则不会被声明。
try {
throw new \Exception('FooBar');
$foo = 'foo';
} catch(\Exception $e) {
var_dump(isset($foo)); //false
}
var_dump(isset($foo)); //false