Created
July 25, 2025 15:05
-
-
Save addzycullen/e170ed07d2e0ccaf470efde892f3cf57 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| declare(strict_types=1); | |
| /** | |
| * Plugin Name: MU Plugin Loader | |
| * Plugin URI: https://orbital.co.uk | |
| * Description: Safely loads essential plugins as must-use plugins with proper error handling and logging. | |
| * Author: Adam Cullen | |
| * Version: 2.0.0 | |
| * Requires at least: 6.0 | |
| * Requires PHP: 8.1 | |
| * License: GPL v2 or later | |
| * Network: true | |
| */ | |
| // Prevent direct access | |
| if (!defined('ABSPATH')) { | |
| exit; | |
| } | |
| /** | |
| * MU Plugin Loader Class | |
| * | |
| * Handles loading of essential plugins from mu-plugins subdirectories | |
| * with proper error handling, logging, and dependency management. | |
| */ | |
| final class MU_Plugin_Loader | |
| { | |
| /** | |
| * Enable/disable logging | |
| * Set to false to disable all log output for performance | |
| */ | |
| private const ENABLE_LOGGING = false; | |
| /** | |
| * Plugin configuration with priority and dependencies | |
| * | |
| * @var array<string, array{ | |
| * path: string, | |
| * priority: int, | |
| * required: bool, | |
| * description: string, | |
| * depends_on?: array<string> | |
| * }> | |
| */ | |
| private const PLUGINS = [ | |
| 'acf' => [ | |
| 'path' => 'advanced-custom-fields-pro/acf.php', | |
| 'priority' => 10, | |
| 'required' => true, | |
| 'description' => 'Advanced Custom Fields Pro - Field management system' | |
| ], | |
| 'orbitools' => [ | |
| 'path' => 'orbitools/orbitools.php', | |
| 'priority' => 20, | |
| 'required' => true, | |
| 'description' => 'Orbital Tools - Development and utility functions', | |
| 'depends_on' => ['acf'] | |
| ], | |
| 'breadcrumb' => [ | |
| 'path' => 'breadcrumb-navxt/breadcrumb-navxt.php', | |
| 'priority' => 30, | |
| 'required' => false, | |
| 'description' => 'Breadcrumb NavXT - Navigation breadcrumbs' | |
| ] | |
| ]; | |
| /** | |
| * Track loaded plugins | |
| */ | |
| private static array $loaded_plugins = []; | |
| /** | |
| * Track failed plugins | |
| */ | |
| private static array $failed_plugins = []; | |
| /** | |
| * Initialize the loader | |
| */ | |
| public static function init(): void | |
| { | |
| // Load plugins in priority order | |
| $plugins_by_priority = self::get_plugins_by_priority(); | |
| foreach ($plugins_by_priority as $plugin_key => $config) { | |
| self::load_plugin($plugin_key, $config); | |
| } | |
| // Register admin notice for failed required plugins | |
| if (!empty(self::$failed_plugins)) { | |
| add_action('admin_notices', [__CLASS__, 'display_admin_notices']); | |
| } | |
| // Log summary | |
| self::log_loading_summary(); | |
| } | |
| /** | |
| * Load a single plugin with dependency checking | |
| */ | |
| private static function load_plugin(string $plugin_key, array $config): bool | |
| { | |
| // Check dependencies first | |
| if (isset($config['depends_on'])) { | |
| foreach ($config['depends_on'] as $dependency) { | |
| if (!in_array($dependency, self::$loaded_plugins, true)) { | |
| self::log_error("Plugin '{$plugin_key}' skipped - dependency '{$dependency}' not loaded"); | |
| if ($config['required']) { | |
| self::$failed_plugins[$plugin_key] = "Missing dependency: {$dependency}"; | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| $plugin_path = WPMU_PLUGIN_DIR . '/' . $config['path']; | |
| // Check if file exists and is readable | |
| if (!is_readable($plugin_path)) { | |
| $error_msg = "Plugin file not found or not readable: {$config['path']}"; | |
| self::log_error($error_msg); | |
| if ($config['required']) { | |
| self::$failed_plugins[$plugin_key] = $error_msg; | |
| } | |
| return false; | |
| } | |
| // Load the plugin | |
| try { | |
| require_once $plugin_path; | |
| self::$loaded_plugins[] = $plugin_key; | |
| self::log_success("Loaded {$plugin_key}: {$config['description']}"); | |
| return true; | |
| } catch (Throwable $e) { | |
| $error_msg = "Failed to load {$plugin_key}: " . $e->getMessage(); | |
| self::log_error($error_msg); | |
| if ($config['required']) { | |
| self::$failed_plugins[$plugin_key] = $error_msg; | |
| } | |
| return false; | |
| } | |
| } | |
| /** | |
| * Get plugins sorted by priority | |
| */ | |
| private static function get_plugins_by_priority(): array | |
| { | |
| $plugins = self::PLUGINS; | |
| uasort($plugins, fn($a, $b) => $a['priority'] <=> $b['priority']); | |
| return $plugins; | |
| } | |
| /** | |
| * Display admin notices for failed required plugins | |
| */ | |
| public static function display_admin_notices(): void | |
| { | |
| if (empty(self::$failed_plugins)) { | |
| return; | |
| } | |
| foreach (self::$failed_plugins as $plugin => $error) { | |
| printf( | |
| '<div class="notice notice-error"><p><strong>MU Plugin Loader Error:</strong> %s - %s</p></div>', | |
| esc_html(ucfirst($plugin)), | |
| esc_html($error) | |
| ); | |
| } | |
| } | |
| /** | |
| * Log loading summary | |
| */ | |
| private static function log_loading_summary(): void | |
| { | |
| $total_plugins = count(self::PLUGINS); | |
| $loaded_count = count(self::$loaded_plugins); | |
| $failed_count = count(self::$failed_plugins); | |
| self::log_info("MU Plugin Loading Summary: {$loaded_count}/{$total_plugins} loaded, {$failed_count} failed"); | |
| if (!empty(self::$loaded_plugins)) { | |
| self::log_info("Loaded plugins: " . implode(', ', self::$loaded_plugins)); | |
| } | |
| } | |
| /** | |
| * Log success message | |
| */ | |
| private static function log_success(string $message): void | |
| { | |
| if (self::ENABLE_LOGGING && WP_DEBUG_LOG) { | |
| error_log("[MU Loader SUCCESS] {$message}"); | |
| } | |
| } | |
| /** | |
| * Log error message | |
| */ | |
| private static function log_error(string $message): void | |
| { | |
| if (self::ENABLE_LOGGING && WP_DEBUG_LOG) { | |
| error_log("[MU Loader ERROR] {$message}"); | |
| } | |
| } | |
| /** | |
| * Log info message | |
| */ | |
| private static function log_info(string $message): void | |
| { | |
| if (self::ENABLE_LOGGING && WP_DEBUG_LOG) { | |
| error_log("[MU Loader INFO] {$message}"); | |
| } | |
| } | |
| /** | |
| * Get loaded plugins (for debugging) | |
| */ | |
| public static function get_loaded_plugins(): array | |
| { | |
| return self::$loaded_plugins; | |
| } | |
| /** | |
| * Get failed plugins (for debugging) | |
| */ | |
| public static function get_failed_plugins(): array | |
| { | |
| return self::$failed_plugins; | |
| } | |
| } | |
| // Initialize the loader | |
| MU_Plugin_Loader::init(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment