Skip to content

Error Handling

Muhammet Şafak edited this page May 24, 2026 · 1 revision

Error Handling

DBAL throws six exception classes. Four sit under \Exception, two under \InvalidArgumentException. All of them live in either the Connection\Exceptions or DataMapper\Exceptions namespace.

Hierarchy

\Exception
├── InitORM\DBAL\Connection\Exceptions\ConnectionException
│   ├── SQLExecuteException
│   └── ValidConnectionAvailableException        [deprecated alias parent]
│       └── ConnectionAlreadyEstablishedException  [preferred name]
│
└── InitORM\DBAL\DataMapper\Exceptions\DataMapperException

\InvalidArgumentException
├── InitORM\DBAL\Connection\Exceptions\ConnectionInvalidArgumentException
└── InitORM\DBAL\DataMapper\Exceptions\DataMapperInvalidArgumentException

When each is thrown

ConnectionException

The base exception for the Connection layer. Raised by connect() when PDO itself rejects the credentials (bad host, wrong password, missing driver extension). The original PDO/Throwable is always preserved as $e->getPrevious().

use InitORM\DBAL\Connection\Exceptions\ConnectionException;

try {
    $db->getPDO();
} catch (ConnectionException $e) {
    error_log($e->getMessage());
    error_log((string) $e->getPrevious()?->getMessage());
}

SQLExecuteException

Thrown by Connection::query() when prepare() returns false or execute() returns false — i.e. when the driver reports failure without raising an exception.

Under the default ATTR_ERRMODE => ERRMODE_EXCEPTION, PDO normally raises a PDOException before this gets a chance to fire. It becomes useful when you intentionally switch to a non-exception error mode:

$db = new Connection([
    'options' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING],
    // ...
]);

try {
    $db->query('SELECT * FROM nope');
} catch (SQLExecuteException $e) {
    // ...
}

ConnectionAlreadyEstablishedException

Raised when a setter (setHost, setDatabase, setCharset, setDriver, setOptions, setDsn, setPort, setUsername, setPassword) is called after the underlying PDO has already been instantiated. To recover, disconnect() first or clone() a fresh instance:

use InitORM\DBAL\Connection\Exceptions\ConnectionAlreadyEstablishedException;

try {
    $db->setDatabase('other');
} catch (ConnectionAlreadyEstablishedException $e) {
    $db->disconnect();
    $db->setDatabase('other');
    $db->getPDO();
}

The historical name ValidConnectionAvailableException is the parent class of ConnectionAlreadyEstablishedException, so 1.x catch blocks keep matching the same instances:

use InitORM\DBAL\Connection\Exceptions\ValidConnectionAvailableException;

try {
    $db->setHost('h');
} catch (ValidConnectionAvailableException $e) {
    // still catches the new instances
}

ConnectionInvalidArgumentException

Raised by setCharset(), setDriver(), and (internally) the SET NAMES step when an identifier contains characters outside [A-Za-z0-9_]. These values cannot be parameterised in SQL, so the strict whitelist is defence-in-depth against accidental injection through configuration:

use InitORM\DBAL\Connection\Exceptions\ConnectionInvalidArgumentException;

try {
    $db->setCharset("utf8'; DROP TABLE users; --");
} catch (ConnectionInvalidArgumentException $e) {
    // ...
}

DataMapperException / DataMapperInvalidArgumentException

Currently reserved for future use — the DataMapper implementation lets PDO exceptions bubble up unwrapped. They are declared so library extensions can throw them without inventing a new hierarchy.

PDOException

Anything PDO itself raises. With ERRMODE_EXCEPTION (the library default), this is the most common exception you will see from Connection::query().

Catching everything

There is no single root class deliberately — ConnectionException and DataMapperException are siblings under \Exception. For a blanket catch, use \Throwable:

try {
    $db->query('SELECT 1');
} catch (\Throwable $e) {
    // any DBAL or PDO failure lands here
}

For a more targeted catch:

try {
    $db->query('SELECT 1');
} catch (\InitORM\DBAL\Connection\Exceptions\ConnectionException $e) {
    // DBAL-thrown
} catch (\PDOException $e) {
    // PDO-thrown
}

Logging vs throwing

Connection::query():

  1. Catches the failure.
  2. Appends the query to the log buffer (with its timing).
  3. Calls Connection::createLog() with the failure message.
  4. Re-throws the original exception.

So you do not need to log inside your own catch block. Configure the log credential once (see Logging) and let DBAL fan-out for you.

What's next

  • Logging for the failure-message sink.
  • Connection for the operations that can throw which exception.

Clone this wiki locally