Created
October 20, 2022 09:43
-
-
Save bentideswell/0f811e5f8ab0b767ae5f6e6fc7f805c0 to your computer and use it in GitHub Desktop.
Provide debug information for a WordPress installation using FishPig. Especially useful for Multisite/Network installs.
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 | |
/** | |
* @FishPig.com | |
*/ | |
ini_set('display_errors', 1); | |
error_reporting(E_ALL); | |
$isCli = PHP_SAPI === 'cli'; | |
define('DS', DIRECTORY_SEPARATOR); | |
try { | |
// Load WordPress code | |
_bootstrap_wordpress(); | |
// Extract data we need for debugging | |
$data = [ | |
// Get wp-config.php | |
// Remove DB_PASSWORD for security | |
'wp-config.php' => _get_wp_config_file(), | |
'.htaccess' => _get_htaccess_file(), | |
'data' => _get_data_from_database() | |
]; | |
if (!$isCli) { | |
// If a browser, let's send a header | |
header('Content-Type: application/json;charset=utf-8'); | |
} | |
echo json_encode($data); | |
} catch (\Exception $e) { | |
if ($isCli) { | |
echo $e->getMessage() . "\n\n" . $e->getTraceAsString() . "\n\n"; | |
exit(1); | |
} else { | |
echo sprintf( | |
'<h1><span style="color:red;">Exception:</span> %s</h1><pre>%s</pre>', | |
$e->getMessage(), | |
$e->getTraceAsString() | |
); | |
} | |
} | |
/** | |
* | |
*/ | |
function _bootstrap_wordpress(): void | |
{ | |
$wpLoadFile = __DIR__ . DS . 'wp-load.php'; | |
if (!is_file($wpLoadFile)) { | |
throw new \RuntimeException( | |
sprintf( | |
'Cannot find WordPress. Failed @ "%s"', | |
$wpLoadFile | |
) | |
); | |
} | |
require_once $wpLoadFile; | |
if (!defined('ABSPATH')) { | |
throw new \RuntimeException( | |
'Unable to find ABSPATH. WordPress bootstrap failed' | |
); | |
} | |
} | |
/** | |
* Try to get wp-config.phpfile | |
* If fails, return error message | |
* All data will be returned as a JSON string | |
* The password for the DB will be removed for security | |
*/ | |
function _get_wp_config_file(): string | |
{ | |
return _read_file( | |
ABSPATH . 'wp-config.php', | |
function ($data) { | |
// Remove Database password | |
return preg_replace( | |
'/(define\(\s*([\'"]{1})DB_PASSWORD\2\s*,\s*([\'"]{1}))[^\3]+(\3\s*\))/U', | |
'$1--REMOVED--$4', | |
$data | |
); | |
} | |
); | |
} | |
/** | |
* Get .htaccess file | |
*/ | |
function _get_htaccess_file() | |
{ | |
return _read_file(ABSPATH . '.htaccess'); | |
} | |
/** | |
* Reads $file and handles errors | |
*/ | |
function _read_file($file, \Closure $callback = null): string | |
{ | |
if (!file_exists($file)) { | |
return [ | |
'error' => sprintf('File does not exist @ "%s"', $file), | |
'source' => 'file_exists' | |
]; | |
} elseif (!is_readable($file)) { | |
return [ | |
'error' => sprintf('File is not readable @ "%s"', $file), | |
'source' => 'is_readable' | |
]; | |
} | |
// Try to catch any errors | |
ob_start(); | |
$data = @file_get_contents($file); | |
if ($output = ob_get_clean()) { | |
return [ | |
'error' => (string)$output, | |
'source' => 'file_get_contents' | |
]; | |
} | |
return is_callable($callback) ? $callback($data) : $data; | |
} | |
/** | |
* | |
*/ | |
function _get_data_from_database(): array | |
{ | |
global $wpdb; | |
$data = [ | |
'is_multisite' => (int)is_multisite(), | |
'blogs' => [] | |
]; | |
$wpdb->show_errors = false; | |
try { | |
$blogs = $wpdb->get_results( "SELECT blog_id, domain, path FROM {$wpdb->prefix}blogs WHERE deleted = 0", ARRAY_A ); | |
} catch (\Exception $e) { | |
// Multisite not enabled in the DB | |
} | |
if (!isset($blogs) || !$blogs || (is_array($blogs) && count($blogs) === 0)) { | |
$blogs = [['blog_id' => 1]]; | |
} | |
$urlFuncMap = [ | |
'home' => 'get_home_url', | |
'siteurl' => 'get_site_url' | |
]; | |
foreach ($blogs as $blog) { | |
$blogId = (int)$blog['blog_id']; | |
$key = 'blog_' . $blogId; | |
$prefix = $blogId === 1 ? $wpdb->prefix : $wpdb->prefix . $blogId . '_'; | |
$data['blogs'][$key] = $blog; | |
// Get URLs via functions | |
// This gives plugins a chance to change values | |
foreach ($urlFuncMap as $optionName => $func) { | |
$data['blogs'][$key][$optionName] = _try_catch_callback(function() use($func) { return $func(); }); | |
$data['blogs'][$key]['option__' . $optionName] = _try_catch_callback(function() use ($wpdb, $prefix, $optionName) { | |
return $wpdb->get_var("SELECT option_value FROM {$prefix}options WHERE option_name = '{$optionName}' LIMIT 1"); | |
}); | |
} | |
} | |
return $data; | |
} | |
/** | |
* | |
*/ | |
function _try_catch_callback(\Closure $callback) | |
{ | |
try { | |
return $callback(); | |
} catch (\Exception $e) { | |
return $e->getMessage(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment