- 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)
145 lines
3.6 KiB
PHP
145 lines
3.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Development Hot Reload Endpoint
|
|
* Bypasses full framework bootstrap to avoid deprecation warnings
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
// Disable deprecation warnings for this development endpoint
|
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
|
|
|
// Set error reporting for debugging
|
|
ini_set('display_errors', 1);
|
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
|
|
|
// Set up minimal autoloading
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
try {
|
|
// Minimal framework classes needed
|
|
use App\Framework\Filesystem\FilePath;
|
|
use App\Framework\Filesystem\FileWatcher;
|
|
use App\Framework\DateTime\SystemTimer;
|
|
|
|
// Simple .env parsing for development check
|
|
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
|
|
|
|
// Create file watcher
|
|
$fileWatcher = new FileWatcher(
|
|
FilePath::create(__DIR__ . '/..'),
|
|
new SystemTimer()
|
|
);
|
|
|
|
// Watch patterns
|
|
$watchPatterns = [
|
|
'src/**/*.php',
|
|
'resources/views/**/*.php',
|
|
'resources/views/**/*.view.php',
|
|
'config/**/*.php',
|
|
];
|
|
|
|
$ignorePatterns = [
|
|
'vendor/**',
|
|
'var/**',
|
|
'storage/**',
|
|
'tests/**',
|
|
'public/**',
|
|
];
|
|
|
|
// 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();
|
|
|
|
// Keep connection alive and watch for changes
|
|
$lastCheck = time();
|
|
|
|
while (connection_aborted() === 0) {
|
|
// Check for file changes every 500ms
|
|
$changes = $fileWatcher->watchOnce($watchPatterns, $ignorePatterns);
|
|
|
|
foreach ($changes as $change) {
|
|
$reloadType = determineReloadType($change);
|
|
|
|
echo "event: reload\n";
|
|
echo "data: " . json_encode([
|
|
'type' => $reloadType->value,
|
|
'file' => $change->getPath(),
|
|
'timestamp' => $change->getTimestamp()->format('c'),
|
|
'message' => 'File changed: ' . basename($change->getPath())
|
|
]) . "\n\n";
|
|
flush();
|
|
}
|
|
|
|
// Send heartbeat every 30 seconds
|
|
if (time() - $lastCheck >= 30) {
|
|
echo "event: heartbeat\n";
|
|
echo "data: " . json_encode([
|
|
'timestamp' => date('c'),
|
|
'message' => 'Connection alive'
|
|
]) . "\n\n";
|
|
flush();
|
|
$lastCheck = time();
|
|
}
|
|
|
|
// Small delay to prevent high CPU usage
|
|
usleep(500000); // 500ms
|
|
}
|
|
|
|
function determineReloadType(FileChangeEvent $event): ReloadType
|
|
{
|
|
$path = $event->getPath();
|
|
|
|
// PHP files need full page reload
|
|
if (str_ends_with($path, '.php')) {
|
|
return ReloadType::FULL;
|
|
}
|
|
|
|
// CSS files can use hot replacement
|
|
if (str_ends_with($path, '.css')) {
|
|
return ReloadType::CSS;
|
|
}
|
|
|
|
// JS modules can use HMR if supported
|
|
if (str_ends_with($path, '.js') || str_ends_with($path, '.ts')) {
|
|
return ReloadType::HMR;
|
|
}
|
|
|
|
// Templates need full reload
|
|
if (str_ends_with($path, '.view.php')) {
|
|
return ReloadType::FULL;
|
|
}
|
|
|
|
return ReloadType::FULL;
|
|
} |