How do I throw my own exceptions with PDO?

8

When executing the line below, in the constructor of my database access class:

$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

The PDO will report errors by throwing exceptions of type PDOException . Is there any configuration so I can get it to cast my DatabaseException ?

I did not want to have to do try catch() with a throw new DatabaseException() in everything that is method that has in my class of access to the bank, that already it extends another class with the concrete methods, that is of a framework. p>

EDIT: My Database class, whose methods can launch to DatabaseException , extends DAO catfan / medoo which is loaded via Composer . Therefore, I should not touch the structure of the original class, since this will always be updated. So I did not want to create a strong dependency as a proxy.

    
asked by anonymous 24.01.2014 / 04:13

1 answer

3

I did some research and looked at documentation official . It does not seem possible to replace the PDOException exception with a custom exception in some simple configuration.

However, I thought of some ways to get around the problem. Maybe some suits your case.

Capturing the exception globally

A simplistic and limited solution, if the exception can be handled globally, would be to use the set_exception_handler() to catch exceptions not handled by try/catch blocks.

The example below captures the exceptions and checks to see if it is of type PDOException . If it is, it displays a message and allows the program to continue running. Otherwise it throws the exception.

function pdoExceptionHandler($e) {
    if ($e instanceof PDOException) {
        echo 'Erro PDO Capturado!';
    } else {
        throw $e;
    }
}
set_exception_handler("pdoExceptionHandler");

See the working example on codepad .

This solution is limited because the handler function is only executed if the exception is not caught anywhere by a catch .

Encapsulate the PDO

Another approach would be to not use PDO classes directly, but to create wrappers to abstract their functionality.

Encapsulation with inheritance

The first approach is to create classes that inherit from the original PDO and override the required methods, adding try/catch and relaunching the custom exception. So you do the treatment once and reuse all the bank accesses you need.

In this SOEN question , I found an example similar to this, this is, a class abstracts the use of PDO. Note that the questioner states that there is a problem closing the connection with this class. Unfortunately I lack an environment to test and validate how it works. If you want to use it as a basis to develop yours, follow the code:

class Database extends PDO {
    private $driver = "mysql";
    private $host = "localhost";
    private $dbname = "dbname";
    private $user = "user";
    private $pass = "pass";
    private $connect = false;
    private $error = "";
    private $stmt = "";

    public function __construct() {
        $options = array(
            PDO::ATTR_PERSISTENT => true, 
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        );

        try {
            parent::__construct($this->driver.":host=".$this->host.";dbname=".$this->dbname, $this->user, $this->pass, $options);
        } catch (PDOException $e) {
            $this->error = $e->getMessage();
        }
        $this->connect = true;
    }

    public function run($statement, $bind = array()) {
        try {
            $this->stmt = $this->prepare($statement);
            $this->stmt->execute($bind);
        } catch (Exception $e) {
            throw $e;
        }

    }

    public function fetchAssoc() {
        return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    public function rowCount() {
        return $this->stmt->rowCount();
    }

    public function getErrorMessage() {
        return $this->error;
    }

    public function isOpen() {
        return $this->connect;
    }

    public function close() {
        //$this->connect = false;
    }

    public function __destruct() {
        $this->connect = false;
    }
}

Encapsulation with a proxy

Another approach would be to create a class that would function as a proxy for the true PDO. It would have an attribute that references the PDO and methods with the same signatures as the PDO, which delegates execution to the PDO, but handle the exception properly.

One technique that would help in this last approach to not need to create all methods manually would be to use the triggers __call and __callStatic (see documentation ). With them you can delegate normal and static calls to methods without having to create each method dynamically and doing the processing in one point.

When a class has a __call($name, $arguments) method, for example, and you call any method in that class, even though the method is not declared , PHP will execute __call passing the name of the method called ( $name ) and the parameters in an array ( $arguments ). It's a very cool PHP feature!

I did a basic implementation

class MyPDO {
    private $pdo = null;
    function __construct($url, $user, $pw) {
        $this->pdo = new PDO($url, $user, $pw);
    }
    public function __call($name, $arguments) {
        try {
            call_user_func_array(array($this->pdo, $name), $arguments);
        } catch (PDOException $ex) {
            throw new DataBaseException('database error');
        }
    }
}

See a functional example here .

Encapsulating with a library

I found a project called php-pdo-wrapper-class that aims to make things easier the use of PDO. In addition to bringing in some useful methods, it has a method called setErrorCallbackFunction() that might resolve your problem without you having to create your own solution.

    
24.01.2014 / 12:03