- Move 45 debug/test files from root to organized scripts/ directories - Secure public/ directory by removing debug files (security improvement) - Create structured scripts organization: • scripts/debug/ (20 files) - Framework debugging tools • scripts/test/ (18 files) - Test and validation scripts • scripts/maintenance/ (5 files) - Maintenance utilities • scripts/dev/ (2 files) - Development tools Security improvements: - Removed all debug/test files from public/ directory - Only production files remain: index.php, health.php Root directory cleanup: - Reduced from 47 to 2 PHP files in root - Only essential production files: console.php, worker.php This improves: ✅ Security (no debug code in public/) ✅ Organization (clear separation of concerns) ✅ Maintainability (easy to find and manage scripts) ✅ Professional structure (clean root directory)
153 lines
4.0 KiB
PHP
153 lines
4.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Minimal Hot Reload Endpoint
|
|
* Simple SSE implementation without complex framework dependencies
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
// Disable deprecation warnings
|
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
|
ini_set('display_errors', 1);
|
|
|
|
// Simple .env parsing
|
|
function loadEnvVar(string $key, string $envFile = null): ?string {
|
|
$envFile ??= __DIR__ . '/../.env';
|
|
if (!file_exists($envFile)) {
|
|
return null;
|
|
}
|
|
|
|
$content = file_get_contents($envFile);
|
|
if (preg_match("/^{$key}\s*=\s*(.*)$/m", $content, $matches)) {
|
|
return trim($matches[1], " \t\n\r\0\x0B\"'");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Only allow in development
|
|
$appDebug = loadEnvVar('APP_DEBUG');
|
|
if ($appDebug !== 'true') {
|
|
http_response_code(403);
|
|
exit('Hot Reload is only available in development mode');
|
|
}
|
|
|
|
// Set SSE headers
|
|
header('Content-Type: text/event-stream');
|
|
header('Cache-Control: no-cache');
|
|
header('Connection: keep-alive');
|
|
header('X-Accel-Buffering: no'); // Disable nginx buffering
|
|
|
|
// Get watched directories
|
|
$watchedDirs = [
|
|
__DIR__ . '/../src',
|
|
__DIR__ . '/../resources/views',
|
|
__DIR__ . '/../config',
|
|
];
|
|
|
|
// Store file modification times
|
|
$fileCache = [];
|
|
|
|
// Function to scan directories for PHP files
|
|
function scanDirectory(string $dir, array &$fileCache): array {
|
|
$changes = [];
|
|
|
|
if (!is_dir($dir)) {
|
|
return $changes;
|
|
}
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::SELF_FIRST
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if (!$file->isFile()) {
|
|
continue;
|
|
}
|
|
|
|
$path = $file->getPathname();
|
|
|
|
// Only watch specific file types
|
|
if (!preg_match('/\.(php|view\.php|css|js|ts)$/', $path)) {
|
|
continue;
|
|
}
|
|
|
|
$mtime = $file->getMTime();
|
|
|
|
if (!isset($fileCache[$path])) {
|
|
$fileCache[$path] = $mtime;
|
|
} elseif ($fileCache[$path] < $mtime) {
|
|
$changes[] = [
|
|
'path' => $path,
|
|
'type' => 'modified',
|
|
'time' => $mtime
|
|
];
|
|
$fileCache[$path] = $mtime;
|
|
}
|
|
}
|
|
|
|
return $changes;
|
|
}
|
|
|
|
// Send initial connection event
|
|
echo "event: connected\n";
|
|
echo "data: " . json_encode([
|
|
'status' => 'connected',
|
|
'timestamp' => date('c'),
|
|
'message' => 'Hot Reload server started'
|
|
]) . "\n\n";
|
|
flush();
|
|
|
|
// Initialize file cache
|
|
foreach ($watchedDirs as $dir) {
|
|
scanDirectory($dir, $fileCache);
|
|
}
|
|
|
|
$lastHeartbeat = time();
|
|
|
|
// Keep connection alive and watch for changes
|
|
while (connection_aborted() === 0) {
|
|
$hasChanges = false;
|
|
|
|
// Check each watched directory
|
|
foreach ($watchedDirs as $dir) {
|
|
$changes = scanDirectory($dir, $fileCache);
|
|
|
|
foreach ($changes as $change) {
|
|
$reloadType = 'full';
|
|
if (str_ends_with($change['path'], '.css')) {
|
|
$reloadType = 'css';
|
|
} elseif (str_ends_with($change['path'], '.js') || str_ends_with($change['path'], '.ts')) {
|
|
$reloadType = 'hmr';
|
|
}
|
|
|
|
echo "event: reload\n";
|
|
echo "data: " . json_encode([
|
|
'type' => $reloadType,
|
|
'file' => basename($change['path']),
|
|
'path' => $change['path'],
|
|
'timestamp' => date('c'),
|
|
'message' => 'File changed: ' . basename($change['path'])
|
|
]) . "\n\n";
|
|
flush();
|
|
|
|
$hasChanges = true;
|
|
}
|
|
}
|
|
|
|
// Send heartbeat every 30 seconds
|
|
if (time() - $lastHeartbeat >= 30) {
|
|
echo "event: heartbeat\n";
|
|
echo "data: " . json_encode([
|
|
'timestamp' => date('c'),
|
|
'message' => 'Connection alive'
|
|
]) . "\n\n";
|
|
flush();
|
|
$lastHeartbeat = time();
|
|
}
|
|
|
|
// Small delay to prevent high CPU usage
|
|
usleep(500000); // 500ms
|
|
} |