Programmation Orientée Aspect & PHP

L’AOP (Programmation Orientée Aspect) est un concept relativement répandu dans le monde Java ou deux acteurs majeurs proposent leur vision :

En PHP, jusqu’à ce jour, aucun projet n’a réellement vu le jour pour permettre aux développeurs de profiter de ce paradigme, si ce n’est peut être Flow 3 qui le propose à ses utilisateurs (mais cela implique l’utilisation de l’ensemble du framework), ou Symfony via un plugin dédié, qui présente les mêmes inconvénients.

Mais ça, c’était avant


Depuis peu il existe une extension PHP dédiée à l’AOP. Cette extension est encore en phase de test mais devrait prochainement voir le jour sur les dépôts PECL officiels de PHP.

Bien, mais l’AOP ? Qu’est-ce que c’est vraiment ?

Dans le monde de la programmation objet ou l’on découpe un problème de grande envergure en problèmes de moindre envergure, il existe souvent une limitation à cette découpe : les problématiques transverses (cache, logs, droits, …).

Ainsi, votre code métier peut être pollué de lignes de code purement techniques qui n’ont rien à voir avec le métier, ce qui en perturbe la lecture.

Prenons l’exemple de la classe suivante :

    class Services
    {
        public function doGetDataOn ($idData)
        {
            //Du code compliqué et lent de récupération de données
            echo "Calculating doGetDataOn($idData)";
            sleep(1);
            return "doGetDataOn($idData)";
        }
        public function doGetDataOn2 ($idData2)
        {
            //encore du code compliqué et lent de récupération de données
            echo "Calculating doGetDataOn2($idData2)";
            sleep(2);
            return "doGetDataOn2($idData2)";
        }
    }
    

Cette classe va rechercher des données en base, données dont on sait qu’elles évoluent peu. Si vous souhaitez optimiser les temps de réponse des méthodes doGetDataOn et doGetDataOn2, vous avez plusieurs solutions :

  • Faire en sorte, dans le code client, de n’appeler le service qu’une seule fois
  • Dans le code de la méthode, ajouter un cache, soit par l’intermédiaire d’une variable statique, soit par l’intermédiaire d’un objet de type CacheManager.
  • Ajouter un objet de type Proxy Cache qui sera distribué au code client, ou dont le client devra se servir s’il souhaite améliorer les temps de réponse

Toutes ces solutions impliquent ainsi soit une modification lors de l’appel, soit une complexité supplémentaire dans le code de la classe elle même.

AOP will save my hello the world

L’AOP va rendre possible de dire, de façon programmatique, « ce qui serait vraiment bien, c’est que chaque fois que l’on appel doGetDataQqchose on regarde d’abord si cela existe pas déjà en cache ».

Création du « ce serait bien que l’on vérifie en cache »

Voici le code source commenté de ce que pourrait être cette méthode.

    //création de la fonction, "ce serait bien si"
    $conseil = function (AopJoinPoint $atjp) {
        //Le cache sera réalisé sous la forme d'une variable statique
        static $cache = array();

        //On récupère les arguments envoyés à la méthode doGetDataQqChose
        $callArguments = $atjp->getArguments();
        $idData = $callArguments[0];

        //On récupère le nom de la méthode appelée (doGetDataQqchose)
        $calledMethodName = $atjp->getMethodName();

        //vérifie s'il existe un cache pour l'appel courant
        if (isset($cache[$calledMethodName][$idData])) {
           return $cache[$calledMethodName][$idData];
        } else {
           //Le cache n'existe pas, on exécute la méthode d'origine
           $cache[$calledMethodName][$idData] = $atjp->process();        
        }
    };
    

A part les détails de syntaxe du barbare AopJoinpoint, dont les détails ne sont pas pertinents pour l’exemple, le code parle de lui même.

Faire en sorte que PHP prenne en compte ce conseil

Donc l’idéal maintenant serait que l’on puisse donner ce conseil à PHP lui même, et c’est là qu’intervient l’extension AOP (Aspect Oriented Programming on PHP).

    aop_add_around('Services->doGetDataOn*()', $conseil);
    

Et voilà ! PHP va maintenant toujours exécuter le conseil donné pour tous les appels de méthodes doGetDataOnQqchose d’objets de type Services ou dérivés.

Fantastique n’est-ce pas ?

Voyons si ce test fonctionne !?

Créons le code client qui sollicite ces services, de plusieurs instances différentes.

    $service1 = new Services();
    echo $service1->doGetDataOn(1);

    $service2 = new Services();
    echo $service2->doGetDataOn2(1);

    //Le cache est maintenant disponible pour doGetDataOn(1) et doGetDataOn2(1)
    echo $service1->doGetDataOn2(1);
    echo $service2->doGetDataOn(1);
    

Sans AOP, le code afficherait ce qui suit et mettrais 6 secondes à s’afficher (car pas de cache)

    Calculating doGetDataOn(1)
    doGetDataOn(1)
    Calculating doGetDataOn2(1)
    doGetDataOn2(1)
    Calculating doGetDataOn2(1)
    doGetDataOn2(1)
    Calculating doGetDataOn(1)
    doGetDataOn(1)
    

Avec AOP, le code affiche ce qui suit et ne met plus que 3 secondes à s’afficher (car le cache est actif)

    Calculating doGetDataOn(1)
    doGetDataOn(1)
    Calculating doGetDataOn2(1)
    doGetDataOn2(1)
    doGetDataOn2(1)
    doGetDataOn(1)
    

Et maintenant ?

Vous souhaitez utiliser l’extension ? Vous pouvez récupérer sa version de développement sur github.

Vous souhaitez en savoir plus sur les possibilités de l’extension (en anglais pour le moment) ? Vous pouvez consulter sa documentation PDF.

L’équipe qui travaille sur l’extension met tout en oeuvre afin de livrer la première beta officielle cette semaine, testez là et remontez un maximum d’avis, de bug, de demandes de nouveautés, d’améliorations, …

  1. L’AOP est aussi dispo pour les utilisateurs du framework symfony via un bundle (https://github.com/schmittjoh/JMSAopBundle) qui est inclus dans la distribution standard.

    L’ensemble repose sur une librairie de génération de code venant du même auteur: https://github.com/schmittjoh/cg-library

    La nouvelle de cette extension est ceci dit tout à fait bienvenue.

  2. Merci bcp pour ce commentaire pertinent, j’ai mis à jour l’article en conséquence pour ajouter le lien sur la partie Symfony.

    A titre d’information, ce plugin a t il des limitations particulières ? (ex fonctionnement unique sur les composants injectés) ou non ?

    L’idée étant bien sûr d’améliorer l’extension afin de permettre aux développeurs d’obtenir le meilleur des mondes, le tout en natif.

  3. Très intéressant !

    Il y a t-il un impact significatif sur les perfs ?

  4. Bonjour,

    au niveau performance, la question est délicate car variable en fonction de l’usage.

    « En brut », l’AOP est bien sûr impactant, mais pas bien plus que si on effectuait ces appels via proxy / code ajouté à la main, soit 300% je crois.

    En l’état, cela semble énorme. Toutefois, nous avons testé une page Symfony basique avec plusieurs pointcuts (une vingtaine), et nous passons de 53ms à 54ms, ce qui est loin d’être équivalent au coté « brut », et donc négligeable.

    Nous avons discuté avec Benjamin Eberlei (doctrine) de certains aspects de performances, et sommes arrivés à un compromis très raisonnable (même vitesse environs que du PHP pur).

    Il reste encore plusieurs travaux à accomplir maintenant que la beta 1 est disponible, et l’optimisation des performances est l’un des sujets.

    En résumé donc : Impact oui, mais normalement très peu significatif si utilisé à bon escient.

  5. Merci pour ce retour.

    Je vais vraiment regarder ça de plus près.

  6. Merci à toi et Julien pour cet ajout qui peut parfois sauver des (cycles de) vies.

  7. Ne serait pas plus simple si PHP pouvait permettre une fonction magique __trigger qui nous permettrait de faire la même chose ?

  8. __trigger dont l’usage serait proche de __call à la différence près qu’elle se déclenche à chaque appel d’une méthode.

    • Bonjour,

      l’idée de l’AOP n’est pas seulement de décider d’intercepter des méthodes & propriétés à la conception des classes du système, mais bien de gérer des aspects transverses grâce à un tissage (dynamique ou statique).

      Un __trigger serait dans les faits une facilité pour réaliser des proxys, toutefois cela ne permettrait pas d’indiquer, par exemple, en une ligne, « intercepter tous les appels à admin* des classes du namespace B.

  9. Félicitation pour cette extension, c’est vraiment ce qu’il manque à PHP et je l’attendais depuis quelques années (avec des rustines du genre proxy qui du coup rend obsolète class_name, instanceof latebinding, … ce n’est pas top).

    Petite erreur dans les exemples (surement à cause – grace – à l’évolution de l’API) : il n’y a plus la notion de trigger dans le nommage de $atjp->getTriggeringMethodName() et AopTriggeredJoinPoint $atjp).

    A moins que j’ai loupé quelque chose, est-ce que aop-php prend en compte les namespaces ?
    Je n’arrive pas à le faire fonctionner avec une méthodes statiques dans un namespace différent (je n’ai pas encore testé avec une méthode objet avec namespace, le problème est peut être le même).

    Niveau doc, j’ai la doc pdf et le slide de la conférence phptour, existe t-il une doc API pour connaitre la liste des fonctions, paramètres, etc ?

    Au passage il y a la doc en français ?

    Je suis en train de l’intégrer dans un framework open source qui sera bientôt publié sur GitHub :grin:

    Tout conseil est le bienvenue !

    • Merci.

      j’ai corrigé en effet l’article pour refléter les dernières évolutions de l’extension (non mention du trigger*).

      L’extension prends bien en compte les namespaces, il faut simplement les spécifier si besoin.

      Niveau doc, pour le moment le pdf est bien le seul document à jour… en attendant la doc intégrée à php.net

      Pas encore de doc en français.

  10. Ok merci pour la réponse.

    Le phpdoc qui a sur le dépôt est à jour ?

    Pour les namespaces, en fait :

    Mon\Plugin\Config::get() // Fonctionne
    \Mon\Plugin\Config::get() // Fonctionne pas

    Je l’ai ajouté dans les issues sur GitHub histoire de ne pas orienter les commentaires du blog en bug tracker.

    Bonne continuation.

  11. Bonjour,

    j’ai testé la POA avec un des développeurs de mon agence, et je dois avouer que cela m’a agréablement surpris.
    Cependant, j’admets avoir encore du mal à lui trouver une réelle utilité dans le sens où j’ai l’impression que c’est une méthode à appliquer sur des projets de grandes envergures, mais je me trompe peut-être….

Laisser un commentaire


NOTE - Vous pouvez utiliser les éléments et attributs HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>