¿Error Fatal en Joomla con PHP 8? Solución al conflicto entre DJ-WebP y SP Page Builder
Si gestionas sitios en Joomla, es posible que recientemente te hayas encontrado con una situación frustrante: tu web funciona perfectamente de cara al público, pero al intentar editarla desde el frontend con SP Page Builder, la pantalla se queda en blanco o arroja un error fatal.
Este problema suele aparecer "de la nada", pero casi siempre tiene un culpable silencioso: una actualización de tu servidor a PHP 8 combinada con el plugin de optimización de imágenes DJ-WebP.
El Síntoma: Un error crítico en PHP 8
El mensaje de error específico que suele aparecer es similar a este:
Básicamente, PHP 8 se ha vuelto muy estricto. El plugin intenta procesar una imagen que no existe o es temporal (algo común cuando el editor de SP Page Builder está activo), y en lugar de ignorarla, el servidor detiene todo el proceso provocando un error fatal.
La Solución: Parchear el archivo djimgtowebp.php
Hasta que los desarrolladores lancen una actualización oficial que cubra completamente la compatibilidad con PHP 8.4+, podemos aplicar un pequeño parche de seguridad manualmente. El objetivo es decirle al código: "Si la imagen falla al cargar, ignórala y sigue funcionando".
Paso 1: Localiza el archivo
Accede a tus archivos vía FTP o desde el administrador de archivos de tu hosting y busca la siguiente ruta:
/plugins/system/djimgtowebp/djimgtowebp.php
Paso 2: Aplica el código corregido
Debemos modificar la función encargada de crear la imagen para asegurarnos de que la variable no esté vacía antes de procesarla. A continuación, comparto el código completo del archivo con la corrección de seguridad ya aplicada.
Simplemente, haz una copia de seguridad de tu archivo actual, borra su contenido y pega este código íntegro:
<?php
/**
* @version 1.0
* @package DJ Img To Webp
* Fix PHP 8 compatibility by adding null checks for image creation
*/
defined('_JEXEC') or die;
jimport('joomla.plugin.plugin');
jimport('joomla.filesystem.file');
require_once JPATH_ROOT.'/plugins/system/djimgtowebp/helper.php';
require_once JPATH_ROOT.'/plugins/system/djimgtowebp/lib/autoload.php';
use WebPConvert\WebPConvert;
class PlgSystemDJImgToWebp extends JPlugin
{
protected $_webps;
function onAfterRender()
{
$app = JFactory::getApplication();
$user = JFactory::getUser();
if (JFactory::getDocument()->getType() !== 'html' || !$app->isClient('site')) {
return;
}
$gdInfo = gd_info();
if(!isset($gdInfo['WebP Support']) OR !$gdInfo['WebP Support']) return;
if($this->menuItemIsExcluded()) return;
if (DJWebPHelper::browserSupportWebp()) {
$sHtml = $app->getBody();
$filters =(array) $this->params->get('filters');
$webp_purge = $app->input->get('webp_purge', false, 'bool');
$purge = false;
if ($webp_purge ) {
$purge = true;
}
$debugData = array();
if(is_countable($filters) && count($filters)) {
foreach ($filters as $filter) {
if ($filter->directory) {
$extensions = $filter->extensions;
$quality = $filter->quality;
$stored_time = $filter->stored_time;
$excluded = $filter->excluded;
$excludedArr = strlen($excluded) ? explode(';', $excluded) : array();
$debugData[$filter->directory] = array();
$debugTarget = &$debugData[$filter->directory];
if (substr($filter->directory, 0, 1) === '/') {
$filter->directory = substr($filter->directory, 1);
}
if (substr($filter->directory, -1) === '/') {
$filter->directory = substr($filter->directory, 0, -1);
}
if(count($extensions)) {
$regexPath = str_replace("/", "\/", $filter->directory);
$sHtml = preg_replace_callback(
'/' . $regexPath . '\/\S+(?:' . implode( '|',$extensions) . ')\b/',
function ($match) use ($quality, $stored_time, $excludedArr, $purge, &$debugTarget, $regexPath, $extensions) {
$img = $match[0];
$imgInfo = pathinfo($img);
$extensionsArr = explode("|",implode( '|',$extensions));
if (!in_array($imgInfo['extension'],$extensionsArr)) {
return $img;
}
$newImg = $this->imgToWebp($img, $quality, $excludedArr, $stored_time, $purge, $regexPath, $match);
$debugTarget[] = array(
'source' => $img,
'target' => $newImg
);
return $newImg ? $newImg : $img;
},
$sHtml
);
}
}
}
}
if($this->params->get('debug')) {
$sHtml .= '
' . print_r($debugData, true) . '
'; } $app->setBody($sHtml); } } private function menuItemIsExcluded() { $app = JFactory::getApplication(); $excludedMenuItems = $this->params->get('excludedMenus', array(), 'array'); $activeMenu = $app->getMenu()->getActive(); if(isset($activeMenu->id)) return in_array($activeMenu->id, $excludedMenuItems); else return false; } private function isExcludedDirectory($image, $excluded) { $exist = false; foreach ($excluded as $exclude) { if( strpos( $image, $exclude ) !== false) { $exist = true; break; } } return $exist; } private function exifOrientationRotate($imgObject, $imgPath, $imgMime) { if (function_exists('exif_read_data') && $imgMime == 'image/jpeg') { $file_exif = @exif_read_data($imgPath); if (!is_array($file_exif) || !isset($file_exif['Orientation'])) { return $imgObject; } switch ($file_exif['Orientation']) { case 3: $degrees = 180; break; case 6: $degrees = 270; break; case 8: $degrees = 90; break; default: $degrees = 0; } if ($degrees > 0) { $imgObject = imagerotate($imgObject, $degrees, 0xffffff); } } return $imgObject; } private function imgToWebp($image, $quality = 100, $excluded = array(), $stored_time = 5, $purgePrevious = false, $regexPath = '', $fullRegex = '') { $imgPath = JPATH_ROOT . '/' . $image; $imgInfo = pathinfo($imgPath); $imgHash = md5($imgPath); if(!isset($imgInfo['extension']) || !$imgInfo['extension']) return; if(count($excluded)) { if(in_array($image, $excluded) || $this->isExcludedDirectory($image, $excluded)) { return; } } if(JFile::exists($imgPath)) { if ((mime_content_type($imgPath) != 'image/jpeg') && (mime_content_type($imgPath) != 'image/png')) { return; } if (!isset($this->_webps[$imgHash])) { $newImagePath = $imgInfo['dirname'] . '/'; if ($this->params->get('includeFileExtension') == '1' ) { $newImage = $newImagePath . $imgInfo['filename'] . '_'. $imgInfo['extension'] . '.webp'; } else { $newImage = $newImagePath . $imgInfo['filename'] . '.webp'; } if (JFile::exists($newImage) && $stored_time != '0') { $fileCreated = filemtime($newImage); if($fileCreated && (((time() - $fileCreated) / 3600) >= $stored_time || $purgePrevious)) { JFile::delete($newImage); } } if (!JFile::exists($newImage)) { if (JFile::exists($imgPath)) { if($this->params->get('mode') !== '') { // --- FIX START: Secure image creation --- $img = null; switch (strtolower($imgInfo['extension'])) { case 'png' : $img = @imagecreatefrompng($imgPath); break; case 'jpg' : case 'jpeg' : $img = @imagecreatefromjpeg($imgPath); } // If image creation failed, abort to avoid PHP 8 Fatal Error if (!$img) { return; } // --- FIX END --- if (!JFolder::exists($newImagePath)) { JFolder::create($newImagePath); } imagepalettetotruecolor($img); imagealphablending($img, true); imagesavealpha($img, true); $img = $this->exifOrientationRotate($img, $imgPath, mime_content_type($imgPath)); imagewebp($img, $newImage, $quality); if (function_exists('imagedestroy')) { imagedestroy($img); } } elseif ($this->params->get('mode') == 'library' ) { $options = array( 'converters' => array($this->params->get('method', 'gd')), 'convert' => ['quality' => $quality] ); WebPConvert::convert($imgPath, $newImage, $options); } } } $newFile = str_replace(JPATH_ROOT . '/', "", $newImage); $this->_webps[$imgHash] = $newFile; } return $this->_webps[$imgHash]; } } public function onAjaxDJImgToWebp() { $app = JFactory::getApplication(); $like = trim($app->input->get('like', null, 'string')); $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select('m.id AS value, m.title AS text') ->from($db->quoteName('#__menu', 'm')) ->where($db->quoteName('m.title') . ' LIKE ' . $db->quote('%' . $db->escape($like) . '%')) ->where('m.menutype <> ' . $db->quote('main')) ->where('m.published = 1'); $query->order('title ASC'); $db->setQuery($query); $results = $db->loadObjectList(); echo json_encode($results); $app->close(); } }
Nota importante: Recuerda que si actualizas el plugin en el futuro, este archivo se sobrescribirá y el error podría volver si el desarrollador no ha incluido una solución oficial. Te recomiendo guardar este artículo en favoritos por si necesitas volver a aplicar el parche.
- Detalles
- Escrito por: Paco Guio
- Categoría: Componentes
- Tiempo de lectura: 7 mins






