PHP: La syntaxe objet

Par mdevoldere le 24/11/2023 à 10:00

L’objet est un paradigme de programmation très répandu et qui a fait ses preuves dans de nombreux projets. Son utilisation n’apporte pas de fonctionnalités au langage, c’est à dire que tout ce que l’on peut faire en utilisant la programation orientée objet peut être fait sans, cependant l’objet apporte beaucoup de choses en termes de maintenance, factorisation et découpage de code, travail collaboratif ou encore en conception.

Toutes ces qualités font de l’objet un mécanisme indispensable à maîtriser pour tout développeur PHP. D’ailleurs, presque toutes les bibliothèques et frameworks que vous serez amenés à utiliser se basent sur le paradigme objet.

Cet article montre des exemples qui vous permettront de prendre en main la syntaxe Objet de PHP.

Classes et instanciation d’objet

<?php
class User
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function sayHello(): void
    {
        echo 'Hello, I am '.$this->name."!\n";
    }
}

Remarquez que:

Un objet de cette classe s’instanciera alors de la manière suivante:

<?php
$user = new User('Bob');
$user->sayHello(); // affiche "Hello, I am Bob !"

Méthodes et attributs statiques

En PHP, il est possible de rendre des attributs et/ou méthodes statiques à l’aide du modifier “static”.

Par convention, un attribut statique est directement initialisé avec une valeur.

Pour rappel, un attribut ou une méthode statique ne requiert pas la création d’une instance pour être utilisé. Ils ne sont pas spécifiques à une instance.

<?php
class Example
{
    public static $counter = 0;

    public int $number;

    public function __construct()
    {
        // chaque instance créée incrémente $counter de 1.
        $this->number = ++Example::$counter;
        // ou 
        // $this->number = ++self::$counter;
    }
}

$a = new Example(); 
echo $a->number."\n"; // 1

$b = new Example(); 
echo $b->number."\n"; // 2

// appel d'un attribut statique
echo Example::$counter; // 2

Dans l’exemple ci-dessus, l’attribut $counter n’est pas répété dans $a et dans $b mais n’est présent qu’une seule fois, ce qui explique que les valeurs sont différentes à l’affichage.

Héritage

Pour déclarer un héritage, le mot clé extends est utilisé (comme en Java)

<?php
class A
{
    public int $a = 12;
}

class B extends A // La classe B hérite de A
{
    public int $b = 34;
}

$b = new B();
echo $b->a; // 12
echo $b->b; // 34

Accès à la classe “mère”

L’accès aux méthodes et aux attributs de la classe mère peut se faire à l’aide du mot clé parent et l’opérateur de résolution de portée ::.

<?php
class Rectangle
{
    protected int $width;
    protected int $height;

    public function __construct(int $width, int $height)
    {
        $this->width = $width;
        $this->height = $height;
    }
}

class Square extends Rectangle
{
    public function __construct(int $width)
    {
        // Appel du constructeur parent
        parent::__construct($width, $width);
    }
}

Classes Abstraites

PHP vous permet de déclarer des classes ou des méthodes comme abstraites à l’aide du mot clé abstract. Si au moins une méthode d’une classe est abstraite, ou que la classe est marquée elle même comme abstraite, elle ne pourra pas être instanciée.

<?php
abstract class Message
{
    abstract public function getName(): string;
    abstract public function getBody(): string;

    public function display(): void
    {
        echo 'From: '.$this->getName()."\n";
        echo 'Contents: '.$this->getBody()."\n";
    }
}

$m = new Message(); // Erreur, une classe abstraite ne peut pas être instanciée

Méthodes et classes “finales”

Il est possible d’utiliser le mot clé final sur une classe ou une méthode afin d’en empêcher la surcharge.

<?php
class A
{
    public final function maFonction(): int
    {
        return 42;
    }
}

class B extends A
{
    // erreur, cette méthode ne peut être surchargée
    public function maFonction(): int 
    {
        return 30; 
    }
}

Interfaces

En PHP, les interfaces se déclarent comme une classe à l’aide du mot clé « interface » , elles ne contiennent que des prototypes de méthodes. Une classe peut implémenter une ou plusieurs interfaces avec la notation : maClasse implements nomInterface

<?php
interface CanSpeak
{
    public function speak(): void;
}

// La classe Human doit implémenter les méthodes déclarées dans l'interface Canspeak
class Human implements CanSpeak
{
    public function speak(): void
    {
        echo "I am Human!\n";
    }
}

$human = new Human();
$human->speak(); // Affiche "I am Human!"

Remarques sur l’héritage

Il n’y a pas d’héritage multiple en PHP.

Par ailleurs, PHP ne supporte pas la surcharge (méthodes ayant le même nom mais des prototypes différents). Vous pouvez cependant utiliser des paramètres optionnels, voici un exemple illustrant un argument optionel ayant une valeur par défaut:

<?php
class A
{
    public function f(int $x = 42): void
    {
        echo "x = $x";
    }
}

$a = new A;

$a->f();  // x = 42
$a->f(67);  // x = 67

Exceptions

Comme la plupart des langages orienté objet, PHP propose un mécanisme d’exceptions permettant d’affiner la gestion d’erreur. Par défaut, une exception remontera jusqu’à être disposée sous forme d’erreur:

<?php
throw new Exception('Erreur, ça a planté !');

// Donnera lieu à :

/*
PHP Fatal error: Uncaught exception 'Exception' with message 'Erreur, ça a planté !' in index.php:2
Stack trace:
#0 {main}
thrown in index.php on line 2
*/

Try / Catch

Il est possible de capturer les exceptions grâce aux mots clés try et catch :

<?php 
class MaSuperClass 
{
    public function estMajeur(int $age): bool 
    {
        if($age < 18) {
            throw new Exception("Interdit aux mineurs");
        }

        return true;
    }
}

try
{
    $maClasse = new MaSuperClasse();
    $maClasse->estMajeur(17); // soulève une erreur
}
catch (Exception $e) // on attrappe l'erreur
{
    echo 'Erreur: ' .$e->getMessage() . "\n"; // affiche "Erreur: Interdit aux mineurs"
}

Exceptions personnalisées

PHP vous offre également la possibilité de surcharger les classes d’exception, dont Exception est la “racine” pour créer vos propres types d’exceptions.

<?php
class MyException extends Exception
{
}

try
{
    throw new MyException();
} 
catch (MyException $my) 
{
    echo "MyException\n";
} 
catch (Exception $e) 
{
    echo "Exception\n";
}

Les exceptions peuvent être capturées avec un certain ordre de priorité.

Problèmes fréquents

Références

Lorsque l’on passe un objet en argument d’une fonction, on ne passe pas une copie de cette objet mais une référence vers l’objet (à ne pas confondre avec une référence vers la variable qui décrit l’objet). Ainsi, toute modification se fera directement sur l’objet.

<?php
class MaClasse
{
    public $attr = 1;
}

function maFonction(MaClasse $a): void
{
    $a->attr = 2;
}

$a = new MaClasse();

echo $a->attr; // 1

maFonction($a); // la fonction modifie l'attribut de l'objet

echo $a->attr; // 2

Attention aux références !!!

Attention à ne pas confondre référence vers un objet et référence entre les variables, regardons l’exemple suivant:

<?php
class A
{
    public $attr = 1;
}

$a = new A;
$b = $a; // $b et $a référencent le même objet
$b->attr = 2;
echo $a->attr; // 2

$b = null; // la référence de $b vers l'objet est détruite. $a reste inchangé.
echo gettype($a); // object

$c = &$a; // $c est une référence vers $a
$c = null; // $si on détruit $c, $a est également détruit.
echo gettype($a); // null

Dans ce cas, la ligne $b = $a fait en sorte que la variable $b référence le même objet que $a.

Ainsi la modification de l’attribut sur $b->attr est aussi visible sur $a->attr (c’est le même objet).

En revanche, la variable $b est bien différente de $a, c’est pourquoi l’affecter à null ne change pas la valeur de $a.

Par contre, l’utilisation de l’opérateur de référence & pour créer la variable $c fait en sorte que $c soit un alias de $a, il référencera alors non pas seulement le même objet mais aussi la même variable. De ce fait, détruire $c détruira également $a.

Clonage d’objets

Si vous souhaitez créer une copie d’un objet, vous pouvez utiliser le mécanisme de clonage de cet objet. PHP vous propose pour cela d’utiliser le mot clé clone.

<?php
class A
{
    public $attr = 1;
}

$a = new A;
$a->attr = 5;
$b = clone $a; // $b est un clone, donc un objet distinct de $a
$b->attr = 6;

echo $a->attr."\n"; // 5
echo $b->attr."\n"; // 6

Clonage personnalisé

Le comportement du clonage peut cependant être non trivial et soulève souvent des questions:

Pour répondre à ces questions, il vous est possible d’écrire votre propre méthode de clonage, avec le nom “magique” __clone():

Plus d’info sur le clonage personnalisé en PHP

Les espaces de noms

Souvent, la création de classes et d’interfaces engendre un problème de nommage, car il peut devenir difficile d’éviter les problèmes de collisions de noms (deux classes ayant le même nom). Depuis PHP 5.3, il est possible d’utiliser des espaces de nom (namespace) pour éviter ce problème.

Par exemple, si le fichier alice/image.php contient:

<?php
namespace Alice;

class Image
{
    // Code de la classe
}

On pourra l’utiliser comme cela:

<?php
require 'alice/image.php';

use Alice\Image;

$image = new Image;

Ainsi, la classe de Alice ne “pollue” pas l’espace de nom global mais est disponible sous Alice\Image , si quelqu’un d’autre souhaite écrire un classe de gestion d’images, il pourra le faire en utilisant un autre espace de nom.

Multiples classes du même nom

Si Bob écrit à son tour une classe Image et la place sous l’espace de noms Bob\Image, il sera possible d’utiliser les deux soit à l’aide de la déclaration entière du nom des classes.

<?php
$a = new Alice\Image;
$b = new Bob\Image;

soit en important la ou les classe(s) à l’aide du mot clé use, par défaut, le nom de la classe (ici, Image) sera un raccourci vers son emplacement complet (ici, Alice\Image ):

<?php
use Alice\Image;
$a = new Image;
$b = new Bob\Image;

Enfin, le mot clé as permet de donner un nom de substitution (ou alias) à la classe dans le fichier courant:

<?php
use Bob\Image as BobImage;
use Alice\Image as AliceSuperImage; // AliceSuperImage est un alias de Alice\Image
$a = new AliceSuperImage;
$b = new BobImage;

Tests d’instance

Il est possible de tester qu’un objet est bien l’instance d’une classe en PHP à l’aide du mot clé instanceof:

<?php
interface P {};
class A {};
class B extends A {};
class Q implements P {};
$a = new A;
$b = new B;
$q = new Q;
var_dump($a instanceof A); // true
var_dump($b instanceof A); // true
var_dump($a instanceof B); // false
var_dump($q instanceof A); // false
var_dump($q instanceof P); // true

Notez que si l’objet testé est l’instance d’une classe fille de la classe passée, instanceof retournera vrai, comme par exemple pour l’expression $b instanceof A ci-dessus.

Ce système fonctionne également pour tester si un objet implémente une interface, comme avec $q instanceof P ci-dessus.

Crédits

https://php.net https://gregwar.com/php/programmation-orientee-objet.html