You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Joomla 5.2.3自定义插件报错:Class 'Factory' not found

Joomla插件报错:Class "Factory" not found 排查方案

我开发了一个自定义Joomla内容插件,功能是指定艺术家/专辑目录后,读取目录中的MP3文件,解析ID3标签并以表格形式展示播放、下载入口及统计数据。运行时Joomla抛出错误:0 Class "Factory" not found,环境为Joomla 5.2.3、PHP 8.2、Linux服务器。

错误日志

2025-01-27T10:39:44+00:00   CRITICAL xxx.xx.xxx.xx  error   Uncaught Throwable of type Error thrown with message "Class "Factory" not found". Stack trace: #0 [ROOT]/plugins/content/mp3streaming/mp3streaming.php(29): PlgContentMp3StreamingHelper::getTracksFromFolder('media/mp3/New...')
#1 [ROOT]/libraries/src/Plugin/CMSPlugin.php(289): PlgContentMp3Streaming->onContentPrepare('com_content.art...', Object(stdClass), Object(Joomla\Registry\Registry), 0)
#2 [ROOT]/libraries/vendor/joomla/event/src/Dispatcher.php(454): 

插件主文件(mp3streaming.php)

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri; // 补充缺失的Uri命名空间

// Load the helper file
require_once __DIR__ . '/helper.php';

class PlgContentMp3Streaming extends CMSPlugin
{
    protected $autoloadLanguage = true;

    public function onContentPrepare($context, &$article, &$params, $limitstart)
    {
        // Regex to match the shortcode
        $regex = '/\{mp3streaming\s+artist="([^"]+)"(?:\s+album="([^"]+)")?\}/';

        if (preg_match($regex, $article->text, $matches)) {
            $artist = $matches[1];
            $album = isset($matches[2]) ? $matches[2] : null;
            $folderPath = 'media/mp3/' . $artist;
            if ($album) {
                $folderPath .= '/' . $album;
            }

            // Fetch and display tracks
            $tracks = PlgContentMp3StreamingHelper::getTracksFromFolder($folderPath);
            $html = '<div class="mp3-streaming">';
            $html .= '<h3>Artist: ' . htmlspecialchars($artist) . '</h3>';
            if ($album) {
                $html .= '<h4>Album: ' . htmlspecialchars($album) . '</h4>';
            }
            $html .= '<table class="table table-striped table-hover">';
            $html .= '<thead><tr>
                        <th>Title</th>
                        <th>Player</th>
                        <th>Download</th>
                        <th>Plays</th>
                        <th>Downloads</th>
                      </tr></thead><tbody>';

            foreach ($tracks as $track) {
                $playUrl = Route::_('index.php?option=com_ajax&plugin=mp3streaming&format=raw&task=play&id=' . $track->id);
                $downloadUrl = Route::_('index.php?option=com_ajax&plugin=mp3streaming&format=raw&task=download&id=' . $track->id);

                $html .= '<tr>
                            <td>' . htmlspecialchars($track->title) . '</td>
                            <td>
                                <audio controls preload="none" onplay="fetch(\'' . $playUrl . '\')">
                                    <source src="' . Uri::root() . $folderPath . '/' . $track->filename . '" type="audio/mpeg">
                                </audio>
                            </td>
                            <td>
                                <a href="' . $downloadUrl . '" class="btn btn-success btn-sm" download>Download</a>
                            </td>
                            <td>' . $track->play_count . '</td>
                            <td>' . $track->download_count . '</td>
                          </tr>';
            }

            $html .= '</tbody></table></div>';
            $article->text = preg_replace($regex, $html, $article->text);
        }
    }
}

辅助类文件(helper.php)

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Factory; // 补充缺失的Factory命名空间

require_once __DIR__ . '/getid3/getid3.php';

class PlgContentMp3StreamingHelper
{
    public static function getTracksFromFolder($folderPath)
    {
        $folderPath = rtrim($folderPath, '/');
        $fullPath = JPATH_ROOT . '/' . $folderPath;

        if (!is_dir($fullPath)) {
            return [];
        }

        $files = array_diff(scandir($fullPath), ['.', '..']);
        $db = Factory::getDbo();
        $getID3 = new getID3;
        $tracks = [];

        foreach ($files as $file) {
            if (strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'mp3') {
                $filePath = $fullPath . '/' . $file;
                $fileInfo = $getID3->analyze($filePath);

                $title = $fileInfo['tags']['id3v2']['title'][0] ?? pathinfo($file, PATHINFO_FILENAME);
                $artist = $fileInfo['tags']['id3v2']['artist'][0] ?? '';
                $album = $fileInfo['tags']['id3v2']['album'][0] ?? '';
                $track = $fileInfo['tags']['id3v2']['track_number'][0] ?? '';
                $size = filesize($filePath);
                $playtime = $fileInfo['playtime_seconds'] ?? 0;

                $query = $db->getQuery(true)
                    ->select('*')
                    ->from($db->quoteName('#__mp3_tracks'))
                    ->where($db->quoteName('filename') . ' = ' . $db->quote($file));

                $db->setQuery($query);
                $trackRecord = $db->loadObject();

                if (!$trackRecord) {
                    $query = $db->getQuery(true)
                        ->insert($db->quoteName('#__mp3_tracks'))
                        ->columns($db->quoteName(['filename', 'album', 'artist', 'title', 'track', 'size', 'playtime', 'play_count', 'download_count', 'upload_date']))
                        ->values(implode(',', [
                            $db->quote($file),
                            $db->quote($album),
                            $db->quote($artist),
                            $db->quote($title),
                            $db->quote($track),
                            $db->quote($size),
                            $db->quote($playtime),
                            0, 0,
                            $db->quote(date('Y-m-d H:i:s'))
                        ]));

                    $db->setQuery($query);
                    $db->execute();

                    $trackRecord = (object)[
                        'id' => $db->insertid(),
                        'filename' => $file,
                        'album' => $album,
                        'artist' => $artist,
                        'title' => $title,
                        'track' => $track,
                        'size' => $size,
                        'playtime' => $playtime,
                        'play_count' => 0,
                        'download_count' => 0,
                    ];
                }

                $tracks[] = $trackRecord;
            }
        }

        return $tracks;
    }
}

问题原因及解决说明

  1. 核心错误原因:helper.php中直接调用Factory::getDbo(),但未导入Joomla\CMS\Factory命名空间。Joomla 4+版本全面采用PSR-4命名空间规范,非继承自CMS核心类的文件(如独立helper类)必须手动声明所需类的命名空间,否则PHP无法找到对应类。

  2. 修复步骤:

    • 在helper.php开头添加use Joomla\CMS\Factory;语句,导入Factory类
    • 额外修复:mp3streaming.php中使用了JUri::root(),该别名在Joomla 5中虽仍可用,但建议使用官方推荐的命名空间use Joomla\CMS\Uri\Uri;,并将JUri::root()改为Uri::root(),避免潜在兼容问题
  3. 验证方法:修改后重新启用插件,测试短代码渲染,确认错误消失且MP3表格正常展示。

内容的提问来源于stack exchange,提问作者9944990

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:44:52