Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Bibliothèques et frameworks PHP Discussion :

PHP-AOP, une extension dédiée à la Programmation Orientée Aspect en PHP


Sujet :

Bibliothèques et frameworks PHP

  1. #1
    Expert éminent
    PHP-AOP, une extension dédiée à la Programmation Orientée Aspect en PHP
    Lorsqu'on réalise une application orientée objet en PHP, on se retrouve souvent confronté à des problématiques transversales (cross-cutting concerns en anglais), c'est-à-dire que notre code métier se retrouve vite entremêlé avec la gestion des droits, du cache ou encore du journal par exemple.
    La Programmation Orientée Aspect (ou AOP) permet de s'affranchir de cette contrainte en isolant les responsabilités dans des aspects que le programmeur doit ensuite tisser à l'aide de points de jonction (join-points en anglais).

    En d'autres termes, l’AOP va rendre possible à dire, de façon programmatique, “ce qui serait vraiment bien, c’est que chaque fois que l’on appelle doGetDataQqchose on regarde d’abord si cela n'existe pas déjà en cache”.

    C'est désormais chose faite avec l'extension PECL AOP-PHP. Disponible sur GitHub et depuis peu dans les dépôts officiels PECL, cette extension permet la création de conseils ou advices qui sont la partie du code à exécuter, de points de jointures ou join-points qui caractérisent le lien entre l'advice et le déclencheur - par exemple la méthode dont l'appel va déclencher l'advice, et les points de rupture ou pointcut qui déterminent si le join-point va déclencher l'advice.

    Pour découvrir les possibilités de cette extension ou apprendre à utiliser ses fonctions, une documentation utilisateur est mise à disposition ici.

    Sources:


    Et vous ?
    Allez-vous utiliser cette extension ou l'utilisez-vous déjà ?
    Trouvez-vous cette initiative intéressante ou inutile ?

  2. #2
    Membre du Club

    En d'autres termes, l’AOP va rendre possible à 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 n'existe pas déjà en cache”.
    je ne connais pas l'AOP mais cette phrase ressemble à de la programmation par contrat.

  3. #3
    Membre du Club
    On fait aussi bien avec un design pattern observer, comme l'EventListener de Symfony2 ...

  4. #4
    Membre éprouvé
    c'est-à-dire que notre code métier se retrouve vite entremêlé avec la gestion des droits, du cache ou encore du journal par exemple.
    Je trouve ca super top, que tu puisse expliquer une grande partie de ton boulot, qui es pour ma part très procédural... Donc, je prends, je vais lire, moi petit merisien.
    Conception / Dev

  5. #5
    Modérateur

    Citation Envoyé par JackDaniels93 Voir le message
    On fait aussi bien avec un design pattern observer, comme l'EventListener de Symfony2 ...
    En effet, mais symfony2 a crée cette fonctionnalité leur niveau (framework). Ici, c'est une extension du langage en natif.
    Cela affect les performances. D'ailleurs, je crois que symfony2 envisage d'intégrer cette extension.

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  6. #6
    Membre à l'essai
    Citation Envoyé par JackDaniels93 Voir le message
    On fait aussi bien avec un design pattern observer, comme l'EventListener de Symfony2 ...
    Et non !

    Même si on peut faire avec l'AOP ce que l'on fait avec l'EventListener de Symfony, on ne pourra pas faire tout ce qu'on fait avec L'AOP avec l'EventListener.

    D'ailleurs, pour être précis au niveau design pattern, l'observé se doit d'avoir la connaissance de ses observateurs, ce qui n'est pas le cas avec l'AOP. Nulle part dans le code de l'objet "observé" on ne trouve d'élément spécifique.

    Ainsi, avec l'AOP, on peut modifier le comportement d'anciennes bibliothèques, qu'elles soit ou non observables en natif.

    Bcp de frameworks utilisent l'AOP, et la façon dont ce dernier est implémenté est souvent par l'intermédiaire de proxy qui réalisent d'autres actions avant / autours / après les éléments englobés.

    Le vocabulaire de l'AOP, contrairement à son concept, n'est pas simple, ce qui en perturbe la compréhension.

    L'AOP peut être vu comme la possibilité de faire des proxys sur n'importe quoi, et que ces proxys soient utilisés automatiquement par le code, sans modification.

  7. #7
    Membre émérite
    Citation Envoyé par Benjamin Delespierre Voir le message
    Lorsqu'on réalise une application orientée objet en PHP, on se retrouve souvent confronté à des problématiques transversales (cross-cutting concerns en anglais), c'est-à-dire que notre code métier se retrouve vite entremêlé avec la gestion des droits, du cache ou encore du journal par exemple.
    Je ne connaît pas php, mais cette phrase m'intrigue (et je lis cette news, parce que pour une fois quelque chose n'a trait ni aux navigateurs ni aux mobiles, ça fait du bien parfois).

    En orienté objet, le code ne devrait pas se retrouver "emmêlé" logiquement, puisque chaque classe doit avoir un rôle précis.
    Si une classe doit gérer : une ressource, les droits de cette ressource, un journal, les droits du journal, c'est peut-être que cette classe devrait faire appel à 2 classes: une pour le journal et ses droits, une autre pour la ressources et ses droits, non?

    Je dois cependant reconnaître ne pas connaître l'AOP...

  8. #8
    Futur Membre du Club
    Citation Envoyé par Freem Voir le message

    En orienté objet, le code ne devrait pas se retrouver "emmêlé" logiquement, puisque chaque classe doit avoir un rôle précis.
    Si une classe doit gérer : une ressource, les droits de cette ressource, un journal, les droits du journal, c'est peut-être que cette classe devrait faire appel à 2 classes: une pour le journal et ses droits, une autre pour la ressources et ses droits, non?
    Je suis d'accord que logiquement, le fait de programmer en orienté objet permet de ne pas tout "emmêlé", mais reste que si tu veux par exemple logger toutes les requêtes SQL, il te faut rajouter une dépendance à un logger (avec de l'injection de dépendance par exemple) dans ton driver de bdd, et ensuite faire appel explicitement à la commande pour logger. Tu te retrouve bien avec du code de log dans une classe sensé gérer uniquement la bdd.

    Et l'aop va te permettre de dire, "ce qui serait vraiment bien, c'est qu'avant d'executer la méthode doQuery sur les classes DriverDb, j'appel mon logger en lui donnant le paramètre query"

  9. #9
    Expert éminent
    Citation Envoyé par Freem Voir le message
    En orienté objet, le code ne devrait pas se retrouver "emmêlé" logiquement, puisque chaque classe doit avoir un rôle précis.
    Si une classe doit gérer : une ressource, les droits de cette ressource, un journal, les droits du journal, c'est peut-être que cette classe devrait faire appel à 2 classes: une pour le journal et ses droits, une autre pour la ressources et ses droits, non?
    En effet, et l'AOP ne remets pas en cause ce découpage. Mais je vais prendre un exemple simple en PHP:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
     
    class Service {
        public function doSomething ($parameters) {
            // validons les paramètres
            Validator::validate($parameters);
     
            // enregistrons un message de log
            Log::message("Doing something !");
     
            // Verifions si on a un cache
            if ($value = Cache::get(__METHOD__, $parameters))
                return $value;
     
            // maintenant faisont notre travail
            $value = $parameters[0] * ($parameters[1] / $parameters[2]);
            $results = Database::query("INSERT INTO something (value) VALUES ('$value')");
            if (!$results)
                throw new RuntimeException("Unable to insert in database");
     
            // sauvegardons le résultat dans un cache
            Cache::set(__METHO__, $parameters, $value);
     
            return $value;
        }
    }

    On a bien réparti les responsabilités, la journalisation est prise en charge par Log, la validation par Validator etc. Il n'empêche que c'est notre classe service qui va devoir se charger de les appeller alors que son "job", c'est uniquement la partie de la ligne 14 jusqu'a la ligne 18. On pourrait bien entendu déléguer ce travail à une autre classe ou méthode ou encore effectuer les opérations de journalisation, de validation et de cache au niveau supérieur (autours de l'appel à Service::doSomething) mais cela alourdirait inutilement notre interface.

    Avec PHP-AOP, il devient possible de règler le problème ainsi:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?php
     
    class Service {
        public function doSomething ($parameters) {
            $value = $parameters[0] * ($parameters[1] / $parameters[2]);
            $results = Database::query("INSERT INTO something (value) VALUES ('$value')");
            if (!$results)
                throw new RuntimeException("Unable to insert in database");
     
            return $value;
        }
    }
     
    aop_add_before('Service::doSomething', 'Validator::validate');
    aop_add_before('Service::doSomething', 'Validator::message');
    aop_add_before('Service::doSomething', 'Cache::get');
    aop_add_after('Service::doSomething', 'Cache::set');

    C'est beaucoup plus propre non ? Là le découpage est total et on peu à son gré ajouter ou retirer des comportements "autours" de Service::getSomething sans jamais toucher à son code.

    Je suis d'accord sur le fait qu'il existe d'autres façon de découpler des interfaces, je voulais simplement vous en présenter une nouvelle, élégante et surtout performante. A vous d'en juger

  10. #10
    Membre éprouvé
    Pour ma part je trouve cela pratique, c'est comme une fonction _call que l'on faisait à la main ( pas très recommandé ) , mais la c'est clair c'est prévu pour, en plus je vois bien une gestion trigger possible, comme avec js...
    je regarde quand même de plus prêt pour être sur , mais dit moi, tu na pas une doc fr?
    Conception / Dev

  11. #11
    Membre confirmé
    ça m'a l'air fort intéressant tout ça, mais pourquoi revenir vers une écriture procédurale ? Il y a une raison à cela ?

    Pourquoi pas quelque chose du genre :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Aop::addBefore('Service::doSomething', 'Validator::validate');


    Plutôt que :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    aop_add_before('Service::doSomething', 'Validator::validate');


    Risque que ça se morde la queue ?

  12. #12
    Membre à l'essai
    Citation Envoyé par Sylvain71 Voir le message
    ça m'a l'air fort intéressant tout ça, mais pourquoi revenir vers une écriture procédurale ? Il y a une raison à cela ?

    Pourquoi pas quelque chose du genre :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Aop::addBefore('Service::doSomething', 'Validator::validate');


    Plutôt que :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    aop_add_before('Service::doSomething', 'Validator::validate');


    Risque que ça se morde la queue ?
    Dans l'usage, Aop::addBefore n'est pas bien plus objet que aop_add_before.

    Il aurait pu être intéressant, pourquoi pas, d'avoir des choses du style :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    $weaver = new AopWeaver();
    $weaver->add(new AopAdvice(new AopPointCut('**/*()'))->setKind(AopAdvice::AROUND)->setCallBack($callback))
    $weaver->activate();
    ...

    voir pourquoi pas des interface AopAdviceAround / AopAdviceBefore que les utilisateurs pourraient implémenter et utiliser comme advice....

    Mais au final, je pense que aop_add_before a l'avantage de ne pas surcharger les nouveaux venus dans l'AOP d'un vocabulaire compliqué, en plus de permettre à ceux qui programment de façon procédural d'en profiter sans se préoccuper d'une couche objet inutile.

    Il n'est pas impossible à l'avenir qu'une version objet de l'API voie le jour, mais pour le moment c'est bien ainsi :-)

  13. #13
    Membre confirmé
    Dans l'usage, Aop::addBefore n'est pas bien plus objet que aop_add_before.
    On est d'accord, je donnais juste un exemple simple pour montrer l'idée.

    Mais au final, je pense que aop_add_before a l'avantage de ne pas surcharger les nouveaux venus dans l'AOP d'un vocabulaire compliqué, en plus de permettre à ceux qui programment de façon procédural d'en profiter sans se préoccuper d'une couche objet inutile.
    Je ne vois pas vraiment l'intérêt de la programmation par aspect sur du procédural et je doute que ceux qui programment encore de cette manière se lancent dans l'AOP avant l'objet.

    Du coup je trouve ça dommage de revenir à une notation procédurale pour quelque chose qui est grandement destiné à une utilisation dans des applis objet.

    Mais bon après pourquoi pas ... j'utilise bien des strtolower et trucs du genre tous les jours après tout

  14. #14
    Membre à l'essai
    Citation Envoyé par Sylvain71 Voir le message
    Je ne vois pas vraiment l'intérêt de la programmation par aspect sur du procédural
    Le concept de l'AOP n'est pas vraiment réservé à l'objet, on peut vouloir vérifier des droits, dans du procédural, avant tout appel à une fonction nommée x ou y.

    Citation Envoyé par Sylvain71 Voir le message
    et je doute que ceux qui programment encore de cette manière se lancent dans l'AOP avant l'objet.
    Encore une fois, je pense que ce n'est pas impossible. Le paradigme objet est touffu, utiliser un brin d'AOP se fait facilement sur n'importe quelle base de code existante.

    On peut très bien imaginer un aspect qui remplace tous les mots grossiers des chaines de caractères d'appels aux méthodes "echo" ou "print"... que l'on fasse de l'objet ou du procédural, c'est une belle économie de lignes.

    Du coup je trouve ça dommage de revenir à une notation procédurale pour quelque chose qui est grandement destiné à une utilisation dans des applis objet.
    Je peux comprendre la frustration et je pense que toute proposition de syntaxe sera la bienvenue sur l'espace "issue" dans le GitHub du projet.

    Si le but de la syntaxe est de coller à PHP et d'être simple à comprendre et utiliser.... je trouve le contrat rempli... perfectible mais rempli

###raw>template_hook.ano_emploi###