<?php
/*
Plugin Name: Plugin Debugging Tool
Description: Proof of concept of a WordPress mu-plugin to automatically disable plugins that start throwing errors. Must be installed as mu-plugin. The first time a plugin errors, it will white screen or show an error. Next page load, it is disabled. There has not been any functionality to remove plugins from the black list. If you'd like to see it developed more, please email chase@crumbls.com Only tested on Apache running PHP7.
Author: Chase C. Miller
Author Email: chase@crumbls.com
Version: 1.0
Author URI: http://crumbls.com
*/
namespace Crumbls\Debug\Plugins;
class Plugin
{
protected static $errHandler = false;
protected static $errLog = [];
public function __construct()
{
}
public static function getInstance()
{
static $instance;
$class = get_called_class();
if (!$instance instanceof $class) {
$instance = new $class;
add_action('admin_enqueue_scripts', [get_called_class(), 'adminEnqueue'], 10, 1);
self::$errHandler = set_error_handler([get_called_class(), 'errorHandler']);
set_exception_handler([get_called_class(), 'errorHandler']);
error_reporting(E_ALL);
add_filter('option_active_plugins', [get_called_class(), 'filterActivePlugins'], PHP_INT_MAX, 1);
add_action('shutdown', [get_called_class(), 'wordpressShutdown'], PHP_INT_MAX);
register_shutdown_function([get_called_class(), 'systemShutdown']);
}
return $instance;
}
/**
* Tied to filter "option_active_plugins"
* @param array $plugins
* @return array
*/
public static function filterActivePlugins($plugins = [])
{
global $wp_object_cache;
if (!$dis = get_option('disabled_plugins', [])) {
return $plugins;
}
// TODO: If you want to re-enable plugins, this is the spot.
// Return plugins, with disabled not activated.
$plugins = array_diff($plugins, array_column($dis, 2));
return $plugins;
}
public static function wordpressShutdown()
{
// The following plugins gave a horrible error.
if (!self::$errLog) {
return true;
}
$option = get_option('disabled_plugins', []);
$option = array_column($option, null, 'f');
foreach (self::$errLog as $err) {
if (array_key_exists($err[2], $option)) {
continue;
}
$option[$err[2]] = $err;
}
update_option('disabled_plugins', $option, true);
return true;
}
/**
* Tied to register_shutdown_function
* Not currently used.
*/
public static function systemShutdown()
{
// echo __METHOD__;
}
/**
* Error handling.
*/
public static function errorHandler($n = null, $s = null, $f = null, $ln = null)
{
if (is_object($n) && get_class($n) == 'Exception') {
$s = $n->getMessage();
$f = $n->getFile();
$ln = $n->getLine();
$n = $n->getCode();
}
if (
strpos($f, WP_CONTENT_DIR) !== 0
||
strpos($f, WP_CONTENT_DIR . '/plugins/') !== 0
) {
// Send to default error handler.
$func = self::$errHandler;
return call_user_func($func, $n, $s, $f, $ln);
}
$f = substr($f, strlen(WP_CONTENT_DIR . '/plugins/'));
self::$errLog[] = [$n, $s, $f, $ln];
return true;
}
/**
* Enqueue special CSS
* @param null $hook
*/
public static function adminEnqueue($hook = null)
{
if ($hook != 'plugins.php') {
return;
}
if (!$option = get_option('disabled_plugins', [])) {
return;
}
echo '<style>';
foreach ($option as $e) {
printf('.plugins tr[data-plugin="%s"] { background-color: rgba(255,0,0,0.5) !important; }', $e[2]);
}
echo '</style>';
}
}
Plugin::getInstance();