Configurer Doctrine DBAL avec Oracle

Il y a quelques jours j’ai eu à configurer Doctrine DBAL avec un serveur Oracle. Je n’ai que très peu de connaissances en configuration d’Oracle, mais la documentation de Doctrine semblait claire.

Voici les informations dont je disposais : un utilisateur, un mot de passe et un hôte de la forme ip:port/service.domaine (merci à ce sujet de Stackoverflow d’ailleurs).

Les informations à compléter pour connecter Doctrine à Oracle sont les suivantes : utilisateur, mot de passe, hôte, port, nom de la base de données, charset. Après un peu de lecture supplémentaire pour savoir ce qu’est un service, un SID et en quoi ils diffèrent du nom de la base de données, il semblerait que je dispose de toutes les informations nécessaires pour connecter Doctrine DBAL à Oracle.

Ma première tentative fut de découper la chaîne de caractères hôte afin d’avoir le port et le nom de la base de données à renseigner pour Doctrine. Première tentative infructueuse puisque Doctrine n’arrive pas à se connecter au serveur et lance l’exception suivante :

ORA-12505: TNS:listener does not currently know of SID given in connect descriptor

En y regardant de plus près l’exception est levée dans le constructeur de la classe Doctrine\DBAL\Driver\OCI8\OCI8Connection lors de l’appel à oci_connect ou oci_pconnect.

Un petit tour sur la documentation de oci_connect nous indique que la fonction prend 5 paramètres : username, password, connection_string, character_set et session_mode. N’ayant pas de doutes sur les deux premiers paramètres, c’est le connection_string qui nous intéresse.
Le dump de la variable $db servant de connection_string donne la chaîne de caractères suivante :

(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1554)))(CONNECT_DATA=(SID=dbname.world)))

Partant d’un code existant utilisant directement la fonction oci_connect avec un troisième paramètre formaté comme ceci : ip:port/service.domaine je fus surpris de voir une telle chaîne de caractères à la place. Pourtant, il semblerait que cette chaîne soit bien formée.

Après un peu de reverse engineering supplémentaire, on découvre que c’est la classe Doctrine\DBAL\Driver\OCI8\Driver qui instancie la classe Doctrine\DBAL\Driver\OCI8\OCI8Connection :

public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
    return new OCI8Connection(
        $username,
        $password,
        $this->_constructDsn($params),
        isset($params['charset']) ? $params['charset'] : null,
        isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT,
        isset($params['persistent']) ? $params['persistent'] : false
    );
}

Ce qui nous intéresse ici est la méthode _constructDsn de cette première classe qui renvoie la connection_string :

protected function _constructDsn(array $params)
{
    $dsn = '';
    if (isset($params['host']) && $params['host'] != '') {
        $dsn .= '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' .
               '(HOST=' . $params['host'] . ')';

        if (isset($params['port'])) {
            $dsn .= '(PORT=' . $params['port'] . ')';
        } else {
            $dsn .= '(PORT=1521)';
        }

        if (isset($params['service']) && $params['service'] == true) {
            $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . '))';
        } else {
            $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . '))';
        }
        if (isset($params['pooled']) && $params['pooled'] == true) {
            $dsn .= '(SERVER=POOLED)';
        }
        $dsn .= ')';
    } else {
        $dsn .= $params['dbname'];
    }

    return $dsn;
}

Si le paramètre « host » est défini la connection_string sera de la même forme que celle générée un peu plus haut. Cependant, si le paramètre « host » n’est pas défini la connection_string sera directement égale au paramètre « dbname », plus précisément une chaîne EZ Connect de la forme ip:port/service.domaine.
Pour rappel, en PHP, une variable n’est pas définie quand elle n’existe pas ou que sa valeur est à « null ».

Seconde tentative donc, cette fois-ci avec la paramètre « host » défini à « null » et la chaine EZ Connect affectée au paramètre « dbname » :

$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
    'db.options' => array(
        'driver'   => 'oci8',
        'host' => null,
        'dbname' => HOST,
        'user' => LOGIN,
        'password' => PWD,
        'charset' => 'UTF8',
    ),
));

Et Doctrine DBAL arrive à correctement se connecter à la base Oracle.

Commentaires

Ajouter un commentaire