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.
<?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; }