Exécuter des requete style sql sur un array

Soyez le premier à donner votre avis sur cette source.

Vue 5 017 fois - Téléchargée 251 fois

Description

La classe myArray, pour l'instant en version "j'ai codé comme un cochon" permet l'exécution de requête de type sql sur un tableau (Array).

Elle peut être utile, par exemple, lorsque l'on travaille avec une DB et que des données que l'on y récupère sont stockées dans un tableau.
Ce tableau pourra ainsi être traitée via la classe myArray pour ainsi faciliter sa manipulation

la structure du tableau a traiter doit se présenter de la maniere suivante:

$toto = Array {
[x] => Array(
champ1 => x1,
champ2 => x2,
champ3 => x3
),
[y] => Array(
champ1 => y1,
champ2 => y2,
champ3 => y3
),
[z] => Array(
champ1 => z1,
champ2 => z2,
champ3 => z3
)
}

ainsi les cas d'utilisation:

- myArray::query($toto, "select champ1,champ2 ")

donnera :

Array {
[x] => Array( champ1 => x1, champ1 => x2 ),
[y] => Array( champ1 => y1, champ1 => y2 ),
[z] => Array( champ1 => z1, champ1 => z2 )
}

- myArray::query($toto, "select champ1,champ2 group by champ1")

donnera :

Array {
[x1] => Array(
[x] => Array( champ1 => x1, champ1 => x2 )
),
[y1] => Array(
[y] => Array( champ1 => y1, champ1 => y2 )
),
[z1] => Array(
[z] => Array( champ1 => z1, champ1 => z2 )
)
}

Source / Exemple :


<?
/**

  • class facilitant la manipulation de tableau
  • @author: p418
  • @version: 0.9b
    • /
class myArray { /**
  • ranger un tableau associatif par rapport a un champs
  • ne marche que sur un array de type
  • toto {
  • [x] => Array(
  • champ1 => x1,
  • champ2 => x2,
  • champ3 => x3
  • ),
  • [y] => Array(
  • champ1 => y1,
  • champ2 => y2,
  • champ3 => y3
  • ),
  • [z] => Array(
  • champ1 => z1,
  • champ2 => z2,
  • champ3 => z3
  • )
  • }
  • @access private
  • <code>
  • myArray::orderby($toto,"champ1")
  • retournera un tableau avec x,y,z ordonnés par champs1
  • @param (array) $arrayToOrder
  • @param (string) $orderBy
  • @return (array)
  • /

  • public static function orderBy($arrayToOrder,$orderBy)
    {

    $result = array();

    if(!is_array($arrayToOrder) || count($arrayToOrder) == 0)
    return $result ;
    $tmp = $arrayToOrder;
    $sorting = $tmp[0][$orderBy];
    for($i=0;$i<count($arrayToOrder);$i++)
    {
    if($tmp[$i][$orderBy]==$sorting)
    {
    $result[]=$tmp[$i];
    array_splice($tmp,$i,1);
    }
    }
    return array_merge($result,self::orderBy($tmp,$orderBy));
    }

    /**
    • semblable a array_unique mais basé sur un champs donné.
    • le tableau en entrée doit avoir la meme structure que pour la fontion orderby
    • @access private
    • @param (array) $arrayToOrder
    • @param (string) $orderBy
    • @param (bool) $distinct
    • @return array
    • /

    public static function groupBy($arrayToOrder,$orderBy,$distinct=false)
    {
    $exists = self::fieldExists($orderBy,$arrayToOrder);
    if($exists !== true)
    throw new Exception("Champs '$orderBy' inconnu dans la clause 'group by' $idx");
    $res = array();
    while(list($k,$v) = each($arrayToOrder))
    $res[$v[$orderBy]][]=$v;
    return $res;
    }

    /**
    • Interprete la requete donnée en parametre sur le tableau $myArray
    • @access private
    • @param (Array) $myArray
    • @param (string) $query
    • @return (array)
    • /

    public static function query($myArray,$query)
    {

    try
    {
    if(eregi("(select|delete) *([\*a-zA-Z,_\(\)]*) *(where)? *(.*)",$query,$reg))
    {
    $todo = array();
    $res = array();
    $columns = explode(",",str_replace(" ","",$reg[2]));

    if(empty($reg[4]))
    $reg[4]="";
    else
    {
    $groupBy = preg_split("/group by/i",$reg[4]);
    $reg[4] = empty($groupBy[0])?"":$groupBy[0];

    }

    //on exécute, la reqquete
    while(list($idx,$champs) = each($columns))
    {
    if(eregi("([0-9a-z_]*)\((.*)\)",$champs,$func))
    {
    $todo[$func[1]]=$func[2];
    continue;
    }

    if($champs=='*')
    foreach( self::getFields($myArray) as $field )
    $res=array_merge($res,self::select($myArray,$field,$reg[4]));
    else
    {
    $exists=self::fieldExists($champs,$myArray);
    if($exists!==true)
    throw new Exception("Champs '$champs' inexistant à l'index '$exists'\n".print_r($myArray,true));
    $res=array_merge($res,self::select($myArray,$champs,$reg[4]));
    }
    }
    $tmp = array();
    $size = self::getMaxSize($res);
    for($i=0;$i<$size;$i++)
    foreach($res as $nom => $tab)
    $tmp[$i][$nom]=isset($tab[$i])?$tab[$i]:'';

    /**
    *
    • action spécifique

    *
      • /


    // si on veut grouper
    if(!empty($groupBy[1]))
    $tmp=self::groupBy($tmp,trim($groupBy[1]));

    //autre actions
    if(count($todo))
    {
    foreach($todo as $func => &$do)
    if(!self::functionExists($func))
    throw new Exception("Fonction '$func()' inconnue");
    }
    //*****
    return $tmp;
    }
    else
    throw new Exception("requete incomprise");

    }
    catch(Exception $e)
    {
    throw new Exception("[myArrayError] une erreur dans la requete : '$query'\n\t{$e->getMessage()}\n");
    }
    }

    /**
    • retourne un array contenant les valeurs du champs $fieldName dans le tableau $myArray
    • une condition peut etre définie pour la récupération.
    • cette condition est évaluée par la fonction eval() de php
    • pour faire appel a la valeur d'un champs donné, il suffit dencadrer ce nom de champs par des %%
    • ainsi %mon_champs% fera référence à la valeur de $row['mon_champs']
    • @access private
    • @param (array) $myArray
    • @param (string) $fieldName
    • @param (string) $condition
    • @return (array)
    • /

    public static function getField($myArray,$fieldName,$condition=true)
    {
    try
    {

    $res = array();

    //préparation de la condition
    if(is_string($condition) && trim($condition)!="")
    {
    $patterns=array("/^(.*)$/","/\s*([\*<>=+-]{1,2})\s*/");
    $condition=preg_replace($patterns," \$1 ",$condition);
    $condition=preg_replace("/\s+=\s+/"," == ",$condition);

    if(preg_match_all("/[\s]?%?([a-z_]+[\w]+)%?[\s]+/i",$condition,$reg))
    {
    while(list($idx,$field) = each($reg[0]))
    {
    $cleanned_field = $reg[1][$idx];
    if(in_array(strtolower($cleanned_field),array("and","or","between")) or self::functionExists($cleanned_field))
    continue;

    $fieldExists = self::fieldExists($cleanned_field,$myArray);
    if($fieldExists===true)
    $condition = str_ireplace($field,' $row["'.$cleanned_field.'"] ',$condition);
    else
    throw new Exception("Champs '{$cleanned_field}' à l'index '$fieldExists' inconnu dans la clause 'where'");
    }

    }
    }
    else
    $condition = "1";

    //global $row;
    while( list($row_idx,$row) = each($myArray))
    if(isset($row[$fieldName]) && self::where($condition,array("row" => $row)))
    $res[$fieldName][]=$row[$fieldName];
    return $res;
    }
    catch(Exception $e)
    {
    throw $e;
    }
    }

    /**
    • Alias de la méthode getField
    • @see getField
    • /

    public static function select($myArray,$fieldName,$condition){ return self::getField($myArray,$fieldName,$condition);}


    /**
    • /

    private static function where($condition,$scope=null)
    {
    //on défini le scope par défaut
    $myScope = $GLOBALS;
    //si un scope a été spécifié
    if(is_array($scope))
    $myScope = $scope;
    //on charge les variables du scope pour pourvoir faire lévaluation
    extract($myScope,EXTR_OVERWRITE);

    // on utilise les saut de lignes pour pouvoir se repérer si une erreur est générée
    // car eval ne spécifie qu'un numéro de ligne pour une erreur.
    $condition = str_replace(" ","\n",$condition);

    //inhibition du mess d'erreur
    //j'ai pas trouvé de vraie méthode pour ca :( cest crade
    ob_start();
    $evaluation = eval("return $condition;");
    $errorMsg = ob_get_clean();

    if(!empty($errorMsg))
    $error = error_get_last();
    $error_msg = "Erreur de syntaxe dans la clause 'where': ";
    $arrow_pad = strlen($error_msg);

    // si une erreur à été généré lors de l'eval, on va préciser où se trouve l'erreur
    if(!empty($error['line']))
    {
    $error_line = $error['line'];

    for($i=1,$pos = strpos($condition,"\n");$i<$error_line-1;$pos=strpos($condition,"\n",$pos+1),$i++ ){}
    $condition = str_replace("\n"," ",$condition);

    $arrow_pad += ($pos-2);
    $arrow_pad += strlen($condition);

    $arrow = str_repeat(' ',$arrow_pad).'^';
    throw new Exception("$error_msg$condition \n\t $arrow ({$error['message']})");
    }

    return $evaluation;
    }


    /**
    • retourne la taille la plus grande parmis les array contenu dans $myArray

    *
    • @param (array) $myArray
    • @return (int)
    • /

    public static function getMaxSize($myArray)
    {
    $sizes = self::count($myArray);
    if(!count($sizes))
    return 0;
    return max($sizes);
    }


    /**
    • retourne l'existence d'une clé dans l'ensemble des sous tableau de $myArray

    *
    • @param (string) $aColumnName
    • @param (array) $myArray
    • @return (bool)
    • /

    public static function fieldExists($aColumnName,Array $myArray)
    {
    if(empty($myArray))
    {
    //print_r(debug_backtrace());
    throw new Exception("Erreur test d'existence");
    }
    while(list($idx,$row)=each($myArray))
    if(!array_key_exists($aColumnName,$row))
    return $idx;
    return (bool)true;
    }

    public static function functionExists($funcName)
    {
    return method_exists(get_class(),$funcName);
    }
    /**
    • retourne les champs
    • si le parametre optionel $all est définie à true (false par défaut),
    • alors ce sont tous les champs qui sont retournés
    • sans se soucier si ils sont tous commun aux sous-tableaux

    *
    • @param (array) $myArray
    • @param (bool) [$all]
    • @return (array)
      • /

    public static function getFields($myArray,$all=false)
    {
    $res= array();
    while(list($idx,$row)=each($myArray))
    $res = ($all)?$res+array_keys($row):(count($res)?array_intersect($res,array_keys($row)):array_keys($row));
    return $res;

    }


    /**
    • Fonctions type msyql
    • /



    /**
    • retourne pour chaque ligne du tableau la taille

    *
    • @param (array) $myArray
    • @return (array)
    • /

    public static function count($myArray)
    {
    $res = array();
    while(list($idx,$row) = each($myArray))
    $res[$idx] = count($row);
    return $res;
    }

    }

    ?>
    </code>

    Conclusion :


    @TODO:

    - Remettre au propre le code
    - Revoir le "moteur"
    - faire une version réellement orientée objet

    what else...

    Codes Sources

    A voir également

    Ajouter un commentaire

    Commentaires

    malalam
    Messages postés
    10843
    Date d'inscription
    lundi 24 février 2003
    Statut
    Modérateur
    Dernière intervention
    2 mars 2010
    17 -
    Hello,

    moi je comprends...j'ai fait la même chose dans ma classe aDB.
    Organiser les résultats peut-être un gros gain de temps (à défaut de ressources), et on ne peut pas toujours les organiser comme on le veut en SQL : le SQL renvoie des lignes uniquement, PAS un tableau PHP. Et un tableau PHP bien organisé peut faciliter grandement la tâche (regrouper les jeux de résultats dans un tableau indexé sur la clef primaire, par exemple).
    Par contre, ton code me parait bien compliqué pour ce qu'il fait...et tu aurais sans doute gagné à utiliser la SPL. Maintenant, je ne l'ai pas regardé en détail pour être franc. Simplement, ta "doc" propose des fonctionnalités basiques et le code qui en découle me parait bien complexe pour ces fonctionnalités : manque d'optimisation du code, OU doc bien trop succinte ? :-)
    neigedhiver
    Messages postés
    2483
    Date d'inscription
    jeudi 30 novembre 2006
    Statut
    Membre
    Dernière intervention
    14 janvier 2011
    13 -
    Mmmmmm... Je continue de penser qu'il est préférable (car possible) de faire ce genre de tri/groupement lors de la requête, parce qu'un SGBDR est optimisé pour faire ça (grâce aux index) alors que PHP pas vraiment...
    J'ai donc du mal à être convaincu, mais ce n'est que mon opinion...
    prince418
    Messages postés
    2
    Date d'inscription
    samedi 2 avril 2005
    Statut
    Membre
    Dernière intervention
    15 octobre 2009
    -
    Euh ouais cochon dans le sens "codé à l'arrache entre 2 cuites et 3 pauses café"

    Oui oui j'avais pas fait gaffe au phpdoc XD..ça confirme ce que je disais avant ^^

    En fait ça permet, à partir du résultat d'une "grosse" requête, de procéder à différentes manip.

    par exemple:

    On récupère une liste d'utilisateur depuis la DB (c'est un exemple totalement bidon hein).
    Disons que chaque utilisateur est définie par un type de profil, un département etc.
    Sans requete supplémentaire en DB, il nous est possible de classé notre tableau d'utilisateir par profil ou département pour ainsi facilité les tratements par la suite.

    tableau résultat de la requete:

    Array(
    [0] => Array( login => "User1", profil => "Modérateur", department => "kfc"),
    [1] => Array( login => "User2", profil => "Modérateur", department => "q8k"),
    [2] => Array( login => "User3", profil => "Modérateur", department => "kfc"),
    [3] => Array( login => "User4", profil => "Modérateur", department => "maqdo")
    )

    donnera, si on fait un "order by department":

    Array(
    [kfc] => array(
    [0] => Array( login => "User1", profil => "Modérateur", department => "kfc"),
    [1] => Array( login => "User3", profil => "Modérateur", department => "kfc"),
    ),
    [q8k] => array(
    [0] => Array( login => "User2", profil => "Modérateur", department => "q8k")
    ),
    [maqdo] => array(
    [0] => Array( login => "User4", profil => "Modérateur", department => "maqdo")
    )
    )

    voila voila
    neigedhiver
    Messages postés
    2483
    Date d'inscription
    jeudi 30 novembre 2006
    Statut
    Membre
    Dernière intervention
    14 janvier 2011
    13 -
    Salut,

    J'ai pas regardé en détails le code, mais pour quelqu'un qui dit avoir codé comme un cochon, c'est loin d'être aussi crade que ça ! Je peux t'assurer qu'on en a vu des bien pires, parfois qualifiées de "bien codées"...

    Bref... J'avoue n'avoir pas bien bien cerné l'intérêt du machin... En fait, c'est surtout ton exemple qui me perturbe : si on récupère des données d'une bdd sous forme de tableau... En gros, tu proposes de faire une requête sur un résultat de requête... Je suis perdu, je ne comprends vraiment pas... Moi, les résultats de mes requêtes, ce sont des objets PDOStatement, qui implémente l'interface Traversable et que je passe directement comme un itérateur dans une boucle foreach dans la vue (ou dans le template).

    Sinon, y'a un truc qui me fait bien rire :
    * @access private
    public static function groupBy($arrayToOrder,$orderBy,$distinct=false)
    En commentaire phpDoc, tu mets que la méthode est en accès privé, mais la déclaration dit tout le contraire... Que faut-il en penser ? :)

    Hum... faudrait que je creuse pour mieux comprendre ce que tu veux faire avec ta classe, mais je crains de n'avoir pas trop le temps...

    Vous n'êtes pas encore membre ?

    inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

    Les membres obtiennent plus de réponses que les utilisateurs anonymes.

    Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

    Le fait d'être membre vous permet d'avoir des options supplémentaires.