Le mot clef final en PHP objet

Depuis la version 5 de PHP, on peut empêcher les classes filles de surcharger une méthode en précisant le mot clef final avant la définition de celle-ci.

Si la classe elle-même est définie avec le mot clef final alors celle-ci ne pourra tout simplement pas être étendue.

Cela peut être utile si vous souhaitez empêcher explicitement certains développeurs de surcharger certaines méthodes ou d’étendre certaines classes dans le cas d’un projet Open Source par exemple.

 

Définir une méthode finale

Illustrons cela avec quelques exemples, en commençant avec la définition d’une méthode finale.

Pour cela, on peut reprendre nos classes Utilisateur, Abonne et Admin et par exemple déjà surcharger la méthode getNom() définie dans la classe parent Utilisateur depuis notre classe étendue Admin :

<?php
    abstract class Utilisateur{
        protected $user_name;
        protected $user_region;
        protected $prix_abo;
        protected $user_pass;
        protected $x = 0;
        public const ABONNEMENT = 15;
        
        public function __destruct(){
            //Du code à exécuter
        }
        
        abstract public function setPrixAbo();
        
        public function getNom(){
            echo $this->user_name;
        }

        public function getPrixAbo(){
            echo $this->prix_abo;
        }
        
        public function plusUn(){
            $this->x++;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
        public function moinsUn(){
            $this->x--;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
    }
?>

 

<?php
    class Admin extends Utilisateur{
        protected static $ban;
        
        public function __construct($n, $p, $r){
            $this->user_name = strtoupper($n);
            $this->user_pass = $p;
            $this->user_region = $r;
        }
        
        public function getNom(){
            echo $this->user_name. '(Admin)';
        }
        
        public function setBan(...$b){
            foreach($b as $banned){
                self::$ban[] .= $banned;
            }
        }
        public function getBan(){
            echo 'Utilisateurs bannis : ';
            foreach(self::$ban as $valeur){
                echo $valeur .', ';
            }
        }
        
        public function setPrixAbo(){
            if($this->user_region === 'Sud'){
                return $this->prix_abo = parent::ABONNEMENT / 6;
            }else{
                return $this->prix_abo = parent::ABONNEMENT / 3;
            }
        }
    }
?>

Ici, lorsqu’on tente d’appeler notre méthode getNom() depuis un objet de la classe Admin, la définition de la méthode mère est bien surchargée et c’est la définition de la classe fille qui est utilisée.

<!DOCTYPE html>
<html>
    <head>
        <title>Cours PHP & MySQL</title>
        <meta charset="utf-8">
        <link rel="stylesheet" href="cours.css">
    </head>
    
    <body>
        <h1>Titre principal</h1>
        <?php
            spl_autoload_register(function($classe){
                require 'classes/' .$classe. '.class.php';
            });
            
            $pierre = new Admin('Pierre', 'abcdef', 'Sud');
            $mathilde = new Admin('Math', 123456, 'Nord');
            $florian = new Abonne('Flo', 'flotri', 'Est');
            
            $pierre->getNom();
            echo '<br>';
            $mathilde->getNom();
            echo '<br>';
            $florian->getNom();
        ?>
        <p>Un paragraphe</p>
    </body>
</html>

En déclarant une méthode comme finale avec final en PHP, on empêche la surcharge

Essayons maintenant de définir notre méthode getNom() comme finale dans la classe Utilisateur.

<?php
    abstract class Utilisateur{
        protected $user_name;
        protected $user_region;
        protected $prix_abo;
        protected $user_pass;
        protected $x = 0;
        public const ABONNEMENT = 15;
        
        public function __destruct(){
            //Du code à exécuter
        }
        
        abstract public function setPrixAbo();
        
        final public function getNom(){
            echo $this->user_name;
        }

        public function getPrixAbo(){
            echo $this->prix_abo;
        }
        
        public function plusUn(){
            $this->x++;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
        public function moinsUn(){
            $this->x--;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
    }
?>

Comme notre méthode est définie avec le mot clef final, on n’a plus le droit de la surcharger dans une classe étendue. Si on tente de faire cela, une erreur fatale sera levée par le PHP :

Si on essaie de surcharger une méthode final, une erreur est renvoyée en PHP

 

Définir une classe finale

Si on définit une classe avec le mot clef final, on indique que la classe ne peut pas être étendue. Là encore, si on tente tout de même d’étendre la classe, le PHP renverra une erreur fatale.

<?php
    final class Utilisateur{
        protected $user_name;
        protected $user_region;
        protected $prix_abo;
        protected $user_pass;
        protected $x = 0;
        public const ABONNEMENT = 15;
        
        public function __destruct(){
            //Du code à exécuter
        }
        
        public function setPrixAbo(){}
        
        final public function getNom(){
            echo $this->user_name;
        }

        public function getPrixAbo(){
            echo $this->prix_abo;
        }
        
        public function plusUn(){
            $this->x++;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
        public function moinsUn(){
            $this->x--;
            echo '$x vaut ' .$this->x. '<br>';
            return $this;
        }
    }
?>

En déclarant une classe comme finale avec final en PHP, on empêche la surcharge

Notez ici que déclarer une classe comme abstraite et finale n’a aucun sens puisqu’une classe abstraite est par définition une classe qui va laisser à ses classes étendues le soin d’implémenter certains de ses éléments alors qu’une classe finale ne peut justement pas être étendue.

Par définition, une classe finale est une classe dont l’implémentation est complète puisqu’en la déclarant comme finale on indique qu’on ne souhaite pas qu’elle puisse être étendue. Ainsi, aucune méthode abstraite n’est autorisée dans une classe finale.

Laisser un commentaire