How To Create A PHP Daemon (Simple Examples)

Welcome to a quick tutorial on how to create a PHP daemon. Need to run a “service” PHP script in the background? Send out emails, fetch updates, or monitor something?

An easy alternative way to create a PHP daemon script is to:

  • Set the script to run infinitely – while (true) { /* DO SOMETHING */ sleep(1); }
  • Then run in the command line – php DAEMON.PHP

Let us walk through a few examples in this guide – Read on!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TLDR – QUICK SLIDES

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

SIMPLE PHP DAEMON EXAMPLES

All right, let us now get into some simple examples of PHP daemons.

 

PRELUDE) COMMAND LINE CHECK

0-cli-only.php
<?php
// CREDITS : https://stackoverflow.com/questions/933367/php-how-to-best-determine-if-the-current-invocation-is-from-cli-or-web-server
function is_cli () {
  if (php_sapi_name()==="cli") { return true; } 
  if (defined("STDIN")) { return true; }
  if (array_key_exists("SHELL", $_ENV)) { return true; }
  if (!array_key_exists("REQUEST_METHOD", $_SERVER)) { return true; }
  if (empty($_SERVER["REMOTE_ADDR"]) && !isset($_SERVER["HTTP_USER_AGENT"]) && count($_SERVER["argv"])>0) { return true; }
  return false;
}
if (!is_cli()) { exit("Please run this in the command line."); }

Before we get into the examples, here is a quick snippet to check if the PHP “daemon script” is launched from the command line.

  • php_sapi_name()==="cli" The most recommended solution all over the Internet, but it is not always cli when the script is run as a scheduled task.
  • defined("STDIN") “Standard input” is only available when the script is launched in CLI, but this is not always the case again.
  • $_ENV["SHELL"] “Shell” is a “CLI-only” environment variable.
  • $_SERVER["REQUEST_METHOD"] The HTTP request method only exists when the script is run through the web.
  • empty($_SERVER["REMOTE_ADDR"])... Yet another common check recommended on the Internet.

Take note that it may not be 100% accurate despite all these checks, but it’s good enough. As to why we do a “command-line check”:

  • Security. Daemon scripts are supposed to be “background services”, not “scripts that are accessible on the Internet”.
  • Dummy proofing. To prevent newbies from putting and launching the daemon script on the Internet.

If you want to launch the daemon from an Intranet website or something, feel free to omit this check entirely.

 

EXAMPLE 1) BASIC MECHANICS

1-basic.php
<?php
// (A) COMMAND LINE ONLY
require "0-cli-only.php";

// (B) SETTINGS
define("CYCLE_PAUSE", 5); // wait 5s between cycles
 
// (C) RUN - INFINITE LOOP
while (true) {
  echo "It works!" . PHP_EOL;
  sleep(CYCLE_PAUSE);
}

Yep, that’s all for the “basics of a daemon script” in PHP. Just run this script in the command line php 1-basic.php, and it will output It works! every 5 seconds. Not very useful, so let’s go through more practical examples below.

 

 

EXAMPLE 2) FETCH DATA FROM API

2a-fetch.php
<?php
// (A) COMMAND LINE ONLY
require "0-cli-only.php";

// (B) SETTINGS
define("CYCLE_PAUSE", 3600); // wait 1hr (3600s) between cycles
define("DATA_URL", "http://localhost/2b-dummy.txt"); // get dummy data from this url

// (C) RUN - FETCH DATA ON INTERVALS
while (true) {
  // (C1) FETCH UPDATES FROM DUMMY SERVER
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, DATA_URL);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $result = curl_exec($ch);
 
  // (C2) RESULTS - UPDATE DATABASE
  if (curl_errno($ch)) { echo curl_error($ch) . PHP_EOL; }
  else {
    echo $result . PHP_EOL;
    $result = json_decode($result, true);
    // $sql = "UPDATE `rates` SET `EUR` = ?";
    // $sql = "UPDATE `rates` SET `GBP` = ?";
  }
 
  // (C3) NEXT CYCLE
  curl_close($ch);
  sleep(CYCLE_PAUSE);
}

2b-dummy.txt
{"EUR":1.17,"GBP":1.38}

Let’s say that we have a website that deals with multiple currencies. So in this one, we periodically:

  • Use CURL to fetch the latest exchange rate from another server/service.
  • Then update the rates in the local database.

 

 

EXAMPLE 3) SERVER MONITORING

3-monitor.php
<?php
// (A) COMMAND LINE ONLY
require "0-cli-only.php";
 
// (B) SETTINGS
define("CYCLE_PAUSE", 10); // wait 10s between cycles
define("CHECK_URL", "http://localhost/2b-dummy.txt"); // url to monitor
define("LOG_FILE", __DIR__ . DIRECTORY_SEPARATOR . "stats.txt"); // log file

// (C) HELPER FUNCTION - ADD LINE TO LOG FILE
function logger ($msg) {
  file_put_contents(LOG_FILE, sprintf("[%s] %s\r\n", date("Y-m-d H:i:s"), $msg), FILE_APPEND);
}
 
// (D) START!
logger("Start monitoring");
while (true) {
  // (D1) CURL TEST FETCH
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, CHECK_URL);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $result = curl_exec($ch);

  // (D2) RESULT
  if (curl_errno($ch)) { logger(curl_error($ch)); }
  else {
    $info = curl_getinfo($ch);
    logger($info["http_code"]." - ".$info["total_time_us"]);
  }
 
  // (D3) NEXT CYCLE
  curl_close($ch);
  sleep(CYCLE_PAUSE);
}

Need to monitor the performance or uptime of a server? Use this script… Although a text file is not the best, recording the stats in a database will be a better idea.

 

 

EXTRA BITS & LINKS

That’s all for this tutorial, and here is a small section on some extras and links that may be useful to you.

 

LIMITATIONS & NOTES

  • This method will permanently take up one process thread. Some may flip at an infinite loop, but it is an easy alternative for people who have technical restrictions on the server.
  • Take extra care not to run multiple instances of the daemon script. Some form of an “it’s already running lock” will be a good idea – Follow the “Run PHP Script In Background” link below.
  • If you need to “launch the daemon in the browser” for whatever reason – Command line PHP scripts have no time limit by default, but SAPI or CGI scripts are controlled. You can use set_time_limit(0) and ignore_user_abort(false) to override and let the daemon continue running.

 

“PROPER PHP DAEMONS”

Right. Before the trolls start to sing demented tunes, here are the “proper ways” to create daemons.

  • It is possible to create PHP daemons with the help of the PCNTL and POSIX extensions, but they will only work on Unix-based systems.
  • On Linux, use systemd to run a PHP script as a system service. Do a “systemd php script” research on your own, it is too much to explain in a few lines.
  • The “Mac variant” of systemd is launchd.
  • Rather than an infinite loop, a softer approach is to set the script to run at fixed intervals. Follow the “schedule PHP scripts” link below.

 

LINKS & REFERENCES

 

INFOGRAPHIC CHEAT SHEET

Simple PHP Daemon (click to enlarge)

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

4 thoughts on “How To Create A PHP Daemon (Simple Examples)”

    1. I can’t remember the exact steps and commands, but basically, run the PHP like a background process. In Linux, it goes something like nohup php SCRIPT.php > /dev/null. As for Windows, I remember using PHP to spawn another “silent command”, something like pclose(popen('start /B php.exe SCRIPT.php));.

      1. I do remember getting supervisor to run on Leopard (?) before. Not so sure about the current versions though…

Leave a Comment

Your email address will not be published.