Eine ganze WordPress-Installation zu durchsuchen ist nicht ganz unkompliziert. Mit dem MU-Plugin Search Everywhere geht das aber relativ einfach. Dateitypen und Datenbank-Tabellen können konfiguriert werden. Für den Aufruf muss man als Administrator in WordPress angemeldet sein, die Funktionsweise ist in der Kurzdokumentation im Kommentar oben beschrieben.
<?php
/**
* Plugin Name: Search Everywhere
* Version: 1.0
* Description: Finds files and database entries that contain a specific string or regex
*
* INSTALLATION:
* Place this file in: /wp-content/mu-plugins/search-everywhere-mu-plugin.php
* (Create the mu-plugins directory if it doesn't exist)
*
* USAGE:
* 1. Configure search patterns in the $patterns array below
* 2. Access via: yoursite.com/?search_everywhere_scan=1
*
* CONFIGURATION:
* Edit the $patterns array to define what strings/regex to search for.
* Edit the $file_extensions array to define what file types to search.
* Edit the $database_config array to define what database tables/columns to search.
*
* SEARCH PATTERNS:
* SIMPLE STRINGS (case-insensitive):
* $patterns = ["debug_code", "test_mode", "dev_flag"];
*
* CLEAN REGEX FORMAT (recommended for regex):
* $patterns = [
* "mn_dev", // Simple string
* ['regex' => '\$_GET\s*\[\s*[\'"]debug[\'"]\s*\]'], // $_GET['debug'] - no escaping needed!
* ['regex' => 'isset\s*\(\s*\$_GET\s*\[\s*[\'"]test'], // isset($_GET['test']
* ['regex' => 'define\s*\(\s*[\'"]DEBUG[\'"]'], // define('DEBUG'
* ];
*
* HEREDOC FORMAT (for very complex patterns):
* $complex_pattern = <<<'REGEX'
* \$_GET\s*\[\s*['"]debug['"]\s*\]
* REGEX;
* $patterns = ["mn_dev", ['regex' => $complex_pattern]];
*
* LEGACY FORMAT (still supported but harder to read):
* $patterns = ["/\\$_GET.*debug/"]; // Requires double escaping
*
* FILE EXTENSIONS:
* $file_extensions = ['php']; // PHP only (default)
* $file_extensions = ['php', 'js', 'css']; // PHP, JavaScript, CSS
* $file_extensions = ['php', 'js', 'css', 'html', 'htm', 'txt', 'json', 'xml']; // All common web files
*
* DATABASE CONFIGURATION:
* $database_config = [
* 'posts' => ['post_content'], // Standard posts/pages
* 'postmeta' => ['meta_value'], // Post metadata only
* 'options' => ['option_value'], // WordPress options only
* ];
*
* $database_config = [
* 'posts' => ['post_content', 'post_title'], // Search content AND titles
* 'postmeta' => ['meta_value', 'meta_key'], // Search values AND keys
* 'options' => ['option_value', 'option_name'], // Search values AND names
* 'users' => ['user_email', 'display_name'], // Custom: search users
* 'usermeta' => ['meta_value'], // Custom: user metadata
* ];
*/
if (!defined('ABSPATH')) { exit; }
// ===== CONFIGURATION =====
$patterns = [
// Simple strings (exact match, case-insensitive)
'mn_dev',
// Regex patterns using array format (much cleaner!)
['regex' => '\$_GET\s*\[\s*[\'"]mn_dev[\'"]\s*\]'], // $_GET['mn_dev']
];
// File extensions to search (without the dot)
$file_extensions = ['php', 'js', 'css', 'html', 'htm', 'txt', 'json', 'xml'];
// Database tables and columns to search
$database_config = [
'posts' => ['post_content'], // Posts, pages, custom post types
'postmeta' => ['meta_value'], // Post metadata
'options' => ['option_value'], // WordPress options
'comments' => ['comment_content'], // User comments
'commentmeta' => ['meta_value'], // Comment metadata
// 'usermeta' => ['meta_value'], // User settings
];
add_action('init', function () {
global $patterns, $file_extensions, $database_config;
// Handle AJAX request for unserializing data
if (isset($_GET['search_everywhere_unserialize'])) {
handle_unserialize_request();
return;
}
// Handle AJAX request for file context
if (isset($_GET['search_everywhere_context'])) {
handle_context_request();
return;
}
// Only run scanner when specifically requested and user is admin
if (!isset($_GET['search_everywhere_scan']) || !current_user_can('manage_options')) return;
// Prevent timeout for large scans
set_time_limit(300);
// Add dark mode styling
echo "<!DOCTYPE html>\n";
echo "<html>\n";
echo "<head>\n";
echo "<meta charset='UTF-8'>\n";
echo "<meta name='viewport' content='width=device-width, initial-scale=1.0'>\n";
echo "<title>Search Everywhere Scanner</title>\n";
echo "<style>\n";
echo "body { background: #1a1a1a; color: #e0e0e0; font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Courier New', monospace; margin: 20px; line-height: 1.6; }\n";
echo "h1, h2, h3 { color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; }\n";
echo "h1 { border-bottom: 2px solid #333; padding-bottom: 10px; }\n";
echo "h2 { color: #4fc3f7; margin-top: 30px; }\n";
echo "h3 { color: #81c784; }\n";
echo "pre { background: #2d2d2d; border: 1px solid #444; border-radius: 6px; padding: 15px; overflow-x: auto; font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Courier New', monospace; }\n";
echo "strong { color: #ffb74d; }\n";
echo "a { color: #64b5f6; text-decoration: none; }\n";
echo "a:hover { color: #90caf9; text-decoration: underline; }\n";
echo ".button { background: #0073aa; color: white; padding: 8px 16px; text-decoration: none; border-radius: 3px; display: inline-block; margin: 5px 0; }\n";
echo ".button:hover { background: #005a87; color: white; }\n";
echo "ul { margin: 10px 0; }\n";
echo "li { margin: 5px 0; }\n";
echo ".context { background: #2d2d2d; padding: 15px; border: 1px solid #444; border-radius: 6px; margin: 10px 0; }\n";
echo ".unserialize-btn { background: #28a745; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 12px; margin: 5px 0; }\n";
echo ".unserialize-btn:hover { background: #218838; }\n";
echo ".context-btn { background: #6c757d; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 12px; margin: 5px 0; }\n";
echo ".context-btn:hover { background: #5a6268; }\n";
echo ".unserialized-data, .context-data { background: #2d2d2d; border: 1px solid #444; border-radius: 6px; padding: 15px; margin: 10px 0; white-space: pre-wrap; font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Courier New', monospace; }\n";
echo "</style>\n";
echo "<script>\n";
echo "function unserializeData(table, column, id, metaKey = '') {\n";
echo " const btn = event.target;\n";
echo " const container = btn.nextElementSibling;\n";
echo " if (container && container.classList.contains('unserialized-data') && container.style.display === 'block') {\n";
echo " container.style.display = 'none';\n";
echo " btn.textContent = 'Decode';\n";
echo " return;\n";
echo " }\n";
echo " btn.textContent = 'Loading...';\n";
echo " const url = window.location.origin + window.location.pathname + '?search_everywhere_unserialize=1&table=' + encodeURIComponent(table) + '&column=' + encodeURIComponent(column) + '&id=' + encodeURIComponent(id) + '&meta_key=' + encodeURIComponent(metaKey);\n";
echo " fetch(url)\n";
echo " .then(response => response.text())\n";
echo " .then(data => {\n";
echo " let targetContainer = container;\n";
echo " if (!targetContainer || !targetContainer.classList.contains('unserialized-data')) {\n";
echo " targetContainer = document.createElement('div');\n";
echo " targetContainer.className = 'unserialized-data';\n";
echo " btn.parentNode.insertBefore(targetContainer, btn.nextSibling);\n";
echo " }\n";
echo " targetContainer.innerHTML = data;\n";
echo " targetContainer.style.display = 'block';\n";
echo " btn.textContent = 'Hide';\n";
echo " })\n";
echo " .catch(error => {\n";
echo " btn.textContent = 'Error';\n";
echo " console.error('Error:', error);\n";
echo " });\n";
echo "}\n";
echo "function showFileContext(filePath, lineNumber) {\n";
echo " const btn = event.target;\n";
echo " const container = btn.nextElementSibling;\n";
echo " if (container && container.classList.contains('context-data') && container.style.display === 'block') {\n";
echo " container.style.display = 'none';\n";
echo " btn.textContent = 'Show Context';\n";
echo " return;\n";
echo " }\n";
echo " btn.textContent = 'Loading...';\n";
echo " const url = window.location.origin + window.location.pathname + '?search_everywhere_context=1&file=' + encodeURIComponent(filePath) + '&line=' + encodeURIComponent(lineNumber);\n";
echo " fetch(url)\n";
echo " .then(response => response.text())\n";
echo " .then(data => {\n";
echo " let targetContainer = container;\n";
echo " if (!targetContainer || !targetContainer.classList.contains('context-data')) {\n";
echo " targetContainer = document.createElement('div');\n";
echo " targetContainer.className = 'context-data';\n";
echo " btn.parentNode.insertBefore(targetContainer, btn.nextSibling);\n";
echo " }\n";
echo " targetContainer.innerHTML = data;\n";
echo " targetContainer.style.display = 'block';\n";
echo " btn.textContent = 'Hide Context';\n";
echo " })\n";
echo " .catch(error => {\n";
echo " btn.textContent = 'Error';\n";
echo " console.error('Error:', error);\n";
echo " });\n";
echo "}\n";
echo "</script>\n";
echo "</head>\n";
echo "<body>\n";
echo "<h1>Search Everywhere Scanner</h1>\n";
$pattern_displays = array_map('get_pattern_display', $patterns);
echo "<p>Scanning for patterns: <strong>" . esc_html(implode(', ', $pattern_displays)) . "</strong></p>\n";
echo "<p>File types: <strong>" . esc_html(implode(', ', array_map(function($ext) { return '.' . $ext; }, $file_extensions))) . "</strong></p>\n";
// Debug: Show pattern types
echo "<p><small>Pattern types: ";
foreach ($patterns as $p) {
echo esc_html(get_pattern_display($p)) . " (" . (is_regex_pattern($p) ? "regex" : "string") . "), ";
}
echo "</small></p>\n";
$found_files = [];
$found_db_entries = [];
// 1. Scan files for the search patterns
echo "<h2>Scanning Files...</h2>\n";
$php_files = scan_php_files_for_patterns($patterns, $file_extensions);
if (!empty($php_files)) {
echo "<h3>Found in Files:</h3>\n";
foreach ($php_files as $file_info) {
echo "<strong>File:</strong> " . esc_html($file_info['file']) . "<br>\n";
echo "<strong>Line:</strong> " . esc_html($file_info['line']) . "<br>\n";
echo "<strong>Pattern:</strong> " . esc_html($file_info['matched_pattern']) . "<br>\n";
echo "<strong>Code:</strong> <pre>" . esc_html($file_info['code']) . "</pre>\n";
// Add context button for all files
echo "<button class='context-btn' onclick='showFileContext(\"" . esc_attr($file_info['file']) . "\", \"" . esc_attr($file_info['line']) . "\")'>Show Context</button>\n";
echo "<br><br>\n";
}
$found_files = $php_files;
} else {
echo "<p>No files found with configured patterns.</p>\n";
}
// 2. Scan database for the search patterns
echo "<h2>Scanning Database...</h2>\n";
$db_entries = scan_database_for_patterns($patterns, $database_config);
if (!empty($db_entries)) {
echo "<h3>Found in Database:</h3>\n";
foreach ($db_entries as $entry) {
echo "<strong>Table:</strong> " . esc_html($entry['table']) . "<br>\n";
echo "<strong>Column:</strong> " . esc_html($entry['column']) . "<br>\n";
echo "<strong>ID:</strong> " . esc_html($entry['id']) . "<br>\n";
echo "<strong>Pattern:</strong> " . esc_html($entry['matched_pattern']) . "<br>\n";
// Show revision info if available
if (isset($entry['is_revision'])) {
echo "<strong>Post Type:</strong> " . esc_html($entry['post_type'] ?? 'unknown') . "<br>\n";
echo "<strong>Post Status:</strong> " . esc_html($entry['post_status'] ?? 'unknown') . "<br>\n";
if ($entry['is_revision']) {
echo "<strong>🔄 REVISION:</strong> Yes" . esc_html($entry['parent_info'] ?? '') . "<br>\n";
} else {
echo "<strong>🔄 REVISION:</strong> No<br>\n";
}
if (isset($entry['post_title'])) {
echo "<strong>Post Title:</strong> " . esc_html($entry['post_title']) . "<br>\n";
}
}
if (isset($entry['meta_key'])) {
echo "<strong>Meta Key:</strong> " . esc_html($entry['meta_key']) . "<br>\n";
}
echo "<strong>Preview:</strong> <pre>" . esc_html(substr($entry['content'], 0, 300)) . "...</pre>\n";
// Add decode button for all database entries
$meta_key = isset($entry['meta_key']) ? $entry['meta_key'] : '';
echo "<button class='unserialize-btn' onclick='unserializeData(\"" . esc_attr($entry['table']) . "\", \"" . esc_attr($entry['column']) . "\", \"" . esc_attr($entry['id']) . "\", \"" . esc_attr($meta_key) . "\")'>Decode</button>\n";
echo "<br><br>\n";
}
$found_db_entries = $db_entries;
} else {
echo "<p>No database entries found with configured patterns.</p>\n";
}
// Count revisions
$revision_count = 0;
foreach ($found_db_entries as $entry) {
if (isset($entry['is_revision']) && $entry['is_revision']) {
$revision_count++;
}
}
echo "<h2>Summary</h2>\n";
echo "<p>Total files with patterns: " . count($found_files) . "</p>\n";
echo "<p>Total database entries with patterns: " . count($found_db_entries) . "</p>\n";
echo "<p>└── Revisions: {$revision_count}</p>\n";
echo "<p>└── Active content: " . (count($found_db_entries) - $revision_count) . "</p>\n";
if (empty($found_files) && empty($found_db_entries)) {
echo "<p><strong>No configured patterns found!</strong> The code might be:</p>\n";
echo "<ul>\n";
echo "<li>In a theme file outside wp-content</li>\n";
echo "<li>In a plugin that's not currently active</li>\n";
echo "<li>In a cached file</li>\n";
echo "<li>Using a different parameter name</li>\n";
echo "</ul>\n";
}
echo "</body>\n";
echo "</html>\n";
exit();
});
function scan_php_files_for_patterns($search_patterns, $file_extensions = ['php']) {
$found = [];
$found_unique = []; // Track unique file:line combinations to prevent duplicates
// Scan ENTIRE WordPress installation recursively
$root_directories = [
ABSPATH // This will scan everything: wp-content, wp-includes, wp-admin, root files, etc.
];
foreach ($root_directories as $root_dir) {
if (!is_dir($root_dir)) continue;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root_dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if (!$file->isFile() || !in_array(strtolower($file->getExtension()), $file_extensions)) continue;
$file_path = $file->getPathname();
// Skip our own scanner file to avoid self-detection
if (strpos($file_path, 'search-everywhere-mu-plugin.php') !== false) continue;
// Skip some heavy/unnecessary directories for performance
if (preg_match('/\/(cache|tmp|temp|logs|backups)\//', $file_path)) continue;
$content = @file_get_contents($file_path);
if ($content === false) continue;
$lines = explode("\n", $content);
foreach ($search_patterns as $pattern) {
foreach ($lines as $line_num => $line) {
$matches = false;
// Check if pattern is regex
if (is_regex_pattern($pattern)) {
$regex = get_regex_from_pattern($pattern);
$matches = @preg_match($regex, $line);
// Handle regex errors
if ($matches === false) {
continue; // Skip invalid regex
}
$matches = $matches > 0; // Convert to boolean
} else {
// Simple string search (case-insensitive)
$matches = stripos($line, $pattern) !== false;
}
if ($matches) {
$unique_key = $file_path . ':' . ($line_num + 1);
// Prevent duplicates
if (!isset($found_unique[$unique_key])) {
$found[] = [
'file' => $file_path,
'line' => $line_num + 1,
'code' => trim($line),
'matched_pattern' => get_pattern_display($pattern)
];
$found_unique[$unique_key] = true;
}
}
}
}
}
}
return $found;
}
function scan_database_for_patterns($search_patterns, $database_config) {
global $wpdb;
$found = [];
foreach ($search_patterns as $pattern) {
foreach ($database_config as $table_name => $columns) {
foreach ($columns as $column) {
// Handle special table configurations
if ($table_name === 'posts') {
$results = search_in_posts_table($pattern, $column, $wpdb);
} elseif ($table_name === 'postmeta') {
$results = search_in_postmeta_table($pattern, $column, $wpdb);
} elseif ($table_name === 'options') {
$results = search_in_options_table($pattern, $column, $wpdb);
} elseif ($table_name === 'breakdance_template') {
$results = search_in_breakdance_table($pattern, $column, $wpdb);
} else {
// Generic table search for custom tables
$results = search_in_generic_table($pattern, $table_name, $column, $wpdb);
}
$found = array_merge($found, $results);
}
}
}
return $found;
}
function search_in_posts_table($pattern, $column, $wpdb) {
$found = [];
if (is_regex_pattern($pattern)) {
$posts = $wpdb->get_results("
SELECT ID, post_title, {$column}, post_type, post_status, post_parent
FROM {$wpdb->posts}
WHERE {$column} != ''
");
$regex = get_regex_from_pattern($pattern);
foreach ($posts as $post) {
if (@preg_match($regex, $post->{$column})) {
$found[] = create_post_result($post, $column, $pattern);
}
}
} else {
$search_pattern = '%' . $pattern . '%';
$posts = $wpdb->get_results($wpdb->prepare("
SELECT ID, post_title, {$column}, post_type, post_status, post_parent
FROM {$wpdb->posts}
WHERE {$column} LIKE %s
", $search_pattern));
foreach ($posts as $post) {
$found[] = create_post_result($post, $column, $pattern);
}
}
return $found;
}
function search_in_postmeta_table($pattern, $column, $wpdb) {
$found = [];
if (is_regex_pattern($pattern)) {
$meta = $wpdb->get_results("
SELECT pm.post_id, pm.meta_key, pm.{$column}, p.post_type, p.post_status, p.post_title, p.post_parent
FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE pm.{$column} != ''
");
$regex = get_regex_from_pattern($pattern);
foreach ($meta as $meta_entry) {
if (@preg_match($regex, $meta_entry->{$column})) {
$found[] = create_postmeta_result($meta_entry, $column, $pattern);
}
}
} else {
$search_pattern = '%' . $pattern . '%';
$meta = $wpdb->get_results($wpdb->prepare("
SELECT pm.post_id, pm.meta_key, pm.{$column}, p.post_type, p.post_status, p.post_title, p.post_parent
FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE pm.{$column} LIKE %s
", $search_pattern));
foreach ($meta as $meta_entry) {
$found[] = create_postmeta_result($meta_entry, $column, $pattern);
}
}
return $found;
}
function search_in_options_table($pattern, $column, $wpdb) {
$found = [];
if (is_regex_pattern($pattern)) {
$options = $wpdb->get_results("
SELECT option_name, {$column}
FROM {$wpdb->options}
WHERE {$column} != ''
");
$regex = get_regex_from_pattern($pattern);
foreach ($options as $option) {
if (@preg_match($regex, $option->{$column})) {
$found[] = [
'table' => 'options',
'column' => $column,
'id' => $option->option_name,
'matched_pattern' => get_pattern_display($pattern),
'content' => $option->{$column}
];
}
}
} else {
$search_pattern = '%' . $pattern . '%';
$options = $wpdb->get_results($wpdb->prepare("
SELECT option_name, {$column}
FROM {$wpdb->options}
WHERE {$column} LIKE %s
", $search_pattern));
foreach ($options as $option) {
$found[] = [
'table' => 'options',
'column' => $column,
'id' => $option->option_name,
'matched_pattern' => get_pattern_display($pattern),
'content' => $option->{$column}
];
}
}
return $found;
}
function search_in_breakdance_table($pattern, $column, $wpdb) {
$found = [];
if (is_regex_pattern($pattern)) {
$posts = $wpdb->get_results("
SELECT ID, post_title, {$column}, post_type, post_status, post_parent
FROM {$wpdb->posts}
WHERE post_type = 'breakdance_template'
AND {$column} != ''
");
$regex = get_regex_from_pattern($pattern);
foreach ($posts as $post) {
if (@preg_match($regex, $post->{$column})) {
$result = create_post_result($post, $column, $pattern);
$result['table'] = 'posts (Breakdance)';
$found[] = $result;
}
}
} else {
$search_pattern = '%' . $pattern . '%';
$posts = $wpdb->get_results($wpdb->prepare("
SELECT ID, post_title, {$column}, post_type, post_status, post_parent
FROM {$wpdb->posts}
WHERE post_type = 'breakdance_template'
AND {$column} LIKE %s
", $search_pattern));
foreach ($posts as $post) {
$result = create_post_result($post, $column, $pattern);
$result['table'] = 'posts (Breakdance)';
$found[] = $result;
}
}
return $found;
}
function search_in_generic_table($pattern, $table_name, $column, $wpdb) {
$found = [];
if (is_regex_pattern($pattern)) {
$results = $wpdb->get_results("
SELECT * FROM {$table_name}
WHERE {$column} != ''
");
$regex = get_regex_from_pattern($pattern);
foreach ($results as $result) {
if (@preg_match($regex, $result->{$column})) {
$found[] = [
'table' => $table_name,
'column' => $column,
'id' => $result->ID ?? $result->id ?? 'unknown',
'matched_pattern' => get_pattern_display($pattern),
'content' => $result->{$column}
];
}
}
} else {
$search_pattern = '%' . $pattern . '%';
$results = $wpdb->get_results($wpdb->prepare("
SELECT * FROM {$table_name}
WHERE {$column} LIKE %s
", $search_pattern));
foreach ($results as $result) {
$found[] = [
'table' => $table_name,
'column' => $column,
'id' => $result->ID ?? $result->id ?? 'unknown',
'matched_pattern' => get_pattern_display($pattern),
'content' => $result->{$column}
];
}
}
return $found;
}
function create_post_result($post, $column, $pattern) {
$is_revision = $post->post_type === 'revision';
$parent_info = '';
if ($is_revision && $post->post_parent > 0) {
$parent_post = get_post($post->post_parent);
$parent_info = $parent_post ? " (Parent: {$parent_post->post_title} - ID: {$parent_post->ID})" : " (Parent ID: {$post->post_parent})";
}
return [
'table' => 'posts',
'column' => $column,
'id' => $post->ID,
'title' => $post->post_title,
'post_type' => $post->post_type,
'post_status' => $post->post_status,
'is_revision' => $is_revision,
'parent_info' => $parent_info,
'matched_pattern' => get_pattern_display($pattern),
'content' => $post->{$column}
];
}
function create_postmeta_result($meta_entry, $column, $pattern) {
$is_revision = $meta_entry->post_type === 'revision';
$parent_info = '';
if ($is_revision && $meta_entry->post_parent > 0) {
$parent_post = get_post($meta_entry->post_parent);
$parent_info = $parent_post ? " (Parent: {$parent_post->post_title} - ID: {$parent_post->ID})" : " (Parent ID: {$meta_entry->post_parent})";
}
return [
'table' => 'postmeta',
'column' => $column,
'id' => $meta_entry->post_id,
'meta_key' => $meta_entry->meta_key,
'post_type' => $meta_entry->post_type,
'post_status' => $meta_entry->post_status,
'post_title' => $meta_entry->post_title,
'is_revision' => $is_revision,
'parent_info' => $parent_info,
'matched_pattern' => get_pattern_display($pattern),
'content' => $meta_entry->{$column}
];
}
function is_json($string) {
if (!is_string($string)) return false;
// Trim whitespace
$string = trim($string);
// Check if it's empty
if (empty($string)) return false;
// Check if it starts with { or [ (JSON object/array) or " (JSON string)
if (!in_array($string[0], ['{', '[', '"'])) return false;
// Try to decode
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
function format_json_recursively($data) {
// Recursively check for nested JSON strings and format them
if (is_array($data)) {
foreach ($data as $key => $value) {
if (is_string($value) && is_json($value)) {
// Found nested JSON string, decode and format it
$nested_data = json_decode($value, true);
if ($nested_data !== null) {
$data[$key] = $nested_data;
}
} elseif (is_array($value)) {
$data[$key] = format_json_recursively($value);
}
}
}
// Return formatted JSON
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
function get_file_context($file_path, $target_line, $context_lines = 5) {
$content = @file_get_contents($file_path);
if ($content === false) return "Could not read file.";
$lines = explode("\n", $content);
$start = max(0, $target_line - $context_lines - 1);
$end = min(count($lines) - 1, $target_line + $context_lines - 1);
$context = "";
for ($i = $start; $i <= $end; $i++) {
$line_num = $i + 1;
$marker = ($line_num == $target_line) ? ">>> " : " ";
$context .= sprintf("%s%3d: %s\n", $marker, $line_num, htmlspecialchars($lines[$i]));
}
return $context;
}
function handle_unserialize_request() {
global $wpdb;
// Set content type for AJAX response
header('Content-Type: text/html; charset=utf-8');
// Validate required parameters
if (!isset($_GET['table']) || !isset($_GET['column']) || !isset($_GET['id'])) {
echo "Missing required parameters.";
exit;
}
$table = sanitize_text_field($_GET['table']);
$column = sanitize_text_field($_GET['column']);
$id = sanitize_text_field($_GET['id']);
$meta_key = isset($_GET['meta_key']) ? sanitize_text_field($_GET['meta_key']) : '';
// Fetch the data based on table type
$content = '';
if ($table === 'posts' || $table === 'posts (Breakdance)') {
$result = $wpdb->get_var($wpdb->prepare("SELECT {$column} FROM {$wpdb->posts} WHERE ID = %d", $id));
$content = $result;
} elseif ($table === 'postmeta') {
$result = $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s", $id, $meta_key));
$content = $result;
} elseif ($table === 'options') {
$result = $wpdb->get_var($wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name = %s", $id));
$content = $result;
}
if (empty($content)) {
echo "No content found.";
exit;
}
// Try to unserialize or decode
echo "<strong>Unserialized/Decoded Data:</strong><br><br>";
if (is_serialized($content)) {
$unserialized = @unserialize($content);
if ($unserialized !== false) {
echo "<strong>Type:</strong> PHP Serialized Data<br><br>";
echo "<pre>" . esc_html(print_r($unserialized, true)) . "</pre>";
} else {
echo "Failed to unserialize data.";
}
} elseif (is_json($content)) {
$json_data = json_decode($content, true);
if ($json_data !== null) {
echo "<strong>Type:</strong> JSON Data<br><br>";
// Handle JSON strings (quoted JSON) vs JSON objects/arrays
if (is_string($json_data)) {
// This is a JSON string, try to decode it further
if (is_json($json_data)) {
$nested_json = json_decode($json_data, true);
if ($nested_json !== null) {
echo "<strong>Note:</strong> JSON string containing nested JSON<br><br>";
$formatted_json = format_json_recursively($nested_json);
echo "<pre>" . esc_html($formatted_json) . "</pre>";
} else {
echo "<pre>" . esc_html($json_data) . "</pre>";
}
} else {
echo "<pre>" . esc_html($json_data) . "</pre>";
}
} else {
// Regular JSON object/array
$formatted_json = format_json_recursively($json_data);
echo "<pre>" . esc_html($formatted_json) . "</pre>";
}
} else {
echo "Failed to decode JSON data.";
}
} else {
echo "Content is not serialized or JSON. Raw content:<br>";
echo "<pre>" . esc_html($content) . "</pre>";
}
exit;
}
function handle_context_request() {
// Set content type for AJAX response
header('Content-Type: text/html; charset=utf-8');
// Validate required parameters
if (!isset($_GET['file']) || !isset($_GET['line'])) {
echo "Missing required parameters.";
exit;
}
$file_path = sanitize_text_field($_GET['file']);
$line_number = intval($_GET['line']);
// Security check - ensure file is within WordPress directory
if (strpos(realpath($file_path), realpath(ABSPATH)) !== 0) {
echo "File access denied for security reasons.";
exit;
}
if (!file_exists($file_path)) {
echo "File not found.";
exit;
}
echo "<strong>📋 Context (±10 lines):</strong><br><br>";
$context = get_file_context($file_path, $line_number, 10);
echo "<pre>" . $context . "</pre>";
exit;
}
function is_regex_pattern($pattern) {
// Check if pattern is array with 'regex' key OR starts and ends with forward slashes
if (is_array($pattern) && isset($pattern['regex'])) {
return true;
}
return is_string($pattern) && strlen($pattern) >= 3 && $pattern[0] === '/' && substr($pattern, -1) === '/';
}
function get_regex_from_pattern($pattern) {
// Extract regex from pattern
if (is_array($pattern) && isset($pattern['regex'])) {
return '/' . $pattern['regex'] . '/';
}
return $pattern; // Already a string regex
}
function get_pattern_display($pattern) {
// Get display string for pattern
if (is_array($pattern) && isset($pattern['regex'])) {
return $pattern['regex'];
}
return $pattern;
}