Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

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

Le , par Benjamin Delespierre

0PARTAGES

6  0 
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 ?

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de 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.
1  0 
Avatar de 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"
1  0 
Avatar de 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 :-)
1  0 
Avatar de o.deb
Nouveau membre du Club https://www.developpez.com
Le 01/08/2012 à 20:10

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.
1  1 
Avatar de JackDaniels93
Membre du Club https://www.developpez.com
Le 01/08/2012 à 20:43
On fait aussi bien avec un design pattern observer, comme l'EventListener de Symfony2 ...
1  1 
Avatar de 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.
0  0 
Avatar de 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.
0  0 
Avatar de 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...
0  0 
Avatar de 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
0  0 
Avatar de 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?
0  0