Doctrine DBAL 中的错误处理与 PHP PDO 的比较
Error handling in Doctrine DBAL with comparison to PHP PDO
有了 PDO,我们有 \PDO::ATTR_ERRMODE
, and with setting it to \PDO::ERRMODE_EXCEPTION
we are able to get detailed exception stack trace (getTrace
)。
DBAL 怎么样?如何在 DBAL 级别设置它如何抛出错误?
DBAL 是 PDO 实例的包装器,与 DBAL 的大多数 PDO 方法工作相同,是否还有 ATTR_ERRMODE
或其他类似的报告控制方法?
我想使用 set_error_handler
来更好地控制错误的行为方式,ERRMODE_EXCEPTION
级别非常有用。
在 PDO 中:
<?php
$dsn = 'mysql:host='.$_SESSION['options']['host'].';port='.$_SESSION['options']['port'].';dbname='.$_SESSION['options']['dbname'].';charset='.$_SESSION['options']['charset'];
try {
$conn = new \PDO($dsn, $_SESSION['options']['user'], $_SESSION['options']['pass']);
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); // how to set this line in DBAL?
} catch (\PDOException $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
DBAL 中相同:
<?php
require_once "lib/autoload.php";
$config = new \Doctrine\DBAL\Configuration();
$params = array(
'dbname' => $_SESSION['options']['dbname'],
'user' => $_SESSION['options']['user'],
'password' => $_SESSION['options']['pass'],
'host' => $_SESSION['options']['host'],
'port' => $_SESSION['options']['port'],
'driver' => 'pdo_mysql',
'charset' => $_SESSION['options']['charset'],
);
try {
$conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config);
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
Doctrine DBAL 版本:2.5.1
DBAL 如何抛出错误
我检查了一下,看起来 PDO 相关驱动程序的 DBAL 默认将 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
(here and here),所以我们有详细的堆栈跟踪,就像在 PDO 中一样。
事实上 DBAL 异常,甚至比 PDO 更详细 - DBAL 在错误描述中添加了一些信息(例如 here)。
更改错误级别的选项
DBAL 似乎缺少更改其抛出错误级别的功能。
无法将 PDO::ATTR_ERRMODE
更改为 PDO::ERRMODE_SILENT
或 PDO::ERRMODE_WARNING
.
理论上,它应该在 driverOptions option 之前可用,像这样:
$params = array(
'dbname' => $options['dbname'],
'user' => $options['user'],
'password' => $options['pass'],
'host' => $options['host'],
'port' => $options['port'],
'driver' => 'pdo_mysql',
'charset' => $options['charset'],
'driverOptions' => array(
3 => 0 //here "3" is value of constant ATTR_ERRMODE, and "0" value of constant ERRMODE_SILENT
)
);
它会起作用,但在连接声明后不久,DBAL 会覆盖 PDO::ATTR_ERRMODE
(here)。
在 PDO 中也有 setAttribute
方法,有了它我们可以设置 PDO::ATTR_ERRMODE
,但不幸的是 DBAL 没有实现 setAttribute
方法。
如何控制DBAL中的错误
在 DBAL 中,我们已经有了最好的错误级别,我们可以捕获所有错误,为了更好地控制我们可以使用 set_error_handler
:
<?php
/**
* Error handler
*
* @param int $err_code the numeric error code
* @param str $err_str description of the error
* @param str $err_file the name of the file that contains the error
* @param int $err_line the line number of the error
* @param str $err_context details of the error
*/
function errorHandler($err_code, $err_str, $err_file, $err_line, $err_context) {
global $_SESSION;
$_SESSION['errorCounter']++;
$error_types = array (
E_WARNING => 'WARNING',
E_NOTICE => 'NOTICE',
E_USER_ERROR => 'USER ERROR',
E_USER_WARNING => 'USER WARNING',
E_USER_NOTICE => 'USER NOTICE',
E_STRICT => 'STRICT',
E_DEPRECATED => 'DEPRECATED',
E_USER_DEPRECATED => 'USER DEPRECATED'
);
$error_type = isset($error_types[$err_code]) ? $error_types[$err_code] : 'UNKNOWN ERROR TYPE['.$err_code.']';
$err = "\n>--------------------------------------------------------------------";
$err .= "\n".'EXCEPTION #'.$_SESSION['errorCounter'].':';
$err .= "\n\n---- PHP ".$error_type.": ----";
$err .= "\n".$err_str;
//$err .= print_r($err_context, 1);
if (isset($err_context['e'])){
$err .= "\n\n---- STACKTRACE: ----";
$dir_root = $_SERVER["DOCUMENT_ROOT"];
$err .= "\nroot: " . $dir_root;
foreach ($err_context['e']->getTrace() as $a => $b) {
$err .= "\n" . strval($a) . '# ';
foreach ($b as $c => $d) {
switch($c){
case 'file':
$err .= str_replace($dir_root,'(root)',str_replace('\','/',$d));
break;
case 'line':
$err .= '(' . $d . "); ";
break;
case 'function':
$err .= $c . ' ' . $d . "; ";
break;
case 'class':
$err .= $c . ' ' . $d . "; ";
break;
case 'type':
$err .= $c . ': ' . $d . "; ";
break;
case 'args':
foreach ($d as $e => $f){
$e = is_object($e) ? 'object' : is_array($e) ? 'array' : trim(preg_replace('/\s+/', ' ', $e));//do not return objects, arrays and change new lines to space
$f = is_object($f) ? 'object' : is_array($f) ? 'array' : trim(preg_replace('/\s+/', ' ', $f));//do not return objects, arrays and change new lines to space
$err .= 'args-' . $e . ': ' . $f . '; ';
}
break;
}
}
}
}
else {
$err .= "\n\n---- LOCATION: ----";
$err .= "\n".$err_file.'(' . $err_line . ')';
}
$err .= "\n\n---- CONNECTION: ----";
$err .= "\n".'host: '.$_SERVER['SERVER_NAME'];
$err .= "\n".'client: '.$_SERVER['REMOTE_ADDR'];
$err .= "\n".'timestamp: '.date("d-m-Y h:i:s A");
$err .= "\n<--------------------------------------------------------------------\n";
if (ini_get('log_errors')) {
error_log($err, 0);
if($err_code == E_USER_ERROR) {
if (!$_SESSION['options']['debug']) {
//send email with error
}
}
}
if ($_SESSION['options']['debug'])
print("\n<pre>".$err."</pre>\n");
return true;
}
set_error_handler("errorHandler");
有了 PDO,我们有 \PDO::ATTR_ERRMODE
, and with setting it to \PDO::ERRMODE_EXCEPTION
we are able to get detailed exception stack trace (getTrace
)。
DBAL 怎么样?如何在 DBAL 级别设置它如何抛出错误?
DBAL 是 PDO 实例的包装器,与 DBAL 的大多数 PDO 方法工作相同,是否还有 ATTR_ERRMODE
或其他类似的报告控制方法?
我想使用 set_error_handler
来更好地控制错误的行为方式,ERRMODE_EXCEPTION
级别非常有用。
在 PDO 中:
<?php
$dsn = 'mysql:host='.$_SESSION['options']['host'].';port='.$_SESSION['options']['port'].';dbname='.$_SESSION['options']['dbname'].';charset='.$_SESSION['options']['charset'];
try {
$conn = new \PDO($dsn, $_SESSION['options']['user'], $_SESSION['options']['pass']);
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); // how to set this line in DBAL?
} catch (\PDOException $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
DBAL 中相同:
<?php
require_once "lib/autoload.php";
$config = new \Doctrine\DBAL\Configuration();
$params = array(
'dbname' => $_SESSION['options']['dbname'],
'user' => $_SESSION['options']['user'],
'password' => $_SESSION['options']['pass'],
'host' => $_SESSION['options']['host'],
'port' => $_SESSION['options']['port'],
'driver' => 'pdo_mysql',
'charset' => $_SESSION['options']['charset'],
);
try {
$conn = \Doctrine\DBAL\DriverManager::getConnection($params, $config);
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
?>
Doctrine DBAL 版本:2.5.1
DBAL 如何抛出错误
我检查了一下,看起来 PDO 相关驱动程序的 DBAL 默认将 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
(here and here),所以我们有详细的堆栈跟踪,就像在 PDO 中一样。
事实上 DBAL 异常,甚至比 PDO 更详细 - DBAL 在错误描述中添加了一些信息(例如 here)。
更改错误级别的选项
DBAL 似乎缺少更改其抛出错误级别的功能。
无法将 PDO::ATTR_ERRMODE
更改为 PDO::ERRMODE_SILENT
或 PDO::ERRMODE_WARNING
.
理论上,它应该在 driverOptions option 之前可用,像这样:
$params = array(
'dbname' => $options['dbname'],
'user' => $options['user'],
'password' => $options['pass'],
'host' => $options['host'],
'port' => $options['port'],
'driver' => 'pdo_mysql',
'charset' => $options['charset'],
'driverOptions' => array(
3 => 0 //here "3" is value of constant ATTR_ERRMODE, and "0" value of constant ERRMODE_SILENT
)
);
它会起作用,但在连接声明后不久,DBAL 会覆盖 PDO::ATTR_ERRMODE
(here)。
在 PDO 中也有 setAttribute
方法,有了它我们可以设置 PDO::ATTR_ERRMODE
,但不幸的是 DBAL 没有实现 setAttribute
方法。
如何控制DBAL中的错误
在 DBAL 中,我们已经有了最好的错误级别,我们可以捕获所有错误,为了更好地控制我们可以使用 set_error_handler
:
<?php
/**
* Error handler
*
* @param int $err_code the numeric error code
* @param str $err_str description of the error
* @param str $err_file the name of the file that contains the error
* @param int $err_line the line number of the error
* @param str $err_context details of the error
*/
function errorHandler($err_code, $err_str, $err_file, $err_line, $err_context) {
global $_SESSION;
$_SESSION['errorCounter']++;
$error_types = array (
E_WARNING => 'WARNING',
E_NOTICE => 'NOTICE',
E_USER_ERROR => 'USER ERROR',
E_USER_WARNING => 'USER WARNING',
E_USER_NOTICE => 'USER NOTICE',
E_STRICT => 'STRICT',
E_DEPRECATED => 'DEPRECATED',
E_USER_DEPRECATED => 'USER DEPRECATED'
);
$error_type = isset($error_types[$err_code]) ? $error_types[$err_code] : 'UNKNOWN ERROR TYPE['.$err_code.']';
$err = "\n>--------------------------------------------------------------------";
$err .= "\n".'EXCEPTION #'.$_SESSION['errorCounter'].':';
$err .= "\n\n---- PHP ".$error_type.": ----";
$err .= "\n".$err_str;
//$err .= print_r($err_context, 1);
if (isset($err_context['e'])){
$err .= "\n\n---- STACKTRACE: ----";
$dir_root = $_SERVER["DOCUMENT_ROOT"];
$err .= "\nroot: " . $dir_root;
foreach ($err_context['e']->getTrace() as $a => $b) {
$err .= "\n" . strval($a) . '# ';
foreach ($b as $c => $d) {
switch($c){
case 'file':
$err .= str_replace($dir_root,'(root)',str_replace('\','/',$d));
break;
case 'line':
$err .= '(' . $d . "); ";
break;
case 'function':
$err .= $c . ' ' . $d . "; ";
break;
case 'class':
$err .= $c . ' ' . $d . "; ";
break;
case 'type':
$err .= $c . ': ' . $d . "; ";
break;
case 'args':
foreach ($d as $e => $f){
$e = is_object($e) ? 'object' : is_array($e) ? 'array' : trim(preg_replace('/\s+/', ' ', $e));//do not return objects, arrays and change new lines to space
$f = is_object($f) ? 'object' : is_array($f) ? 'array' : trim(preg_replace('/\s+/', ' ', $f));//do not return objects, arrays and change new lines to space
$err .= 'args-' . $e . ': ' . $f . '; ';
}
break;
}
}
}
}
else {
$err .= "\n\n---- LOCATION: ----";
$err .= "\n".$err_file.'(' . $err_line . ')';
}
$err .= "\n\n---- CONNECTION: ----";
$err .= "\n".'host: '.$_SERVER['SERVER_NAME'];
$err .= "\n".'client: '.$_SERVER['REMOTE_ADDR'];
$err .= "\n".'timestamp: '.date("d-m-Y h:i:s A");
$err .= "\n<--------------------------------------------------------------------\n";
if (ini_get('log_errors')) {
error_log($err, 0);
if($err_code == E_USER_ERROR) {
if (!$_SESSION['options']['debug']) {
//send email with error
}
}
}
if ($_SESSION['options']['debug'])
print("\n<pre>".$err."</pre>\n");
return true;
}
set_error_handler("errorHandler");