PHP has a reputation as a request-and-response web language, but a plain while (true) loop makes a perfectly good long-running worker: a queue processor, a poller, a scheduled-tasks daemon. The PHP CLI application runs exactly this kind of script. This guide shows a worker that shuts down cleanly when Falix stops it, and explains what restart, stop, and crash actually do here.
| At a glance | |
|---|---|
| You need | A Falix server running the PHP CLI application (or PHP Composer if you have dependencies) |
| Plan | Any — on free it runs while your session timer has time left, premium runs 24/7 |
| Time | Fifteen minutes |
| New to PHP here? | PHP on Falix |
Which PHP application?
A worker needs PHP CLI (runs php index.php and nothing else) or, if your code has Composer dependencies, PHP Composer (runs composer install first, then your file). Not the PHP Web Server — that's for websites served by nginx. A worker makes no inbound connections, so it needs no port at all. See PHP on Falix for the full comparison.
A worker that stops gracefully
Here's a complete index.php. It loops forever doing work, and — this is the important part — traps the signal Falix sends when you press Stop, so it can finish the current tick instead of being killed mid-job:
<?php
// Deliver signals as soon as they arrive, even during sleep().
pcntl_async_signals(true);
$running = true;
$shutdown = function (int $signo) use (&$running) {
echo "received signal $signo, finishing up..." . PHP_EOL;
$running = false;
};
pcntl_signal(SIGTERM, $shutdown);
pcntl_signal(SIGINT, $shutdown);
echo "worker started (pid " . getmypid() . ")" . PHP_EOL;
while ($running) {
// ... do one unit of work here ...
echo "tick at " . date("H:i:s") . PHP_EOL;
sleep(5); // signal-friendly: a stop signal interrupts it instantly
}
echo "clean exit" . PHP_EOL;
How it works:
pcntl_async_signals(true)tells PHP to deliver signals the moment they arrive, so you don't have to sprinklepcntl_signal_dispatch()through your loop.pcntl_signal(SIGTERM, …)andSIGINTregister your shutdown handler. Falix's Stop button sends a termination signal; trapping both covers the common cases.- The handler just flips
$runningtofalse. The loop finishes its current tick, falls out cleanly, and the process exits with status0— a graceful shutdown, not a hard kill. sleep()is signal-friendly: an incoming signal interrupts it immediately, so Stop feels instant even mid-sleep.
🎯 Good to know: The
pcntlandposixextensions ship with the PHP CLI image, sopcntl_signaland friends are available out of the box — no install needed.
What keeps the server "running"
A Falix app server is "running" for as long as your process is alive. A while (true) worker never returns, so the server stays up. The flip side matters just as much:
⚠️ Heads up: A PHP script that reaches the end and returns exits cleanly, and the panel then shows the server as stopped. That's not a crash — it's a finished program. If your worker is meant to run forever, make sure the loop condition really keeps it alive.
Restart, stop, and crash on Falix
| Event | What happens | What your code should do |
|---|---|---|
| You press Stop / Restart | Falix signals your process to shut down | Trap SIGTERM/SIGINT (above) to finish the current job first |
| A fatal error (uncaught exception, out of memory) | The process dies and the server shows stopped | Read the Console — the error prints there; the first one is usually the cause |
| Out-of-memory kill | Container killed, exit code 137 |
Free leaked memory; see Out of memory |
Two ways to recover automatically from a crash: premium's Crash Detection (auto-restart, on the Settings page), or a Schedule with a server crashed → Start trigger. Either one turns a 3 AM crash into a few seconds of downtime instead of hours.
Keeping a worker healthy over time
Long-lived PHP processes don't get the "fresh start every request" that a web app enjoys, so a couple of habits pay off:
- Watch memory. PHP holds onto whatever you keep referenced.
unset()large arrays when you're done, avoid growing a variable forever, and don't load an entire table into memory when you can page through it. A leaky worker eventually hits the137kill above. - A nightly restart is cheap insurance. A Schedule that restarts the worker once a day clears any slow creep. Because your worker shuts down gracefully, that restart is clean.
- Log so you can see it working.
echogoes straight to the Console. If lines ever seem to lag, callflush()after important output to push it out immediately.
Everything past the Falix-shaped parts is standard PHP — the official manual at php.net documents pcntl, signals, and CLI behaviour in full.