
Como dizem os clássicos, “sabia que mais cedo ou mais tarde chegaríamos a isso”. Então eu, depois de muitos anos de vida tranquila com Symfony em workers e ReactPHP em projetos de estimação, me encaixo na criação do meu framework.
Mas sua história está apenas começando. E quanto àqueles cuja ideia cresceu ao nível de produção, mas permaneceu uma solução de nicho? Eu encontrei alguém que sabe a resposta para esta pergunta - o autor e desenvolvedor líder de uma estrutura orientada a aspectos.
Olá! Aqui está uma transcrição parcial do meu podcast "Between the Brackets" - entrevistas com pessoas interessantes do mundo do PHP. Neste episódio, falaremos com Alexander Lisachenko, o criador de uma estrutura baseada em Java que os gophers costumam pesquisar no Google.
Se você se sentir mais confortável ouvindo, há mais exemplos técnicos na versão em áudio .
Provavelmente, nem todos nós encontramos algo como AOP - programação orientada a aspectos em PHP. E Sasha travou e tomou sua própria decisão de arrastar essa coisa para nossos aplicativos.
Sergey Zhuk, Skyeng e DriftPHP: Vamos começar do zero - o que é AOP e que problema ele resolve?
Alexander Lisachenko, PHP Rússia e Go! AOP : A ideia não é nova. O AOP foi inventado na Xerox para resolver o problema de funcionalidade de ponta a ponta. E no mundo Java, a abordagem é usada ativamente para verificar funcionalidades como autorização, autenticação, armazenamento em cache, registro, gerenciamento de alternadores de recursos e disjuntores.
Uma ampla gama de tarefas pode ser resolvida usando aspectos. Vamos usar exemplos:
- . , . , , . — , .
- — . , - . , , - .
- : , , . Xdebug — .
- , — circuit breaker, , , , .
Se olharmos para o aplicativo, veremos que tais fragmentos de código são encontrados em todos os lugares e não é muito conveniente trabalhar com eles. Essa funcionalidade de ponta a ponta está presente em todo o aplicativo.
E não há uma boa maneira de resolver isso de alguma forma usando a programação orientada a objetos tradicional.
Sim, claro, podemos tentar usar um decorador - mas se quisermos que nossa classe implemente a mesma interface, precisamos implementar ou gerar cada método diretamente no decorador. Ou seja, ter decoradores para todas as classes que queremos armazenar em cache.
Sergey Zhuk: Ok, entendi. Mas para isso já havia algumas extensões PECL, o framework até existia. Por que você decidiu escrever o seu próprio?
Aleksandr Lisachenko: Sim, você observou corretamente que já havia uma série de soluções. Mas, como me pareceu, eles tinham desvantagens significativas.
Algumas das soluções tentaram transformar o código-fonte, imediatamente incorporar um conselho específico em uma classe específica - um trecho de código que se repete de um método para outro. Com o xlt, eles geram uma classe e colocam absolutamente tudo nela. Em geral, é melhor não abrir esse código.) A
segunda classe de soluções são as extensões. Por exemplo, PHP AOP - é, em princípio, bastante funcional, mas faz tudo em tempo de execução. Quando adicionamos um conselho, parece que está tudo bem, mas adicionamos um segundo e a velocidade começa a diminuir proporcionalmente. Assim, em dez dicas, o aplicativo começa a ficar lento e, se adicionarmos mais algumas dezenas, é tudo - o tempo limite é garantido.
De alguma forma, eu vi como uma ideia chamada "filtros" foi implementada no framework de Lithium - um protótipo de middleware moderno. Criamos alguns pontos previamente conhecidos no programa e filtros podem ser aplicados a esses pontos - antes e depois da chamada. Essa ideia parecia tão interessante que decidi escrever um aplicativo e expor a funcionalidade de corte transversal usando esses filtros. Comecei a estudar como isso é feito em Java.
E percebi que em PHP não será possível implementá-lo de uma só vez. Esse foi provavelmente o momento mais interessante.
Em geral, todo o processo de escrever o framework foi uma luta constante com "não" e "impossível". Pareceria que eu não conseguia implementar coisas fundamentais, mas era ainda mais interessante lidar com elas.
Sergey Zhuk: Não posso deixar de perguntar. Por que ir AOP? O primeiro lançamento do framework ocorreu no início de 2013, a linguagem Go já estava lá e você tinha o PHP. É como com JavaScript e Java, certo?)
Alexander Lisachenko: O primeiro commit não é igual ao início do desenvolvimento local no meu computador) Naquela época, Go ainda não era popular. Pesquisei, vi que existe algum tipo de desenvolvimento interno do Google, ele ocupa algum nicho próprio ... E não incomodou.
Escolhi o nome do próprio framework, com base nos desejos internos, por assim dizer. De ir - "vá", "avance", "faça".
Sergei Zhuk: E então não houve pensamentos para renomear?
Alexander Lisachenko: Formalmente, o nome completo do Go! AOPPHP . Mas com o tempo, removi o PHP, porque ele é instalado através do Composer e parece que não adianta deixar o óleo oleoso.
Até agora, estou recebendo um tráfego extra: muitas pessoas tentando encontrar AOP for Go acabam no meu framework PHP. Talvez em alguma versão futura seja necessário enfatizar que não se trata de Go. Até agora, ninguém iniciou um problema no GitHub a esse respeito. Não houve reclamações do Google nem da comunidade.
Sergey Zhuk: Ok, como isso é implementado em termos de código do cliente? Aqui eu tenho um aplicativo, por exemplo, no Laravel, há algumas integrações com serviços de terceiros, quero registrar essas chamadas.
Alexander Lisachenko: Já existe um módulo especial para o Laravel. Ele vai colocar tudo que você precisa no sistema, configurá-lo. Você precisará escrever um aspecto - será um serviço, você o marcará e ele será automaticamente selecionado pelo núcleo AOP. No próprio aspecto, você precisa entender em quais pontos do aplicativo, em quais classes e métodos, você deseja implementar a funcionalidade de cache. Você pode especificar um método diretamente com uma assinatura (mas a opção não é muito flexível, então o método pode ser renomeado, mas permanece no aspecto), ou você pode marcar o método com uma anotação. A segunda opção é mais flexível: onde for necessário fazer o cache, basta marcá-lo como cacheable, e a engine fará o caching para você e chamará o callback, que está no código do aspecto.
Na hora de carregar sua classe com o Composer, o framework intervém e verifica se existe uma versão pronta para esta classe no cache com um aspecto embutido. Se houver, ele o devolve imediatamente, nenhuma verificação adicional é feita no tempo de execução. Se não houver cache, então vamos construir uma árvore AST, e no topo desta árvore vamos criar uma classe de reflexão, mas sem carregá-la na memória. E neste momento podemos alterá-lo da maneira que quisermos, ou seja, tecer o código do aspecto dentro desta classe.
Não gosto de macarrão por dentro e decidi dividir a aula em duas partes.
A classe original permanece praticamente inalterada - apenas o nome muda. E uma segunda classe aparece com o nome da classe original, que estende a principal e pode, se necessário, substituir vários métodos.
Por que, na verdade, herança. Existem muitos métodos e classes que retornam instância de volta. Por exemplo, o conhecido método em cadeia: chamamos a cadeia de métodos no objeto, ele retorna $ this. Se decorarmos, a primeira chamada funcionará, mas o aspecto cairá. Junto com a herança, a memória é salva - porque ainda há uma instância na memória.
Sergey Zhuk: Toda essa arquitetura, o motor, você pensou em tudo desde o início ou?
Alexander Lisachenko:Havia muitas coisas em que mergulhar. Por exemplo, eu não estava muito familiarizado com AST, então estudei muitas disciplinas relacionadas à descrição de gramáticas. E se você olhar para o meu framework, então meu pointcut implementa uma gramática completa e tem sua própria sintaxe - esta é provavelmente uma das grandes conquistas. Você pode escrever quantas expressões complexas quiser, por exemplo, “chamada de qualquer método público que não comece com ativo, implementa interface tal e tal dentro do espaço de tal e tal”.
Também cavei muito dentro do PHP. Observei onde estão as extensões, como funcionam. Em algum lugar eu sentei com perfil, algo otimizado, ajustado: mas agora, se você apenas conectar a estrutura AOP, o aplicativo adicionará alguns engraçados 7 a 10 milissegundos. No nível dos clássicos 100 milissegundos de resposta, é até imperceptível que uma estrutura tão grande esteja sendo chamada de sob o capô.
Sergey Zhuk: Existe uma especificidade para diferentes frameworks PHP?
Alexander Lisachenko: Em princípio, a estrutura AOP foi concebida como uma biblioteca geral que não requer fiação específica. A principal condição é usar o Composer. Mas não é muito amigável com o Symfony.
Existe muita magia negra no Symfony, e quando ela entra em conflito com a magia do meu framework, o mais forte, Symfony, vence.
Em geral, a ideia do Symfony é que existe um contêiner, você precisa usá-lo e não inventar frameworks separados para obter a funcionalidade AOP. Existem maneiras mais tradicionais: incluir um pacote - digamos, JMS AOP ou meu pacote Symfony Go AOP .
Sergey Zhuk: Vamos falar sobre a comunidade e os concorrentes. Você os tem?
Alexander Lisachenko: Pelo que eu sei, existem três frameworks agora. Ali está o Ray. Aop, mas não será útil para a produção, porque não sabe como trabalhar efetivamente com o Composer. Os autores de Flow servem sua estrutura com o molho que temos aqui AOP. Há algo mais próximo aos frameworks chineses, há alças em cima do Swoole - mas isso é tudo no nível das extensões, e as extensões não podem ser perdidas pelos administradores por razões de segurança. Eu ainda tenho um framework clássico e ele permanece vivo em qualquer versão.
Quanto à comunidade. Provavelmente existem apenas quatro pessoas que entendem e entendem bem: eu, um cara da Sérvia e duas pessoas do meu emprego anterior que participaram de tudo o que eu fiz. Naturalmente, mostrei a eles todos os meus desenvolvimentos e resultados. Nos últimos meses, conforme mudei de emprego, coloquei muito pouco esforço e energia no código aberto, mas ele vive e funciona de forma autônoma.
, - Z-Engine — c , , PHP.
Eu planejo, assim que o tempo ficar livre, continuar trabalhando no Z-Engine e fazer a próxima versão do framework, baseado nas estruturas internas da própria linguagem. Funcionará quase como o AspectJ do Java. O objetivo é chegar lá no PHP 8.
Sergey Zhuk: Isso é quase uma reescrita completa da estrutura?
Alexander Lisachenko: Não, tenho tudo decomposto. Apenas o processo de injeção de código muda: existem literalmente algumas classes que são responsáveis por fazer edições em uma classe específica. E neste caso, esta classe não fará no cache arquivos com estruturas diferentes, mas neste momento em tempo de execução mudará o OPcache e modificará as estruturas do PHP na memória.
Sergey Zhuk: E qual é a atitude geral da comunidade PHP em relação a esse tópico? Gosto de PHP assíncrono, deixa poucas pessoas indiferentes. Como você está lidando com isso?
Alexander Lisachenko: Sempre haverá aqueles que dizem que podemos fazer isso sem AOP, por que precisamos disso nos aplicativos. Minha resposta é que, se você não trabalha em uma empresa, os aspectos não são úteis para você. E se você acha que tem uma empresa, mas tem um serviço e 2-3 desenvolvedores, isso também não funcionará para você) AOP funciona bem em equipes onde há várias dezenas de pessoas, cada uma delas escreve em seu próprio estilo, há, normalmente, vários aplicativos e, normalmente, uma arquitetura de microsserviço.
Tenho certeza de que há várias grandes empresas que usam a estrutura em casa: francesa, russa. Acontece que algumas mensagens de agradecimento chegam pelo correio: dizem, acharam que tínhamos um mês de trabalho, mas desenterraram sua estrutura e concluíram essa tarefa em alguns dias. Os caras economizaram um mês do trabalho de seus desenvolvedores, isso é ótimo.
Sergey Zhuk: Você realiza alguma atividade educacional? Seu framework é antigo o suficiente, mas eu dei uma olhada rápida nos tutoriais - esparsamente. Parece que perguntando a dez pessoas o que é AOP, nove dirão que não sabem.
Alexander Lisachenko: Sim, é verdade.
Sergey Zhuk: Embora, talvez seja bom que eles não saibam?
Alexander Lisachenko:Este é um ponto discutível) Mas há um problema que eu tive e ainda tenho - é a documentação. Não gosto de escrever documentação por natureza. Eu posso escrever soluções legais, posso inventar algumas coisas incomuns, bibliotecas, mas com documentação é só uma dor.
Num determinado momento, um amigo da Sérvia me sugeriu: vamos escrever. E ele até começou a escrever, mas depois de um tempo o fusível também acabou ... E então descobri que eles não são ricos em documentação, digamos.
Sergey Zhuk: Então, seleção natural? Os mais persistentes, que escalam, cavam mais fundo, usam ...
Alexander Lisachenko: Sim, e essas são as pessoas que têm nível suficiente para não atrapalhar .
Sergey Zhuk: Você recebeu algum feedback de pessoas que usaram a estrutura de uma forma que você não imaginaria?
Alexander Lisachenko: Sim, houve casos assim. Por exemplo, Mikhail Bodnarchuk, que escreveu AspectMock . Ele pegou o framework e percebeu que com ele poderia resolver o problema de fazer com que métodos, classes e até funções finais fossem absorvidos.
Outra história foi contada por caras que refatoraram um aplicativo antigo e não tinham testes - em geral, tudo é clássico. Com a ajuda do meu framework, eles gravaram como cada método particular é chamado e fizeram um aspecto global. Antes de chamar todos os métodos públicos em todas as classes desta pasta, foi escrito o que é chamado e o que é retornado com: quais tipos, quais valores. Em seguida, eles começaram a executar este aplicativo sob carga para obter todos os estados possíveis do código. Eles têm um conjunto automático de valores permitidos para cada um dos métodos e valores de retorno. Ou seja, eles tiraram um instantâneo de todo o estado e, em seguida, configuraram o aspecto reverso, que chamou o método e verificou se a lógica havia mudado.
Na verdade, eles conseguiram implementar a refatoração automática de código sem cobri-la com testes.
Foi uma ideia tão legal que por algum tempo pensei em fazer uma ferramenta para aplicativos legados para que pudesse congelar todas as classes, ver o que e como são chamadas e depois, durante a refatoração, verificar se os relacionamentos existentes não foram rompidos. Mas implementá-lo em algum tipo de ferramenta que poderia ser terceirizada ainda não deu certo.
PS Obrigado por ler e ouvir até o fim! Mais episódios de podcast podem ser encontrados aqui .