Optimisation de classe

Je souhaite de l'aide pour optimiser ma classe

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

J'ai créé une fonction MySQLi (qui peut peut être servir pour d'autres) que je souhaite optimiser. Car j'ai l'impression que dans ma fonction query, j’exécute 2 fois la requête :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
class sql {

    public $db;
    public $config = Array();
    public $affected_rows;
    public $num_rows;
    public $last_id;
    public $sql;

    public function __construct() {
        include('config.php');
        $this->db = mysqli_connect($config['HOST'],$config['USER'],$config['PASSWORD'],$config['DBNAME']);
        if ($this->db->connect_errno) {
            die("Echec lors de la connexion à MySQL");
        }
        else {
            $this->db->query("SET NAMES UTF8");
            $rq = $this->db->query("SELECT `name`,`value` FROM `config` ORDER BY `id` ASC");
            while($rp = $this->fetch($rq)) {
                $this->config[$rp['name']] = $rp['value'];
            }
            return true;
        }
    }

    public function get_user_ip() {
        if($_SERVER) { 
            if(@$_SERVER['HTTP_X_FORWARDED_FOR']) 
                $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 
            elseif(@$_SERVER['HTTP_CLIENT_IP']) 
                $ip = $_SERVER['HTTP_CLIENT_IP']; 
            else 
                $ip = $_SERVER['REMOTE_ADDR']; 
        } 
        else { 
            if(getenv('HTTP_X_FORWARDED_FOR')) 
                $ip = getenv('HTTP_X_FORWARDED_FOR'); 
            elseif(getenv('HTTP_CLIENT_IP')) 
                $ip = getenv('HTTP_CLIENT_IP'); 
            else 
                $ip = getenv('REMOTE_ADDR'); 
        } 
        return $ip; 
    }

    public function microtime_float() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    public function result($rq) {
        $res = mysqli_fetch_row($rq);
        if($res) {
            return $res[0];
        }
        else {
            return false;
        }
    }

    public function results($rq) {
        $res = mysqli_fetch_row($rq);
        if($res) {
            return $res;
        }
        else {
            return false;
        }
    }

    public function num_results($sql) {
        $nums = Array();
        $rq = $this->db->query($sql);
        while($row = $rq->fetch_array(MYSQLI_NUM)) {
            $nums[] = $row[0];
        }
        if(count($nums)) {
            return $nums;
        }
        else {
            return false;
        }
    }

    public function query($sql) {
        if($sql) {
            $this->sql = $sql;
            if(preg_match('#INSERT#i',$sql)) {
                $rq = $this->db->query($sql);
                $this->last_id = $this->db->insert_id;
                return $rq;
            }
            else if(preg_match('#(UPDATE|DELETE|SELECT|KILL|SHOW)#i',$sql)) {
                $stmt = $this->db->stmt_init();
                if(!$stmt->prepare($sql)) {
                    die("SQL error in query {$sql}");
                }
                $stmt->execute();
                if(preg_match('#SELECT#i',$sql))
                    $stmt->store_result();
                $this->num_rows = trim(str_replace('-','',(int)$stmt->affected_rows));
                $this->affected_rows = $this->num_rows;
                $stmt->close();
                $rq = $this->db->query($sql);
                return $rq;
            }
            else {
                return false;
            }
        }
        else {
            die("SQL error in query {$sql}");
        }
    }
    public function close() {
        $this->db->close();
    }
    public function fetch($query) {
        if($query) {
            $rows = $query->fetch_array(MYSQLI_ASSOC);
            $this->affected_rows = count($rows);
            return $rows;
        }
        else
            return false;
    }

}
?>

Merci pour votre aide.

Sylvain

+0 -0

Tu prépares une requête (prepare + execute lignes 96 + 99) pour derrière aussi l'exécuter en non-préparé (query ligne 105) donc tes requêtes sont exécutées deux fois.

Ta requête préparée ne sert strictement à rien : il n'y a aucun paramètre/bind. Si tu pensais te protéger des injections, tu te mets le doigt dans l'oeil : ce n'est pas le simple fait de faire appel aux méthodes prepare et execute qui va te protéger mais bel et bien d'externaliser les valeurs par des paramètres/marqueurs de la requête SQL elle-même.

Que vient faire l'adresse IP dans cette classe ? Idem pour ta "config" ?

$this->db->query("SET NAMES UTF8");

On ne doit JAMAIS exécuter directement un SET NAMES sauf si c'est le seul moyen de préciser son jeu de caractères. Il y a mysqli_set_charset (et son équivalent OO) pour le faire proprement à la place.

Ta classe n'est pas vraiment réutilisable entre le jeu de caractères forcé et l'include d'un config.php au lieu de fournir ces paramètres en argument du constructeur.

Pas lieu de mettre un return dans un constructeur non plus.

Quitte à encapsuler l'exécution des requêtes, on s'attendrait à ce que la classe vérifie d'abord que l'exécution de la requête n'a pas planté avant de chercher à l'exploiter et ne puisse planter en erreur fatale.

Édité par vibrice

+1 -0

Juste une petite remarque par rapport à ton code existant ci-dessous.

Dans ta fonction query(), tu peux utiliser explode() suivi de array_shift() au lieu de preg_match() afin de récupérer le type de requête select, show, insert, update, delete, kill dans une variable.

1
2
3
4
<?php
$sql     = 'SELECT * FROM mytable';
$explode = explode(" ", $sql)
$option  = strtolower(array_shift($explode)); // select

HTTP/1.1 418 I'm a teapot

+0 -0
Auteur du sujet

k merci pour ces conseils.

Comment je fais pour avoir l'équivalent de :

1
$rq = $this->db->query($sql);

Avec

1
2
3
4
5
6
7
$stmt = $this->db->stmt_init();
if(!$stmt->prepare($sql)) {
      die("SQL error in query {$sql}");
}
$stmt->execute();
if(preg_match('#SELECT#i',$sql))
   $stmt->store_result();

Merci pour vos réponses

Édité par Necatis

+0 -0

Elle n'a pas d'équivalent, justement, préparé et non-préparé ne s'utilisent pas de la même manière : les valeurs non connues à l'avance (genre sur WHERE pseudo = X) doivent être substituées par un marqueur (? en mysqli) que tu bindes après [la préparation].

Si tu veux toujours utiliser du non-préparé, façon fonctions mysql_*, tu échappes toi-même avec mysqli_real_escape_string (ou son équivalent OO) et tu exécutes ta requête avec query.

Dans le cas contraire, tu prévois le passage des valeurs à binder en paramètre de ta méthode. Mais de toute façon, ta classe ne permet pas d'exécuter plusieurs fois une même requête préparée, ce qui est bien dommage aussi.

Édité par vibrice

+0 -0
Auteur du sujet

J'ai finit par faire cette fonction et elle marche bien sauf qu'il faut installer mysqlnd :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<?php
class sql {

    public $db;
    public $config = Array();
    public $affected_rows;
    public $num_rows;
    public $last_id;
    public $sql;

    public function __construct() {
        include('config.php');
        $this->db = mysqli_connect($config['HOST'],$config['USER'],$config['PASSWORD'],$config['DBNAME']);
        if ($this->db->connect_errno) {
            die("Echec lors de la connexion à MySQL");
        }
        else {
            $this->db->query("SET NAMES UTF8");
            $rq = $this->db->query("SELECT `name`,`value` FROM `config` ORDER BY `id` ASC");
            while($rp = $this->fetch($rq)) {
                $this->config[$rp['name']] = $rp['value'];
            }
            return true;
        }
    }

    public function get_user_ip() {
        if($_SERVER) { 
            if(@$_SERVER['HTTP_X_FORWARDED_FOR']) 
                $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 
            elseif(@$_SERVER['HTTP_CLIENT_IP']) 
                $ip = $_SERVER['HTTP_CLIENT_IP']; 
            else 
                $ip = $_SERVER['REMOTE_ADDR']; 
        } 
        else { 
            if(getenv('HTTP_X_FORWARDED_FOR')) 
                $ip = getenv('HTTP_X_FORWARDED_FOR'); 
            elseif(getenv('HTTP_CLIENT_IP')) 
                $ip = getenv('HTTP_CLIENT_IP'); 
            else 
                $ip = getenv('REMOTE_ADDR'); 
        } 
        return $ip; 
    }

    public function microtime_float() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    public function result($rq) {
        $res = mysqli_fetch_row($rq);
        if($res) {
            return $res[0];
        }
        else {
            return false;
        }
    }

    public function results($rq) {
        $res = mysqli_fetch_row($rq);
        if($res) {
            return $res;
        }
        else {
            return false;
        }
    }

    public function num_results($sql) {
        $nums = Array();
        $rq = $this->db->query($sql);
        while($row = $rq->fetch_array(MYSQLI_NUM)) {
            $nums[] = $row[0];
        }
        if(count($nums)) {
            return $nums;
        }
        else {
            return false;
        }
    }

    public function query($sql) {
        if($sql) {
            $this->sql = $sql;
            if(preg_match('#INSERT#i',$sql)) {
                $result = $this->db->query($sql);
                $this->last_id = $this->db->insert_id;
                return $result;
            }
            else if(preg_match('#(UPDATE|DELETE|SELECT|KILL|SHOW)#i',$sql)) {
                if(preg_match('#SELECT#i',$sql)) {
                    $stmt = $this->db->stmt_init();
                    if(!$stmt->prepare($sql)) {
                        die("SQL error in query {$sql}");
                    }
                    $stmt->execute();
                    $result = $stmt->get_result() or die("SQL error in query {$sql}");
                    $num_rows = trim(str_replace('-','',(int)$stmt->affected_rows));
                    if($num_rows) { 
                        $this->num_rows = $num_rows;
                        $this->affected_rows = $num_rows;
                    }
                    else {
                        $this->num_rows = 0;
                        $this->affected_rows = 0;
                    }
                    $stmt->close();
                }
                else {
                    $result = $this->db->query($sql) or die("SQL error in query {$sql}");
                }
                return $result;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }

    public function close() {
        $this->db->close();
    }
    public function fetch($res) {
        if($res) {
            $rows = $res->fetch_array(MYSQLI_ASSOC);
            return $rows;
        }
        else
            return false;
    }

}
?>
+0 -0

Tu as corrigé la double exécution de requête mais ta requête préparée ne sert toujours à rien. Je serais curieux de savoir comment tu utilises ça parce que ton code pue les injections SQL …

Au final, ta classe est toujours mal conçue : elle n'est pas du tout réutilisable, tu lui confies des choses qu'on ne devrait pas trouver dans une telle classe, ta surcouche de mysqli n'est que très partielle pour avoir réellement un intérêt (du moins, pour autrui).

Édité par vibrice

+1 -0
Staff

J'ai pas touché PHP depuis quelques années du coup je sais pas du tout ce qui se fait en ce moment, du coup est-ce que quelqu'un peut m'expliquer l'intérêt de faire un wrapper OO autour de mysqli alors que pdo existe ?

Je parle de JavaScript et d'autres trucs sur mon blog : https://draft.li/blog

+3 -0

J'ai pas touché PHP depuis quelques années du coup je sais pas du tout ce qui se fait en ce moment, du coup est-ce que quelqu'un peut m'expliquer l'intérêt de faire un wrapper OO autour de mysqli alors que pdo existe ?

victor

Je pense que ça peut valoir la peine si tu souhaites mettre en place un système de transactions automatiques sur des suites de requêtes, notamment si tu gères à la main une représentation intervallaire, par exemple, là où chaque insertion demande au ninimum deux requêtes UPDATE en plus de l'insertion en elle-même. Mais autrement, vu que mysqli possède aussi une interface orienté objet (c'est d'ailleurs l'interface de base, la couche procédurale a été ajoutée pour faciliter les migrations depuis mysql_*), je ne trouverais pas nécessaire non-plus.

Evitez qu'on vous dise de les lire : FAQ PHP et Symfony 2Tutoriel WAMP • Cliquez 👍 pour dire merci • Marquez vos sujets résolus

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte