Rebonjour,

J'utilise comme base de donnée postgresql pour Dolibarr et je rencontre 
beaucoup de soucis au niveau des types de données comme par exemple les entiers 
mis entre quote et/ou vide.
Je viens de passer à la 3.3.1 et je me suis retrouvé avec plein de fonction qui 
ne marchent plus.

C'est souvent le même genre de problème, les variables ne sont pas vérifiées ou 
mal avant d'être intégrées dans une requête.
Exemple dans l'insertion d'une propale :  $sql.= " 
".(isset($this->fk_fournprice)?"'".$this->fk_fournprice."'":"null").",";
La variable fk_fournprice est dans les variables de l'objet, le test isset sera 
donc toujours vrai. Un test empty() serait plus approprié.
(en fait ce code n'est pas sur github, voir mon précédent mail sur la mailing 
list mais il y en a un peu partout des tests comme celui-là).

Il n'a jamais été envisagé d'utiliser une fonction pour la construction des 
requêtes ? Cela fournit de nombreux avantages :

-          Augmentation de la sécurité, les variables sont toujours vérifiés 
vis-à-vis de leur type

-          Le code est beaucoup plus lisible

-          Le code est réduit : on ne refait pas continuellement la même chose 
(price2num, MAIN_DB_PREFIX , escape ... )

-          La construction des requêtes est transparente pour l'utilisateur 
vis-à-vis du gestionnaire de base de données
Les avantages sont encore nombreux et les désavantage quasiment inexistant (en 
tout cas je n'en vois pas)

Une solution à la drupal 6 est plutôt élégante : 
http://api.drupal.org/api/drupal/includes!database.pgsql.inc/function/db_query/6
  et 
http://api.drupal.org/api/drupal/includes!database.inc/function/_db_query_callback/6

Ce qui donnerai par exemple pour dolibarr (pour le fichier pgsql.class.php), 
deux petites fonctions supplémentaires  (code fait rapidement sans 
vérification, c'est juste pour présenter la solution) :

/**
* Fonction pour la construction d'une requête
*             Les tables sont encadrées par {}, le préfix est ajouté 
automatiquement
*             Les variables sont définies à partir de leur type
*                             %d Entier
*                             %s Chaine de caractère
*                             %n Réel
*                             %p Prix
*                             %b Boolean
*                             %% Signe %
*
*             @param string $query Chaîne de la requête à construire
*             @param mixed $args Variables à insérer dans la requête
*             @return string La requite complete
*/
function makeQuery($query, $args = array())
{
                               //récupération des variables
                               $this->query_args = is_array($args) ? $args : 
array($args);

                               //on ajoute le préfix au nom des tables
                               $query = preg_replace('`\{([a-z_]+)\}`', 
MAIN_DB_PREFIX .'\1', $query);

                               return 
preg_replace_callback('/(%d|%s|%n|%p|%b|%%)/', array(&$this, 
'makeQueryCallback'), $query);
}


/**
* Callback pour remplacer les arguments de la requetes.
* Permet d'eviter des injections sql et des erreurs dués au type de données
*  @url http://api.drupal.org/api/function/_db_query_callback/6
*  @url 
http://api.drupal.org/api/drupal/includes!database.inc/function/_db_query_callback/6
*
* @param String $match Pattern de la variable soit sont type
* @return string La variable correctement formatée
*/
function makeQueryCallback($match)
{

                switch ($match[1])
                {

                               case '%d': //entier
                                               $value = 
array_shift($this->query_args);
                                               $value = (int) $value;
                                               return $value;

                               case '%s': //chaîne
                                               return 
"'".$this->escape(array_shift($this->query_args))."'";

                               case '%p': //prix
                                               return 
"'".price2num(array_shift($this->query_args))."'";

                               case '%n':
                                               $value = 
trim(array_shift($this->query_args));
                                               return is_numeric($value) && 
!preg_match('/x/i', $value) ? $value : '0';

                               case '%%':
                                               return '%';

                               case '%b':
                                               $val  = 
array_shift($this->query_args);
                                               // boolean as text
                                               return ( strtolower($val) == 
'true' || $val ?  'TRUE' : 'FALSE' );
    }
}



On aurait du coup une requête actuellement codée comme ceci  (je prends un 
exemple simple):

$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn)';
$sql.= ' VALUES ('.$this->id.')';
$resql = $this->db->query($sql);

Qui deviendrait  :

                $sql = 'INSERT INTO {facture_fourn_det} (fk_facture_fourn) 
VALUES (%d)';
                $sql  = $this->db->makeQuery($sql, $this->id);
                $resql = $this->db->query($sql);


C'est relativement simple, les données sont protégées, la lecture n'est pas 
plus compliquée...
Une fonction comme celle-là ne casse rien et permet de passer au fur et à 
mesure les requêtes sur ce système.

Dans l'attente de vos avis ...
Cordialement,
Sébastien
_______________________________________________
Dolibarr-dev mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/dolibarr-dev

Répondre à