Manipulation de SQL: remarques préliminaires

Mammouth du PHP | 2278 Messages

03 oct. 2013, 16:31

I Mysql
Mysql est déclaré obsolète et sera donc abandonné dans une version ultérieure:
toute application qui y recourt, sauf si elle passe par des fonctions personnelles, devra être modifiée...
Evidemment, si on a écrit:

Code : Tout sélectionner

function connexion ($hote, $nom, $passe) { $lien = mysql_connect("localhost", "mysql_user", "mysql_password") or die("Impossible de se connecter : " . mysql_error()); return $lien; }
et de même pour toutes les fonctions, ça réduira le travail.
Mais, pour l'avoir fait pour assurer la compatibilité de mon code entre mysqli et sqlite sans passer par PDO, je peux dire que c'est du sport.
II Requêtes MySqli
Je vois souvent dans les questions des choses de ce genre:

Code : Tout sélectionner

mysql_query("UPDATE users SET situation='divorcé' WHERE pseudo='mickey_la_banane' ") or die(mysql_error());
ce qui est mieux que rien sauf le recours à mysql.
Mais le message d'erreur n'est souvent pas compris.
Il me semble préférable de procéder ainsi:

Code : Tout sélectionner

$requete= ""UPDATE users SET situation='divorcé' WHERE pseudo='mickey_la_banane' "; $resultat = mysqli_query ($requete); if ($resultat == false) { print "traitement échoué<br />$requete"; //et un véritable traitement de l'erreur enregistrant le module et la reqête //on peut ainsi passer la reqête dans PHPmyadmin ou autre, et la modifier jusqu'à ce qu'on ait trouvé } else { print "traitement réussi"; }
III Variables et requêtes.
On voit souvent ça:
"UPDATE users SET situation='{$_GET['nature']}' WHERE pseudo='{$_SESSION['pseudo']}' and dieu_sait_quoi = '{$_SESSION['dieu_sait_quoi']}'"
Je trouve plus clair:
<?php
$erreur = 0;
if (isset($_GET['nature']) )
{
     $nature =  $_GET['nature'] ;
    if (isset ($_SESSION['pseudo']))
    {
         $pseudo = $_SESSION['pseudo'];
         if (isset($_SESSION['dieu_sait_quoi']))
        {
            $dieu_sait_quoi =  $_SESSION['dieu_sait_quoi'];
        }
        else
       {
           $erreur = 3;
       }
     }
    else
    {
        $erreur = 2;
    }
}
else
{
    $erreur = 1;
}
if ($erreur == 0)
{
     $requete = "UPDATE users SET situation='$nature' WHERE pseudo='$pseudo' and dieu_sait_quoi = '{$'dieu_sait_quoi''";
//et  traitement comme ci-dessus
}
else
{
//traitement d'erreur
}
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Eléphant du PHP | 79 Messages

04 oct. 2013, 01:55

Merci pour ton explication Sirakawa, mais je t'invite toi et les autres d’ailleurs a ne pas faire des empilement de conditions, il est plus intéressant d'exploiter les Exceptions. Il vaut mieux créer un tableau de gestion d'erreurs que tu vérifies à la fin, sachant que si les clés voulues ne sont pas dans la variable POST le script ne dois pas chercher à aller plus loin que de ré afficher le formulaire.

Un petit exemple avec une page d'inscription :
<?php

$erreur = array();

$dsn = "mysql:dbname=mydb;host=127.0.0.1;port=3306;" ;

$bdd = new PDO( $dsn, 'root', null );

if( !empty( $_POST )
and isset( $_POST['mail'], $_POST['mdp'], $_POST['pseudo'] ) )
{
    foreach( $_POST as $key => $value )
    {
        if( empty( $value ) )
        {
            $erreur[$key] = "Ne peux pas être vide" ;
        }
        
        switch( $key )
        {
            case 'mail' :
                
                if( !filter_var( $value, FILTER_VALIDATE_EMAIL ) )
                {
                    $erreur[$key] = "N'est pas un mail valide." ;
                    
                    continue ;
                }
                
                $sql = "SELECT * FROM utilisateur WHERE mail = ?" ;
                
                $req = $bdd-> prepare( $sql );
        
                $req-> execute( array( $_POST['mail'] ) );
                
                if( $req-> rowCount() > 0 )
                {
                    $erreur[$key] = "Le mail est déjà utilisé." ;
                    
                    continue ;
                }
                
                break ;
                
            case 'pseudo' :
                
                $sql = "SELECT * FROM utilisateur WHERE pseudo = ?" ;
                
                $req = $bdd-> prepare( $sql );
        
                $req-> execute( array( $_POST['pseudo'] ) );
                
                if( $req-> rowCount() > 0 )
                {
                    $erreur[$key] = "Le pseudo est déjà utilisé." ;
                    
                    continue ;
                }
                
                break ;
        }
    }
    
    if( empty( $erreur ) )
    {
        $sql = "INSERT INTO utilisateur(mail,mdp,pseudo) VALUES(?,?,?)" ;
        
        $req = $bdd-> prepare( $sql );
        
        $req-> execute( array( $_POST['mail'], md5( $_POST['mdp'] ), $_POST['pseudo'] ) );
        
        header( "location:index.php?page=ok" );
    }
}

?>

<!-- FORMULAIRE D'INSCRIPTION -->

<form method="POST" action="index.php">
    
    <!-- INPUT PSEUDO -->
    <div class="input">
    <label>Pseudo :</label>
    <input type="text" name="pseudo" value="<?php if( isset( $_POST['pseudo'] ) ) echo $_POST['pseudo'] ?>"/>
    <?php if( isset( $erreur['pseudo'] ) ): ?>
    <span style="color: red;"><?php echo $erreur['pseudo'] ?></span>
    <?php endif ?>
    </div><br>
    
    <!-- INPUT MAIL -->
    <div class="input">
    <label>Mail :</label>
    <input type="text" name="mail" value="<?php if( isset( $_POST['mail'] ) ) echo $_POST['mail'] ?>"/>
    <?php if( isset( $erreur['mail'] ) ): ?>
    <span style="color: red;"><?php echo $erreur['mail'] ?></span>
    <?php endif ?>
    </div><br>
    
    <!-- INPUT MOT DE PASSE -->
    <div class="input">
    <label>Mot de passe :</label>
    <input type="password" name="mdp" value="<?php if( isset( $_POST['mdp'] ) ) echo $_POST['mdp'] ?>"/>
    <?php if( isset( $erreur['mdp'] ) ): ?>
    <span style="color: red;"><?php echo $erreur['mdp'] ?></span>
    <?php endif ?>
    </div><br>
    
    <input type="submit" value="Valider"/>
    
</form>
Le formulaire est sans style dsl, la grosse flemme.

N'oublie pas que la programmation objet peut vous aider a englober des actions / fonctions (on appel sa des méthodes). Admettons qu'on créer un objet "utilisateur" dans lequel on va stocker toutes les méthodes nécessaire au fonctionnement ou la validation d'information de l'utilisateur.

Exemple en se basant des éléments fait au dessus :

Sources : http://www.dev-systeme.fr/data/result4.rar

Ma classe 'database' créer le lien avec ma base de données grâce à l'objet PDO et les variables publiques 'name','host','port','user','password'. La classe (l'objet database) stock l'objet PDO dans la variable protégé 'bdd'.

La classe 'utilisateur' est étendu (extend) à la classe 'database'. Quand nous allons lancer (instancier) la classe 'utilisateur' à la ligne 7 du fichier index.php, automatique la classe 'database sera lancer' (c'est la parenté entre les classes / objets). Ce qui signifie que tout ce qui est fait dans l'objet 'database' le sera aussi dans l'objet 'utilisateur', donc dans l'objet 'utilisateur' la variable protégé 'bdd' est accessible, notre lien PDO est donc disponible pour effectuer les requêtes sql dans l'objet 'utilisateur'.

Ciao +

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

04 oct. 2013, 10:14

Sait,

Point de vu intéressant mais
- tu as autant de point de décision avec le switch que le if, mais tu ajoute en complexité avec le foreach or cela n'as pas de raison d'être lorsque tu connais les champs.
- la requête préparée est inutile et exploité a tord. (Surtout que par défaut ce n'est qu'une émulation donc autant utiliser query et la méthode quote. (D'ailleurs la requête préparée n'a de sens que dans une boucle).

Pour la partie objet :
- le problème la c'est ton modèle objet est foireux une entité (en java j'aurais pojo, mais en php ça fait popo du coup c'est pas terrible ;) ) ne doit pas contenir "d'intelligence".
Tu appliques un pattern Dao et donc une classe qui fait les accès base et retourne un objet utilisateur ;)

Dans le même genre on évitera l'héritage entre objet utilitaire et métier cela inclus un couplage fort qui peux être source d'emmerde plus tard :)
Dans l'objet dao ou effectue une injection de dépendance (par le constructeur généralement quand c'est pas optionnel)

d'ailleurs je t'invite a donner des noms explicite aux méthodes et paramètre de celle ci afin que l'on puisse comprendre rapidement ton code.
Et pendant que l'on y est
- ne pas abuser du transtypage (un return rowCount pour un booleen c'est moyen)
- Ne pas oublier de fermer les curseurs
- Le select * c'est le mail (surtout quand on a pas besoin des données)

Un code un poil plus cohérent serait ainsi (par exemple).

<?php
class User {
    private $id;
    private $email;
    private $pseudo;

    /**
     * @return mixed
     */
    public function getEmail() {
        return $this->email;
    }

    /**
     * @param mixed $email
     */
    public function setEmail($email) {
        $this->email = $email;
    }

    /**
     * @return mixed
     */
    public function getId() {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id) {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getPseudo() {
        return $this->pseudo;
    }

    /**
     * @param mixed $pseudo
     */
    public function setPseudo($pseudo) {
        $this->pseudo = $pseudo;
    }

}

include_once './class/database.php';

class utilisateurDao {
    /**
     * Acces base
     * @var PDO
     */
    private $db = new PDO('');

    public function __construct(PDO $db) {
        if ($db !== null) {
            $this->db = $db;
        }
        else {
            throws new Exception('Objet database obligatoire');
        }
    }

    /**
     * Vérifie l'existance d'une adresse email dans la base
     * @param $email
     * @return mixed
     */
    public function mailExist($email) {
        $sql = 'SELECT count(*) as nb FROM utilisateur WHERE mail = ';
        $stmt = $this->db->query($sql . $this->db->quote($email));
        $this->dbError($stmt);

        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        $stmt->closeCursor();
        return $data['nb'];
    }

    /**
     * Leve une exception si le stmt est false l'erreur est dans l'ibjet pdo
     * @param $stmt
     * @throws Exception
     */
    private function dbError(PDOStatement $stmt) {
        if ($stmt === false) {
            $err = $this->db->errorInfo();
            throw new Exception($err[2]);
        }
    }

    /**
     * Vérifier l'existance d'un pseudo dans la base
     * @param $pseudo
     * @return mixed
     */
    public function checkExistePseudo($pseudo) {
        $sql = 'SELECT count(*) FROM utilisateur WHERE pseudo =' . $this->db->quote($pseudo);
        $stmt = $this->db->query($sql);
        $this->dbError($stmt);
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        $stmt->closeCursor();
        return $data['nb'];

    }

    /**
     * Ajout d'un utilisateur
     * @param $mail
     * @param $mdp
     * @param $pseudo
     */
    public function creer($mail, $mdp, $pseudo) {
        $sql = 'INSERT INTO utilisateur(mail,mdp,pseudo) VALUES(' . $this->db->quote($mail) . ',' .
            $this->db->quote($mdp) . ',' . $this->db->quote($pseudo) . ')';
        $stmt = $this->db->query($sql);
       $this->dbError($stmt);
        $user = new User();
        $user->setEmail($mail);
        $user->setPseudo($pseudo);
        $user->setId($this->db->lastInsertId());
        
        return $user;
    }

    /**
     * Retourne un utilisateur en fonction de son id
     * @param $id
     * @return mixed
     * @throws Exception
     */
    public function getById($id) {
        $sql = 'SELECT * FROM utilisateur WHERE id = ';
        $stmt = $this->db->query($sql . $this->db->quote($id));
        if ($stmt === false) {
            $err = $this->db->errorInfo();
            throw new Exception($err[2]);
        }
        $data = $stmt->fetch(PDO::FETCH_CLASS, 'User');
        $stmt->closeCursor();
        return $data;
    }

    /**
     * Authentification de l'utilisateur
     * @param $mail
     * @param $mdp
     * @return mixed
     */
    public function authentifier($mail, $mdp) {
        $sql = 'SELECT mdp FROM utilisateur WHERE mail = ' . $this->db->quote($mail);
        $stmt = $this->db->query($sql . $this->db->quote($id));
        if ($stmt === false) {
            $err = $this->db->errorInfo();
            throw new Exception($err[2]);
        }
        $data = $stmt->fetch(PDO::FETCH_CLASS, 'user');
        $stmt->closeCursor();

        if ($data['mdp'] == $mdp) {
            return true;
        }
        else {
            return false;
            // ou throw Exception au choix
        }
    }
}
@+
Modifié en dernier par moogli le 04 oct. 2013, 10:51, modifié 1 fois.
Raison : Ajout code exemple
Il en faut peu pour être heureux ......

Mammouth du PHP | 2278 Messages

06 oct. 2013, 17:12

Je vous invite FORTEMENT à utiliser MYSQLI
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD