4cc3f5bee4
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 6s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Release configuration (pull_request) Successful in 6s
Generic: Repo Health / Scripts governance (pull_request) Successful in 6s
Generic: Repo Health / Repository health (push) Successful in 14s
Generic: Repo Health / Repository health (pull_request) Successful in 12s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 44s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been skipped
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been skipped
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
- Convert tabs to spaces (3,413 violations) - Fix line endings, trailing whitespace, brace placement - Break lines exceeding 150-char absolute limit - Replace heredoc tab closers with spaces - Fix empty elseif, forbidden function calls - Update phpcs.xml: exclude rules inappropriate for CLI scripts (SideEffects, MissingNamespace, MultipleClasses, HeaderOrder, empty catch blocks) Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
285 lines
8.7 KiB
PHP
285 lines
8.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* This file is part of a Moko Consulting project.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* FILE INFORMATION
|
|
* DEFGROUP: MokoStandards.Enterprise.Plugins
|
|
* INGROUP: MokoStandards.Enterprise
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
* PATH: /lib/Enterprise/PluginRegistry.php
|
|
* BRIEF: Plugin registry for available project plugins
|
|
*/
|
|
|
|
namespace MokoEnterprise;
|
|
|
|
use MokoEnterprise\Plugins\JoomlaPlugin;
|
|
use MokoEnterprise\Plugins\DolibarrPlugin;
|
|
use MokoEnterprise\Plugins\GenericPlugin;
|
|
use MokoEnterprise\Plugins\DocumentationPlugin;
|
|
use MokoEnterprise\Plugins\NodeJsPlugin;
|
|
use MokoEnterprise\Plugins\PythonPlugin;
|
|
use MokoEnterprise\Plugins\TerraformPlugin;
|
|
use MokoEnterprise\Plugins\WordPressPlugin;
|
|
use MokoEnterprise\Plugins\MobilePlugin;
|
|
use MokoEnterprise\Plugins\ApiPlugin;
|
|
use MokoEnterprise\Plugins\McpServerPlugin;
|
|
|
|
/**
|
|
* Plugin Registry - Central registry for all project type plugins
|
|
*
|
|
* Manages plugin discovery, registration, and lifecycle
|
|
*
|
|
* @package MokoStandards\Enterprise
|
|
* @version 1.0.0
|
|
*/
|
|
class PluginRegistry
|
|
{
|
|
/** @var array<string, string> Map of project types to plugin class names */
|
|
private static $pluginClasses = [
|
|
'joomla' => JoomlaPlugin::class,
|
|
'dolibarr' => DolibarrPlugin::class,
|
|
'generic' => GenericPlugin::class,
|
|
'documentation' => DocumentationPlugin::class,
|
|
'nodejs' => NodeJsPlugin::class,
|
|
'python' => PythonPlugin::class,
|
|
'terraform' => TerraformPlugin::class,
|
|
'wordpress' => WordPressPlugin::class,
|
|
'mobile' => MobilePlugin::class,
|
|
'api' => ApiPlugin::class,
|
|
'mcp-server' => McpServerPlugin::class,
|
|
];
|
|
|
|
/** @var array<string, ProjectPluginInterface> Instantiated plugins */
|
|
private static $plugins = [];
|
|
|
|
/** @var AuditLogger|null Shared audit logger */
|
|
private static $logger = null;
|
|
|
|
/** @var MetricsCollector|null Shared metrics collector */
|
|
private static $metricsCollector = null;
|
|
|
|
/**
|
|
* Set shared logger for all plugins
|
|
*
|
|
* @param AuditLogger $logger Audit logger instance
|
|
* @return void
|
|
*/
|
|
public static function setLogger(AuditLogger $logger): void
|
|
{
|
|
self::$logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* Set shared metrics collector for all plugins
|
|
*
|
|
* @param MetricsCollector $metricsCollector Metrics collector instance
|
|
* @return void
|
|
*/
|
|
public static function setMetricsCollector(MetricsCollector $metricsCollector): void
|
|
{
|
|
self::$metricsCollector = $metricsCollector;
|
|
}
|
|
|
|
/**
|
|
* Register a custom plugin for a project type
|
|
*
|
|
* @param string $projectType Project type identifier
|
|
* @param string $pluginClass Fully qualified plugin class name
|
|
* @return void
|
|
* @throws \InvalidArgumentException If plugin class doesn't implement ProjectPluginInterface
|
|
*/
|
|
public static function registerPlugin(string $projectType, string $pluginClass): void
|
|
{
|
|
if (!class_exists($pluginClass)) {
|
|
throw new \InvalidArgumentException("Plugin class does not exist: {$pluginClass}");
|
|
}
|
|
|
|
if (!is_subclass_of($pluginClass, ProjectPluginInterface::class)) {
|
|
throw new \InvalidArgumentException(
|
|
"Plugin class must implement ProjectPluginInterface: {$pluginClass}"
|
|
);
|
|
}
|
|
|
|
self::$pluginClasses[$projectType] = $pluginClass;
|
|
|
|
// Clear cached instance if exists
|
|
if (isset(self::$plugins[$projectType])) {
|
|
unset(self::$plugins[$projectType]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get plugin instance for a project type
|
|
*
|
|
* @param string $projectType Project type identifier
|
|
* @param array $config Optional plugin configuration
|
|
* @return ProjectPluginInterface|null Plugin instance or null if not found
|
|
*/
|
|
public static function getPlugin(string $projectType, array $config = []): ?ProjectPluginInterface
|
|
{
|
|
// Check if plugin is already instantiated
|
|
if (isset(self::$plugins[$projectType])) {
|
|
return self::$plugins[$projectType];
|
|
}
|
|
|
|
// Check if plugin class is registered
|
|
if (!isset(self::$pluginClasses[$projectType])) {
|
|
return null;
|
|
}
|
|
|
|
// Instantiate plugin
|
|
$pluginClass = self::$pluginClasses[$projectType];
|
|
$plugin = new $pluginClass(self::$logger, self::$metricsCollector, $config);
|
|
|
|
// Cache plugin instance
|
|
self::$plugins[$projectType] = $plugin;
|
|
|
|
return $plugin;
|
|
}
|
|
|
|
/**
|
|
* Get all registered project types
|
|
*
|
|
* @return array List of project type identifiers
|
|
*/
|
|
public static function getRegisteredTypes(): array
|
|
{
|
|
return array_keys(self::$pluginClasses);
|
|
}
|
|
|
|
/**
|
|
* Get all registered plugins
|
|
*
|
|
* @param array $config Optional plugin configuration
|
|
* @return array<string, ProjectPluginInterface> Map of project types to plugin instances
|
|
*/
|
|
public static function getAllPlugins(array $config = []): array
|
|
{
|
|
$plugins = [];
|
|
foreach (self::$pluginClasses as $projectType => $pluginClass) {
|
|
$plugins[$projectType] = self::getPlugin($projectType, $config);
|
|
}
|
|
return $plugins;
|
|
}
|
|
|
|
/**
|
|
* Check if a plugin is registered for a project type
|
|
*
|
|
* @param string $projectType Project type identifier
|
|
* @return bool True if plugin is registered
|
|
*/
|
|
public static function hasPlugin(string $projectType): bool
|
|
{
|
|
return isset(self::$pluginClasses[$projectType]);
|
|
}
|
|
|
|
/**
|
|
* Unregister a plugin
|
|
*
|
|
* @param string $projectType Project type identifier
|
|
* @return void
|
|
*/
|
|
public static function unregisterPlugin(string $projectType): void
|
|
{
|
|
unset(self::$pluginClasses[$projectType]);
|
|
unset(self::$plugins[$projectType]);
|
|
}
|
|
|
|
/**
|
|
* Clear all plugin instances (forces re-instantiation)
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function clearCache(): void
|
|
{
|
|
self::$plugins = [];
|
|
}
|
|
|
|
/**
|
|
* Get plugin information
|
|
*
|
|
* @param string $projectType Project type identifier
|
|
* @return array|null Plugin info or null if not found
|
|
*/
|
|
public static function getPluginInfo(string $projectType): ?array
|
|
{
|
|
$plugin = self::getPlugin($projectType);
|
|
if ($plugin === null) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'project_type' => $plugin->getProjectType(),
|
|
'plugin_name' => $plugin->getPluginName(),
|
|
'plugin_version' => $plugin->getPluginVersion(),
|
|
'required_files' => $plugin->getRequiredFiles(),
|
|
'recommended_files' => $plugin->getRecommendedFiles(),
|
|
'best_practices_count' => count($plugin->getBestPractices()),
|
|
'commands_count' => count($plugin->getCommands()),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get all plugins information
|
|
*
|
|
* @return array Map of project types to plugin information
|
|
*/
|
|
public static function getAllPluginsInfo(): array
|
|
{
|
|
$info = [];
|
|
foreach (self::getRegisteredTypes() as $projectType) {
|
|
$info[$projectType] = self::getPluginInfo($projectType);
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Find plugin by feature/capability
|
|
*
|
|
* @param string $feature Feature name (e.g., 'package_manager', 'type_checking')
|
|
* @return array List of project types supporting the feature
|
|
*/
|
|
public static function findPluginsByFeature(string $feature): array
|
|
{
|
|
$matches = [];
|
|
foreach (self::getRegisteredTypes() as $projectType) {
|
|
$plugin = self::getPlugin($projectType);
|
|
if ($plugin !== null) {
|
|
$bestPractices = $plugin->getBestPractices();
|
|
foreach ($bestPractices as $practice) {
|
|
if (
|
|
stripos($practice['title'] ?? '', $feature) !== false ||
|
|
stripos($practice['description'] ?? '', $feature) !== false
|
|
) {
|
|
$matches[] = $projectType;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $matches;
|
|
}
|
|
|
|
/**
|
|
* Get plugin registry statistics
|
|
*
|
|
* @return array Registry statistics
|
|
*/
|
|
public static function getStatistics(): array
|
|
{
|
|
return [
|
|
'total_plugins' => count(self::$pluginClasses),
|
|
'instantiated_plugins' => count(self::$plugins),
|
|
'registered_types' => self::getRegisteredTypes(),
|
|
'has_logger' => self::$logger !== null,
|
|
'has_metrics_collector' => self::$metricsCollector !== null,
|
|
];
|
|
}
|
|
}
|