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 = '
'; if ($this->onlyViewVars) { $html .= '

Variables Found in $view Declarations

'; } else { $html .= '

Variable Declarations Found in PHP Files

'; } if (empty($resultData)) { $html .= '
No variables found.
'; } else { foreach ($resultData as $file => $vars) { $totalFiles++; $totalVars += count($vars); $html .= '
'; $html .= '
' . htmlspecialchars($file) . ' (' . count($vars) . ' variables)
'; $html .= '
'; foreach ($vars as $var) { $html .= '
' . htmlspecialchars($var) . '
'; } $html .= '
'; } // Summary section $html .= '
'; $html .= '

Summary

'; if ($this->onlyViewVars) { $html .= '

Total files with $view variables: ' . $totalFiles . '

'; $html .= '

Total variables found in $view declarations: ' . $totalVars . '

'; } else { $html .= '

Total files with variable declarations: ' . $totalFiles . '

'; $html .= '

Total variable declarations found: ' . $totalVars . '

'; // 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 .= '
'; $html .= '

Additionally found ' . $viewVarCount . ' variables in $view declarations across ' . $viewFileCount . ' files

'; $html .= '
'; } } $html .= '
'; } // Toggle button $html .= '
'; if ($this->onlyViewVars) { $html .= 'Show All Variable Declarations'; } else { $html .= 'Show Only $view Variables'; } $html .= '
'; // Download button $html .= '
'; $html .= 'Download Results as JSON'; $html .= '
'; $html .= '
'; 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 ?> PHP Variable Finder

PHP Variable Finder

findDeclarations(); // Output results as HTML echo $finder->getHtmlResults(); ?>