Skip to content

Instantly share code, notes, and snippets.

@addzycullen
Created July 25, 2025 15:05
Show Gist options
  • Select an option

  • Save addzycullen/e170ed07d2e0ccaf470efde892f3cf57 to your computer and use it in GitHub Desktop.

Select an option

Save addzycullen/e170ed07d2e0ccaf470efde892f3cf57 to your computer and use it in GitHub Desktop.
<?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