2nd update
This commit is contained in:
388
variable_scan.php
Normal file
388
variable_scan.php
Normal file
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Variable Declaration Finder - Web Interface
|
||||
* Save this file in your project root and access it through your browser
|
||||
* Regular Mode: Shows all variable declarations found in your PHP files
|
||||
* View-Only Mode: Shows only variables used in $view declarations (accessed by adding ?view_only=1 to the URL)
|
||||
* Download: Get results as JSON by adding ?download=all or ?download=view to the URL
|
||||
*/
|
||||
|
||||
class PHPVariableDeclarationFinder {
|
||||
private $variables = [];
|
||||
private $viewVariables = [];
|
||||
private $rootDir;
|
||||
private $onlyViewVars = false;
|
||||
|
||||
public function __construct($rootDir = '.', $onlyViewVars = false) {
|
||||
$this->rootDir = rtrim($rootDir, '/');
|
||||
$this->onlyViewVars = $onlyViewVars;
|
||||
}
|
||||
|
||||
public function findDeclarations() {
|
||||
$this->scanDirectory($this->rootDir);
|
||||
|
||||
if ($this->onlyViewVars) {
|
||||
return $this->viewVariables;
|
||||
}
|
||||
|
||||
return $this->variables;
|
||||
}
|
||||
|
||||
private function scanDirectory($dir) {
|
||||
$items = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->isFile() && $item->getExtension() === 'php') {
|
||||
// Skip scanning this finder script itself
|
||||
if (basename($item->getPathname()) === basename($_SERVER['SCRIPT_FILENAME'])) {
|
||||
continue;
|
||||
}
|
||||
$this->extractDeclarationsFromFile($item->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function extractDeclarationsFromFile($filePath) {
|
||||
$content = file_get_contents($filePath);
|
||||
|
||||
// Remove comments to avoid false positives
|
||||
$content = preg_replace('!/\*.*?\*/!s', '', $content);
|
||||
$content = preg_replace('#//.*#', '', $content);
|
||||
|
||||
$relativePath = str_replace($this->rootDir . '/', '', $filePath);
|
||||
|
||||
// If we're looking for view variables specifically
|
||||
if ($this->onlyViewVars) {
|
||||
$this->findViewVariables($content, $relativePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise proceed with normal variable declaration extraction
|
||||
$this->findAllVariableDeclarations($content, $relativePath);
|
||||
|
||||
// Additionally find view variables but store them separately
|
||||
$this->findViewVariables($content, $relativePath);
|
||||
}
|
||||
|
||||
private function findAllVariableDeclarations($content, $filePath) {
|
||||
// Patterns to match variable declarations
|
||||
$patterns = [
|
||||
// Variable assignments
|
||||
'/\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=(?!=)/',
|
||||
|
||||
// Function parameters
|
||||
'/function\s+\w+\s*\(([^)]*)\)/',
|
||||
|
||||
// foreach loops
|
||||
'/foreach\s*\(\s*\$\w+\s+as\s+(\$[^=)]+)/',
|
||||
|
||||
// Class properties
|
||||
'/(?:public|private|protected|var)\s+(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/',
|
||||
|
||||
// Global declarations
|
||||
'/global\s+(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*,\s*\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)/',
|
||||
|
||||
// Static declarations
|
||||
'/static\s+(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*,\s*\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)/',
|
||||
|
||||
// Catch exception blocks
|
||||
'/catch\s*\(\s*\w+\s+(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\)/'
|
||||
];
|
||||
|
||||
$declarations = [];
|
||||
|
||||
// Process regular variable assignments
|
||||
if (preg_match_all($patterns[0], $content, $matches)) {
|
||||
foreach ($matches[1] as $var) {
|
||||
$declarations[] = '$' . $var;
|
||||
}
|
||||
}
|
||||
|
||||
// Process function parameters
|
||||
if (preg_match_all($patterns[1], $content, $matches)) {
|
||||
foreach ($matches[1] as $paramList) {
|
||||
preg_match_all('/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $paramList, $params);
|
||||
$declarations = array_merge($declarations, $params[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process foreach variables
|
||||
if (preg_match_all($patterns[2], $content, $matches)) {
|
||||
foreach ($matches[1] as $foreachVars) {
|
||||
preg_match_all('/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $foreachVars, $vars);
|
||||
$declarations = array_merge($declarations, $vars[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process class properties
|
||||
if (preg_match_all($patterns[3], $content, $matches)) {
|
||||
$declarations = array_merge($declarations, $matches[1]);
|
||||
}
|
||||
|
||||
// Process global declarations
|
||||
if (preg_match_all($patterns[4], $content, $matches)) {
|
||||
foreach ($matches[1] as $globalVars) {
|
||||
preg_match_all('/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $globalVars, $vars);
|
||||
$declarations = array_merge($declarations, $vars[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process static declarations
|
||||
if (preg_match_all($patterns[5], $content, $matches)) {
|
||||
foreach ($matches[1] as $staticVars) {
|
||||
preg_match_all('/\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $staticVars, $vars);
|
||||
$declarations = array_merge($declarations, $vars[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process catch block variables
|
||||
if (preg_match_all($patterns[6], $content, $matches)) {
|
||||
$declarations = array_merge($declarations, $matches[1]);
|
||||
}
|
||||
|
||||
if (!empty($declarations)) {
|
||||
$this->variables[$filePath] = array_unique($declarations);
|
||||
}
|
||||
}
|
||||
|
||||
private function findViewVariables($content, $filePath) {
|
||||
// Patterns specifically for $view declarations
|
||||
$viewPatterns = [
|
||||
// $view = "..."
|
||||
'/\$view\s*=\s*([^;]+)/',
|
||||
|
||||
// $view .= "..."
|
||||
'/\$view\s*\.=\s*([^;]+)/'
|
||||
];
|
||||
|
||||
$viewVars = [];
|
||||
|
||||
foreach ($viewPatterns as $pattern) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
foreach ($matches[1] as $viewContent) {
|
||||
// Find variables within the view content
|
||||
// Look for PHP variable pattern with braces like {$variable}
|
||||
if (preg_match_all('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/', $viewContent, $varMatches)) {
|
||||
foreach ($varMatches[1] as $var) {
|
||||
$viewVars[] = '$' . $var;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for PHP variable pattern without braces in double-quoted strings
|
||||
if (strpos($viewContent, '"') !== false &&
|
||||
preg_match_all('/"\s*\.\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\.\s*"/', $viewContent, $varMatches)) {
|
||||
foreach ($varMatches[1] as $var) {
|
||||
$viewVars[] = '$' . $var;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for variables in double-quoted strings
|
||||
if (preg_match_all('/"[^"]*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[^"]*"/', $viewContent, $varMatches)) {
|
||||
foreach ($varMatches[1] as $var) {
|
||||
$viewVars[] = '$' . $var;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($viewVars)) {
|
||||
$this->viewVariables[$filePath] = array_unique($viewVars);
|
||||
}
|
||||
}
|
||||
|
||||
public function getHtmlResults() {
|
||||
$resultData = $this->onlyViewVars ? $this->viewVariables : $this->variables;
|
||||
$totalVars = 0;
|
||||
$totalFiles = 0;
|
||||
|
||||
$html = '<div style="font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px;">';
|
||||
|
||||
if ($this->onlyViewVars) {
|
||||
$html .= '<h1>Variables Found in $view Declarations</h1>';
|
||||
} else {
|
||||
$html .= '<h1>Variable Declarations Found in PHP Files</h1>';
|
||||
}
|
||||
|
||||
if (empty($resultData)) {
|
||||
$html .= '<div style="padding: 20px; background-color: #f8f9fa; border-radius: 5px;">No variables found.</div>';
|
||||
} else {
|
||||
foreach ($resultData as $file => $vars) {
|
||||
$totalFiles++;
|
||||
$totalVars += count($vars);
|
||||
|
||||
$html .= '<div style="margin-bottom: 20px; border: 1px solid #ddd; border-radius: 5px; overflow: hidden;">';
|
||||
$html .= '<div style="padding: 10px; background-color: #f8f9fa; border-bottom: 1px solid #ddd; font-weight: bold;">' . htmlspecialchars($file) . ' <span style="font-weight: normal; color: #666;">(' . count($vars) . ' variables)</span></div>';
|
||||
$html .= '<div style="padding: 10px; background-color: #fff;">';
|
||||
|
||||
foreach ($vars as $var) {
|
||||
$html .= '<div style="padding: 5px; margin: 2px 0; background-color: #f1f8ff; border-radius: 3px;">' . htmlspecialchars($var) . '</div>';
|
||||
}
|
||||
|
||||
$html .= '</div></div>';
|
||||
}
|
||||
|
||||
// Summary section
|
||||
$html .= '<div style="margin-top: 20px; padding: 15px; background-color: #f8f9fa; border-radius: 5px;">';
|
||||
$html .= '<h2>Summary</h2>';
|
||||
|
||||
if ($this->onlyViewVars) {
|
||||
$html .= '<p>Total files with $view variables: <strong>' . $totalFiles . '</strong></p>';
|
||||
$html .= '<p>Total variables found in $view declarations: <strong>' . $totalVars . '</strong></p>';
|
||||
} else {
|
||||
$html .= '<p>Total files with variable declarations: <strong>' . $totalFiles . '</strong></p>';
|
||||
$html .= '<p>Total variable declarations found: <strong>' . $totalVars . '</strong></p>';
|
||||
|
||||
// Show view variables summary if not in view-only mode
|
||||
if (!empty($this->viewVariables)) {
|
||||
$viewVarCount = 0;
|
||||
$viewFileCount = count($this->viewVariables);
|
||||
foreach ($this->viewVariables as $vars) {
|
||||
$viewVarCount += count($vars);
|
||||
}
|
||||
|
||||
$html .= '<div style="margin-top: 15px; padding: 10px; background-color: #e6f7ff; border-radius: 3px;">';
|
||||
$html .= '<p>Additionally found <strong>' . $viewVarCount . '</strong> variables in $view declarations across <strong>' . $viewFileCount . '</strong> files</p>';
|
||||
$html .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
// Toggle button
|
||||
$html .= '<div style="margin-top: 20px; text-align: center;">';
|
||||
if ($this->onlyViewVars) {
|
||||
$html .= '<a href="' . $_SERVER['PHP_SELF'] . '" style="display: inline-block; padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px;">Show All Variable Declarations</a>';
|
||||
} else {
|
||||
$html .= '<a href="' . $_SERVER['PHP_SELF'] . '?view_only=1" style="display: inline-block; padding: 10px 15px; background-color: #2196F3; color: white; text-decoration: none; border-radius: 4px;">Show Only $view Variables</a>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
// Download button
|
||||
$html .= '<div style="margin-top: 15px; text-align: center;">';
|
||||
$html .= '<a href="' . $_SERVER['PHP_SELF'] . '?download=' . ($this->onlyViewVars ? 'view' : 'all') . '" style="display: inline-block; padding: 10px 15px; background-color: #FF9800; color: white; text-decoration: none; border-radius: 4px;">Download Results as JSON</a>';
|
||||
$html .= '</div>';
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function exportToJson() {
|
||||
$resultData = $this->onlyViewVars ? $this->viewVariables : $this->variables;
|
||||
return json_encode($resultData, JSON_PRETTY_PRINT);
|
||||
}
|
||||
}
|
||||
|
||||
// ------ MAIN EXECUTION ------
|
||||
|
||||
// Set execution time to a higher value to handle large codebases
|
||||
set_time_limit(300);
|
||||
|
||||
// Check for download request
|
||||
if (isset($_GET['download'])) {
|
||||
$viewOnly = ($_GET['download'] === 'view');
|
||||
$finder = new PHPVariableDeclarationFinder('.', $viewOnly);
|
||||
$finder->findDeclarations();
|
||||
|
||||
$filename = $viewOnly ? 'view_variables.json' : 'all_variables.json';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
echo $finder->exportToJson();
|
||||
exit;
|
||||
}
|
||||
|
||||
// HTML header
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PHP Variable Finder</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
header {
|
||||
background-color: #4a76a8;
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
.progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background-color: #f1f1f1;
|
||||
z-index: 1000;
|
||||
}
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background-color: #4CAF50;
|
||||
width: 0%;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="progress"><div class="progress-bar" id="progressBar"></div></div>
|
||||
<header>
|
||||
<h1>PHP Variable Finder</h1>
|
||||
</header>
|
||||
|
||||
<script>
|
||||
// Simple progress animation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const bar = document.getElementById('progressBar');
|
||||
let width = 0;
|
||||
const interval = setInterval(function() {
|
||||
if (width >= 90) {
|
||||
clearInterval(interval);
|
||||
} else {
|
||||
width += Math.random() * 10;
|
||||
if (width > 90) width = 90;
|
||||
bar.style.width = width + '%';
|
||||
}
|
||||
}, 300);
|
||||
|
||||
// When everything is loaded
|
||||
window.addEventListener('load', function() {
|
||||
bar.style.width = '100%';
|
||||
setTimeout(function() {
|
||||
document.querySelector('.progress').style.display = 'none';
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
// Process the request
|
||||
$viewOnly = isset($_GET['view_only']) && $_GET['view_only'] == '1';
|
||||
$finder = new PHPVariableDeclarationFinder('.', $viewOnly);
|
||||
|
||||
// Scan for variables with progress indicator
|
||||
$finder->findDeclarations();
|
||||
|
||||
// Output results as HTML
|
||||
echo $finder->getHtmlResults();
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user