Decidiste escribir tu propio marco. ¿Valió la pena?





Como decían los clásicos, “sabía que tarde o temprano llegaríamos a eso”. Entonces, después de muchos años de vida tranquila con Symfony en los trabajadores y ReactPHP en los proyectos favoritos, encajo en la creación de mi marco.



Pero su historia apenas comienza. ¿Y aquellos cuya creación ha crecido hasta el nivel de producción, pero sigue siendo una solución de nicho? Encontré a alguien que conoce la respuesta a esta pregunta: el autor y desarrollador líder de un marco orientado a aspectos.



¡Hola! Aquí hay una transcripción parcial de mi podcast "Between the Brackets": entrevistas con personas interesantes del mundo PHP. En este episodio, hablaremos con Alexander Lisachenko, el creador de un marco de trabajo basado en Java que a menudo busca en Google.



Si se siente más cómodo escuchando, hay más ejemplos técnicos en la versión de audio .


Lo más probable es que no todos nos hayamos encontrado con AOP: programación orientada a aspectos en PHP. Y Sasha se quedó atascado y tomó su propia decisión de incluir esto en nuestras aplicaciones.



Sergey Zhuk, Skyeng y DriftPHP: Empecemos desde cero: ¿qué es AOP y qué problema resuelve?



Alexander Lisachenko, PHP Rusia y Go! AOP : La idea no es nueva. AOP se inventó en Xerox para abordar el problema de funcionalidad de un extremo a otro. Y en el mundo de Java, el enfoque se utiliza activamente para verificar funcionalidades como autorización, autenticación, almacenamiento en caché, registro, gestión de alternancia de funciones, interruptores automáticos.



Se puede resolver una amplia gama de tareas utilizando aspectos. Usemos ejemplos:



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


Si miramos la aplicación, veremos que este tipo de fragmentos de código se encuentran por todas partes y no es muy conveniente trabajar con ella. Esta llamada funcionalidad de extremo a extremo está presente en toda la aplicación.



Y no hay una buena manera de arreglarlo de alguna manera usando la programación tradicional orientada a objetos.



Sí, por supuesto, podemos intentar tomar un decorador, pero si queremos que nuestra clase implemente la misma interfaz, necesitamos implementar o generar cada método directamente en el decorador. Es decir, tener decoradores para todas las clases que queremos almacenar en caché.



Sergey Zhuk: Ok, lo tengo. Pero para esto ya existían algunas extensiones PECL, incluso existía el framework. ¿Por qué decidiste escribir el tuyo propio?



Aleksandr Lisachenko: Sí, ha notado correctamente que ya existían varias soluciones. Pero, como me pareció, tenían importantes inconvenientes.



Algunas de las soluciones intentaron transformar el código fuente, incrustar inmediatamente un consejo específico en una clase específica: un fragmento de código que se repite de un método a otro. Con xlt generan una clase y ponen absolutamente todo allí. En general, es mejor no abrir este código en absoluto) La



segunda clase de soluciones son las extensiones. Por ejemplo, PHP AOP es, en principio, bastante funcional, pero hace todo en tiempo de ejecución. Cuando agregamos un consejo, parece que todo está bien, pero agregamos un segundo y la velocidad comienza a disminuir proporcionalmente. En consecuencia, con diez consejos, la aplicación comienza a ralentizarse, y si agregamos un par de docenas más, eso es todo: el tiempo de espera está garantizado.



De alguna manera sucedió que vi cómo se implementó una idea llamada "filtros" en el marco de Lithium, un prototipo de middleware moderno. Creamos algunos puntos previamente conocidos en el programa, y ​​se pueden aplicar filtros a estos puntos, tanto antes como después de la llamada. Esta idea me pareció tan interesante que decidí escribir una aplicación y exponer la funcionalidad transversal usando estos filtros. Empecé a estudiar cómo se hace en Java.



Y me di cuenta de que en PHP no será posible implementarlo de una sola vez. Ese fue probablemente el momento más interesante.



En general, todo el proceso de redacción del marco fue una lucha constante con el "no" y el "imposible". Parecería que no podía implementar cosas fundamentales, pero era aún más interesante lidiar con ellas.



Sergey Zhuk: No puedo evitar preguntar. ¿Por qué ir a AOP? La primera versión del marco tuvo lugar a principios de 2013, el lenguaje Go ya existía en ese momento y tenías PHP. Es como con JavaScript y Java, ¿verdad?)



Alexander Lisachenko: La primera confirmación no equivale al comienzo del desarrollo local en mi computadora) En ese momento, Go aún no era popular. Busqué en Google, vi que hay algún tipo de desarrollo interno por parte de Google, ocupa algún nicho propio ... Y no me molesté.



Elegí el nombre del marco en sí, basado en los deseos internos, por así decirlo. De ir - "ir", "avanzar", "hacer".



Sergei Zhuk: ¿Y luego no hubo pensamientos de cambiar el nombre?



Alexander Lisachenko: Formalmente, el nombre completo de Go! AOPPHP . Pero con el tiempo, eliminé PHP, porque pasa por Composer y parece que no tiene sentido hacer que el aceite sea aceitoso.



Hasta ahora, estoy obteniendo algo de tráfico adicional: mucha gente que intenta encontrar AOP para Go terminan en mi marco PHP. Quizás en alguna versión futura sea necesario destacar que no se trata de Go. Hasta ahora, nadie ha iniciado un problema en GitHub a este respecto. Tampoco hubo quejas de Google ni de la comunidad.



Sergey Zhuk: Ok, ¿cómo se implementa esto en términos del código de cliente? Aquí tengo una aplicación, por ejemplo, en Laravel, hay un par de integraciones con servicios de terceros, quiero registrar estas llamadas.



Alexander Lisachenko: Ya existe un módulo especial para Laravel. Él pondrá todo lo que necesita en el sistema, lo configurará. Deberá escribir un aspecto: será un servicio, lo etiquetará y el núcleo de AOP lo recogerá automáticamente. En el aspecto mismo, debe comprender en qué puntos de la aplicación, en qué clases y métodos, desea implementar la funcionalidad de almacenamiento en caché. Puede especificar un método directamente con una firma (pero la opción no es muy flexible, por lo que se puede cambiar el nombre del método, pero permanece en el aspecto), o puede marcar el método con una anotación. La segunda opción es más flexible: cuando sea necesario almacenar en caché, simplemente márquelo como caché, y el motor hará el almacenamiento en caché por usted y llamará a la devolución de llamada, que está en el código del aspecto.



En el momento de cargar su clase con Composer, el marco interviene y verifica si hay una versión lista para esta clase en el caché con un aspecto incrustado. Si lo hay, lo devuelve inmediatamente, no se realizan comprobaciones adicionales en tiempo de ejecución. Si no hay caché, construiremos un árbol AST, y encima de este árbol crearemos una clase de reflexión, pero sin cargarla en la memoria. Y en este momento podemos cambiarlo como queramos, es decir, tejer el código de aspecto dentro de esta clase.



No me gustan los aspectos internos de los fideos y decidí dividir la clase en dos partes.



La clase original permanece prácticamente sin cambios, solo cambia el nombre. Y aparece una segunda clase con el nombre de la clase original, que amplía la principal y puede, si es necesario, anular varios métodos.



Por qué, de hecho, herencia. Hay muchos métodos y clases que devuelven la instancia. Por ejemplo, el conocido método de cadena: llamamos a la cadena de métodos en el objeto, devuelve $ this. Si decoramos, la primera llamada funcionará, pero luego el aspecto se caerá. Junto con la herencia, la memoria se guarda, porque todavía hay una instancia en la memoria.



Sergey Zhuk: Toda esta arquitectura, el motor, ¿has pensado en todo desde el principio o?



Alexander Lisachenko:Había muchas cosas en las que sumergirse. Por ejemplo, no estaba muy familiarizado con AST, así que estudié muchas disciplinas relacionadas con la descripción de gramáticas. Y si miras mi marco, entonces mi pointcut implementa una gramática completa y tiene su propia sintaxis; este es probablemente uno de los grandes logros. Puede escribir tantas expresiones complejas como desee, por ejemplo, "llamar a cualquier método público que no comience con activo, implemente interfaz tal o cual dentro del espacio de tal o cual".



También investigué mucho dentro de PHP. Observé dónde están las extensiones, cómo funcionan. En algún lugar me senté con perfiles, optimicé algo, sintonicé: pero ahora, si solo conecta el marco AOP, la aplicación agregará algunos divertidos 7-10 milisegundos. En el nivel de los clásicos 100 milisegundos de respuesta, es incluso imperceptible que se esté llamando bajo el capó un marco tan enorme.



Sergey Zhuk: ¿Existe una especificidad para diferentes marcos PHP?



Alexander Lisachenko: En principio, el marco AOP se concibió como una biblioteca general que no requiere cableado específico. La condición principal es utilizar Composer. Pero no es muy amigable con Symfony.



Hay demasiada magia negra en Symfony, y cuando choca con la magia en mi marco, el más fuerte, Symfony, gana.



En general, la idea de Symfony es que hay un contenedor, es necesario usarlo y no inventar marcos separados para obtener la funcionalidad AOP. Hay formas más tradicionales: incluir un paquete, por ejemplo, JMS AOP o mi paquete Symfony Go AOP .



Sergey Zhuk: Hablemos de la comunidad y los competidores. ¿Los tienes?



Alexander Lisachenko: Hasta donde yo sé, ahora hay tres marcos. Ahí está Ray. Aop, pero no será útil para la producción, porque no sabe cómo trabajar eficazmente con Composer. Los autores de Flow sirven su marco con la salsa que tenemos AOP aquí. Hay algo más junto a los marcos chinos, hay correas en la parte superior de Swoole, pero todo esto es a nivel de extensiones, y los administradores no pueden perderse las extensiones por razones de seguridad. Todavía tengo un marco clásico y se mantiene vivo en cualquier versión.



En cuanto a la comunidad. Probablemente solo hay cuatro personas que entienden y entienden bien: yo, un chico de Serbia y dos personas de mi trabajo anterior que participaron en todo lo que hice. Naturalmente, les mostré todos mis desarrollos y resultados. Los últimos meses, cuando cambié de trabajo, puse muy poco esfuerzo y energía en el código abierto, pero vive y funciona de manera autónoma.



, - Z-Engine — c , , PHP.



Planeo, tan pronto como tenga tiempo libre, seguir trabajando en Z-Engine y hacer la próxima versión del framework, basada en las estructuras internas del propio lenguaje. Funcionará casi como AspectJ de Java. El objetivo es llegar allí en PHP 8.



Sergey Zhuk: ¿Esto es casi una reescritura completa del marco?



Alexander Lisachenko: No, tengo todo descompuesto. Solo cambia el proceso de inyección de código: hay literalmente algunas clases que son responsables de realizar ediciones en una clase en particular. Y en este caso, esta clase no hará archivos en la caché con diferentes estructuras, pero en este momento en tiempo de ejecución cambiará OPcache y modificará las estructuras PHP en la memoria.



Sergey Zhuk: ¿Y cuál es la actitud general de la comunidad PHP hacia este tema? Me gusta el PHP asincrónico, deja indiferente a pocas personas. ¿Cómo te va con eso?



Alexander Lisachenko: Siempre habrá quienes digan que podemos hacer esto sin AOP, ¿por qué lo necesitamos en las aplicaciones? Mi respuesta es que si no trabaja en una empresa, no necesitará los aspectos. Y si crees que tienes una empresa, pero tienes un servicio y 2-3 desarrolladores, esto tampoco te funcionará) AOP funciona bien en equipos donde hay varias decenas de personas, cada una escribe con su propio estilo, hay, normalmente varias aplicaciones y normalmente una arquitectura de microservicio.



Estoy seguro de que hay una serie de grandes empresas que utilizan el marco en casa: francés, ruso. Sucede que por correo llegan algunos mensajes con agradecimiento: dicen que pensaban que teníamos un mes de trabajo, pero desenterraron tu marco y completaron esa tarea en un par de días. Los chicos salvaron un mes del trabajo de sus desarrolladores, eso es genial.



Sergey Zhuk: ¿Realiza alguna actividad educativa? Su marco es lo suficientemente antiguo, pero busqué rápidamente los tutoriales, escasamente. Creo que si le preguntas a diez personas qué es AOP, nueve dirán que no saben.



Alexander Lisachenko: Sí, es cierto.



Sergey Zhuk: Aunque, ¿quizás sea bueno que no lo sepan?



Alexander Lisachenko:Este es un punto discutible) Pero hay un problema que tenía y todavía tengo: es la documentación. No me gusta escribir documentación por naturaleza. Puedo escribir soluciones interesantes, puedo inventar algunas cosas inusuales, bibliotecas, pero con la documentación es una molestia.



Incluso en un momento un amigo de Serbia me sugirió: escribamos. E incluso empecé a escribirlo, pero al cabo de un tiempo también se acabó la mecha ... Y así resultó que no son ricos en documentación, digamos.



Sergey Zhuk: Entonces, ¿selección natural? Los más persistentes, que subieron, cavan más profundo, lo usan ...



Alexander Lisachenko: Sí, y estas son las personas que tienen un nivel suficiente para no meter la pata.



Sergey Zhuk: ¿Ha recibido comentarios de personas que han utilizado el marco de una manera que no habría adivinado?



Alexander Lisachenko: Sí, hubo casos de este tipo. Por ejemplo, Mikhail Bodnarchuk, quien escribió AspectMock . Tomó el marco y se dio cuenta de que con él podía resolver el problema de conseguir que los métodos finales, clases e incluso funciones empaparan.



Otra historia fue lanzada por tipos que refactorizaron una aplicación antigua y no tenían pruebas; en general, todo es clásico. Con la ayuda de mi marco, registraron cómo se llama cada método en particular e hicieron un aspecto global. Antes de llamar a todos los métodos públicos en todas las clases de esta carpeta, se escribió lo que se llama y lo que se devuelve: qué tipos, qué valores. Luego comenzaron a ejecutar esta aplicación bajo carga para obtener todos los estados posibles del código. Obtuvieron un conjunto automático de valores permitidos para cada uno de los métodos y valores de retorno. Es decir, tomaron una instantánea de todo el estado y luego establecieron el aspecto inverso, que llamó al método y verificó si la lógica había cambiado.



De hecho, lograron implementar la refactorización automática de código sin cubrirlo con pruebas.



Fue una idea tan genial que durante algún tiempo pensé en cómo hacer una herramienta para aplicaciones heredadas para poder congelar todas las clases, ver con qué y cómo se llaman, y luego, durante la refactorización, verificar que las conexiones existentes no estén rotas. Pero implementarlo en algún tipo de herramienta que podría subcontratarse aún no ha funcionado.



ps ¡ Gracias por leer y escuchar hasta el final! Se pueden encontrar más episodios de podcast aquí .



All Articles