AOP-PHP : la programmation orientée aspect en PHP
Une nouvelle extension PECL est disponible

Le , par Benjamin Delespierre, Expert éminent
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 ?


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de ascito ascito - Membre éprouvé https://www.developpez.com
le 01/08/2012 à 21:43
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.
Avatar de kolodz kolodz - Modérateur https://www.developpez.com
le 02/08/2012 à 10:28
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.
Avatar de Disco Disco - Membre à l'essai https://www.developpez.com
le 02/08/2012 à 11:48
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.
Avatar de Freem Freem - Membre émérite https://www.developpez.com
le 02/08/2012 à 13:01
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...
Avatar de juguul juguul - Futur Membre du Club https://www.developpez.com
le 02/08/2012 à 13:29
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"
Avatar de Benjamin Delespierre Benjamin Delespierre - Expert éminent https://www.developpez.com
le 02/08/2012 à 13:51
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
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
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
Avatar de ascito ascito - Membre éprouvé https://www.developpez.com
le 02/08/2012 à 16:21
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?
Avatar de Sylvain71 Sylvain71 - Membre confirmé https://www.developpez.com
le 03/08/2012 à 10:50
ç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
Aop::addBefore('Service::doSomething', 'Validator::validate');
Plutôt que :
Code : Sélectionner tout
aop_add_before('Service::doSomething', 'Validator::validate');
Risque que ça se morde la queue ?
Avatar de Disco Disco - Membre à l'essai https://www.developpez.com
le 03/08/2012 à 16:12
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
Aop::addBefore('Service::doSomething', 'Validator::validate');
Plutôt que :
Code : Sélectionner tout
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
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 :-)
Avatar de Sylvain71 Sylvain71 - Membre confirmé https://www.developpez.com
le 03/08/2012 à 16:54
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
Avatar de Disco Disco - Membre à l'essai https://www.developpez.com
le 03/08/2012 à 18:44
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
Offres d'emploi IT
Ingénieur H/F
Safran - Ile de France - Moissy-Cramayel (77550)
Architecte électronique de puissance expérimenté H/F
Safran - Ile de France - Villaroche - Réau
Spécialiste systèmes informatiques qualité et référent procédure H/F
Safran - Ile de France - Colombes (92700)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique PHP