Classes en PHP et méthode d'utilisation

:hello:

Je me tente à utiliser des classes en php.
J’essais juste de faire un petit truc pour ajouter par exemple un utilisateur à une base. Mais j’ai de gros doute sur ma de coder :heink:
En effet, cela me parait beaucoup plus long et contraignant qu’une approche classique non POO.
Voici ce que j’ai fait (je ne met que deux variables pour alléger le code:

fichier compte.class.php


class compte
{
	var $id;
	var $login;
	var $passwd;


// Constructeur: j'initialise l'ID
	function compte()
	{
		$request = mysql_query('SELECT MAX(id) FROM comptes');
		$result = mysql_fetch_array($request);
		@$result[0]++;
		$this->id = $result[0];
	}

// Fonction d'ajout: première idée/méthode
	function ajoutCompte($flogin, $fpasswd)
	{
		$this->login = $flogin;
		$this->passwd = $fpasswd;
		$ajout = mysql_query('INSERT INTO user (login, passwd) VALUES ("'.$this->login.'", "'.$this->passwd.'"');
	}

// Fonction d'ajout: seconde idée/méthode
	function ajoutCompte2()
	{
		$ajout = mysql_query('INSERT INTO user (login, passwd) VALUES ("'.$this->login.'", "'.$this->passwd.'"'); 
	}
}

Comme vous pouvez le constater, je prépare deux fonctions (deux méthodes) pour insérer les données.
Ce qui se traduit comme ceci dans ma page “normale” (celle qui récupère les données) :


//récup login et passwd
$login = $_POST['login'];
$passwd = $_POST['passwd'];

include "compte.class.php";

$user = new compte;

// utilisation de la méthode 1:
$user->ajoutCompte($login,$passwd);

// ou bien seconde méthode :
$user->login = $login;
$user->passwd = $passwd;
$user->ajoutCompte2();


Quelle serait selon vous la meilleure méthode à utiliser ?
(gloups… peut-être même que je fais n’importe quoi et que ça ne fonctionne pas du tout comme ça :neutre: )
Edité le 21/02/2008 à 14:31

ajoutCompte.

Ceci étant je trouve particulièrement débile de faire ça :

function ajoutCompte($flogin, $fpasswd)
{
	[b]$this->login = $flogin;
	$this->passwd = $fpasswd;[/b]
	$ajout = mysql_query('INSERT INTO user (login, passwd) VALUES ("'.$this->login.'", "'.$this->passwd.'"');
}

Vois tu, tu changes l’état de ton objet pour une fonction qui ne devrait que créer un compte utilisateur.

Si tu veux faire bien, je te propose ça (PHP5 powered, oublie PHP4 s’il te plaît):

class CompteException extends Exception {}
class Compte {
  private $login;
  private $password;
  // private $...; // XXX autre membre privé
  
  private function __construct($login, $password/*, ... // XXX autre partie spécifique à l'user */) {
    $this->login = $login;
    $this->password = $password;
  }
  
  /**
   * Crée un nouvel utilisateur
   *
   * @param string le login
   * @param string le password   
   * @throws CompteException en cas de non création du compte (erreur mySQL, etc)
   * @return Compte renvoi le compte crée   
   */
  public static function create($login, $password) {
    if (!mysql_query('INSERT INTO user (login, passwd) VALUES (\''. 
          mysql_real_escape_string($login).'\', \''.mysql_real_escape_string($password).'\'')) {
      throw new CompteException('impossible de créer le compte ' . $login);
    }
    return new Compte($login, $password);
  }
  
  /**
   * Recupère un compte utilisateur
   *
   * @param string le login
   * @param string le password   
   * @throws CompteException en cas de non création du compte (erreur mySQL, etc)
   * @return Compte renvoi null si pas de compte retrouvé
   */
  public static function getCompte($login, $password) {
    if (!($res = mysql_query('SELECT login, passwd FROM user (login, passwd) WHERE login = \''. 
          mysql_real_escape_string($login).'\' AND passwd = \''.mysql_real_escape_string($password).'\''))) {
      throw new CompteException('impossible de créer le compte ' . $login);
    }
    if ($result = mysql_fetch_assoc($res)) {
      return new Compte($result['login'], $result['passwd']);
    }
    return null;
  }  

  public function getLogin() {return $this->login;}
  public function getPassword() {return $this->password;}
}

En d’autres termes, ton approche n’est pas bonne: tu utilises un objet pour l’encapsulation, et aussi parce que tu penses réellement objet, là ce n’est pas le cas (et c’est normal, vu que tu débutes). En ce sens, créer ou récupérer un compte doit :

  1. soit passer par des méthodes statiques et un constructeur privé (donc personne sauf tes méthodes statiques peut créer un objet Compte)
  2. soit passer par un manager - ie: un autre objet avec les deux méthodes statiques.

A mon avis, si tu fais de la POO, tu ne dois pas commencer par écrire du code, mais commencer par penser objet, modulaire, petit, encapsulation, etc

En fait, ce que tu me dis, c’est que je tente tout bêtement de transformer grossièrement une fonction en classe, ce qui n’est pas du tout la bonne approche/méthode, c’est ça ? :confused:

J’ai étudié le code que tu me proposes. Il à l’air tout à fait clair, et je suis disposé à apprendre les bonnes méthodes.
Mais il y a des concepts encore un peu obscurs :sweet:
Par conséquent, le juste retour de baton pour m’avoir foutu les pieds dans le plat (le vrai, le bon) c’est que je m’en vais te poser quelques questions d’éclaircissement de ce pas :slight_smile:

Premièrement:

class CompteException extends Exception {}

Tu hérites de la class Exception pour la classe CompteException.
Cette classe est-elle interne à PHP5 ou faut-il la créer en parallèle (avec toute la batterie de tests qui va bien pour produire un code erreur si erreur il y a) ?

Ensuite, vient l’utilisation de la fonction __construct :

Est-elle équivalente à ce que j’ai tenté de faire en créant function compte{…} ? (donc méthode du même nom que la classe)

Et je vois dans ton exemple que le constructeur prend en paramètre login et password…
Est ce que l’initialiser avec seulement UN attribut est possible (par exemple dans mon cas, avec l’attribut id) ?
Cela change t-il grand chose ? (c’est pour une histoire l’allocation mémoire si je ne me trompe pas ?)

Ensuite, lors de la déclaration des fonctions publiques, tu utilises le mot-clé “static”.
A quoi ce mot-clé correspond t-il ? (manque évident de connaissances là… :yeux4: )

Enfin une dernière question, peut-être la plus absurde…
Mais je ne comprend pas comment fonctionnent ces deux dernières fonctions:
public function getLogin() {return $this->login;}
public function getPassword() {return $this->password;}

Voici comment je l’interprete:
On veut bien sur récupérer le login ou le password. Mais à aucun moment dans la méthode getCompte je ne vois l’initialisation de ces attributs (du style $this->login = $result[‘login’] par exemple).
De plus, peux-tu me confirmer (cela parait presque évident, mais je demande tout de même) la nécessité de créer une méthode pour chaque attribut ? (car bien sur, là on s’est arrêté à login et password, mais derrière il y aura beaucoup plus d’information).

Ah… en fait encore une dernière question:
Dans la méthode getCompte:


if ($result = mysql_fetch_assoc($res)) {
 return new Compte($result['login'], $result['passwd']);
 }

Ne fallait-il pas plutôt lire


if ($result = mysql_fetch_assoc($res)) {
 return new getCompte($result['login'], $result['passwd']);
 }

Merci pour tes réponses :slight_smile:

  1. La classe Exception est une classe interne de PHP5 pour gérer le mécanisme d’exception (try {} catch (CompteException $e) {} … mais pas de finally :/)
  2. __construct() est le constructeur de la classe en PHP5, équivalent à compte() dans ton exemple.
Et je vois dans ton exemple que le constructeur prend en paramètre login et password...
Est ce que l'initialiser avec seulement UN attribut est possible (par exemple dans mon cas, avec l'attribut id) ?
Cela change t-il grand chose ? (c'est pour une histoire l'allocation mémoire si je ne me trompe pas ?)

Tu peux ajouter autant d’attributs que tu veux. Ce n’est qu’un exemple. Il est vrai que tu pourrais dans les deux cas (création, récupération) ajouter l’identifiant:

Cette fonction te sera d’ailleurs utile après l’insertion: fr2.php.net…

Je te laisse ajouter l’identifiant dans les deux cas (création, récupération) histoire de t’entraîner :slight_smile:

  1. Le mot clef statique équivaut à créer une fonction, mais au niveau d’une classe. C’est à dire une méthode statique, que tu appelles ainsi :

Compte::create(‘foo’, ‘bar’);
Compte::getCompte(‘foo’, ‘bar’);

  1. $this->login & $this->password sont initialisés dans le constructeur. Il est là pour ça.

  2. Certainement pas. Ta correction provoque une erreur d’analyse syntaxique. Et si ne gueulait pas, ça ne trouverait pas la fonction getCompte().

Je te suggère de lire la doc de PHP5 sur tout ce qui est POO.

Je ne suis pas un expert en POO, mais voila quelques réponses :

La classe Exception {} n’a pas besoin d’être recoder. Par contre, en faisant class CompteException extends Exception {} tu peux ajouter des méthodes pour la gestion d’erreur.

Oui c’est le cas, la méthode construct est appelé lors de l’initialisation d’un objet : $obj = new objet($arguments);

Changer la méthode pour que l’argument soit l’id est possible. Mais bon, ca va pas changer grand chose question vitesse, et pour la clarté du code, il vaut mieux prendre le login et le password.

C’est ce qu’on appelle l’encapsulation. En gros, c’est pour garder tous les attributs de l’objet dans la classe.
Dans ton code, tu peux écrire : echo $obj->getLogin(); --> cela t’affiche la login … (la variable login est de type private, il faut donc une méthode pour l’afficher …)

Je suis trop lent. Désolé Sans-Nom
Edité le 21/02/2008 à 13:07

Je vous remercie pour toutes ces réponses détaillées. Voilà une bonne base pour commencer :slight_smile:

Je souhaiterais juste avoir une dernière précision concernant ces deux lignes:


fonction create et getCompte:

return new Compte($login, $password);

Que vont-elles retourner exactement ?
Simplement le login et le mot de passe ?

Voici comment je les traduit et comprend, dites moi où je me trompe dans mon interprétation:

Dans mon fichier source, je veux ajouter un nouvel utilisateur:

$obj = new Compte(‘toto’, ‘1234’); // je le déclare avec ma classe
$obj->create(); // je l’insère dans ma base

Voilà… donc là, si tout se passe bien… je fais un new Compte($login, $password) !

Et c’est là… que je ne comprend pas… :etonne2:
Pourquoi réinitialiser mon constructeur avec le même login/passwd ?

Idem pour la fonction getCompte si elle ne retourne aucun enregistrement.
Pourquoi repartir sur un return new Compte($login,$password) ? a quoi cela sert-il ?

Merci de votre aide
Edité le 21/02/2008 à 14:29

tu n’as pas compris alors.

TU fais ça :

$obj = Compte::create(‘toto’, ‘1234’);

ou ça:

$obj = Compte::getCompte(‘toto’, ‘1234’);

Le constructeur ne doit pas être utilisé directement, c’est tout.

Au passage, quand tu mets une méthode static, tu ne dois pas l’appeler comme si c’était une méthode non statique => $obj->create() == MAL.

Utilisation de l’ORP bien sur… :expressionless:

Je n’avais effectivement pas lié l’ensemble. Toujours cette mauvaise façon de voir les choses !

Juste une dernière précision:


if ($result = mysql_fetch_assoc($res)) {
 return new Compte($result['login'], $result['passwd']);
 }
 return null;

Pourquoi le “return null” ?

Je n’ai pas encore testé le script (je suis en train de l’adapter). Si je ne le met, qu’arrive t-il ?

Merci sans-nom :jap:

$obj = Compte::getcompte(‘toto’,‘pass’);

Le return null rend ton code plus propre (dans la logique des choses il faut que la fonction affecte une valeur a $obj, meme nulle).
Après c’est à toi de voir comment tu veux gérer ca.

A mon tour de poser une petite question :

C’est une histoire d’encapsulation ou autre ?
Edité le 21/02/2008 à 17:28

Merci pour la précision manu

Peux-tu me confirmer que le

throw new CompteException('impossible de créer le compte ' . $login);

arrête le script ?

Sans quoi il faudrait (pour mon adaptation) que je lies la condition à un else derrière; si la requête est bonne, on récupère le résultat:


 if (!($res = mysql_query('SELECT login, passwd FROM user (login, passwd) WHERE login = \''. 
 mysql_real_escape_string($login).'\' AND passwd = \''.mysql_real_escape_string($password).'\'')))
  {
    throw new CompteException('impossible de créer le compte ' . $login);
   }
  else
  {
   $result = mysql_fetch_assoc($res));
   return new Compte($result['login'], $result['passwd']);
   }
 }
 return null;

Notons que je supprime la condition ( if ($result = mysql_fetch_assoc($res)) ). on peut supposer (quitte à faire raler sans-nom :slight_smile: ) que si la requete à bien fonctionnée, ceci fonctionnera egalement correctement
Edité le 21/02/2008 à 18:45

Juste un p’tit up pour avoir confirmation sur mon message précédent :slight_smile:

enfait, donc ton code (pas dans la classe) :


try {
   $obj = Compte::create('toto', '1234');
   $obj->methide();
   ....
}

catch(CompteException $e){
   echo $e->getMessage();
}

Dans le try, dès qu’il rencontre une exception, “il passe au catch”.
En d’autre mots, si $obj = Compte::create(‘toto’, ‘1234’); renvoie une exception, $obj->methide(); ne sera pas executé.
J’éspère que j’ai été claire.

Sinon : classes.scriptsphp.org…
Je trouve que c’est assez bien expliqué, et pas trop long.
Edité le 22/02/2008 à 19:21

  1. le return null c’est si l’utilisateur n’est pas trouvé : inutile de lâcher une exception métier/technique/etc.
  2. vois le autrement: tu es banquier, et n’importe qui peut créer un compte en faisant new Compte(). Ok? Bon: tu te démerdes pour fermer la création de compte, et la limiter aux seuls applications que toi tu désires.

De manière générale, même si ce n’est pas fait partout, un “new” (donc passer directement par le constructeur), ça doit être évité au profit de méthode adaptée (create(), get(), comme tu veux) puisque tu pourrais aussi vouloir ajouter un cache ou des conneries de ce genre, etc.

ok, je vois la logique des choses et comment tu les présentes. ça me séduit bien :slight_smile:

Dans tous les cas, merci à tous les deux pour vos réponses précises. J’ai de bonnes bases pour développer le sujet.
On se retrouve dans d’autres topic :wink: