From 0afc8b135a6a62cc63865591dca37ad13b0ac299 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 21 Jun 2026 10:12:00 -0500 Subject: [PATCH] fix: replace GD error suppression with logging, remove dead adapters (#49, #36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace @ error suppression in ImageGenerator with Log::add() warnings for missing GD, missing font, corrupt images (#49) - Add GD extension pre-check before attempting image generation - Add WebP function_exists() guard for servers without WebP support - Remove @ suppression from ImageHelper::loadImage() with logging - Remove unused ContentType adapters (HikaShop, K2, VirtueMart) and ContentTypeInterface — not targeting these platforms (#36) --- .../src/ContentType/ContentTypeInterface.php | 60 -------------- .../src/ContentType/HikaShopAdapter.php | 76 ----------------- .../com_mokoog/src/ContentType/K2Adapter.php | 73 ----------------- .../src/ContentType/VirtueMartAdapter.php | 82 ------------------- .../src/Helper/ImageGenerator.php | 23 +++++- .../src/Helper/ImageHelper.php | 19 +++-- 6 files changed, 32 insertions(+), 301 deletions(-) delete mode 100644 source/packages/com_mokoog/src/ContentType/ContentTypeInterface.php delete mode 100644 source/packages/com_mokoog/src/ContentType/HikaShopAdapter.php delete mode 100644 source/packages/com_mokoog/src/ContentType/K2Adapter.php delete mode 100644 source/packages/com_mokoog/src/ContentType/VirtueMartAdapter.php diff --git a/source/packages/com_mokoog/src/ContentType/ContentTypeInterface.php b/source/packages/com_mokoog/src/ContentType/ContentTypeInterface.php deleted file mode 100644 index 6689aad..0000000 --- a/source/packages/com_mokoog/src/ContentType/ContentTypeInterface.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -namespace Joomla\Component\MokoOG\Administrator\ContentType; - -defined('_JEXEC') or die; - -interface ContentTypeInterface -{ - /** - * Check if this adapter can handle the given component/view. - * - * @param string $option Component option (e.g. com_virtuemart) - * @param string $view View name (e.g. productdetails) - * - * @return bool - */ - public function canHandle(string $option, string $view): bool; - - /** - * Get the content type identifier for database storage. - * - * @return string - */ - public function getContentType(): string; - - /** - * Get the title for the content item. - * - * @param int $id Content item ID - * - * @return string - */ - public function getTitle(int $id): string; - - /** - * Get a description for the content item. - * - * @param int $id Content item ID - * - * @return string - */ - public function getDescription(int $id): string; - - /** - * Get the primary image for the content item. - * - * @param int $id Content item ID - * - * @return string Image path relative to JPATH_ROOT, or empty string - */ - public function getImage(int $id): string; -} diff --git a/source/packages/com_mokoog/src/ContentType/HikaShopAdapter.php b/source/packages/com_mokoog/src/ContentType/HikaShopAdapter.php deleted file mode 100644 index 87f9b2f..0000000 --- a/source/packages/com_mokoog/src/ContentType/HikaShopAdapter.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -namespace Joomla\Component\MokoOG\Administrator\ContentType; - -defined('_JEXEC') or die; - -use Joomla\CMS\Factory; - -class HikaShopAdapter implements ContentTypeInterface -{ - public function canHandle(string $option, string $view): bool - { - return $option === 'com_hikashop' && $view === 'product'; - } - - public function getContentType(): string - { - return 'com_hikashop'; - } - - public function getTitle(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('product_name')) - ->from($db->quoteName('#__hikashop_product')) - ->where($db->quoteName('product_id') . ' = ' . $id); - - $db->setQuery($query); - - return $db->loadResult() ?: ''; - } - - public function getDescription(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('product_description')) - ->from($db->quoteName('#__hikashop_product')) - ->where($db->quoteName('product_id') . ' = ' . $id); - - $db->setQuery($query); - $text = $db->loadResult() ?: ''; - $text = strip_tags($text); - $text = trim(preg_replace('/\s+/', ' ', $text)); - - if (mb_strlen($text) > 160) { - $text = mb_substr($text, 0, 157) . '...'; - } - - return $text; - } - - public function getImage(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('f.file_path')) - ->from($db->quoteName('#__hikashop_file', 'f')) - ->where($db->quoteName('f.file_ref_id') . ' = ' . $id) - ->where($db->quoteName('f.file_type') . ' = ' . $db->quote('product')) - ->order($db->quoteName('f.file_ordering') . ' ASC'); - - $db->setQuery($query, 0, 1); - - return $db->loadResult() ?: ''; - } -} diff --git a/source/packages/com_mokoog/src/ContentType/K2Adapter.php b/source/packages/com_mokoog/src/ContentType/K2Adapter.php deleted file mode 100644 index 028d0c4..0000000 --- a/source/packages/com_mokoog/src/ContentType/K2Adapter.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -namespace Joomla\Component\MokoOG\Administrator\ContentType; - -defined('_JEXEC') or die; - -use Joomla\CMS\Factory; - -class K2Adapter implements ContentTypeInterface -{ - public function canHandle(string $option, string $view): bool - { - return $option === 'com_k2' && $view === 'item'; - } - - public function getContentType(): string - { - return 'com_k2'; - } - - public function getTitle(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('title')) - ->from($db->quoteName('#__k2_items')) - ->where($db->quoteName('id') . ' = ' . $id); - - $db->setQuery($query); - - return $db->loadResult() ?: ''; - } - - public function getDescription(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('introtext')) - ->from($db->quoteName('#__k2_items')) - ->where($db->quoteName('id') . ' = ' . $id); - - $db->setQuery($query); - $text = $db->loadResult() ?: ''; - $text = strip_tags($text); - $text = trim(preg_replace('/\s+/', ' ', $text)); - - if (mb_strlen($text) > 160) { - $text = mb_substr($text, 0, 157) . '...'; - } - - return $text; - } - - public function getImage(int $id): string - { - // K2 stores images as media/k2/items/cache/{md5}_L.jpg - $imagePath = 'media/k2/items/cache/' . md5('Image' . $id) . '_L.jpg'; - - if (is_file(JPATH_ROOT . '/' . $imagePath)) { - return $imagePath; - } - - return ''; - } -} diff --git a/source/packages/com_mokoog/src/ContentType/VirtueMartAdapter.php b/source/packages/com_mokoog/src/ContentType/VirtueMartAdapter.php deleted file mode 100644 index d68a03d..0000000 --- a/source/packages/com_mokoog/src/ContentType/VirtueMartAdapter.php +++ /dev/null @@ -1,82 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -namespace Joomla\Component\MokoOG\Administrator\ContentType; - -defined('_JEXEC') or die; - -use Joomla\CMS\Factory; - -class VirtueMartAdapter implements ContentTypeInterface -{ - public function canHandle(string $option, string $view): bool - { - return $option === 'com_virtuemart' && $view === 'productdetails'; - } - - public function getContentType(): string - { - return 'com_virtuemart'; - } - - public function getTitle(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('product_name')) - ->from($db->quoteName('#__virtuemart_products_' . $this->getLangTag())) - ->where($db->quoteName('virtuemart_product_id') . ' = ' . $id); - - $db->setQuery($query); - - return $db->loadResult() ?: ''; - } - - public function getDescription(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('product_s_desc')) - ->from($db->quoteName('#__virtuemart_products_' . $this->getLangTag())) - ->where($db->quoteName('virtuemart_product_id') . ' = ' . $id); - - $db->setQuery($query); - $desc = $db->loadResult() ?: ''; - - return strip_tags($desc); - } - - public function getImage(int $id): string - { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName('m.file_url')) - ->from($db->quoteName('#__virtuemart_product_medias', 'pm')) - ->join('INNER', $db->quoteName('#__virtuemart_medias', 'm') . ' ON ' . $db->quoteName('m.virtuemart_media_id') . ' = ' . $db->quoteName('pm.virtuemart_media_id')) - ->where($db->quoteName('pm.virtuemart_product_id') . ' = ' . $id) - ->order($db->quoteName('pm.ordering') . ' ASC'); - - $db->setQuery($query, 0, 1); - - return $db->loadResult() ?: ''; - } - - /** - * Get the VirtueMart language table suffix. - * - * @return string - */ - private function getLangTag(): string - { - $lang = Factory::getLanguage()->getTag(); - - return strtolower(str_replace('-', '_', $lang)); - } -} diff --git a/source/packages/plg_system_mokoog/src/Helper/ImageGenerator.php b/source/packages/plg_system_mokoog/src/Helper/ImageGenerator.php index 9f94980..4b35818 100644 --- a/source/packages/plg_system_mokoog/src/Helper/ImageGenerator.php +++ b/source/packages/plg_system_mokoog/src/Helper/ImageGenerator.php @@ -1,7 +1,7 @@ * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. @@ -13,6 +13,7 @@ namespace Joomla\Plugin\System\MokoOG\Helper; defined('_JEXEC') or die; use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Log\Log; class ImageGenerator { @@ -40,13 +41,23 @@ class ImageGenerator array $fontColor = [255, 255, 255], int $quality = 90 ): string { + if (!\extension_loaded('gd')) { + Log::add('MokoOG ImageGenerator: GD extension is not loaded. Image generation disabled.', Log::WARNING, 'mokoog'); + + return ''; + } + $templateAbs = JPATH_ROOT . '/' . ltrim($templateImage, '/'); if (!is_file($templateAbs)) { + Log::add('MokoOG ImageGenerator: Template image not found: ' . $templateImage, Log::WARNING, 'mokoog'); + return ''; } if (!$fontFile || !is_file($fontFile)) { + Log::add('MokoOG ImageGenerator: TTF font file not found: ' . ($fontFile ?: '(not configured)'), Log::WARNING, 'mokoog'); + return ''; } @@ -70,17 +81,21 @@ class ImageGenerator $imageInfo = @getimagesize($templateAbs); if (!$imageInfo) { + Log::add('MokoOG ImageGenerator: Cannot read image dimensions: ' . $templateImage, Log::WARNING, 'mokoog'); + return ''; } $source = match ($imageInfo[2]) { - IMAGETYPE_JPEG => @imagecreatefromjpeg($templateAbs), - IMAGETYPE_PNG => @imagecreatefrompng($templateAbs), - IMAGETYPE_WEBP => @imagecreatefromwebp($templateAbs), + IMAGETYPE_JPEG => imagecreatefromjpeg($templateAbs), + IMAGETYPE_PNG => imagecreatefrompng($templateAbs), + IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? imagecreatefromwebp($templateAbs) : false, default => false, }; if (!$source) { + Log::add('MokoOG ImageGenerator: Failed to load image (unsupported type or corrupt): ' . $templateImage, Log::WARNING, 'mokoog'); + return ''; } diff --git a/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php b/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php index 3ea0325..a5f1386 100644 --- a/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php +++ b/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php @@ -1,7 +1,7 @@ * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. @@ -14,6 +14,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Log\Log; class ImageHelper { @@ -211,12 +212,18 @@ class ImageHelper */ private static function loadImage(string $path, int $type) { - return match ($type) { - IMAGETYPE_JPEG => @imagecreatefromjpeg($path), - IMAGETYPE_PNG => @imagecreatefrompng($path), - IMAGETYPE_GIF => @imagecreatefromgif($path), - IMAGETYPE_WEBP => @imagecreatefromwebp($path), + $image = match ($type) { + IMAGETYPE_JPEG => imagecreatefromjpeg($path), + IMAGETYPE_PNG => imagecreatefrompng($path), + IMAGETYPE_GIF => imagecreatefromgif($path), + IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? imagecreatefromwebp($path) : false, default => false, }; + + if (!$image) { + Log::add('MokoOG ImageHelper: Failed to load image: ' . basename($path), Log::WARNING, 'mokoog'); + } + + return $image; } }