LinuxParty
A lo largo de los años he tenido la oportunidad de revisar cientos de Joomla! complementos escritos por diferentes desarrolladores, generalmente cuando están causando que un sitio se rompa de manera inesperada. Resulta que la mayoría de los complementos sufren algunos problemas muy comunes y fáciles de prevenir.
Quizás se pregunte, ¿los desarrolladores publican código roto a sabiendas? Lejos de eso, lo han probado... pero solo lo han hecho en el caso de uso muy limitado en el que esperan que se usen sus complementos. Esto se llama “la prueba del camino feliz” y es casi tan malo como no realizar ningún testing. El problema es que cuando el complemento se usa en cualquier otro contexto (la aplicación CLI, la salida no HTML, en los casos en que el formato de salida no se puede determinar hasta después de que el componente de la página haya terminado de ejecutarse) causará consecuencias no deseadas, es decir, el sitio se romperá. Peor aún, los clientes comenzarán a culpar a las únicas partes inocentes, es decir, el propio Joomla y los desarrolladores de terceros cuyo software está escrito de la manera correcta y funciona perfectamente bien. Debería saber que recibimos al menos dos multas cada semana en Akeeba Ltd sobre este tipo de complementos rotos.
Desafortunadamente, este es un problema del huevo y la gallina. Si nunca ha depurado un caso en el que su complemento rompe el sitio, no sabrá por qué, cómo probarlo o cómo escribir su complemento para que no rompa el código y los sitios de otras personas. Si alguien te señala los problemas, te parecerá algo trivial, incluso obvio. La retrospectiva es 20/20.
Entonces, veamos los errores comunes y cómo evitarlos a través de un ejemplo tomado de un complemento del mundo real que permanecerá sin nombre: su desarrollador me contactó en privado y tuvimos una discusión productiva. De hecho, esta interacción es lo que motivó esta publicación de blog.
Una sinfonía de errores
Usaré una parte muy pequeña de un complemento que estaba depurando la semana pasada para mostrarle cuatro errores importantes que posiblemente pueda cometer como desarrollador de complementos de Joomla en cinco líneas de código .
class plgSystemFoobar extends \Joomla\CMS\Plugin\CMSPlugin { public function __construct(&$subject, $config) { parent::__construct($subject, $config); if (\Joomla\CMS\Factory::getApplication()->isClient('administrator')) return; $document = \Joomla\CMS\Factory::getDocument(); $document->addScript(\Joomla\CMS\Uri\Uri::root(true) . 'plugins/system/foobar/js/foobar.js'); } }
Esto parece un complemento del sistema engañosamente simple. Agrega un archivo JavaScript a cada carga de página en la interfaz. ¿Derecha?
Bueno, esa es su intención, pero no lo que realmente hace. También interrumpe las aplicaciones CLI y API de Joomla 4, interrumpe las páginas con salida que no es HTML, prohíbe que los componentes utilicen una salida que no sea HTML e intenta cargar el archivo JavaScript desde el lugar equivocado. Cuatro errores en cinco líneas de código.
No ejecute la lógica del complemento en el constructor del complemento.
Pensemos en la vida de Joomla. En términos generales, la solicitud termina siendo procesada por el index.php archivo de Joomla. Esto hace girar el Joomla! aplicación, por ejemplo, \Joomla\CMS\Application\SiteApplication para la interfaz. El principal punto de entrada para el objeto de la aplicación es el método "doExecute". Este método realiza una gran cantidad de inicialización antes de enrutar y enviar la aplicación, lo que significa que esta inicialización se lleva a cabo antes de que Joomla haya analizado las URL SEF o haya creado un objeto de documento. De hecho, Joomla cargará todos los complementos del sistema habilitados antes de que Joomla haya descubierto los datos básicos sobre sí mismo.
El desarrollador de este complemento puso su lógica comercial en el constructor del complemento que se ejecuta en esta etapa inicial de la inicialización de la aplicación Joomla. Si bien isClient() funcionará, el resto del código que intenta obtener el objeto del documento es un código incorrecto que rompe el sitio .
El complemento pasa erróneamente por el método "\Joomla\CMS\Factory::getDocument()" para obtener el documento. Esto está en desuso en Joomla 4 y se eliminará en Joomla 5. Se supone que debes usar el método "getDocument()" del objeto de la aplicación . Si el desarrollador hubiera hecho eso, habría visto que se están volviendo nulos porque el documento aún no se ha creado.
De hecho, todo este código se debe mover del constructor de objetos del complemento al método "onAfterDispatch" para que funcione correctamente.
No pases por el \Joomla\CMS\Factory \Joomla\CMS\Factory
El segundo problema con este complemento es la razón exacta por la cual el método getDocument() de Factory está obsoleto.
Llamar a getDocument() de la fábrica creará por la fuerza un objeto de documento que luego será utilizado por el objeto de la aplicación. El objeto de documento se crea en función de la información de la solicitud. Sin embargo, como recordará, en este momento Joomla aún no ha analizado la ruta SEF . Esto también sería cierto si este código se moviera en el evento onAfterInitialise, el primer evento de complemento del sistema activado por Joomla.
Dado que la URL de SEF no ha sido analizada, Joomla no puede saber de manera confiable el tipo de documento a usar. Piense, por ejemplo, en una URL como https://www.example.com/foobar.json que, cuando pasa por el enrutador de URL SEF, entre otras cosas, establecerá format=json en la solicitud. Esto significa que esta solicitud espera que Joomla cree un objeto \Joomla\CMS\Document\JsonDocument de documento.
Sin embargo, dado que format=json aún no se ha configurado, Joomla asumirá format=html cuando llame a getDocument(), por lo tanto, creará un objeto \Joomla\CMS\Document\HtmlDocument de documento que también será utilizado por el objeto de la aplicación. Esto, por supuesto, romperá el componente que está manejando la solicitud y la falla radicará únicamente en el autor del complemento roto .
Si tuviera un centavo cada vez que me acusan de que mis componentes tienen errores porque un desarrollador de complementos de terceros hizo esto, sería un hombre rico.
Solo debe llamar a DOS MÉTODOS de \Joomla\CMS\Factory Joomla 4:
- getContainer(). Esto devuelve el contenedor de inyección de dependencia de Joomla (contenedor DI, a veces abreviado como DIC).
- obtenerAplicación(). Esto devuelve el objeto de la aplicación Joomla actual que maneja la solicitud.
Eso es todo. ¡No puedes usar nada más! Todo lo demás se proporciona a través del contenedor DI o a través del propio objeto de la aplicación.
Para obtener el documento de la solicitud debe hacer \Joomla\CMS\Factory::getApplication()->getDocument().
Hay más en Joomla que la salida HTML
Este es un error absurdamente común. Los desarrolladores parecen asumir que Joomla solo generará salida HTML. ESTE NO HA SIDO EL CASO DESDE QUE JOOMLA 1.0 SE LANZÓ EN 2005 ¡POR EL AMOR DE CRISTO! ¡En serio, gente! Esto fue incluso cierto en Mambo, el precursor de Joomla. Joomla no es WordPress, es perfectamente capaz de generar salida no HTML como XML, JSON, fuentes RSS, fuentes Atom, salida binaria sin procesar (por ejemplo, imágenes) y así sucesivamente.
El desarrollador de este complemento hizo la suposición irrazonable ya que $document siempre contendrá un documento HTML.
Una URL como https://www.example.com/index.php?option=com_whatever&format=json, a pesar de los dos problemas ya mencionados anteriormente, aún llenaría $document con un objeto JSONDocument. Sin embargo, JSONDocument no tiene un método "addScript". Por lo tanto, este complemento provoca un error fatal de PHP de inmediato.
La forma correcta de hacerlo se llama "detección de características":
$document = \Joomla\CMS\Factory::getApplication()->getDocument(); if (!($document instanceof \Joomla\CMS\Document\HtmlDocument)) { return; }
Si el documento devuelto por nuestra aplicación no es un documento HTML, nos largamos de Dodge. Sencillo, ¿no?
Hay más en Joomla que el frontend y el backend
Ahora vayamos al error más grande de todos: suponiendo que Joomla consiste completamente en la aplicación frontend (sitio) y backend (administrador). Esto no ha sido cierto desde Joomla 1.6, lanzado en 2010 ; eso fue hace doce años en el momento de escribir este artículo y todavía veo este error.
Esta línea está mal :
if (\Joomla\CMS\Factory::getApplication()->isClient('administrator')) return;
Claramente, el desarrollador quería escribir "si esta no es la interfaz del sitio, no hagas nada". En cambio, lo que realmente escribieron es "si este es el backend del sitio, por lo tanto, es el frontend del sitio o la aplicación api, o la aplicación de consola, o cualquier aplicación personalizada que se extienda desde la clase WebApplication de Joomla, no lo hagas". cualquier cosa". Ups.
Verá, Joomla 4 tiene una serie de aplicaciones incluidas:
- installation: Este es el instalador web cuando construyes un nuevo sitio. El código de terceros, como nuestro complemento del sistema sinfonía de errores, no se carga en él y no afecta a los desarrolladores de terceros. Ha existido desde Joomla 1.0.
- site: La interfaz del sitio. Ha existido desde Joomla 1.0.
- administrator: El backend del sitio. Ha existido desde Joomla 1.0.
- API: La carpeta /api de su sitio Joomla 4. Introducido en Joomla 4.0.
- cli: La aplicación de línea de comandos cli/joomla.php. Introducido en Joomla 4.0.
Además de eso, existen otras aplicaciones además del sitio y el administrador desde Joomla 1.5.
Desde Joomla 1.5 en adelante, es posible crear su propia aplicación personalizada extendiendo JApplicationWeb. Estas aplicaciones personalizadas cargan complementos del sistema de forma predeterminada. Se utilizaron para crear puntos de entrada personalizados para devoluciones de llamada, por ejemplo, en complementos de pago para componentes de comercio electrónico. Ya no se usan porque la razón de su existencia se ha convertido en un punto discutible desde la llegada de com_ajax en Joomla 2.5.
Desde Joomla 1.6 y hasta el lanzamiento de Joomla 5.0, ha sido posible para los desarrolladores crear aplicaciones CLI personalizadas mediante la extensión de JApplicationCli. Estas aplicaciones no cargan los complementos del sistema de forma predeterminada, por lo que es poco probable que se hayan estropeado.
Esta es la razón por la que, a pesar de que esto ha sido un problema durante al menos 10 años, es posible que los desarrolladores de complementos no se hayan topado con esto hasta que se lanzó Joomla 4.
La forma correcta de hacer esto es, por supuesto, verificar explícitamente la aplicación que está ejecutando:
if (!\Joomla\CMS\Factory::getApplication()->isClient('site')) return;
No cargue recursos estáticos directamente y/o desde la carpeta de su complemento
Esta es una ronda de bonificación y no un error que romperá los sitios hoy , pero romperá los sitios con Joomla 5.0 y un leve problema de seguridad :)
El desarrollador de la extensión eligió cargar su archivo JavaScript estático utilizando el método obsoleto addScript() del documento y ubicó el archivo en la estructura de carpetas del complemento. Este es un problema de dos en uno.
En primer lugar, desde Joomla 1.5 (lanzado en 2007, hace 15 años al momento de escribir este artículo), Joomla introdujo la carpeta de medios donde se espera que las extensiones coloquen todos los archivos estáticos disponibles públicamente, ya sean archivos estáticos o contenido generado por el usuario administrado. fuera del Administrador de medios de Joomla.
El desarrollador de la extensión debería haber colocado su archivo JavaScript en la carpeta media/plg_system_foobar/js utilizando la siguiente sección en el manifiesto XML de su complemento:
<media folder="media" destination="plg_system_foobar"> <folder>js</folder> <file>joomla.asset.json</file> </media>
(Veremos qué es el archivo joomla.asset.json en un momento)
Es una mala práctica de seguridad mezclar código backend ejecutable (archivos .php) con código ejecutable frontend (.js, .es6, etc.) archivos y archivos multimedia estáticos (CSS, imágenes, videos, ...). Joomla se está moviendo hacia la colocación de todas las cosas accesibles desde el frontend en la carpeta de medios, incluso para las plantillas, a partir de Joomla 4.1, y lo más probable es que comience a aplicar controles de seguridad para evitar el acceso web a las carpetas de complementos, componentes, módulos, etc. Usted ha sido advertido.
El siguiente problema es que el método addScript() de HTMLDocument ha quedado obsoleto. Joomla 4 ha pasado a usar dependencias de activos y un administrador de activos web para, bueno, ¡administrar las dependencias de activos!
Los activos y sus dependencias se declaran en el archivo joomla.asset.json ubicado en el subdirectorio de la extensión en el directorio de medios . Entonces, el desarrollador debería haber enviado un archivo media/plg_system_foobar/joomla.asset.json con el siguiente contenido:
{ "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json", "name": "plg_system_foobar", "version": "1.0.0", "description": "Foobar plugin", "license": "GPL-3.0-or-later", "assets": [ { "name": "plg_system_foobar.foobar", "description": "Foobar JavaScript", "type": "script", "uri": "plg_system_foobar/foobar.js", "dependencies": [ "core" ], "attributes": { "defer": true } } ] }
Esto le permite al desarrollador decirle a Joomla que agregue su script usando este código muy simple:
$document->getWebAssetManager()->useScript('plg_system_foobar.foobar');
Aquí está el pateador. Esto es seguro incluso si no marca el tipo de objeto Documento . Sí, si $document es un JSONDocument que no tiene un concepto de activos web, este código aún funcionaría. Todavía es una buena idea verificar el tipo de documento de la forma en que le dije para evitar hacer un trabajo inútil o introducir errores en el futuro.
Poniendolo todo junto
Pongamos todo lo que aprendimos juntos. Nuestro complemento de cinco líneas apenas creció un par de líneas y ya no interrumpe los sitios en los que está instalado. Los cambios están en negrita:
<?php class plgSystemFoobar extends \Joomla\CMS\Plugin\CMSPlugin { public function onAfterDispatch() { if (!\Joomla\CMS\Factory::getApplication()->isClient('site')) return; $document = \Joomla\CMS\Factory::getApplication()->getDocument(); if (!($document instanceof \Joomla\CMS\Document\HtmlDocument)) { return; } $document->getWebAssetManager()->useScript('plg_system_foobar.foobar'); } }
Todavía es corto. Todavía es legible. Está (en su mayoría) preparado para el futuro; bueno, todavía usa la estructura heredada de CMSPlugin, pero esa es otra publicación de blog para otro día.
Con suerte, si eres un Joomla! desarrollador de complementos que lea este artículo, ahora puede escribir complementos que no rompan las extensiones de otras personas y los sitios en los que están instalados. Aún mejor, puede comprender si está rompiendo inadvertidamente los sitios de las personas con sus complementos y arreglarlos. Si no lo hace, lo averiguaremos y le haremos leer esta publicación de blog 😉
-
Programación
- Gracias a la IA, el nuevo lenguaje de programación más popular es...
- Cómo instalar y utilizar Scikit-Learn en Linux
- Thomas E. Kurtz, coinventor de BASIC, muere a los 96 años
- Profesor de informática del MIT prueba el impacto de la IA en la formación de programadores
- Lanzamiento del IDE de código abierto Qt Creator 14 con soporte para complementos basados en Lua
- Plantillas para Joomla - Episodio 1: Plantillas, marcos y clubes o no...
- Este es el mejor libro que he visto para aprender a programar en Python en castellano desde cero, gratis y online
- ¿Deberían los niños seguir aprendiendo a programar en la era de la IA?
- La 'obsolescencia' de VBScript confirmada por Microsoft y su eventual eliminación de Windows
- El Gran Debate: ¿Deberían los Modelos de Inteligencia Artificial Ser de Código Abierto?
- El lenguaje de programación BASIC cumple 60 años
- El CEO de Nvidia dice que los niños no deberían aprender a programar
- 40 años de Turbo Pascal: recuerdos del dinosaurio codificador que revolucionó los IDE
- Los lenguajes de programación más populares y dónde aprenderlos.
- Top 5 de los principales lenguajes de programación para desarrollar aplicaciones de escritorio Linux