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)

FavShield : vérification facile d'intégrité d'une page côté navigateur

Magazine
Marque
MISC
Numéro
129
Mois de parution
septembre 2023
Spécialité(s)
Résumé

Sur le Web, maintenant que le HTTPS s'est généralisé ou presque, la sécurité repose pour l'essentiel sur le serveur. L'idée de chiffrer les informations les plus confidentielles, telles que les mots de passe, côté client n'a pas rencontré de succès, un examen des codes sources des pages HTML et des scripts JavaScript ne permettant pas de s'assurer qu'ils ne soient pas modifiés lors de la prochaine session. Pourtant, avec un peu d'astuce, le défi peut être relevé.

Caddy, un nouveau venu parmi les serveurs web

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

Depuis l'avènement du Web grand public, on a beaucoup entendu parler de la guerre des navigateurs, mais on en sait beaucoup moins sur la rivalité entre les différents serveurs web. Non sans raison : la domination d'Apache est telle que beaucoup ignorent même qu'il existe des alternatives. Il y en a pourtant. Parmi les dernières, l'une d'elles se fait particulièrement remarquer : Caddy.

Gestion des droits sous Symfony

Magazine
Marque
GNU/Linux Magazine
Numéro
264
Mois de parution
juillet 2023
Spécialité(s)
Résumé

Avoir un site web dynamique, c'est bien, mais pour aller plus loin et offrir une expérience complète à vos visiteurs, il faut leur permettre de s'inscrire pour enrichir leurs interactions. À moins que vous ne soyez en train de développer une application métier en ligne. Dans les deux cas, vous aurez à gérer les utilisateurs et leurs droits. Voyons comment Symfony peut vous venir en aide pour y parvenir.

Les derniers articles Premiums

Les derniers articles Premium

Stubby : protection de votre vie privée via le chiffrement des requêtes DNS

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

Depuis les révélations d’Edward Snowden sur l’espionnage de masse des communications sur Internet par la NSA, un effort massif a été fait pour protéger la vie en ligne des internautes. Cet effort s’est principalement concentré sur les outils de communication avec la généralisation de l’usage du chiffrement sur le web (désormais, plus de 90 % des échanges se font en HTTPS) et l’adoption en masse des messageries utilisant des protocoles de chiffrement de bout en bout. Cependant, toutes ces communications, bien que chiffrées, utilisent un protocole qui, lui, n’est pas chiffré par défaut, loin de là : le DNS. Voyons ensemble quels sont les risques que cela induit pour les internautes et comment nous pouvons améliorer la situation.

Surveillez la consommation énergétique de votre code

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

Être en mesure de surveiller la consommation énergétique de nos applications est une idée attrayante, qui n'est que trop souvent mise à la marge aujourd'hui. C'est d'ailleurs paradoxal, quand on pense que de plus en plus de voitures permettent de connaître la consommation instantanée et la consommation moyenne du véhicule, mais que nos chers ordinateurs, fleurons de la technologie, ne le permettent pas pour nos applications... Mais c'est aussi une tendance qui s'affirme petit à petit et à laquelle à terme, il devrait être difficile d'échapper. Car même si ce n'est qu'un effet de bord, elle nous amène à créer des programmes plus efficaces, qui sont également moins chers à exécuter.

Donnez une autre dimension à vos logs avec Vector

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

Avoir des informations précises et détaillées sur ce qu’il se passe dans une infrastructure, et sur les applications qu'elle héberge est un enjeu critique pour votre business. Cependant, ça demande du temps, temps qu'on préfère parfois se réserver pour d'autres tâches jugées plus prioritaires. Mais qu'un système plante, qu'une application perde les pédales ou qu'une faille de sécurité soit découverte et c'est la panique à bord ! Alors je vous le demande, qui voudrait rester aveugle quand l'observabilité a tout à vous offrir ?

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

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous