fix: replace GD error suppression with logging, remove dead adapters (#49, #36)

- 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)
This commit is contained in:
Jonathan Miller
2026-06-21 10:12:00 -05:00
parent 433ecfea71
commit 0afc8b135a
6 changed files with 32 additions and 301 deletions
@@ -1,60 +0,0 @@
<?php
/**
* @package MokoJoomOpenGraph
* @subpackage com_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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;
}
@@ -1,76 +0,0 @@
<?php
/**
* @package MokoJoomOpenGraph
* @subpackage com_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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() ?: '';
}
}
@@ -1,73 +0,0 @@
<?php
/**
* @package MokoJoomOpenGraph
* @subpackage com_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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 '';
}
}
@@ -1,82 +0,0 @@
<?php
/**
* @package MokoJoomOpenGraph
* @subpackage com_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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));
}
}
@@ -1,7 +1,7 @@
<?php
/**
* @package MokoJoomOpenGraph
* @package MokoSuiteOpenGraph
* @subpackage plg_system_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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 '';
}
@@ -1,7 +1,7 @@
<?php
/**
* @package MokoJoomOpenGraph
* @package MokoSuiteOpenGraph
* @subpackage plg_system_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @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;
}
}