Correction classe user

Petit nouveau ! | 4 Messages

21 juin 2008, 15:34

Bonjour,

Pour simplifier les mises à jours de mes site web, j'ai créé une classe user, qui devra être capable de gérer les membres du site (création de compte, ...). Cette classe sera mise dans chacun de mais site.

Je voudrais savoir si ma classe est codée correctement. Merci d'avance pour votre aide.

Exemple d'utilisation :
$user = new User();

$user->setLogin("Brunau");
$user->setPassword("1234");
$user->create();

if ($user->error == NULL)
{
    echo 'Vous êtes inscrit';
}
else
{
    echo $user->getError();
}
Ma classe User:
class User
{
    private $id;
    private $login;
    private $password;
    private $email;
    private $firstName;
    private $surname;
    private $address;
    private $postalCode;
    private $city;
    private $country;
    private $birthday;
    private $created;
    private $level;
    private $ip;
    private $lastVisit;

    private $error;



    /**
    * Constructeur : est appelé a l'instansiation de la classe
    *
    * @param $id                    : id de l'utilisateur
    * @param bool $updateAttributes : mais à jour les attributs
    * @return bool FALSE ou TRUE
    */
    public function __construct($id = 0, $updateAttributes = true)
    {
        $this->id = intval($id);
        if ($this->id != 0 AND $this->id != NULL AND $updateAttributes) {
            $this->updateAttributes();
        }
        
        return true;
    }



    /**
    * Mais à jour les attributs récupérés dans la base de donnée
    *
    * @return bool FALSE ou TRUE
    */
    private function updateAttributes()
    {
        $retour=mysql_query("SELECT * FROM user WHERE id='$id'");
        if (mysql_num_rows($retour)) {
            $donnees=mysql_fetch_array($retour);
            $this->login = $donnees['login'];
            $this->password = $donnees['password'];
            $this->firstName = $donnees['first_name'];
            $this->surname = $donnees['surname'];
            $this->address = $donnees['address'];
            $this->postalCode = $donnees['postal_code'];
            $this->city = $donnees['city'];
            $this->country = $donnees['country'];
            $this->birthday = $donnees['birthday'];
            $this->created = $donnees['created'];
            $this->level = $donnees['level'];
            $this->ip = $donnees['ip'];
            $this->lastVisit = $donnees['last_visit'];
            return true;
        } else {
            $this->error = "imposible de mettre à jour les attributs : l'id n'existe pas";
            return false;
        }
    }



    /**
    * Ajoute dans la base de donnée le nouvelle utilisateur
    *
    * @return bool FALSE ou TRUE
    */
    public function create()
    {
        //on supprime les membre non activé de plus de 24 heures
        $temps = time() - (60 * 60 * 24);
        mysql_query("DELETE FROM user WHERE created < '$temps' AND level='0'");
	
        // On enregistre le membre
        if ($this->error == NULL) {
            $this->created = time();
            $this->lastVisit = time();
            $this->ip = realip();
            mysql_query("INSERT INTO user (login, password, first_name, surname, address, 
                         postal_code, city, country, birthday, created, level, ip, last_visit) 
                         VALUES('$this->login', '$this->password', '$this->firstName', 
                        '$this->surname' '$this->address', '$this->postalCode', '$this->city', 
                        '$this->country', '$this->birthday', '$this->created', '$this->level', 
                        '$this->ip', '$this->lastVisit')");
            return true;
        } else {
            return false;
        }
    }



    /**
    * Modifi les donné de l'utilisateur dans la base de donnée
    *
    * @return bool FALSE ou TRUE
    */
    public function update()
    {
        if ($this->error == NULL) {
            $this->lastVisit = time();
            mysql_query("UPDATE user SET login='$this->login', password='$this->password', 
                         first_name='$this->firstName', surname='$this->surname',
                         adresse='$this->address', postal_code='$this->postalCode', 
                         city='$this->city', country='$this->country', birthday='$this->birthday',
                         level='$this->level', ip='$this->ip', last_visit='$this->lastVisit' 
                         WHERE id='$this->id'");
            return true;
        }
        else
        {
            return false;
        }
    }



    /**
    * Regarde si le pseudo et le mot de passe envoyé sont correcte et mais a 
    * jour les attributs si ceuci sont correcte    
    *
    * @param $login    : identifiant de l'utilisateur
    * @param $password : mot de passe de l'utilisateur
    * @param $attempt  : nombre d'essai avant activation du brute force pandant 5 minutes   
    * @return bool FALSE ou TRUE
    */
    public function checkUser($login, $password, $attempt = 5)
    {
        if ($login == NULL AND $password == NULL) {
            $this->error = _LANG_MEMBRE_CONNEXION_CHAMPSVIDE;
            return false;
        }
        
        $login = mysql_real_escape_string(htmlentities ($login, ENT_QUOTES));
        $password = mysql_real_escape_string(htmlentities ($password, ENT_QUOTES));
		
		    // Supprime les tentatives de brutforce de plus de 5 minutes
		    $temps1 = time() - (60 * 5);
        mysql_query("DELETE FROM user_bruteforce WHERE dateline < '$temps1'") or die (mysql_error());
        
        // Regarde combien de tentative de connexion on été efectué avec le meme pseudo et ip
        $ip = gethostbyaddr(getenv('REMOTE_ADDR'));
        $retour = mysql_query("SELECT attempt FROM user_bruteforce WHERE ip='$ip' AND login='$login'") or die (mysql_error());
        $donnees = mysql_fetch_array($retour);
        if ($donnees['attempt'] > $attempt) {
            $this->error = _LANG_MEMBRE_CONNEXION_TROESSAI;
            return false;
        }
			
				$password = md5(sha1($password));
		
        $retour = mysql_query("SELECT id FROM user WHERE login='$login' AND password='$password'");
        if (mysql_num_rows($retour)) {
            if (empty($donnees)) {
                mysql_query("INSERT INTO user_bruteforce VALUES('$ip','".time()."','','$login')") or die (mysql_error());
            } else { // Sinon on augmente la valeur de tentative de 1
                $tentative = $donnees['tentative'] + 1;
                mysql_query("UPDATE user_bruteforce SET attempt=attempt+1 WHERE ip='$ip' AND login='$login'") or die (mysql_error());
            }
					
            $this->error = _LANG_MEMBRE_CONNEXION_IDENTIFIANTSFAUX;
            return false;
        }
		
        mysql_query("DELETE FROM user_bruteforce WHERE ip='$ip' AND login='$login'") or die (mysql_error());
        
		    $donnees = mysql_fetch_array($retour);
        $this->updateAttributes($donnees['id']);
        
        return true;
    }



    /**
    * Envoi un email de regénération du mot de passe
    *
    * @param $email : email de l'utilisateur
    * @return bool FALSE ou TRUE
    */
    public function generatePassword($email)
    {
        $email = mysql_real_escape_string(htmlentities ($email, ENT_QUOTES));
        $retour = mysql_query("SELECT id FROM user WHERE email = '$email'") or die (mysql_error());
		
        if (!mysql_num_rows($retour)) {
            $this->error = _LANG_MEMBRE_GENERERMOTDEPASSE_EMAILINCONNUE;
            return false;
        }
		
        $donnees = mysql_fetch_array($retour);
        $id_user = $donnees['id'];
        $check = $this->pullString(40);
        $password = $this->pullString(6);
        $password = md5(sha1($password));
        $dateline=time();
        
        mysql_query("DELETE FROM user_generatepassword WHERE id_user = '$id_user'");
        mysql_query("INSERT INTO user_generatepassword (id_user, check, password, dateline) 
                     VALUES('$id_user', '$check', '$password', '$dateline')") OR DIE (mysql_error());
		
        $message = 'Bonjour,'."\n\n".'Vous avez demandé à avoir un nouveau mot de passe sur le site http://www.site.fr.'.\n".'Votre nouveau mot de passe généré aléatoirement est le suivant : '.$password."\n\n".'Pour l\'utiliser à la place de votre mot de passe actuel, veuillez utiliser ce lien : '._URL_SITE.'activer-nouveau-mot-de-passe-'.$check.'.html.'."\n\n".'Merci d\'utiliser nos services et à bientôt.';
        mail($email, "Nouveau mot de passe", $message, 'From: [email protected]');
        
        return true;
    }
    
    
    
    /**
    * Active le nouveau mot de passe si la vérification est juste 
    *
    * @param $checking : code de vérification envoyé au prelable dans le mail
    * @return bool FALSE ou TRUE
    */    
    public function checkgeneratePassword($checking)
    {
        $checking = mysql_real_escape_string(htmlentities ($checking, ENT_QUOTES));
		    
		    // On supprime les gérateurs de plus de 60 minutes
        $time = time() - (60 * 60);
        mysql_query("DELETE FROM user_generatepassword WHERE dateline < '$time'");
		
        $retour = mysql_query("SELECT id_user, password FROM user_generatepassword WHERE check = '$check'") or die (mysql_error());
        if (!mysql_num_rows($retour)) {
            $this->error = "erreur d'activation le lien est faux.";
            return false;
        }
		
        $donnees = mysql_fetch_array($retour);
        $id_user = $donnees['id_user'];
        $password = $donnees['password'];
		
        mysql_query("UPDATE user SET password='$password' WHERE id='$id_user'") OR DIE (mysql_error());
        mysql_query("DELETE FROM user_generatepassword WHERE id_user = '$id_user' ");
		
        return true;
    }



    /**
    * Regarde si le code entirobot est juste  
    *
    * @param $captcha       : code généré
    * @param $captchaResult : code entré par l'utilisateur
    * @return bool FALSE ou TRUE
    */
    public function checkCaptcha($captcha, $captchaResult)
    {
        if ($captcha != $captchaResult) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_CODESECUINCORECT;
            return false;
        } else {
            return true;
        }
    }



    /**
    * Regarde si le mot de passe entré par l'utilisateur correspond a celui
    * de la base de donnée      
    *
    * @param $password : mot de passe
    * @return bool FALSE ou TRUE
    */
    public function checkPassword($password)
    {
        $password=md5(sha1($password));
        $retour = mysql_query("SELECT password FROM user WHERE id='$this->id'");
        $donnees=mysql_fetch_array($retour);
        if ($donnees['password'] != $password) {
            $this->error = _LANG_MEMBRE_PROFIL_ANCIENPASSEINCORRECTE;
            return false;
        } else {
            return true;
        }
    }



    /**
    * Génère un code de chifre et de lettre  
    *
    * @param $number       : longueur du code voulu
    * @return : code généré
    */
    private function pullString($number = 5)
    {
        $string = "";
        $chaine = "abcdefghijklmnpqrstuvwxy0123456789";
        srand((double)microtime()*1000000);
        for ($i = 0; $i < $number; $i++) {
            $string .= $chaine[rand()%strlen($chaine)];
        }
        return $string; 
    }
    
    
    
    /**
    * Permet d'entré pseudo   
    *
    * @param $login      : pseudo
    * @return bool FALSE ou TRUE
    */
    public function setLogin($login)
    {	
        if (!preg_match("#^[0-9a-z]+[0-9a-z._-]*[0-9a-z]+$#", $login)) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_CHAMPNONVALIDE;
            return false;
        }
        if (strlen($login) < 2) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_PSEUDOTROCOURT;
            return false;
        }
        if (strlen($login) > 14) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_PSEUDOTROLON;
            return false;
        }
        $login = mysql_real_escape_string(htmlentities ($login, ENT_QUOTES));
        $login_present = mysql_query("SELECT id FROM user WHERE login='$login'");
        $forbidden_login = mysql_query("SELECT id FROM forbidden_login WHERE login='$login'");
        if (mysql_num_rows($login_present) OR mysql_num_rows($forbidden_login)) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_PSEUDODEJAEXISTANT;
            return false;
        }
        $this->login = $login;
        return true;
    }



    /**
    * Permet d'entré l'email 
    *
    * @param $email   : email
    * @param $email2  : email de verification   
    * @return bool FALSE ou TRUE
    */
    public function setEmail($email, $email2)
    {
        if (!preg_match("#^[a-z0-9._-]+@[a-z0-9._-]{2,}\.[a-z]{2,4}$#", $email)) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_EMAILNONVALIDE;
            return false;
        }
        if ($email != $email2) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_DEUXEMAILFAUX;
            return false;
        }
        $email = mysql_real_escape_string(htmlentities ($email, ENT_QUOTES));
        $email_present = mysql_query("SELECT id FROM user WHERE email='$email'");
        if (mysql_num_rows($email_present)) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_EMAILDEJAEXISTANT;
            return false;
        }
        $this->email = $email;
        return true;
    }
    
    
    
    /**
    * Permet d'entré le mot de passe   
    *
    * @param $password  : mot de passe
    * @param $password2 : mot de passe de vérification
    * @return bool FALSE ou TRUE
    */    
    public function setPassword($password, $password2)
    {
        if (strlen($password) < 4) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_PASSTROCOURT;
            return false;
        }
        if (strlen($password) > 12) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_PASSTROLON;
            return false;
        }
        if ($password != $password2) {
            $this->error = _LANG_MEMBRE_INSCRIPTION_DEUXMOTSPASSFAUX;
            return false;
        }
        $password = mysql_real_escape_string(htmlentities ($password, ENT_QUOTES));
        $password = md5(sha1($password));
        $this->password = $password;
        return true;
    }
    
    

    public function setFirstName($data)
    {
        $this->firstName = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setSurname($data)
    {
        $this->surname = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setAddress($data)
    {
        $this->address = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setPostalCode($data)
    {
        $this->postalCode = intval($data);
    }
    
    public function setCity($data)
    {
        $this->city = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setCountry($data)
    {
        $this->country = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setBirthday($data)
    {
        $this->birthday = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    public function setLevel($data)
    {
        $this->level = intval($data);
    }
    
    public function setIp($data)
    {
        $this->ip = mysql_real_escape_string(htmlentities ($data, ENT_QUOTES));
    }
    
    
    
    public function getId()
    {
        return $this->id;
    }
    
    public function getLogin()
    {
        return $this->login;
    }
    
    public function getPassword()
    {
        return $this->password;
    }
    
    public function getEmail()
    {
        return $this->email;
    }
    
    public function getFirstName()
    {
        return $this->firstName;
    }
    
    public function getSurname()
    {
        return $this->surname;
    }
    
    public function getAddress()
    {
        return $this->address;
    }
    
    public function getPostalCode()
    {
        return $this->postalCode;
    }
    
    public function getCity()
    {
        return $this->city;
    }
    
    public function getCountry()
    {
        return $this->country;
    }
    
    public function getBirthday()
    {
        return $this->birthday;
    }
    
    public function getCreated()
    {
        return $this->created;
    }
    
    public function getLevel()
    {
        return $this->level;
    }
    
    public function getIp()
    {
        return $this->ip;
    }
    
    public function getLastVisit()
    {
        return $this->visit;
    }
    
    public function getError()
    {
        return $this->error;
    }
}
[/code]

Mammouth du PHP | 1511 Messages

21 juin 2008, 15:43

Pour tout ce qui est accession aux variables de ta classe, plutôt que de créer des fonctions accessX et getX, utilise __get() et __set() dans ta classe, c'est très pratique et ca évite pas mal de taf' . ;)

Mammouth du PHP | 1668 Messages

21 juin 2008, 15:46

Moi j'ai une class user qui fonction autrement, seul l'ID, le pseudo et les préférences y sont stockés, après il y a 5 fonctions nouveau() (inscription) connecte (connexion) vérifie (es-t-il connecté ?) change_profile (update son profile), suppression (supprime son compte), ta classse est bien mais il faut jamais stocker le MDP...
"À ceux qui poursuivent leurs rêves et se spécialisent dans l'impossible" Joseph Kong

10 ans de PHP, déjà.

"moi jtrouve que katagoto il déchire!" Nagol

Mammouth du PHP | 1511 Messages

21 juin 2008, 16:08

Ca dépend, si le pseudo est haché en md5 ;)

Mammouth du PHP | 1668 Messages

21 juin 2008, 16:41

...
Je vois pas trop l'utilité...
"À ceux qui poursuivent leurs rêves et se spécialisent dans l'impossible" Joseph Kong

10 ans de PHP, déjà.

"moi jtrouve que katagoto il déchire!" Nagol

Mammouth du PHP | 1511 Messages

22 juin 2008, 16:09

Imagine une classe ou tu puisses avoir accès constament a tes utilisateurs, une sorte de active record sur ta table utilisateurs ?
Le fait ou non de stocker le mdp est je dois dire discutable, car normalement tu n'as pas a y toucher...
Ce que j'aurais fait par contre, c'est une classe users statique avec une fonction get($userId) qui retourne une instance d'une class user avec l'utilisateur actif et qui a sa destruction sauvegarde l'utilisateur. Et une seconde fonction, new() qui retourne aussi une instance de la classe user, mais avec un mode différend, qui au lieu de faire un update a la destruction fait un insert.
A monis évidemment d'utiliser ON DUPLICATE KEY UPDATE avec des clefs uniques sur l'id utilisateur ;)
Des idées comme ca :P

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

22 juin 2008, 16:39

Pour l'utilisation des getter et setter, ayant l'habitude de java, je te conforte dans cette utilisation, l'idéal étant de les blinder quant au type de données (qu'on ne puisse pas mettre de chaine dans un nombre, etc.)
Après tout dépend de l'usage de la classe, si c'est juste pour toi et pour tes sites, ça vaut pas la peine de s'arracher les cheveux. Pour une application plus sérieuse et un travail collaboratif où d'autres devront faire appel à ta classe, ça peut clairement en valloir le coup :)

En revanche, je ne mettrais pas les mysql_real_escape_string() et htmlentities() dans le setter. En effet, cela t'empêche de mettre à jour la valeur et de la réutiliser immédiatement pour de l'affichage, puisqu'elle aura été transformée. Personnellement je te recommanderais plutôt de ne les utiliser que dans tes requêtes :)

Une autre petite remarque, même si c'est du pinaillage, dans tes requête, lorsque le champ est numérique, il ne faut pas mettre d'apostrophes autour de la valeur que tu lui affectes. Certaines bases le refuse tout simplement en te disant que le type ne correspond pas, d'autres comme mysql vont faire la conversion pour transformer la chaine en nombre et pouvoir le comparer. Autant lui économiser cette corvée inutile ;)

Enfin, pour continuer d'optimiser un peu, au lieu de
$retour=mysql_query("SELECT * FROM user WHERE id='$id'"); 
if (mysql_num_rows($retour)) { 
        $donnees=mysql_fetch_array($retour); 
Tu peux directement tester le résultat de mysql_fetch (affecté à $donnees) dans le if(), la fonction retournant FALSE si aucun enregistrement trouvé. Tu peux même d'ailleurs te contenter d'un mysql_fetch_assoc() :
$retour=mysql_query("SELECT login, password, ... FROM user WHERE id=".$id); 
if ($donnees=mysql_fetch_assoc($retour)) {
Et personnellement, je préfère lister les champs que faire un "SELECT *", je vois immédiatement les index disponibles, je m'assure de l'ordre et de la casse, etc. et pour la maintenance c'est très appréciable :)

ta classse est bien mais il faut jamais stocker le MDP...
Tu pourrais etayer un peu plus cette étrange théorie ? Y a quand même bien un moment où il va falloir l'enregistrer ce mot de passe, voire proposer à l'utilisateur de pouvoir le modifier ... :-k
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Mammouth du PHP | 1668 Messages

22 juin 2008, 17:22

ta classse est bien mais il faut jamais stocker le MDP...
Tu pourrais etayer un peu plus cette étrange théorie ? Y a quand même bien un moment où il va falloir l'enregistrer ce mot de passe, voire proposer à l'utilisateur de pouvoir le modifier ... :-k
Dans l'usage courant, on ne se sert d'un champs mot de passe que pour trois choses :
  • Se connecter
  • S'inscrire
  • Changer un paramètre "sensible" du profile, voire même la mot de pass
Je penses qu'il est inutile de créer une variable dans la classe spécifique pour le mot de passe...

Pour se connecter et s'inscrire on fait une seule requête donc l'usage peut se faire comme ça :
// Dans ma class
public function connexion($pseudo, $mdp){
    $this->pseudo=$pseudo;
    // SQL
    if($mdp==$sql->mdp) echo "C'est le bon mdp";
    else die ("mdp incorrect");
}

$MaClass->connexion($_POST['pseudo'], $_POST['mdp']);
Voilà, en gros c'est inutilement dangereux de faire une variable comme ça, on a vite fait de faire une requête de moins et de faire quelques erreurs...

Enfin, ce que j'en dis moi :roll:
"À ceux qui poursuivent leurs rêves et se spécialisent dans l'impossible" Joseph Kong

10 ans de PHP, déjà.

"moi jtrouve que katagoto il déchire!" Nagol