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)

FrankenPHP : vers une nouvelle génération de serveurs PHP ?

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

C'est bien connu, PHP est lent. Si au fil des versions, il a fait énormément de progrès, son principe de fonctionnement originel, celui d'un langage interprété nécessitant la relecture de tous les fichiers impliqués à chaque requête, pose une limite indépassable. Vraiment ? Et si le serveur conservait toute l'application en mémoire à tout moment ? Voilà ce que vous propose FrankenPHP.

Borg Backup, la sauvegarde facile, économique et efficace

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

Retour aux fondamentaux : aucun système d'information n'a plus de valeur que sa sauvegarde ! Malgré cela, cet aspect est souvent négligé, tant on n'en ressent pas l'absence jusqu'au jour où elle se faire cruellement sentir. Mais ce jour-là, il est trop tard. Voyons comment prévenir le pire avec Borg Backup, et son outil compagnon Borgmatic.

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

Quarkus : applications Java pour conteneurs

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

Initié par Red Hat, il y a quelques années le projet Quarkus a pris son envol et en est désormais à sa troisième version majeure. Il propose un cadre d’exécution pour une application de Java radicalement différente, où son exécution ultra optimisée en fait un parfait candidat pour le déploiement sur des conteneurs tels que ceux de Docker ou Podman. Quarkus va même encore plus loin, en permettant de transformer l’application Java en un exécutable natif ! Voici une rapide introduction, par la pratique, à cet incroyable framework, qui nous offrira l’opportunité d’illustrer également sa facilité de prise en main.

De la scytale au bit quantique : l’avenir de la cryptographie

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

Imaginez un monde où nos données seraient aussi insaisissables que le célèbre chat de Schrödinger : à la fois sécurisées et non sécurisées jusqu'à ce qu'un cryptographe quantique décide d’y jeter un œil. Cet article nous emmène dans les méandres de la cryptographie quantique, où la physique quantique n'est pas seulement une affaire de laboratoires, mais la clé d'un futur numérique très sécurisé. Entre principes quantiques mystérieux, défis techniques, et applications pratiques, nous allons découvrir comment cette technologie s'apprête à encoder nos données dans une dimension où même les meilleurs cryptographes n’y pourraient rien faire.

Les nouvelles menaces liées à l’intelligence artificielle

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

Sommes-nous proches de la singularité technologique ? Peu probable. Même si l’intelligence artificielle a fait un bond ces dernières années (elle est étudiée depuis des dizaines d’années), nous sommes loin d’en perdre le contrôle. Et pourtant, une partie de l’utilisation de l’intelligence artificielle échappe aux analystes. Eh oui ! Comme tout système, elle est utilisée par des acteurs malveillants essayant d’en tirer profit pécuniairement. Cet article met en exergue quelques-unes des applications de l’intelligence artificielle par des acteurs malveillants et décrit succinctement comment parer à leurs attaques.

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 64 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous