diff --git a/README.md b/README.md index 77419c2a2348..0ff657636929 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,42 @@ framework are exposed. **Please** read the user guide for a better explanation of how CI4 works! +# 🔥 Hot Reload for Views (Development Feature) + +CodeIgniter 4 includes a lightweight **Hot Reload** system designed to improve development workflow. +It automatically refreshes the browser whenever a file inside `app/Views/` changes — with **no external tools required**. + +### ✨ Key Features + +- No **Node**, **Webpack**, **Vite**, or external watchers +- Runs **entirely in PHP** +- Starts **automatically** when you run: + +`php spark serve` + +When the server starts, CodeIgniter launches: + +1. **The PHP development server** +2. **A file watcher** that monitors: + +app/Views/ + +Whenever a view file changes, the watcher updates a timestamp. +Your browser polls `/hotreload/check`, and when it detects an update, the page reloads instantly. + +--- + +## ⚡ Enabling Hot Reload in Your Views + +To activate Hot Reload on any view, include the following partial: + +```php +include('partials/hotreload') ?> +``` +You may place this partial: + +✔ In each view + ## Repository Management CodeIgniter is developed completely on a volunteer basis. As such, please give up to 7 days diff --git a/app/Commands/HotReload.php b/app/Commands/HotReload.php new file mode 100644 index 000000000000..2636e14b7d00 --- /dev/null +++ b/app/Commands/HotReload.php @@ -0,0 +1,56 @@ +scanFiles($watchPath))); + + if ($lastHash !== null && $currentHash !== $lastHash) { + file_put_contents($flagFile, time()); + echo "♻ Cambio detectado → Recargando navegador…\n"; + } + + $lastHash = $currentHash; + sleep(1); + } + } + + private function scanFiles($path) + { + $rii = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); + $files = []; + + foreach ($rii as $file) { + if ($file->isDir()) continue; + $files[] = [ + 'file' => $file->getPathname(), + 'mtime' => $file->getMTime(), + ]; + } + + return $files; + } +} diff --git a/app/Commands/Serve.php b/app/Commands/Serve.php new file mode 100644 index 000000000000..27832072b548 --- /dev/null +++ b/app/Commands/Serve.php @@ -0,0 +1,36 @@ +startHotReloadWatcher(); + return parent::run($params); + } + + private function startHotReloadWatcher() + { + $php = PHP_BINARY; + $spark = realpath(ROOTPATH . 'spark'); + + if (stripos(PHP_OS, 'WIN') === 0) { + $cmd = "start /B $php $spark hotreload >nul 2>&1"; + pclose(popen($cmd, "r")); + } else { + shell_exec("$php $spark hotreload > /dev/null 2>&1 &"); + } + + CLI::write("🔁 HotReload watcher iniciado.", 'yellow'); + } +} \ No newline at end of file diff --git a/app/Config/Routes.php b/app/Config/Routes.php index fc4914a6923b..af2629a8b64c 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -6,3 +6,8 @@ * @var RouteCollection $routes */ $routes->get('/', 'Home::index'); + +/** + *HOT RELOAD ROUTE + */ +$routes->get('hotreload/check', 'HotReloadController::check'); diff --git a/app/Controllers/HotReloadController.php b/app/Controllers/HotReloadController.php new file mode 100644 index 000000000000..e8e585400e29 --- /dev/null +++ b/app/Controllers/HotReloadController.php @@ -0,0 +1,21 @@ +response->setJSON([ + 'hash' => file_get_contents($cacheFile) + ]); + } +} diff --git a/app/Views/partials/hotreload.php b/app/Views/partials/hotreload.php new file mode 100644 index 000000000000..515d03b669c9 --- /dev/null +++ b/app/Views/partials/hotreload.php @@ -0,0 +1,21 @@ + diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php index c18eca3c9ed3..8946526a9804 100644 --- a/app/Views/welcome_message.php +++ b/app/Views/welcome_message.php @@ -6,7 +6,8 @@ - + + include('partials/hotreload') ?>