Vous avez décidé d'écrire votre propre cadre. Cela en valait-il la peine?





Comme le disaient les classiques, «je savais que tôt ou tard nous y arriverions». Ainsi, après de nombreuses années de vie tranquille avec Symfony chez les ouvriers et ReactPHP dans les projets familiers, je me suis inscrit dans la création de mon framework.



Mais son histoire ne fait que commencer. Et qu'en est-il de ceux dont l'idée est passée au niveau de la production, mais est restée une solution de niche? J'ai trouvé quelqu'un qui connaît la réponse à cette question - l'auteur et le développeur principal d'un framework orienté aspect.



salut! Voici une transcription partielle de mon podcast "Between the Brackets" - des interviews avec des personnes intéressantes du monde PHP. Dans cet épisode, nous parlerons à Alexander Lisachenko, le créateur d'un framework basé sur Java qui est souvent Google googlé.



Si vous êtes plus à l'aise pour écouter, il y a plus d'exemples techniques dans la version audio .


Très probablement, nous n'avons pas tous rencontré une telle chose comme AOP - programmation orientée aspect en PHP. Et Sasha est resté coincé et a pris sa propre décision de faire glisser cette chose dans nos applications.



Sergey Zhuk, Skyeng et DriftPHP: Commençons à partir de zéro - qu'est-ce que l'AOP et quel problème résout-il?



Alexander Lisachenko, PHP Russie et Go! AOP : L'idée n'est pas nouvelle. AOP a été inventé chez Xerox pour résoudre le problème de fonctionnalité de bout en bout. Et dans le monde Java, l'approche est activement utilisée pour vérifier des fonctionnalités telles que l'autorisation, l'authentification, la mise en cache, la journalisation, la gestion des basculements de fonctionnalités, les disjoncteurs.



Un très large éventail de tâches peut être résolu en utilisant des aspects. Prenons des exemples:



  • . , . , , . — , .
  • — . , - . , , - .
  • : , , . Xdebug — .
  • , — circuit breaker, , , , .


Si nous regardons l'application, nous verrons que de tels fragments de code se trouvent partout et qu'il n'est pas très pratique de travailler avec. Cette fonctionnalité dite de bout en bout est présente dans toute l'application.



Et il n'y a aucun bon moyen de le mettre entre parenthèses en utilisant la programmation traditionnelle orientée objet.



Oui, bien sûr, nous pouvons essayer de prendre un décorateur - mais si nous voulons que notre classe implémente la même interface, nous devons implémenter ou générer chaque méthode directement dans le décorateur. Autrement dit, avoir des décorateurs pour toutes les classes que nous voulons mettre en cache.



Sergey Zhuk: Ok, je l'ai compris. Mais pour cela il y avait déjà quelques extensions PECL, le framework existait même. Pourquoi avez-vous décidé d'écrire le vôtre?



Aleksandr Lisachenko: Oui, vous avez correctement noté qu'il existait déjà un certain nombre de solutions. Mais, comme il me semblait, ils présentaient des inconvénients importants.



Certaines des solutions ont tenté de transformer le code source, d'intégrer immédiatement un conseil spécifique dans une classe spécifique - un morceau de code qui est répété d'une méthode à une autre. Avec xlt, ils génèrent une classe et y mettent absolument tout. En général, il vaut mieux ne pas ouvrir du tout ce code) La



deuxième classe de solutions est les extensions. Par exemple, PHP AOP - il est, en principe, assez fonctionnel, mais il fait tout à l'exécution. Quand on ajoute un conseil, il semble que tout va bien, mais on en ajoute un deuxième et la vitesse commence à diminuer proportionnellement. En conséquence, sur dix astuces, l'application commence à ralentir, et si nous en ajoutons quelques dizaines de plus, c'est tout - le délai d'expiration est garanti.



D'une manière ou d'une autre, il se trouve que j'ai vu comment une idée appelée "filtres" était implémentée dans le framework Lithium - un tel prototype de middleware moderne. Nous créons des points précédemment connus dans le programme et des filtres peuvent être appliqués à ces points - à la fois avant et après l'appel. Cette idée m'a paru tellement intéressante que j'ai décidé d'écrire une application et d'exposer des fonctionnalités transversales à l'aide de ces filtres. J'ai commencé à étudier comment cela se fait en Java.



Et je me suis rendu compte qu'en PHP, il ne sera pas possible de l'implémenter à la légère. C'était probablement le moment le plus intéressant.



En général, tout le processus d'écriture du cadre était une lutte constante avec «non» et «impossible». Il semblerait que je ne puisse pas mettre en œuvre des choses fondamentales, mais c'était d'autant plus intéressant de les traiter.



Sergey Zhuk: Je ne peux pas m'empêcher de demander. Pourquoi choisir AOP? La première version du framework a eu lieu début 2013, le langage Go existait déjà à l'époque, et vous aviez PHP. C'est comme avec JavaScript et Java, non?)



Alexander Lisachenko: Le premier commit n'équivaut pas au début du développement local sur mon ordinateur) A cette époque, Go n'était pas encore populaire. J'ai googlé, j'ai vu qu'il y avait une sorte de développement interne par Google, il occupe un créneau qui lui est propre ... Et ne me dérange pas.



J'ai choisi le nom du cadre lui-même, en fonction des souhaits internes, pour ainsi dire. De aller - "aller", "avancer", "faire".



Sergei Zhuk: Et puis il n'y avait aucune pensée à renommer?



Alexander Lisachenko: Formellement, le nom complet du Go! AOPPHP . Mais avec le temps, j'ai supprimé PHP, car il est passé par Composer et il semble qu'il ne sert à rien de rendre l'huile huileuse.



Jusqu'à présent, je reçois du trafic supplémentaire: beaucoup de gens qui essaient de trouver AOP pour Go se retrouvent sur mon framework PHP. Peut-être que dans une prochaine version, il sera nécessaire de souligner qu'il ne s'agit pas de Go. Jusqu'à présent, personne n'a lancé de problème sur GitHub à cet égard. Il n'y a eu aucune plainte de Google ou de la communauté non plus.



Sergey Zhuk: Ok, comment est-ce implémenté en termes de code client? Ici, j'ai une application, par exemple, sur Laravel, il y a quelques intégrations avec des services tiers, je veux enregistrer ces appels.



Alexander Lisachenko: Il existe déjà un module spécial pour Laravel. Il mettra tout ce dont vous avez besoin dans le système, le configurera. Vous devrez écrire un aspect - ce sera un service, vous le marquerez et il sera automatiquement repris par le noyau AOP. Dans l'aspect même, vous devez comprendre à quels points de l'application, dans quelles classes et méthodes vous souhaitez implémenter la fonctionnalité de mise en cache. Vous pouvez spécifier une méthode directement avec une signature (mais l'option n'est pas très flexible, donc la méthode peut être renommée, mais elle reste dans l'aspect), ou vous pouvez marquer la méthode avec une annotation. La deuxième option est plus flexible: là où il est nécessaire de mettre en cache, marquez-la simplement comme pouvant être mise en cache, et le moteur fera la mise en cache pour vous et appellera le rappel, qui est dans le code de l'aspect.



Au moment du chargement de votre classe avec Composer, le framework intervient et vérifie s'il existe une version prête pour cette classe dans le cache avec un aspect embarqué. S'il y en a, il le rend immédiatement, aucune vérification supplémentaire n'est effectuée au runtime. S'il n'y a pas de cache, alors nous construirons un arbre AST, et en haut de cet arbre nous créerons une classe de réflexion, mais sans la charger en mémoire. Et à ce moment-là, nous pouvons le changer comme nous le voulons, c'est-à-dire tisser le code d'aspect à l'intérieur de cette classe.



Je n'aime pas les nouilles à l'intérieur des aspects et j'ai décidé de diviser la classe en deux parties.



La classe d'origine reste pratiquement inchangée - seul le nom change. Et une deuxième classe apparaît avec le nom de la classe d'origine, qui étend la classe principale et peut, si nécessaire, remplacer plusieurs méthodes.



Pourquoi, en fait, l'héritage. Il existe de nombreuses méthodes et classes qui renvoient une instance. Par exemple, la méthode chain bien connue: on appelle la chaîne de méthodes sur l'objet, elle retourne $ this. Si nous décorons, le premier appel fonctionnera, mais l'aspect tombera. En plus de l'héritage, la mémoire est sauvegardée - car il reste une instance en mémoire.



Sergey Zhuk: Toute cette architecture, le moteur, avez-vous pensé à tout depuis le début ou?



Alexander Lisachenko:Il y avait beaucoup de choses dans lesquelles plonger. Par exemple, je n'étais pas très familier avec AST, j'ai donc étudié de nombreuses disciplines liées à la description des grammaires. Et si vous regardez mon cadre, alors mon pointcut implémente une grammaire à part entière et a sa propre syntaxe - c'est probablement l'une des grandes réalisations. Vous pouvez écrire autant d'expressions complexes que vous le souhaitez, par exemple, «appel de toute méthode publique qui ne commence pas par asset, implémente l'interface tel ou tel espace intérieur de tel ou tel».



J'ai aussi beaucoup creusé dans PHP. J'ai regardé où sont les extensions, comment elles fonctionnent. Quelque part, je me suis assis avec le profilage, optimisé quelque chose, réglé: mais maintenant, si vous connectez simplement le framework AOP, l'application ajoutera de drôles 7 à 10 millisecondes. Au niveau des 100 millisecondes de réponse classiques, il est même imperceptible qu'un tel cadre énorme soit appelé sous le capot.



Sergey Zhuk: Y a-t-il une spécificité pour différents frameworks PHP?



Alexander Lisachenko: En principe, le framework AOP a été conçu comme une bibliothèque générale qui ne nécessite pas de liaison spécifique. La condition principale est d'utiliser Composer. Mais ce n'est pas très convivial avec Symfony.



Il y a trop de magie noire dans Symfony, et quand il entre en conflit avec la magie de mon framework, le plus fort, Symfony, gagne.



En général, l'idée de Symfony est qu'il existe un conteneur, vous devez l'utiliser et ne pas inventer des frameworks séparés pour obtenir la fonctionnalité AOP. Il existe des moyens plus traditionnels: inclure un bundle - disons, JMS AOP ou mon bundle Symfony Go AOP .



Sergey Zhuk: Parlons de la communauté et des concurrents. Est-ce que tu les as?



Alexander Lisachenko: Autant que je sache, il existe actuellement trois cadres. Il y a Ray. Aop, mais il ne sera pas utile pour la production, car il ne sait pas comment travailler efficacement avec Composer. Les auteurs de Flow servent leur cadre avec la sauce que nous avons ici AOP. Il y a autre chose à côté des frameworks chinois, il y a des straps au dessus de Swoole, mais tout cela est au niveau des extensions, et les extensions ne peuvent pas être manquées par les administrateurs pour des raisons de sécurité. J'ai toujours un framework classique, et il reste actif sur n'importe quelle version.



Quant à la communauté. Il n'y a probablement que quatre personnes qui comprennent et comprennent bien: moi, un gars de Serbie et deux personnes de mon ancien emploi qui ont participé à tout ce que j'ai fait. Naturellement, je leur ai montré tous mes développements et résultats. Les deux derniers mois, alors que j'ai changé de travail, j'ai mis très peu d'efforts et d'énergie dans l'open source, mais cela vit et fonctionne de manière autonome.



, - Z-Engine — c , , PHP.



Je prévois, dès que le temps sera libre, de continuer à travailler sur le Z-Engine et de créer la prochaine version du framework, basée sur les structures internes du langage lui-même. Cela fonctionnera presque comme AspectJ de Java. Le but est d'y arriver en PHP 8.



Sergey Zhuk: C'est presque une réécriture complète du cadre?



Alexander Lisachenko: Non, j'ai tout décomposé. Seul le processus d'injection de code change: il y a littéralement quelques classes qui sont chargées d'apporter des modifications à une classe particulière. Et dans ce cas, cette classe ne créera pas de fichiers dans le cache avec des structures différentes, mais à ce moment au moment de l'exécution changera OPcache et modifiera les structures PHP en mémoire.



Sergey Zhuk: Et quelle est l'attitude générale de la communauté PHP à ce sujet? J'aime le PHP asynchrone, il laisse peu de gens indifférents. Comment ça va?



Alexander Lisachenko: Il y aura toujours ceux qui diront que nous pouvons faire cela sans AOP, pourquoi en avons-nous besoin dans les applications. Ma réponse est que si vous ne travaillez pas dans une entreprise, les aspects ne vous sont pas utiles. Et si vous pensez que vous avez une entreprise, mais que vous avez un service et 2-3 développeurs, cela ne fonctionnera pas non plus pour vous) AOP fonctionne bien dans des équipes où il y a plusieurs dizaines de personnes, chacun écrit dans son propre style, généralement plusieurs applications et généralement une architecture de microservices.



Je sais avec certitude qu'il existe un certain nombre de grandes entreprises qui utilisent le cadre chez elles: française, russe. Il arrive que des messages de gratitude arrivent par la poste: ils disent, ils pensaient que nous avions un mois de travail, mais ils ont déterré votre cadre et terminé cette tâche en quelques jours. Les gars ont économisé un mois du travail de leurs développeurs, c'est super.



Sergey Zhuk: Menez-vous des activités éducatives? Votre framework est assez ancien, mais j'ai fait un rapide coup d'œil aux tutoriels - peu. Je pense que si vous demandez à dix personnes ce qu'est l'AOP, neuf diront qu'ils ne savent pas.



Alexander Lisachenko: Oui, c'est vrai.



Sergey Zhuk: Cependant, c'est peut-être bien qu'ils ne sachent pas?



Alexander Lisachenko:C'est un point discutable) Mais il y a un problème que j'ai eu et que j'ai toujours - c'est la documentation. Je n'aime pas écrire de documentation par nature. Je peux écrire des solutions sympas, je peux inventer des choses inhabituelles, des bibliothèques, mais avec de la documentation, c'est juste une douleur.



Même à un moment, un ami de Serbie m'a suggéré: écrivons. Et même commencé à l'écrire, mais après un certain temps, le fusible a également pris fin ... Et il s'est donc avéré qu'ils ne sont pas riches en documentation, disons.



Sergey Zhuk: Alors, la sélection naturelle? Les plus persistants, qui ont grimpé, creusent plus profondément, ils l'utilisent ...



Alexander Lisachenko: Oui, et ce sont les gens qui ont un niveau suffisant pour ne pas se tromper.



Sergey Zhuk: Avez-vous reçu des commentaires de personnes qui ont utilisé le cadre d'une manière que vous n'auriez pas devinée?



Alexander Lisachenko: Oui, il y a eu de tels cas. Par exemple, Mikhail Bodnarchuk, qui a écrit AspectMock . Il a pris le framework et s'est rendu compte qu'avec lui, il pouvait résoudre le problème de l'imprégnation des méthodes finales, des classes et même des fonctions.



Une autre histoire a été lancée par les gars qui ont remanié une ancienne application, et ils n'ont pas eu de tests - en général, tout est classique. Avec l'aide de mon cadre, ils ont enregistré le nom de chaque méthode particulière et en ont fait un aspect global. Avant d'appeler toutes les méthodes publiques de toutes les classes de ce dossier, il a été écrit ce qui est appelé et ce qui est renvoyé avec: quels types, quelles valeurs. Ensuite, ils ont commencé à exécuter cette application sous charge pour obtenir tous les états possibles du code. Ils ont obtenu un ensemble automatique de valeurs autorisées pour chacune des méthodes et des valeurs de retour. Autrement dit, ils ont pris un instantané de l'état entier, puis ont défini l'aspect inverse, qui a appelé la méthode et vérifié si la logique avait changé.



En fait, ils ont réussi à implémenter le refactoring automatique du code sans le couvrir de tests.



C'était une idée tellement cool que pendant un certain temps j'ai réfléchi à la façon de créer un outil pour les applications héritées afin de pouvoir figer toutes les classes, voir comment et comment elles sont appelées, puis, lors de la refactorisation, vérifier que les relations existantes ne sont pas rompues. Mais l'implémenter dans une sorte d'outil qui pourrait être externalisé n'a pas encore fonctionné.



ps Merci d'avoir lu et écouté la fin! D'autres épisodes de podcast peuvent être trouvés ici .



All Articles