Last active
September 9, 2019 23:03
-
-
Save chasecmiller/ee3799a613413839fd681f7170c89bff to your computer and use it in GitHub Desktop.
Proof of concept of a WordPress mu-plugin to automatically disable plugins that start throwing errors.
This file contains 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 | |
/* | |
Plugin Name: Plugin Debugging Tool | |
Description: Attempt to automatically de-activate plugins that are causing errors. Doesn't always work. This must be installed in the mu-plugins directory. | |
Author: Chase C. Miller | |
Version: 1.0 | |
Author URI: http://crumbls.com | |
*/ | |
namespace Crumbls\Debug\Plugins; | |
error_reporting(E_ALL); | |
ini_set('display_errors', '1'); | |
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_notices', [get_called_class(), 'adminNotice']); | |
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; | |
} | |
/** | |
* Re-enable plugins when activate is clicked again. | |
* This ugly and doesn't verify the user can do it, but it works for now. | |
*/ | |
if ( | |
is_admin() | |
&& | |
array_key_exists('plugin', $_REQUEST) | |
&& | |
array_key_exists('action', $_REQUEST) | |
&& | |
is_string($_REQUEST['action']) | |
&& | |
$_REQUEST['action'] == 'activate' | |
&& | |
strpos(basename($_SERVER['REQUEST_URI']), 'plugins.php') === 0 | |
) { | |
$dis = array_filter($dis, function ($e) { | |
return strtolower($e[2]) != strtolower($_REQUEST['plugin']); | |
}); | |
update_option('disabled_plugins', $dis, true); | |
} | |
// Return plugins, with disabled not activated. | |
$plugins = array_diff($plugins, array_column($dis, 2)); | |
return $plugins; | |
} | |
/** | |
* Handle Shutdowns | |
* @return bool | |
*/ | |
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; | |
print_r($func); | |
echo $n; | |
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; | |
} | |
/** | |
* Admin Notices | |
*/ | |
public static function adminNotice() | |
{ | |
if (!stripos(__FILE__, '/mu-plugins/') || true) { | |
?> | |
<div class="notice notice-warning"> | |
<p><?php _e('Plugin Debugging Tool must be moved to mu-plugins to work.', __NAMESPACE__); ?></p> | |
</div> | |
<?php | |
} | |
} | |
/** | |
* Enqueue special CSS on our plugin page to let users know what plugins are busted. | |
* @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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment