177 lines
5.2 KiB
PHP
177 lines
5.2 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
/**
|
|
* Check for duplicate class declarations in WordPress plugin
|
|
* Detects duplicate classes, interfaces, traits, functions, and constants
|
|
*/
|
|
|
|
$plugin_dir = $argv[1] ?? __DIR__;
|
|
|
|
echo "=== Checking for Duplicates ===\n\n";
|
|
|
|
function scan_directory($dir, $extensions = ['php']) {
|
|
$files = [];
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if (in_array(strtolower($file->getExtension()), $extensions)) {
|
|
$files[] = $file->getPathname();
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
function extract_declarations($content) {
|
|
$declarations = [];
|
|
|
|
// Extract classes
|
|
if (preg_match_all('/^\s*(?:abstract\s+)?class\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $class) {
|
|
$declarations['classes'][$class][] = 'class';
|
|
}
|
|
}
|
|
|
|
// Extract interfaces
|
|
if (preg_match_all('/^\s*interface\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $interface) {
|
|
$declarations['interfaces'][$interface][] = 'interface';
|
|
}
|
|
}
|
|
|
|
// Extract traits
|
|
if (preg_match_all('/^\s*trait\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $trait) {
|
|
$declarations['traits'][$trait][] = 'trait';
|
|
}
|
|
}
|
|
|
|
// Extract functions (non-namespaced)
|
|
if (preg_match_all('/^\s*function\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $function) {
|
|
if (!isset($declarations['functions'][$function])) {
|
|
$declarations['functions'][$function] = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $declarations;
|
|
}
|
|
|
|
function check_duplicate_classes($files) {
|
|
$all_classes = [];
|
|
$duplicates = [];
|
|
|
|
foreach ($files as $file) {
|
|
$content = file_get_contents($file);
|
|
|
|
// Check for classes
|
|
if (preg_match_all('/^\s*(?:abstract\s+)?class\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $class) {
|
|
if (!isset($all_classes[$class])) {
|
|
$all_classes[$class] = [];
|
|
}
|
|
$all_classes[$class][] = $file;
|
|
}
|
|
}
|
|
|
|
// Check for interfaces
|
|
if (preg_match_all('/^\s*interface\s+(\w+)/m', $content, $matches)) {
|
|
foreach ($matches[1] as $interface) {
|
|
$key = strtolower($interface);
|
|
if (!isset($all_classes[$key])) {
|
|
$all_classes[$key] = [];
|
|
}
|
|
$all_classes[$key][] = $file;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($all_classes as $name => $files_list) {
|
|
if (count($files_list) > 1) {
|
|
$duplicates['classes'][$name] = $files_list;
|
|
}
|
|
}
|
|
|
|
return $duplicates;
|
|
}
|
|
|
|
function check_duplicate_functions($files) {
|
|
$all_functions = [];
|
|
$duplicates = [];
|
|
|
|
foreach ($files as $file) {
|
|
$content = file_get_contents($file);
|
|
|
|
// Skip content inside classes, interfaces, and traits
|
|
$content = preg_replace('/class\s+\w+\s*\{[^}]*\}/s', '', $content);
|
|
$content = preg_replace('/interface\s+\w+\s*\{[^}]*\}/s', '', $content);
|
|
$content = preg_replace('/trait\s+\w+\s*\{[^}]*\}/s', '', $content);
|
|
|
|
if (preg_match_all('/^\s*function\s+(\w+)\s*\(/m', $content, $matches)) {
|
|
foreach ($matches[1] as $function) {
|
|
if (!isset($all_functions[$function])) {
|
|
$all_functions[$function] = [];
|
|
}
|
|
$all_functions[$function][] = $file;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($all_functions as $name => $files_list) {
|
|
if (count($files_list) > 1) {
|
|
$duplicates['functions'][$name] = $files_list;
|
|
}
|
|
}
|
|
|
|
return $duplicates;
|
|
}
|
|
|
|
echo "Scanning directory: $plugin_dir\n";
|
|
$files = scan_directory($plugin_dir);
|
|
echo "Found " . count($files) . " PHP files\n\n";
|
|
|
|
$class_duplicates = check_duplicate_classes($files);
|
|
$function_duplicates = check_duplicate_functions($files);
|
|
|
|
$has_issues = false;
|
|
|
|
if (!empty($class_duplicates['classes'])) {
|
|
$has_issues = true;
|
|
echo "❌ Duplicate class/interface declarations found:\n";
|
|
foreach ($class_duplicates['classes'] as $name => $files_list) {
|
|
echo " - $name found in:\n";
|
|
foreach ($files_list as $file) {
|
|
echo " $file\n";
|
|
}
|
|
}
|
|
echo "\n";
|
|
}
|
|
|
|
if (!empty($function_duplicates['functions'])) {
|
|
$has_issues = true;
|
|
echo "⚠️ Potential duplicate function declarations:\n";
|
|
foreach ($function_duplicates['functions'] as $name => $files_list) {
|
|
echo " - $name() found in:\n";
|
|
foreach ($files_list as $file) {
|
|
echo " $file\n";
|
|
}
|
|
}
|
|
echo "\n";
|
|
}
|
|
|
|
if (!$has_issues) {
|
|
echo "✅ No duplicate declarations found\n";
|
|
} else {
|
|
echo "⚠️ Please review the duplicates above and fix them.\n";
|
|
echo " Solutions:\n";
|
|
echo " - Use class_exists() guards around class definitions\n";
|
|
echo " - Consolidate duplicate declarations into one file\n";
|
|
echo " - Namespace conflicting classes differently\n";
|
|
}
|
|
|
|
echo "\n=== Check Complete ===\n";
|
|
exit($has_issues ? 1 : 0);
|