Déclaration d’une classe

Une classe est une définition, une sorte de moule qui sert à créer des objets. Voici la structure minimaliste de la déclaration d’une classe :

<?php
class MaClasse
{

}

C’est le mot clé class qui définit une classe. On le fait suivre du nom de la classe. Par convention ce nom doit commencer par une majuscule et répondre à la règle StudlyCaps. C’est-à-dire que chaque nouveau mot à l’intérieur du nom de la classe doit commencer par une majuscule (PSR-1). D’autre part le fichier qui contient la classe doit commencer par <?php ou <?= et ne pas comporter de fermeture. Tout ce qui concerne la classe va se trouver entre les deux accolades.

Les propriétés

Ce qui va distinguer deux objets d’une même classe sera la valeur de ses propriétés. Prenons le cas d’une classe Volume. On pourra par exemple définir une propriété qui sera la forme du volume. On peut par exemple avoir un tétraèdre ou un cube. Pour définir une propriété dans une classe il faut déclarer une variable. Au niveau visibilité on peut définir que la propriété peut être publique avec le mot clé public :

<?php
class Volume
{
 public $forme = "cube";
}

Créer un objet

Pour créer un objet on utilise le mot clé new :

$mon_volume = new Volume();

Ici j’ai créé un objet à partir de la classe Volume. La variable $mon_volume est une référence, elle ne contient pas l’objet. Quelle est la valeur de la propriété forme de cet objet ? Comme nous avons défini une valeur par défaut cube, notre objet est donc un cube.

Accéder à une propriété

Pour accéder à une propriété d’un objet on utilise cette syntaxe :

echo $mon_volume->forme;
cube

Pour changer la valeur d’une propriété c’est la même syntaxe :

<meta charset="utf-8">
<?php
class Volume
{
    public $forme = "cube";
}
$mon_volume = new Volume();
$mon_volume->forme = 'tétraèdre';
echo $mon_volume->forme;
tétraèdre

J’ai prévu l’encodage en utf-8 pour un affichage correct des caractères accentués. Je ne ferai plus apparaître cette ligne dans les codes suivants. Je ne mentionnerai plus le tag pour indiquer qu’on a du PHP non plus pour alléger le code.

Créer une méthode

Une méthode est une fonctionnalité d’un objet définie dans la classe. Il suffit de créer une fonction publique à l’intérieur d’une classe pour qu’elle devienne une méthode. Voici un exemple :

class Volume
{
    public $forme = "cube";
    public function affiche_forme() {
        echo $this->forme;
    }
}
$mon_volume = new Volume();
echo $mon_volume->affiche_forme();
cube

J’ai créé une méthode nommée affiche_forme, destinée à afficher la valeur de la propriété forme. Remarquez la pseudo-variable un peu particulière $this. On a vu que pour appeler une propriété d’un objet il faut une référence de cet objet. Dans les cas précédents on avait une variable mais à l’intérieur de la classe comment référencer l’objet ? C’est justement la pseudo-variable $this qui contient une référence de l’objet en cours ! Remarquez également la syntaxe pour utiliser une méthode, c’est la même que pour appeler une propriété.

Cloner un objet

Considérez ce code :

class Volume
{
    public $forme = "cube";
}
$mon_volume = new Volume();
$mon_autre_volume = $mon_volume;

Que pensez-vous de ce que contient la variable $mon_autre_volume ? Voici un petit test pour s’en rendre compte :

$mon_volume->forme = 'sphère';
echo $mon_autre_volume->forme;
sphère

On se rend compte que la variable $mon_autre_volume référence le même objet que la variable $mon_volume. Autrement dit on n’a pas créé un nouvel objet mais juste une nouvelle référence pour le même objet. Mais alors comment créer un objet identique à un objet déjà existant ? C’est ici qu’intervient le clonage. Je modifie le code précédent :

class Volume
{
    public $forme = "cube";
}
$mon_volume = new Volume();
$mon_autre_volume = clone $mon_volume;

Remarquez l’utilisation de la méthode clone cette fois au lieu d’une simple affectation. Maintenant procédons au même test que précédemment :

$mon_volume->forme = 'sphère';
echo $mon_autre_volume->forme;
cube

Cette fois nous avons deux objets distincts !

Constructeur et destructeur

Lorsqu’on crée un objet on a envie en général de définir ses propriétés. Il est possible de procéder comme nous l’avons fait en commençant par créer l’objet avec new et ensuite de définir la valeur de ses propriétés. Il est aussi possible d’utiliser un constructeur, c’est une méthode spéciale qui est appelée lors de la création d’un objet. Voici notre classe équipée d’un constructeur :

class Volume
{
    public $forme;
    public function __construct($forme = 'cube') {
        $this->forme = $forme;
    }
}
$mon_volume = new Volume('sphère');
echo $mon_volume->forme;
sphère

Remarquez la syntaxe du contrôleur avec les deux soulignements qui précèdent le mot construct. Nous retrouverons cette écriture avec d’autres fonctions dites « magiques ». Grâce à ce constructeur le codage devient plus simple et lisible. Il existe aussi un destructeur beaucoup moins utilisé qui est activé lorsqu’il n’y a plus de référence vers l’objet :

class Volume
{
    public $forme;
    public function __construct($forme = 'cube') {
        $this->forme = $forme;
        echo "Création de " . $this->forme.'<br>';
    }
    public function __destruct() {
        echo "Destruction de " . $this->forme;
    }
}
$mon_volume = new Volume();
Création de cube
Destruction de cube

L’héritage

Une classe peut hériter d’une autre classe. Qu’est-ce que ça veut dire concrètement ? Tout simplement qu’elle aura les mêmes propriétés et les mêmes méthodes. Prenons un exemple :

class Volume
{
	public $forme;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function affiche_forme() {
		echo $this->forme;
	}
}
class Parallelepipede extends Volume
{

}
$mon_Parallelepipede = new Parallelepipede();
echo $mon_Parallelepipede->affiche_forme();
cube

La classe Parallelepipede hérite de la classe Volume grâce au mot clé extends. Mais vous allez dire que cette nouvelle classe n’ajoute rien à la première, c’est vrai. Mais une classe fille peut avoir ses propres méthodes et ses propres fonctions en plus de celles de sa mère. Changeons un peu notre exemple :

class Volume
{
	public $forme;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function get_forme() {
		return $this->forme;
	}
}
class Parallelepipede extends Volume
{
	public $materiau;
	public function get_materiau() {
		return $this->materiau;
	}
}
$mon_Parallelepipede = new Parallelepipede();
$mon_Parallelepipede->materiau = 'bois';
echo 'mon ',$mon_Parallelepipede->get_forme(),' est en ',$mon_Parallelepipede->get_materiau();
mon cube est en bois

J’ai un peu modifié la classe mère pour la rendre plus réaliste. J’ai aussi prévu une propriété et une méthode spécifiques pour la classe fille. Je pense que vous avez compris le principe. Nous reviendrons sur cette notion d’héritage qui est importante, pour le moment retenez le principe de base.

Les constantes

Une classe peut comporter des constantes. Autrement dit des valeurs qui ne changent jamais. Regardez cet exemple :

class Volume
{
	public $forme;
	const CUBE = 6;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function get_faces() {
		if($this->forme == 'cube') return self::CUBE;
	}
}
$mon_volume = new Volume();
echo 'J\'ai ',$mon_volume->get_faces(),' faces';
J'ai 6 faces

Une constante est déclarée avec le mot clé const. Par convention le nom de la constante ne commence pas par $ et s’écrit avec des majuscules (PSR-1). Remarquez la syntaxe de l’appel de la constante avec le mot clé self suivi de :: (appelé opérateur de résolution de portée). Ici $this ne fonctionnerait pas et il est important de comprendre la différence. $this fait référence à l’objet en cours, alors que self fait référence à la classe elle-même. Nous reviendrons sur cette syntaxe avec les éléments statiques. Regardez à présent cet exemple :

class Volume
{
	public $forme;
	const CUBE = 6;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
}
echo 'Un cube a ',Volume::CUBE,' faces';
Un cube a 6 faces

Cette fois je n’ai pas créé d’objet, j’ai juste appelé la constante de la classe. Cette syntaxe est possible depuis la version 5.3 de PHP. Cette syntaxe est aussi utilisable à partir d’un objet :

class Volume
{
	public $forme;
	const CUBE = 6;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function get_faces() {
		if($this->forme == 'cube') return self::CUBE;
	}
}
$mon_volume = new Volume();
echo 'J\'ai ',$mon_volume::CUBE,' faces';
J'ai 6 faces

Visibilité et méthodes magiques

Jusque là j’ai déclaré les propriétés et les méthodes avec le mot clé public. Elle sont donc librement accessibles. Ce n’est pas toujours une bonne solution. Il est parfois judicieux de rendre ces éléments uniquement accessibles par la classe elle-même avec le mot clé private en pouvant étendre cet accès aux classes enfants avec le mot clé protected. C’est ce qu’on appelle habituellement l’encapsulation, c’est à dire que la classe fait sa petite cuisine interne à l’abri des regards et ne présente que le nécessaire pour la faire fonctionner. Regardez cet exemple :

<?php
class Volume
{
	private $forme;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function get_forme() {
		return $this->forme;
	}
}
$mon_volume = new Volume();
echo $mon_volume->forme;  // Provoque une erreur
Fatal error: Cannot access private property Volume::$forme in ...\test1.php on line 13

La propriété forme est maintenant devenue privée, l’accès direct à partir d’un objet est interdit et la dernière ligne de code renvoie une erreur. Il faut donc prévoir des méthodes particulières dans la classe pour lire ou écrire les méthodes. L’avantage est de pouvoir effectuer un contrôle au passage. D’ailleurs la méthode get_forme permet de lire la valeur de la propriété. On appelle ce type de fonction un accesseur. On peut aussi prévoir une méthode set_forme pour écrire la valeur de la propriété. Mais on peut aussi utiliser les méthode « magiques » __set et __get. Pourquoi magique ? Parce que si vous essayez d’accéder directement à une propriété privée, avant de lancer une erreur, PHP va voir s’il existe une de ces méthodes. Regardez notre exemple précédent maintenant :

class Volume
{
	private $forme;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function __get($property) {
		if('forme' === $property) {
			return $this->forme;
		} else {
			throw new Exception('Propriété inconnue !');
		}
	}
}
$mon_volume = new Volume();
echo $mon_volume->forme;
cube

Tant qu’on y est on va aussi prévoir une méthode magique pour changer la valeur de la propriété :

class Volume
{
	private $forme;
	public function __construct($forme = 'cube') {
		$this->forme = $forme;
	}
	public function __get($property) {
		if('forme' === $property) {
			return $this->forme;
		} else {
			throw new Exception('Propriété inconnue !');
		}
	}
	public function __set($property, $value)
	{
		if('forme' === $property) {
			$this->forme = $value;
		} else {
			throw new Exception('Propriété inconnue !');
		}
	}
}
$mon_volume = new Volume();
$mon_volume->forme = 'tétraèdre';
echo $mon_volume->forme;
tétraèdre

Certains critiquent l’utilisation des ces deux méthodes magiques en arguant du fait qu’on se retrouve à écrire tout le code du filtrage dans une même fonction qui devient un peu balourde. On dit aussi que certains IDE ne parviennent pas à proposer une auto-complétion du code parce qu’ils ne parviennent pas à identifier correctement celui-ci (réflexion). Je ne pense pas que ces arguments doivent être un frein à leur utilisation. Surtout qu’il est possible de créer du code dynamique dans ce style :

class MaClasse
{
    protected $attributes;
    public function __construct(array $proprietes) {
        $this->attributes = $proprietes;
    }
    public function __get($key)    {
        return $this->attributes[$key];
    }
    public function __set($key, $value)    {
        $this->attributes[$key] = $value;
    }
}
$mon_volume = new MaClasse(array('forme' => 'tétraèdre', 'materiau' => 'bois', 'poids' => 30));
$mon_volume->couleur = 'bleu';
echo $mon_volume->forme,'<br>',$mon_volume->materiau,'<br>',$mon_volume->poids,'<br>',$mon_volume->couleur;
tétraèdre
bois
30
bleu

Remarquez que j’ai prévu la visibilité protected pour les propriétés. Ça ne changerait rien au code d’utiliser private mais si on crée une classe qui hérite de celle-ci avec protected elle pourra utiliser les propriétés, alors qu’avec private elle ne le pourrait pas.

Laisser un commentaire