PHP : découvrez les méthodes magiques et optimisez votre code

Magazine
Marque
GNU/Linux Magazine
Numéro
151
Mois de parution
juillet 2012
Spécialité(s)


Résumé
Souvent méconnues, parfois décriées, les méthodes magiques de PHP ne sont que rarement utilisées. Pourtant, lorsqu'elles sont maniées avec discernement, elles peuvent faire de véritables miracles... jusqu'à transformer profondément votre façon de programmer !

Body

1. Les méthodes magiques

1.1. Quesako?

Les méthodes magiques constituent une fonctionnalité avancée du langage PHP. Elles permettent de définir sur une classe des méthodes qui sont appelées automatiquement et implicitement par PHP lorsque certains événements se produisent sur une instance de cette classe. La plus connue et la plus utilisée est sans doute __construct(), appelée constructeur de la classe et qui est déclenchée lors de la construction d'un nouvel objet. Vient ensuite __destruct(), appelée lors de la destruction d'une instance. Il en existe appelées lors de la lecture ou de l'écriture d'une variable inaccessible, lors de l'appel de méthodes, lors de la conversion de l'objet en chaîne de caractères... Il y en a toute une petite série, que vous pourrez découvrir sur la page du manuel officiel de PHP qui leur est consacrée (http://php.net/manual/fr/language.oop5.magic.php). Leur utilisation peut affecter considérablement le comportement habituel de PHP, ce qui peut poser problème.

1.2. Le contre

Cet article étant tout entier une explication de l'aspect positif de l'utilisation des méthodes magiques, il nous faut tout de même faire un point sur leurs côtés négatifs. Ainsi, c'est en toute lucidité que vous les utiliserez (ou pas).

La principale critique qui peut être faite à l'utilisation des méthodes magiques est qu'une fois qu'elles sont en place, il est facile de perdre conscience de leur action, en particulier lorsqu'il y a un changement de développeur sur un projet. En effet, la magie se glissant à des moments où PHP a un comportement attendu relativement simple, la lecture de la valeur d'une propriété par exemple, on peut complètement ignorer que PHP va se montrer bien plus actif.

print $lukeSkywalker->filsde;

Comment savoir en lisant cette ligne de code que quelque chose de bien plus fort peut se dérouler ? Pourtant, en détaillant un peu le contexte, on peut se rendre compte que la moindre tentative de lecture ou d'écriture sur la propriété filsde va déclencher l'appel à la méthode prevenirPapa...

class Jedi {

 function __get($name) {

 if ($name=='filsde') $this->prevenirPapa();

 }

 function __set($name,$value) {

 if ($name=='filsde') {

 $this->prevenirPapa();

 $this->filsde = $value;

 }

}

class Skywalker extends Jedi {}

$darkVador = new Skywalker();

$lukeSkywalker = new Skywalker();

$lukeSkywalker->filsde = $darkVador;

Ici, pour rendre les choses compréhensibles, nous avons isolé le code pertinent pour notre exemple. Mais imaginez maintenant que votre code soit réparti entre une dizaine de classes dont votre objet final va hériter, il vous sera beaucoup plus difficile de repérer une méthode magique dans la dizaine de fichiers de classes et les quelques milliers de lignes de codes qu'ils vont peut-être contenir, surtout si vous n'en êtes pas l'auteur et que vous n'avez pas conscience de leur présence a priori. Et si un bug s'y glisse, vous risquez d'avoir les plus grandes difficultés à comprendre le comportement de PHP.

À cela, une réponse : ces méthodes ne sont pas appelées « magiques » pour rien. Tout le monde ne peut pas se prétendre Gandalf, c'est évident. Mais cela ne veut pas dire qu'il faut interdire à Gandalf d'être lui-même, non? Surtout si on peut en attendre un bénéfice... Retenons-en qu'il faut user de la magie avec sagesse et parcimonie.

2. Notre exemple : les propriétés autodéterminées

2.1. Pourquoi ?

Dans la pratique quotidienne de la programmation objet, le développeur tend à séparer de manière de plus en plus précise tous les traitements d'une classe dans des méthodes séparées. Celles-ci se multiplient d'autant plus que le développeur est expérimenté et qu'il sait distinguer plus de blocs de traitements pertinents. Il parvient ainsi à la fois à anticiper la factorisation du code et à lui donner une plus grande souplesse, permettant de réorganiser les traitements plus facilement lorsqu'un nouvel usage de la classe apparaît. Tout l'intérêt de la programmation objet est là.

Ce faisant, il va découvrir que certaines de ces méthodes peuvent être appelées plusieurs fois au cours de la vie de l'objet, pour fournir un résultat qui sera toujours le même. Il suffit qu'un nouvel usage de la classe implique l'utilisation de deux autres méthodes qui font toutes les deux appel à une troisième; résultat d'une factorisation. Si le traitement de cette troisième méthode est lourd, comme une requête SQL sur un serveur distant, par exemple, il s'en suit nécessairement une dégradation importante des performances de l'objet. Il peut y avoir bien d'autres cas similaires qui se produisent dans le même objet. Lorsqu'il en a conscience, le programmeur prend la décision d'enregistrer le résultat de la troisième méthode dans une propriété de l'objet. Il ne lui reste alors qu'à tester si la propriété est définie pour savoir s'il doit exécuter le traitement. Il peut le faire avant l'appel de la méthode, et il devra essayer d'y penser à chaque fois ; ou alors, il peut le faire dans la méthode elle-même et l'oublier. Il créé ainsi une sorte de cache interne à l'objet.

À l'usage, il se peut même qu'il implémente ce cache pour chaque propriété dont il sait que la valeur ne variera plus durant la vie de l'objet, sans même se poser la question de savoir si cette méthode va être utilisée plusieurs fois. Une autre pratique qui tend à dégrader les performances consiste à appeler ces méthodes juste pour être sûr que ces propriétés ont été définies, n'étant plus certain du contexte d'utilisation dans lequel on se trouve, ou même de celui où l'on pourrait se trouver... Là, le programmeur commence à se demander s'il sait vraiment ce qu'il fait.

Une solution peut être de définir ces propriétés une fois pour toutes dans le constructeur de la classe, mais cette solution ne conviendra pas dans tous les cas – ce n'est pas parce que la propriété est définie une seule fois qu'elle peut l'être dès la création de l'objet ; et puis cela implique d'exécuter à chaque instance de la classe le traitement permettant de déterminer ces propriétés, alors qu'il se pourrait très bien que ces traitements soient inutiles.

C'est le moment pour le programmeur de devenir magicien.

2.2. Comment ?

#/usr/bin/php
<?php
class withAutocalc {
 protected $ArrayOfAutocalcProperties;
 function __get($name) {
 if (!isset($this->$name)) {
 $method = 'get'.$name.'Autocalc';
 if (method_exists($this,$method)) $this->$method();
 return $this->$name;
 }
 }
 function getArrayOfAutocalcPropertiesAutocalc() {
 $methods = get_class_methods($this);
 $pattern = '/^get(?P<prop>\w+)Autocalc$/';
 foreach ($methods as $method) {
 preg_match($pattern,$method,$matches);
 if (count($matches)) $array[] = $matches['prop'];
 }
 $this->ArrayOfAutocalcProperties = $array;

  function getTestVarAutocalc() {
 return $this->TestVar = "Abracadabra";
 }
}
$test = new withAutocalc();
var_dump($test->ArrayOfAutocalcProperties);
print $test->TestVar."\n";

Si vous exécutez ce code, il affichera:

array(2) {

[0]=>

string(25) "ArrayOfAutocalcProperties"

[1]=>

string(7) "TestVar"

}

Abracadabra

Qu'avons-nous fait ? Que s'est-il passé ici ?

Nous avons défini une nouvelle classe withAutocalc, avec deux méthodes __get et getArrayOfAutocalcProperties. Celle qui nous intéresse prioritairement est la première : en effet, si vous essayez d'accéder en lecture à une propriété dite « inaccessible » et que la méthode __get est définie sur l'objet, alors celle-ci est appelée, avec en paramètre le nom de la propriété à laquelle vous essayez d'accéder.

Qu'est-ce qu'une propriété inaccessible ? Il y a plusieurs cas : tout d'abord, une propriété non encore définie ; puis le cas d'une propriété protected à laquelle vous essayez d'accéder depuis l'extérieur de l'objet ; enfin la propriété private à laquelle vous essayez d'accéder depuis une classe fille ou depuis l'extérieur de l'objet.

Que fait notre méthode __get()? En premier lieu, elle teste si la propriété est déjà définie. Si elle l'est, alors il s'agit d'une propriété private ou protected inaccessible, mais définie. Dans ce cas, notre méthode n'a rien à faire, puisque nous ne voulons l'utiliser que pour définir automatiquement des propriétés indéfinies. Si la propriété n'est pas définie, alors notre méthode va vérifier s'il existe une autre méthode de l'objet qui permettrait de définir cette propriété. Pour cela, __get() s'appuie sur la convention de nommage que voici : pour une propriété X, s'il existe une méthode pour la définir automatiquement, celle-ci doit avoir pour nom getXAutocalc. Si __get() découvre qu'il existe une telle méthode, celle-ci est appelée. Si elle retourne un résultat, celui-ci est retourné.

2.3. Effets indésirables inattendus

2.3.1. Sac percé

Si avez la (mauvaise) habitude d'utiliser un objet indéfini comme sac à variables et que vous essayez d'appliquer notre nouvelle méthode, vous aurez la mauvaise surprise de constater que votre sac est percé...

Par exemple, ajoutons une méthode à notre classe withAutocalc:

class testSacAVar extends withAutocalc {

 function test() {

 $this->sac->mapropriete = 'test';

 var_dump($this->sac->mapropriete);

 }

}

$test = new testSacAVar();

$test->test();

En toute logique, le résultat attendu est :

string(4) "test"

Pourtant, on obtient :

NULL

En fait, PHP ne parvient pas à créer un objet standard lorsqu'on lui affecte une variable si la méthode __get est présente... Il faut légèrement modifier le code comme suit :

class testSacAVar extends withAutocalc {

 function test() {

 $this->sac = new stdClass();

 $this->sac->mapropriete = 'test';

 var_dump($this->sac->mapropriete);

 }

}

$test = new testSacAVar();

$test->test();

2.3.2. Propriétés private lisibles et redéterminables depuis l'extérieur

Dans notre exemple, nous avons pris soin de déclarer la propriété ArrayOfAutocalcProperties comme étant privée. Pourtant, nous sommes parvenus à provoquer sa définition depuis l'extérieur de l'objet et à en lire la valeur. À bien y réfléchir, en l'état actuel de notre code, il serait même possible de provoquer plusieurs fois la redéfinition de la propriété depuis l'extérieur de l'objet. Il est aisé de s'en prévenir : on peut vérifier avant l'appel à la méthode de calcul si la variable ne serait pas déjà définie. Toutefois, il n'y a pas de moyen d'empêcher que la propriété devienne au moins lisible depuis l'extérieur. La chose en soi a peu de conséquences, mais tout de même, on évitera de s'appuyer sur cette méthode pour des classes prenant en charge des éléments de la sécurité d'un site ou d'une application.

2.4. Un bilan globalement positif

Nous ne présenterons pas ici de script écrit selon les techniques habituelles de cache, car pour bien montrer les avantages de la nôtre, il nous faudrait produire beaucoup de code pour seulement en faire ressortir la redondance, ce qui est assez peu intéressant. Sachez simplement que dans le script qui m'a mené à implémenter cette méthode et qui utilise cette technique de façon intensive, j'ai pu réduire mes lignes de code de 30% et celles qui restent sont bien plus lisibles et compréhensibles, presque élémentaires. Les quelques tests que j'ai effectués sur ce même script pour effectuer des redimensionnements d'images en série, m'ont montré que j'avais obtenu un gain de performances de l'ordre de 1%, ce qui n'est pas négligeable pour un traitement identique suivant un algorithme identique – hors le fonctionnement du cache et s'appuyant, comme il est classique de le faire en PHP, massivement sur une bibliothèque externe (ImageMagick en l'occurrence). Enfin, étant donné la simplicité du nouveau code, je pense que la généralisation des variables autodéterminées dans mon travail devrait me permettre également de gagner en productivité. Difficile de dire combien aujourd'hui.

Pour conclure avec une bonne nouvelle, en relisant ce script, je me suis aperçu que la conception en parait moins procédurale et que l'objet devient capable de déterminer lui-même le parcours de méthodes de définition de variables qui lui est nécessaire pour produire le résultat attendu, ne définissant ainsi que le strict minimum nécessaire pour le traitement. L'algorithme s'optimise ainsi sans effort.

class optimum extends withAutocalc {

 function getApropAutocalc() {

 return 'A';

 }

 function getBpropAutocalc() {

 return $this->A.'B';

 }

 function getCpropAutocalc() {

 return $this->B.'C';

 }

}

$test = new optimum();

print $test->Bprop;

Ce code affichera :

AB

Dans cet exemple, la lecture de Bprop provoque sa détermination, qui entraîne la détermination de Aprop. Cprop n'est jamais déterminée, car cela est inutile et non demandé. Il n'y a même nulle part de décision du programmeur pour y parvenir, ni de recherche d'optimisation ou du meilleur parcours possible, il n'a pas même à se poser la question.




Article rédigé par

Par le(s) même(s) auteur(s)

ntfy.sh : installez un service de notification pour suivre les événements de votre SI

Magazine
Marque
Linux Pratique
Numéro
144
Mois de parution
juillet 2024
Spécialité(s)
Résumé

ntfy.sh (prononcez « notify ») est un service en ligne vous permettant d’envoyer et de recevoir des notifications avec une facilité déconcertante. Comme il s’agit d’un logiciel libre, aussi bien en ce qui concerne le serveur que les clients, il n’y a vraiment pas de raison de se priver !

Développement de macros en Rust

Magazine
Marque
GNU/Linux Magazine
Numéro
268
Mois de parution
mars 2024
Spécialité(s)
Résumé

Programmer en Rust, c’est bien. Mais programmer, toujours en Rust, des générateurs de code Rust, exécutés tout juste au moment de la compilation, c’est mieux ! Voilà ce que permettent les macros, avec toujours cette efficacité redoutable à laquelle nous a habitués ce langage.

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 65 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous