Classes & PHP

PHP4

PHP4 et les objets

Dans un premier temps il faut noter que les variables en PHP4 se transmettent par copie et qu'elle ne sont pas typées. Ceci implique que le langage n'est pas réellement destiné à supporter les principes de la programmation orientée objet.

Les objets de PHP4 s'apparentent plus à ce que l'on pourrait nommer des structures évoluées. Voici comment on définit une classe en PHP :

class Vehicle
{
	var $run;

	function Vehicle()
	{
		echo "Appel du constructeur de la classe 'Vehicle'\n";
		$this->run = FALSE;
	}

	function start()
	{
		echo "Appel de la méthode 'start' de la classe 'Vehicle'\n";
		$this->run = TRUE;
	}
}

$v = new Vehicle();
$v->start();

Ce code produira la sortie suivante :

Appel du constructeur de la classe 'Vehicle'
Appel de la méthode 'start' de la classe 'Vehicle'

Le mot-clé class permet de définir une nouvelle classe, la classe Vehicle dans notre exemple. Les attributs sont définis grâce au mot-clé var. Les méthodes de l'objet utilisent le mot-clé function comme n'importe quelle fonction. Finalement, le constructeur est une fonction du nom de la classe.

Il faut bien noter que la définition des attributs via le mot-clé var n'est pas obligatoire. En effet, tout comme dans le reste du code nous ne sommes pas obligés de définir les variables utilisées. Il suffit alors de directement appeler l'attribut en utilisant $this->run pour qu'il soit automatiquement créé. Cependant, ceci rend le code objet très difficile à lire, il est donc recommandé de définir explicitement les attributs.

L'instanciation d'un objet se fait grâce au mot-clé new et les variables et méthodes sont accessibles avec l'opérateur ->.

A l'intérieur d'une méthode, les attributs et méthodes de la classe sont accessibles en utilisant la variable $this suivi de l'opérateur ->.

Code source de l'exemple

Visibilité

Contrairement à la majorité des langages orientés objet, il n'y a pas de distinction de visibilité (public, private ou protected), tout est public. De ce fait, il est d'usage de préfixer les attibuts ou méthodes privés ou protégés par le préfixe "_". Voici un exemple :

class MyClass
{
	var $publicVar;

	var $_privateVar;

	function publicFunction()
	{
		// Do stuff
	}

	function _privateFunction()
	{
		// Do stuff too
	}
}

Cette nomenclature permet lors d'un travail en équipe de savoir quels sont les attributs et méthodes que l'on peut utiliser sur un objet.

Une autre nomenclature utilise le mot-clé @access dans les commentaires javadoc. Ces derniers sont des commentaires commençant par la chaine /**, ils sont utilisés pour décrires les classes et méthodes et sont analysés par différents outils pour genérer une documentation. Exemple :

class MyClass
{
	/**
	 * @access public
	 */
	function publicFunction()
	{
		// Do stuff
	}

	/**
	 * @access private
	 */
	function _privateFunction()
	{
		// Do stuff too
	}
}

Code source de l'exemple mis à jour

Héritage

Bien que l'objet ne soit pas complètement implémenté dans PHP4, il y a tout de même la notion d'héritage dans PHP grâce au mot-clé extends. Exemple :

class Car extends Vehicle
{
	var $_model;

	function Car($model)
	{
		parent::Vehicle();
		echo "Appel du constructeur de la classe 'Car'\n";
		$this->_model = $model;
	}
}

$c = new Car('Renault');
$c->start();

Ceci produira :

Appel du constructeur de la classe 'Vehicle'
Appel du constructeur de la classe 'Car'
Appel de la méthode 'start' de la classe 'Vehicle'

L'appel du constructeur de la classe mère n'est pas automatique. Il faut ajouter l'appel du constructeur parent explicitement via l'instruction parent::Vehicle(); au début du constructeur de Car. Un tel oubli peut entrainer des résultats non souhaités à cause d'une non intialisation des variables.

Une classe ne peut hériter que d'une seule et unique classe mère, ceci est génant étant donné qu'il n'y a pas de notion d'interfaces.

Code source de l'exemple mis à jour

Mot-clé parent

Le mot-clé parent suivi de l'opérateur :: permet d'accéder aux méthodes et attributs de la classe mère, ce qui est utile lors de la surcharge de méthode. Exemple :

class Car extends Vehicle
{
	var $_model;
	var $_music;

	function Car($model)
	{
		parent::Vehicle();
		echo "Appel du constructeur de la classe 'Car'\n";
		$this->_model = $model;
		$this->_music = FALSE;
	}

	function start()
	{
		parent::start();
		echo "Appel de la méthode 'start' de la classe 'Car'\n";
		$this->_music = TRUE;
	}
}

$c = new Car('Renault');
$c->start();

Ceci produira :

Appel du constructeur de la classe 'Vehicle'
Appel du constructeur de la classe 'Car'
Appel de la méthode 'start' de la classe 'Vehicle'
Appel de la méthode 'start' de la classe 'Car'

Code source de l'exemple mis à jour

Opérateur ::

Il n'y a pas de méthodes dites statiques. Toute méthode est accessible sans avoir besoin d'instancier la classe en utilisant l'opérateur :: du moment qu'il n'y a aucune référence à la variable $this dans la méthode.

Voici un exemple :

class Car extends Vehicle
{
	/* Aucun changement dans le reste du code source */

	function getNbWheel()
	{
		echo "Appel de la méthode statique 'getNbWheel' de la classe 'Car'\n";
		return 4;
	}
}

$nb = Car::getNbWheel();

Ceci produira :

Appel de la méthode statique 'getNbWheel' de la classe 'Car'

Code source de l'exemple mis à jour

Méthodes magiques

Il existe deux fonctions dites magiques, les fonctions __sleep() et __wakeup(). Ces deux fonctions sont appelées respectivement à la sérialisation (conversion d'un objet en chaîne de caractères) et à la dé-sérialisation (conversion d'une chaîne de caractères en objet). Un objet est sérialisé automatiquement lors de la sauvegarde des sessions.

Ces deux méthodes permettent de détruire ou restaurer des ressources base de données ou de ne pas sauver les champs inutiles.

La méthode __sleep() doit retourner un tableau contenant la liste des attributs à sauvegarder. Par défaut si la méthode n'est pas définie, tout l'objet est sauvegardé et donc tout l'objet est restauré. Tous les champs, y compris ceux de la classe mère doivent être spécifiés.

La méthode __wakeup() est appelée mais sans argument ni valeur de retour.

Voici un exemple :

class Vehicle
{
	/* Aucun changement dans le reste du code source */

	function printRunStatus()
	{
		echo "Le véhicule est ".($this->_run ? "allumé" : "éteint").".\n";
	}
}

class Car extends Vehicle
{
	/* Aucun changement dans le reste du code source */

	function printModel()
	{
		echo "Modèle $this->_model\n";
	}

	function printMusicStatus()
	{
		echo "La musique est ".($this->_music ? "allumée" : "éteinte").".\n";
	}

	function __sleep()
	{
		echo "Sleep\n";
		return array('_model');
	}

	function __wakeup()
	{
		echo "Wakeup\n";
	}
}

echo "Premier Objet\n";
$c = new Car('Renault');
$c->start();
$c->printMusicStatus();
$nb = Car::getNbWheel();

echo "\nObjet sérialisé\n";
$tmp = serialize($c);
$c2 = unserialize($tmp);
$c2->printModel();
$c2->printRunStatus();
$c2->printMusicStatus();

Ceci produira :

Premier Objet
Appel du constructeur de la classe 'Vehicle'
Appel du constructeur de la classe 'Car'
Appel de la méthode 'start' de la classe 'Vehicle'
Appel de la méthode 'start' de la classe 'Car'
La musique est allumée.
Appel de la méthode statique 'getNbWheel' de la classe 'Car'

Objet sérialisé
Sleep
Wakeup
Modèle Renault
Le véhicule est éteint.
La musique est éteinte.

Code source de l'exemple mis à jour