4

With PDO we have \PDO::ATTR_ERRMODE, and with setting it to \PDO::ERRMODE_EXCEPTION we are able to get detailed exception stack trace (getTrace).

How it's with DBAL? How to set in DBAL level of how it throw errors?

DBAL is a wrapper around the PDO instance, with DBAL most methods of PDO work the same, is there also ATTR_ERRMODE or some other similar reporting control method?

I want to use set_error_handler to better control how errors behave and with ERRMODE_EXCEPTION level it was very useful.

In 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);
}
?>

The same in 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 version: 2.5.1

BlueMark
  • 962
  • 4
  • 23
  • 41

1 Answers1

5

How DBAL throws errors

I checked and it looks, that DBAL for PDO related drivers, by default sets PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION (here and here), so we have detailed stack trace like in PDO.

In fact DBAL exception, is even little more detailed than PDO - DBAL adds some info to the description of the error (for example here).

Options to change level of errors

It seems, that DBAL lacks feature to change level of how it throw errors.
There is no way to change PDO::ATTR_ERRMODE to PDO::ERRMODE_SILENT or PDO::ERRMODE_WARNING.
In theory, it should be available by driverOptions option, something like this:

$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
    )
);

It would work, but soon after connection declaration, DBAL overwrites PDO::ATTR_ERRMODE (here).
Within PDO there is also setAttribute method, with it we would be able to set PDO::ATTR_ERRMODE, but unfortunately DBAL do not implement setAttribute method.

How to control errors in DBAL

In DBAL we already have the best level of errors, we can catch everything and to better control we can use 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");
BlueMark
  • 962
  • 4
  • 23
  • 41