Développer pour Oracle en PHP – Formatter la sortie

But du chapitre

On souhaite améliorer notre package de manière à ce qu’on puisse voir une sortie soit en CSV, soit en HTML soit au final au format que l’on souhaite. Notre première approche (qui n’est pas la bonne, mais qui est instinctive), est d’enrichir notre package Oracle. C’est ce que nous allons faire ici.

Méthode

Pour effectuer cette sortie sur mesure, on va paramétrer la fonction d’exécution de requête pour lui indiquer la sortie que l’on souhaite voir.

/**
  * Exécute le code SQL passé en paramètre en utilisant d'éventuelles variables 
  * pour une sortie au format désiré  
  *  
  * @param  string $sqlText
  * @param  array $binds
  * @param  string $format
  * @return mixed 
  */  
  function execSql(string $sqlText, ?array $binds=array(), ?string $format='array') :mixed {
    $out=array();
    $stid = oci_parse($this->conn,$sqlText);

    foreach ($binds as $var => $val) {
      oci_bind_by_name($stid, $var, $val);
    }
    $r=oci_execute($stid);
    if ( !$r ) { die("Impossible d'exécuter $SQL"); }

    while ($cRow = oci_fetch_array( $stid, OCI_ASSOC+OCI_RETURN_NULLS )) {
      $out[]=$cRow;
    }

    if ( $format === 'array' ) {
      return $out ;
    }

    if ( $format === 'json' ) {
      return json_encode($out, TRUE) ;
    }

    if ( $format === 'csv' ) {
      return $this->a2csv($out);
    }

    if ( $format === 'csvNoHead' ) {
      return $this->a2csv($out, FALSE);
    }

    if ( $format === 'html' ) {
      return $this->a2html($out);
    }

    return FALSE;
  }

On écrit ensuite le code privé suivant pour adresser chaque type de sortie, par exemple pour CSV. Le détail des fonctions est dans notre gitlab (Chapitre-0003).

CSV avec ou sans ligne de titre
  private function a2csv( array $a, ?bool $headers = TRUE, string $colSep = ',' ): string {
    $pHead=$headers;
    $out='';

    foreach ($a as $v) {
      if ( $pHead ) {
        $out.=implode($colSep, array_keys($v) ).PHP_EOL;
        $pHead=FALSE;
      }
      $out.=implode($colSep, $v).PHP_EOL;
    }

    return $out;
  }

On adapte ensuite le fichier index.php pour tester les sorties

define('HOME', __DIR__ );
require_once( HOME . '/etc/conf.php') ;
require_once( LIBDIR . '/oracle.php');

$user='myUser';
$password='myPassword';
$tns='myDBAlias';

$ora=new oracle ;
$ora->connect( $user, $password, $tns, OCI_DEFAULT );

$sql='select begin_interval_time, sql_id, plan_hash_value,  fetches_delta, sorts_delta 
  from dba_hist_sqlstat 
       natural join dba_hist_snapshot 
 where rownum <= 5' ;

$out=$ora->execSql( $sql,array(),'csv' );
var_dump($out);

$out=$ora->execSql( $sql,array(),'csvNoHead' );
var_dump($out);

$out=$ora->execSql( $sql,array(),'json' );
var_dump($out);

$out=$ora->execSql( $sql,array(),'array' );
var_dump($out);

$out=$ora->execSql( $sql,array(),'html' );
var_dump($out);

On obtient la sortie attendue suivante :

D:\DevsGit\wp-lessons\lessons\Chapitre-0003\index.php:19:string 'BEGIN_INTERVAL_TIME,SQL_ID,PLAN_HASH_VALUE,FETCHES_DELTA,SORTS_DELTA
2022-05-03T05:00:10,cb21bacyh3c7d,3452538079,17,0
2022-05-03T05:00:10,gb99vcuuwh2ks,1433922598,34,0
2022-05-03T05:00:10,586577qpbkgnk,796962695,3,3
2022-05-03T05:00:10,ampw9ddqufjd3,0,0,0
2022-05-03T05:00:10,5dqz0hqtp9fru,1227530427,1146145,0
' (length=318)
D:\DevsGit\wp-lessons\lessons\Chapitre-0003\index.php:22:string '2022-05-03T05:00:10,cb21bacyh3c7d,3452538079,17,0
2022-05-03T05:00:10,gb99vcuuwh2ks,1433922598,34,0
2022-05-03T05:00:10,586577qpbkgnk,796962695,3,3
2022-05-03T05:00:10,ampw9ddqufjd3,0,0,0
2022-05-03T05:00:10,5dqz0hqtp9fru,1227530427,1146145,0
' (length=248)
D:\DevsGit\wp-lessons\lessons\Chapitre-0003\index.php:25:string '[{"BEGIN_INTERVAL_TIME":"2022-05-03T05:00:10","SQL_ID":"cb21bacyh3c7d","PLAN_HASH_VALUE":"3452538079","FETCHES_DELTA":"17","SORTS_DELTA":"0"},{"BEGIN_INTERVAL_TIME":"2022-05-03T05:00:10","SQL_ID":"gb99vcuuwh2ks","PLAN_HASH_VALUE":"1433922598","FETCHES_DELTA":"34","SORTS_DELTA":"0"},{"BEGIN_INTERVAL_TIME":"2022-05-03T05:00:10","SQL_ID":"586577qpbkgnk","PLAN_HASH_VALUE":"796962695","FETCHES_DELTA":"3","SORTS_DELTA":"3"},{"BEGIN_INTERVAL_TIME":"2022-05-03T05:00:10","SQL_ID":"ampw9ddqufjd3","PLAN_HASH_VALUE":"0'... (length=699)
D:\DevsGit\wp-lessons\lessons\Chapitre-0003\index.php:28:
array (size=5)
  0 => 
    array (size=5)
      'BEGIN_INTERVAL_TIME' => string '2022-05-03T05:00:10' (length=19)
      'SQL_ID' => string 'cb21bacyh3c7d' (length=13)
      'PLAN_HASH_VALUE' => string '3452538079' (length=10)
      'FETCHES_DELTA' => string '17' (length=2)
      'SORTS_DELTA' => string '0' (length=1)
  1 => 
    array (size=5)
      'BEGIN_INTERVAL_TIME' => string '2022-05-03T05:00:10' (length=19)
      'SQL_ID' => string 'gb99vcuuwh2ks' (length=13)
      'PLAN_HASH_VALUE' => string '1433922598' (length=10)
      'FETCHES_DELTA' => string '34' (length=2)
      'SORTS_DELTA' => string '0' (length=1)
  2 => 
    array (size=5)
      'BEGIN_INTERVAL_TIME' => string '2022-05-03T05:00:10' (length=19)
      'SQL_ID' => string '586577qpbkgnk' (length=13)
      'PLAN_HASH_VALUE' => string '796962695' (length=9)
      'FETCHES_DELTA' => string '3' (length=1)
      'SORTS_DELTA' => string '3' (length=1)
  3 => 
    array (size=5)
      'BEGIN_INTERVAL_TIME' => string '2022-05-03T05:00:10' (length=19)
      'SQL_ID' => string 'ampw9ddqufjd3' (length=13)
      'PLAN_HASH_VALUE' => string '0' (length=1)
      'FETCHES_DELTA' => string '0' (length=1)
      'SORTS_DELTA' => string '0' (length=1)
  4 => 
    array (size=5)
      'BEGIN_INTERVAL_TIME' => string '2022-05-03T05:00:10' (length=19)
      'SQL_ID' => string '5dqz0hqtp9fru' (length=13)
      'PLAN_HASH_VALUE' => string '1227530427' (length=10)
      'FETCHES_DELTA' => string '1146145' (length=7)
      'SORTS_DELTA' => string '0' (length=1)
D:\DevsGit\wp-lessons\lessons\Chapitre-0003\index.php:31:string '<table "class="table">
  <thead>
    <tr>
      <th>BEGIN_INTERVAL_TIME</th><th>SQL_ID</th><th>PLAN_HASH_VALUE</th><th>FETCHES_DELTA</th><th>SORTS_DELTA</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2022-05-03T05:00:10</td><td>cb21bacyh3c7d</td><td>3452538079</td><td>17</td><td>0</td>
    </tr>
    <tr>
      <td>2022-05-03T05:00:10</td><td>gb99vcuuwh2ks</td><td>1433922598</td><td>34</td><td>0</td>
    </tr>
    <tr>
      <td>2022-05-03T05:00:10</td><td>586577qpbkgnk</td><td>79696269'... (length=806)

Le code cette première partie de leçon est disponible sur notre gitlab (Chapitre-0003). il doit évidemment être adapté pour pouvoir se connecter à votre base.

Fonctionnellement oui … Mais la manière de répartir le code n’est pas bonne

La sortie est satisfaisante MAIS pas le code. En effet, il convient, si on souhaite qu’un code soit maintenable, de spécialiser au maximum les classes de manipulation de données et de présentation. Idéalement on doit donc créer une classe dédiée à la présentation des données. O va donc créer la classe table2DOut afin de gérer les affichages des tables php.

La fonction execSql du package redevient spécialisée dans le code SQL

/**
  * Exécute le code SQL passé en paramètre en utilisant d'éventuelles variables 
  *  
  * @param  string $sqlText
  * @param  array $binds
  * @return mixed 
  */  
  function execSql(string $sqlText, ?array $binds=array()): mixed {
    $out=array();
    $stid = oci_parse($this->conn,$sqlText);

    foreach ($binds as $var => $val) {
      oci_bind_by_name($stid, $var, $val);
    }
    $r=oci_execute($stid);
    if ( !$r ) { return FALSE ; }

    while ($cRow = oci_fetch_array( $stid, OCI_ASSOC+OCI_RETURN_NULLS )) {
      $out[]=$cRow;
    }

    return $out ;
  }

Et le package table2DOut encapsule une fonction statique capable de générer du CSV à partir d’une table sortie de requête.

/**
  * Transforme une table PHP à deux dimensions en chaine CSV
  *
  * @param  array $a
  * @param  bool $head
  * @param  string $colSep
  * @return string
  */
  static function csv (array $a, ?bool $head=TRUE, string $colSep=','): string {
    $pHead=$head;
    $out='';

    foreach ($a as $v) {
      if ( $pHead ) {
        $out.=implode($colSep, array_keys($v) ).PHP_EOL;
        $pHead=FALSE;
      }
      $out.=implode($colSep, $v).PHP_EOL;
    }

    return $out;
  }

Le code cette seconde partie de leçon est disponible sur notre gitlab (Chapitre-0003b). il doit évidemment être adapté pour pouvoir se connecter à votre base.