» Create daemons in PHP

Everyone knows PHP can be used to create websites. But it can also be used to create desktop applications and commandline tools. And now with a class called System_Daemon, you can even create daemons using nothing but PHP. And did I mention it was easy?

What is a Daemon?

A daemon is a Linux program that run in the background, just like a 'Service' on Windows. It can perform all sorts of tasks that do not require direct user input. Apache is a daemon, so is MySQL. All you ever hear from them is found in somewhere in /var/log, yet they silently power over 40% of the Internet.

You reading this page, would not have been possible without them. So clearly: a daemon is a powerful thing, and can be bend to do a lot of different tasks.

Why PHP?

Most daemons are written in C. It's fast & robust. But if you are in a LAMP oriented company like me, and you need to create a lot of software in PHP anyway, it makes sense:

  • Reuse & connect existing code
    Think of database connections, classes that create customers from your CRM, etc.
  • Deliver new applications very fast
    PHP has a lot of build in functions that speed up development greatly.
  • Everyone knows PHP (right?)
    If you work in a small company: chances are there are more PHP programmers than there are C programmers. What if your C guy abandons ship?

Possible use cases

  • Website optimization
    If you're running a (large) website, jobs that do heavy lifting should be taken away from the user interface and scheduled to run on the machine separately.
  • Log parser
    Continually scan logfiles and import critical messages in your database.
  • SMS daemon
    Read a database queue, and let your little daemon interface with your SMS provider. If it fails, it can easily try again later!
  • Video converter (think Youtube)
    Scan a queue/directory for incoming video uploads. Make some system calls to ffmpeg to finally store them as Flash video files. Surely you don't want to convert video files right after the upload, blocking the user interface that long? No: the daemon will send the uploader a mail when the conversion is done, and proceed with the next scheduled upload

Deamons vs Cronjobs

Some people use cronjobs for the same Possible use cases. Crontab is fine but it only allows you to run a PHP file every minute or so.

  • What if the previous run hasn't finished yet? Overlap can seriously damage your data & cause siginificant load on your machines.
  • What if you can't afford to wait a minute for the cronjob to run? Maybe you need to trigger a process the moment a record is inserted?
  • What if you want to keep track of multiple 'runs' and store data in memory.
  • What if you need to keep your application listening (on a socket for example)
Cronjobs are a bit rude for this, they may spin out of control and don't provide a framework for logging, etc. Creating a daemon would offer more elegance & possibilities. Let's just say: there are very good reasons why a Linux OS isn't composed entirely of Cronjobs :)

How it works internally

(Nerd alert!) When a daemon program is started, it fires up a second child process, detaches it, and then the parent process dies. This is called forking. Because the parent process dies, it will give the console back and it will look like nothing has happened. But wait: the child process is still running. Even if you close your terminal, the child continues to run in memory, until it either stops, crashes or is killed.

In PHP: forking can be achieved by using the Process Control Extensions. Getting a good grip on it, may take some studying though.

System_Daemon

Because the Process Control Extensions' documentation is a bit rough, I decided to figure it out once, and then wrap my knowledge and the required code inside a PEAR class called: System_Daemon. And so now you can just:

require_once "System/Daemon.php";                 // Include the Class
 
System_Daemon::setOption("appName", "mydaemon");  // Minimum configuration
System_Daemon::start();                           // Spawn Deamon!

And that's all there is to it. The code after that, will run in your server's background. So next, if you create a while(true) loop around that code, the code will run indefinitely. Remember to build in a sleep(5) to ease up on system resources.

Features & Characteristics

Here's a grab of System_Daemon's features:

  • Daemonize any PHP-CLI script
  • Simple syntax
  • Driver based Operating System detection
  • Unix only
  • Additional features for Debian / Ubuntu based systems like:
  • Automatically generate startup files (init.d)
  • Support for PEAR's Log package
  • Can run with PEAR (more elegance & functionality) or without PEAR (for standalone packages)
  • Default signal handlers, but optionally reroute signals to your own handlers.
  • Set options like max RAM usage

Download

You could download the package from PEAR, or, if you have PEAR installed on your system: just run this from a console:

pear install -f System_Daemon

You can also update it using that last command.

Alpha

Though I have quite some daemons set up this way, it's officially still 0.5.0-alpha. So please report any bugs over at the PEAR page. Other comments may be posted here.

Complex Example

Ready to dig a little deeper? This example program is called 'logparser', it takes a look at a more complex use of System_Daemon. For instance, it introduces command line arguments like:

--no-daemon              # just run the program in the console this time
--write-initd # writes a startup file

Read this source to get the picture. Don't forget the comments!

#!/usr/bin/php -q
<?php
/**
 * System_Daemon turns PHP-CLI scripts into daemons.
 * 
 * PHP version 5
 *
 * @category  System
 * @package   System_Daemon
 * @author    Kevin <kevin@vanzonneveld.net>
 * @copyright 2008 Kevin van Zonneveld
 * @license   http://www.opensource.org/licenses/bsd-license.php
 * @version   SVN: Release: $Id: logparser.php 215 2009-04-25 10:10:18Z kevin $
 * @link      http://trac.plutonia.nl/projects/system_daemon
 */
 
/**
 * System_Daemon Example Code
 * 
 * If you run this code successfully, a daemon will be spawned
 * but unless have already generated the init.d script, you have
 * no real way of killing it yet.
 * 
 * In this case wait 3 runs, which is the maximum for this example. 
 * 
 * 
 * In panic situations, you can always kill you daemon by typing
 * 
 * killall -9 logparser.php
 * OR:
 * killall -9 php
 * 
 */
 
// Allowed arguments & their defaults 
$runmode = array(
    "no-daemon" => false, 
    "help" => false,
    "write-initd" => false
);
 
// Scan command line attributes for allowed arguments
foreach ($argv as $k=>$arg) {
    if (substr($arg, 0, 2) == "--" && isset($runmode[substr($arg, 2)])) {
        $runmode[substr($arg, 2)] = true;
    }
}
 
// Help mode. Shows allowed argumentents and quit directly
if ($runmode["help"] == true) {
    echo "Usage: ".$argv[0]." [runmode]\n";
    echo "Available runmodes:\n"; 
    foreach ($runmode as $runmod=>$val) {
        echo " --".$runmod."\n";
    }
    die();
}
 
// Make it possible to test in source directory
// This is for PEAR developers only
ini_set('include_path', ini_get('include_path').':..');
 
// Include Class
error_reporting(E_ALL);
require_once "System/Daemon.php";
 
// Setup
$options = array(
    "appName" => "logparser",
    "appDir" => dirname(__FILE__),
    "appDescription" => "Parses vsftpd logfiles and stores them in MySQL",
    "authorName" => "Kevin van Zonneveld",
    "authorEmail" => "kevin@vanzonneveld.net",
    "sysMaxExecutionTime" => "0",
    "sysMaxInputTime" => "0",
    "sysMemoryLimit" => "1024M",
    "appRunAsGID" => 1000,
    "appRunAsUID" => 1000
);
 
System_Daemon::setOptions($options);
 
// Overrule the signal handler with any function
System_Daemon::setSigHandler(SIGCONT, array("System_Daemon",
    "defaultSigHandler"));
 
 
// This program can also be run in the forground with runmode --no-daemon
if (!$runmode["no-daemon"]) {
    // Spawn Daemon 
    System_Daemon::start();
}
 
// With the runmode --write-initd, this program can automatically write a 
// system startup file called: 'init.d'
// This will make sure your daemon will be started on reboot 
if (!$runmode["write-initd"]) {
    System_Daemon::log(System_Daemon::LOG_INFO, "not writing ".
        "an init.d script this time");
} else {
    if (($initd_location = System_Daemon::writeAutoRun()) === false) {
        System_Daemon::log(System_Daemon::LOG_NOTICE, "unable to write ".
            "init.d script");
    } else {
        System_Daemon::log(System_Daemon::LOG_INFO, "sucessfully written ".
            "startup script: ".$initd_location);
    }
}
 
// Run your code
// Here comes your own actual code
 
// This variable gives your own code the ability to breakdown the daemon:
$runningOkay = true;
 
// This variable keeps track of how many 'runs' or 'loops' your daemon has
// done so far. For example purposes, we're quitting on 3.
$cnt = 1;
 
// While checks on 3 things in this case:
// - That the Daemon Class hasn't reported it's dying
// - That your own code has been running Okay
// - That we're not executing more than 3 runs 
while (!System_Daemon::isDying() && $runningOkay && $cnt <=3) {
    // What mode are we in?
    $mode = "'".(System_Daemon::isInBackground() ? "" : "non-" ).
        "daemon' mode";
    
    // Log something using the Daemon class's logging facility
    // Depending on runmode it will either end up:
    //  - In the /var/log/logparser.log
    //  - On screen (in case we're not a daemon yet)  
    System_Daemon::log(System_Daemon::LOG_INFO,
        System_Daemon::getOption("appName").
        " running in ".$mode." ".$cnt."/3");
    
    // In the actuall logparser program, You could replace 'true'
    // With e.g. a  parseLog('vsftpd') function, and have it return
    // either true on success, or false on failure.
    $runningOkay = true;
    //$runningOkay = parseLog('vsftpd');
    
    // Should your parseLog('vsftpd') return false, then
    // the daemon is automatically shut down.
    // An extra log entry would be nice, we're using level 3,
    // which is critical.
    // Level 4 would be fatal and shuts down the daemon immediately,
    // which in this case is handled by the while condition.
    if (!$runningOkay) {
        System_Daemon::log(System_Daemon::LOG_ERR, "parseLog() ".
            "produced an error, ".
            "so this will be my last run");
    }
    
    // Relax the system by sleeping for a little bit
    // iterate also clears statcache
    System_Daemon::iterate(2);
 
    $cnt++;
}
 
// Shut down the daemon nicely
// This is ignored if the class is actually running in the foreground
System_Daemon::stop();
?>

Console action: Controlling the Daemon

Now that we've created an example daemon, it's time to fire it up! I'm going to assume the name of your daemon is logparser. This can be changed with the statement: System_Daemon::setOption("appName", "logparser"). But the name of the daemon is very important because it is also used in filenames (like the logfile).

Execute

Just make your daemon script executable, and then execute it:

chmod a+x ./logparser.php
./logparser.php

Check

Your daemon has no way of communicating through your console, so check for messages in:

tail /var/log/logparser.log

And see if it's still running:

ps uf -C logparser.php

Kill

Without the start/stop files (see below for howto), you need to:

killall -9 logparser.php

Autch.. Let's work on those start / stop files, right?

Start / Stop files (Debian & Ubuntu only)

Real daemons have an init.d file. Remember you can restart Apache with the following statement?

/etc/init.d/apache2 restart

That's a lot better than killing. So you should be able to control your own daemon like this as well:

/etc/init.d/logparser stop
/etc/init.d/logparser start

Well with System_Daemon you can write autostartup files using the writeAutoRun() method, look:

$path = System_Daemon::writeAutoRun();

On success, this will return the path to the autostartup file: /etc/init.d/logparser, and you're good to go!

Run on boot

Surely you want your daemon to run at system boot..  So on Debian & Ubuntu you could type:

update-rc.d logparser defaults

Done your daemon now starts every time your server boots. Cancel it with:

update-rc.d -f logparser remove

Troubleshooting

Here are some issues you may encounter down the road.

  • Don't use echo()
    Echo writes to the STDOUT of your current session. If you logout, that will cause fatal errors and the daemon to die. Use System_Daemon::log() instead.
  • Connect to MySQL after you start() the daemon.
    Otherwise only the parent process will have a MySQL connection, and since that dies.. It's lost and you will get a 'MySQL has gone away' error.
  • Error handling
    Good error handling is imperative. Daemons are often mission critical applications and you don't want an uncatched error to bring it to it's knees.
    • Reconnect to MySQL
      A connection may be interrupted. Think about network downtime or lock-ups when your database server makes backups. Whatever the cause: You don't want your daemon to die for this, let it try again later.
    • PHP error handler
      As of 0.6.3, System_Daemon forwards all PHP errors to the log() method, so keep an eye on your logfile. This behavior can be controlled using the logPhpErrors (true||false) option.
  • Monit
    Monit is a standalone program that can kickstart any daemon, based on your parameters. Should your daemon fail, monit will mail you and try to restart it.
  • Watch that memory
    Some classes keep a history of executed commands, sent mails, queries, whatever. They were designed without knowing they would ever be used in a daemonized environment.
    Cause daemons run indefinitely this 'history' will expand indefinitely. Since unfortunately your server's RAM is not infinite, you will run into problems at some point.
    This makes it's very important to address these memory 'leaks' when building daemons.
  • Statcache will corrupt your data
    If you do a file_exists(), PHP remembers the results to ease on your disk until the process end. That's ok but since the Daemon process does not end, PHP will not be able to give you up to date information. As of 0.8.0 you should call System_Daemon::iterate(2) instead of e.g. sleep(2), this will sleep & clear the cache and give you fresh & valid data.

I know I'm saying MySQL a lot, but you can obviously replace that with Oracle, MSSQL, PostgreSQL, etc.

Stay up to date

You can track my blog rss articles and rss comments. You may also find my rss bookmarks interesting. Or twitter Follow me on Twitter


Like this article?

   Then Dzone it!
Or use another bookmark button below to show your support &
help me spread the word.


tags: programming, php, daemon, PEAR
category: Programming - PHP - PEAR
read: 52,972 times

Add comment

(required, shown)(required, not shown)for syntax highlighting

[CODE="Javascript"]
your_code_here();
[/CODE]

Replace "Javascript"
with "php", "text", etc.
code (to make sure you are not a spammer)

 Track replies: rss feed comments feed

Comments

#64. Kevin on 26 June 2009

Member avatar: Kevin@ jonathan: the iterate function actually uses clearstatcache() so you can also use that directly. As long as you use it.
But I'd rather have people use my iterate() function so that I can implement additional fixes as I find more issues.

@ Martijn: You could also use cron combined with solo (http://timkay.com/solo/) to prevent overlap and do what you want. If you need more control / intelligence on the other hand, you may need to fire actions and keep track of time yourself.
I have once seen a full table-based cron implementation in PHP, I'm sure you'll find it if that is what you need. Or maybe a simple

while(true) {
if (!$ranToday && date('Hi') > 1700) {
execute();
}
}

#63. Martijn on 21 June 2009

Gravatar.com: MartijnThnx, for the article. have to say it works nice and easy.

I think it's better solution then using cronjobs. Only wondering, i have to do a daily job, processing books. (about 2.000.000 records) Maybe i missing something, how to do a daily update, depending the content of a certain folder?

I'm istill depending on cron or can you give some hints to solve the problem?
... [more]
regards Martijn

#62. jonathan on 19 June 2009

Gravatar.com: jonathanRE: Statcache will corrupt your data

is clearstatcache () broken?

I've been using this successfully, but did I miss something?

#61. Kevin on 18 June 2009

Member avatar: Kevin@ Adam Charnock: It should a boolean when it already exists. And the string when it has created it for the first time. Can you confirm this?

#60. Adam Charnock on 15 June 2009

Gravatar.com: Adam CharnockHi Kevin,

This is really great work, thank you very much. We are going to be using it extensively in the backend of the next version of walltweet.org.

I did notice this:
... [more]
"On success, this will return the path to the autostartup file: /etc/init.d/logparser, and you're good to go!"

... which isn't actually correct. It seems to return boolean, not a string. Just a minor point!

Thanks again :D

Adam Charnock

#59. Kevin on 15 May 2009

Member avatar: Kevin@ vince: Yes it uses pcntl, but it's probably already enabled cause Ubuntu should have this this by default. I think you can just install the class and let me know what happens?

#58. vince on 15 May 2009

Gravatar.com: vinceHi Kevin,
thanks for the reply. I haven't yet installed the pear class because I think that I have to install first the pcntl support. But maybe it's unnecessary... Your pear class isn't based on php-pcntl functions?

#57. Kevin on 14 May 2009

Member avatar: Kevin@ James: That could definitelty work although you would have to make a bridge between web interface + (root) cli, and personally I don't have use cases for something like that. As in: I can't think of scenarios where I would want to have to manually intervene or stop daemons. They should be designed to not require that I think.

@ Mike: Sorry I don't know PHP_Fork, I only know of it.

@ mikesta: thank you :)
... [more]
@ vince: I've tested up until Ubuntu Intrepid and those all work out of the box. Can you give me some exact output/errors?

#56. vince on 13 May 2009

Gravatar.com: vinceHi Kevin,
I would like to start creating some funny daemons in php so I have installed a LAMP + php5-cli on an ubuntu 9.04 system.
But I have no support for pcntl yet! How can I do? Can you suggest me a guide?

vince.

#55. mikesta on 02 May 2009

Gravatar.com: mikestaYou did a good job, keep ahead!

#54. James on 01 May 2009

Gravatar.com: JamesMike, it's back up now. If it's down the next time you try, I'll post a link to a copy of that code that I just snagged for backup purposes.

#53. Mike on 30 April 2009

Gravatar.com: MikeUpdate - I was able to get php_fork running on my server. I did not have the shared memory module installed for PHP - duhhh. I'm going to continue testing that.

Thanks for your help!

#52. Mike on 30 April 2009

Gravatar.com: Mike@James - that link to bipinb.com appears to be dead - nuts, I wanted to look at that.

@Kevin - I looked at that php_fork PEAR package. I'm pretty new to PEAR, but I couldn't even get the basic.php to run - doesn't even throw an error I can diagnose.... I know PEAR is good on my server as I'm able to execute your sample code fine.

Thanks very much for any help you can provide, but I don't want to be a bother...

#51. James on 30 April 2009

Gravatar.com: James@Kevin & @Mike: There is some code from ~2008 that looks like it forks off children pretty elegantly: ( http://bipinb.com/making-php-program-as-daemon.htm ). I have not actually executed the code, however, so don't know for sure.

To control those forked daemons, it seems possible to do the following: insert each daemon's PID into a MySQL table, and have each daemon update MySQL every several minutes informing you of what it's up to. You could then see what's going on with each daemon child via a browser interface, and kill the child if needed.

At least, in my head, that makes sense. But I haven't committed those thoughts to code.

#50. Kevin on 29 April 2009

Member avatar: Kevin@ James: You noticed correctly :) Thanks. If you have any specific ideas on System_Daemons future feel welcome to submit them.

@ Mike Stackhouse: I've found an unmaintained PEAR package: Fork. Maybe that can do what you want. I didn't have plans to make this a system_daemon feature as I've always felt that it should be a simple and basic building block. What do you think?

#49. Mike Stackhouse on 27 April 2009

Gravatar.com: Mike StackhouseNice article - I've tested your sample code on our FreeBSD 7 server.

We have the requirement to have a daemonized server process that would fork off children based on control records in a database. Does your class have functionality to fork off children, keep track of their process id's and kill them if need be?

Still trying to wrap my head arounc pcntl - I might be over-thinking the problem. Could it be as simple as implementing in a database reading loop, and when a new child is needed execute the fork at that time? There are some good examples on the web for managing child processes from that point forward - this one seems to make sense - http://www.van-steenbeek.net/?q=php_pcntl_fork.
... [more]
Might you know of any examples that would have functionality such as we require?

Thank you very much!

#48. James on 25 April 2009

Gravatar.com: JamesI noticed you added version 0.8 alpha to the PEAR repository today. This is some very promising stuff. Keep up the great work!

#47. Tech Blog on 20 April 2009

Gravatar.com: Tech BlogGreat post thanks, I learned something new about PHP today :)

#46. Kevin on 19 April 2009

Member avatar: Kevin@ Saso: Currently your daemon can make iterations without my class executing one line of code. So self monitoring would require my users to call for example:

while (true) {
System_Daemon::ping();
// your code
}


That way I could implement self checks from within the ping method, depending on the configuration you specify.

It could be an interesting addition. As far as advanced communication goes. You could look into Net_Socket (also pear) and/or go for TCP/IP.

Another method would be a central memcached server or actual shared memory using shmop (but that's limited to 1 machine).

Non of these things and methods are standard in System_Daemon however. It merely provides the most fundamental brick for others to build upon.

But if you like to share what you've come up with, give me a ping. I'm not sure yet but maybe we could generalize it and add a little sauce on top of the daemon.

#45. Saso on 19 April 2009

Gravatar.com: Saso@Kevin:
Well, it doesn't help me much :)
Probably I will use Ur project and upgrade it with my ideas and send it back to U, for U to make world use of it :)
I would need sync and async communication between processes, so front-end and daemons can 'talk' with each other. Self-observing would also be necessary, to avoid wild daemons eating resources.
I, for example, need this to make chat bot, which will be daemonized and frontend interfaces (IRC, PHP, what-ever..) will ise it for 'chat API'.
... [more] This is great to avoid the overhead of creating and killing all the objects in heavy front end.
But I agree, for mass mailing, writing to DB is enough.
Regards, Saso

#44. Kevin on 19 April 2009

Member avatar: Kevin@ Yılmaz Uğurlu: Great to hear!

@ Janitha Karunaratne: An ideal daemon in an ideal world would not be written in PHP, I make that clear in the article already. But you actually make it sound immoral :D

Don't be alarmed.. My house is built with good old bricks: not forks & spoons. And yes some of my daemons are created with PHP. Why? Because it works. Because I can use my existing classes and take away load from front-end processes built on the same technology.
... [more]
I feel like using 4 different languages to solve 1 problem is really about creating 1 thing and 1 thing only: a maintenance hell.

If I am to redo my existing classes in C (or perl or python), I will have to duplicate my patches & improvements for the rest of my programming career...
To justify this decrease in productivity there would have to be some very convincing reasons for that. And your (funny but) dogmatic metaphor is not going to cut it.

I don't believe you're not basing your opinion on actual results.
I have PHP daemons running for over 2 years now without a problem. I understand you may get an unary feeling but I'm pragmatic and this is actually working very well.

Anyway, nobody is forcing you to use this, so Happy compiling :)

@ Saso: You could work with a database queue, storing heavy jobs and having daemons eat them away using the same DB class as your frontend process.
Deamons are already limited to 1 per 'appName', and for simple communication you could use POSIX signals. Does this help you out a bit?

#43. Saso on 18 April 2009

Gravatar.com: SasoHi, Kevin, great article.
I am searching for IPC (Inter Process Communicating) for using with Ur Daemon.

I would like to change architecture of my app. to lighten frontend and have heavy duty daemons to do all the work.

... [more] What I find missing (from my point of standing) is automated limiting daemon loops, number of daemons and some mechanism for maintaining pre-forked processes, ready to use (similar as Apache2).
This is needed just due bad PHP-coding practices, to avoid leaked code eating all resources.
And I miss Remote Procedure Call, as stated above: It would be great to have daemoned API, which we 'just use' in our PHP frontend.

Regards, Saso
ps: dont get me wrong, Ur work is just great, I just give U some new ideas.. :)
(aka. being lazy-constructive)

#42. Janitha Karunaratne on 16 April 2009

Gravatar.com: Janitha KarunaratneUsing PHP to do small quick tasks is ok, it's good for rapid development of websites, but it's quite unfit for purposes such as writing service scripts that run in the background.

PHP is meant to be started quickly and killed quickly (such as a page view) and that's what it's memory management is optimized for for the duration of the script... if you run it for prolonged periods of time, expect some seriously nasty memory usage and leaks.

Your reasoning for this is that everyone knows it and it's availability everywhere... this is where I have to disagree. That's like saying "Hey, lets build this house using spoons and forks, because everyone knows how to use them and their everywhere!". It's possible, but it's a very poor choice.
... [more]
Just because it's the only thing a person knows or just because it exists everywhere doesn't make it the proper tool to use.

#41. Yılmaz Uğurlu on 13 April 2009

Gravatar.com: Yılmaz UğurluThank you, the daemon now runing indefinitely.
But i still didn't create startup script correctly. I tried mant things, i guess i have problem with my ubuntu box, whatever.

I wrote feed crawler daemon for test. It's fecthing and writing feed items to databse. I watched very long, and everyting went well. Cpu usage, memory usage, all my concerns are gone. Thank you so much this is really great application.

#40. Kevin on 12 April 2009

Member avatar: Kevin@ Yılmaz Uğurlu: Sorry my bad, fixed in SVN now as well.

It looks though as if you really really don't have permission to write anything to init.d

What if you 'su -' first and then really run it as root instead of sudo-ing it?

About the 3 times thing. Are you using the logparser example? That shuts down because it's only a test.

If you just put a

while(true) {
// Your code here
sleep(5);
}


It should run indefinitely. Unless you run into any problems like stated in the troubleshooting section

#39. Yılmaz Uğurlu on 12 April 2009

Gravatar.com: Yılmaz UğurluThanks Kevin. I tested,

First:
in OS.php line 297 and line 299 must be self::isWritable(...)
because i get this error fist:

Fatal error: Call to undefined function is__writable() in /usr/share/php/System/Daemon/OS.php on line 297

and i changed these lines.

So, after i run my daemon just like: sudo ./myDaemon.php
It went well but, when i looked at the log file i showed these errors
[Apr 12 17:04:31]  warning: [PHP Error] fopen(/etc/init.d/130827514849e1f4ef90c8b.tmp): failed to open stream: Permission denied [l:303]
[Apr 12 17:04:31] notice: Directory: '/etc/init.d' is not writable. Maybe run as root?
[Apr 12 17:04:31] warning: Unable to create startup file. [l:761]


I have one question more, i didn't add the System_Daemon::stop(); line to end of my file.
Bu after 3 times loop my daemon is stopped. i guess i didn't understand how to write endless daemon, how can i do that?

#38. Kevin on 12 April 2009

Member avatar: Kevin@ Yılmaz Uğurlu: On that link, click on Download in other formats: raw, and overwrite it in (probably) /usr/share/php/System/Daemon/

#37. Kevin on 12 April 2009

Member avatar: Kevin@ Yılmaz Uğurlu: Thanks for testing. I've improved the method for is_writable and hope that will help avoid your bug. The fix is in the OS.php file. The new version can be found here:
http://svn.plutonia.nl/projects/system_daemon/browser/trunk/System/Daemon/OS.php

Are you able to confirm this works? If so than I can release a new PEAR package for System_Daemon

#36. Yılmaz Uğurlu on 11 April 2009

Gravatar.com: Yılmaz UğurluHi Kevin, i tested and it's working great.
I have question about startup file. I'm runnig as root my script, after the start i'm checking log file, all script start's has this two line.

notice: Directory: '/etc/init.d' is not writable. Maybe run as root?
[Apr 11 12:23:56] warning: Unable to create startup file. [l:761]


But i'm running script as root, what i'm doing wrong?

#35. anon on 07 April 2009

Gravatar.com: anonhttp://dev.pedemont.com/sonic/

a php daemon, with plugin type arch.

#34. Kevin on 03 April 2009

Member avatar: Kevin@ ger: I am not able to reproduce that behavior. Maybe you can dump all get_defined_vars so make 100% sure nothing is increasing?

#33. ger on 02 April 2009

Gravatar.com: gerI have jabber (XMPP) php daemon running which sends news updates to the users who has subscribed to them.
The only thing I can say from my experience - php seems have memory leaks when works in infinitive loop. Even if he php code itself is well designed to pervent this. If you'll monitor the memory consumed by your php daemon - you will see it continuously grows in time.
The solution of this prob I use - juts kill / restart the daemon with cronjob from time to time.
Hope this will be useful for someone.

#32. Dani on 25 March 2009

Gravatar.com: DaniGreat, I guess I will be able to get some of my php scripts out of their uggly cron configuration. When I needed to run a script every 15 seconds I had it run it 4 times in cron with different sleep times (0, 15, 30 and 45 secs) which is a very uggly thing to do (and 'dangerous' if the script cannot handle concurrent execution).

Sometimes PHP can be the ideal choice to run such things. If you have some LAMP environment with PHP handling your data in the first place and you need some background process it is the best choice to use the same environment for that process instead of risking to corrupt your data because of compatibility issues like character-encoding.

#31. Kevin on 16 March 2009

Member avatar: Kevin@ jatrn: Please upgrade to the latest version of System_Daemon, and try again. Now what does your logfile say?

@ rhuel: This is not going to work on shared-host-environments, because you need to be root to fire up the daemon. Only after you start it, it can become another user.
This is because root will have to setup some dirs like logdir & rundir, and chown it all.

#30. rhuel on 16 March 2009

Gravatar.com: rhuelYour code works and it rocks. Problem is my webhost has php without --enable-pcntl compiled. Can we go around this requirement?. I need it to run in background. Thanks.

#29. jatrn on 11 March 2009

Gravatar.com: jatrnHello,
First of all thank you very much for this lib, it is very usefull.
I got a small problem. I'm able to start my daemon by "/etc/init.d/IMM15 start" but can not stop it, "/etc/init.d/IMM15 stop" doesn't work. There is nothing in the log and on the screen. See my code below.

Thanks for you help.
... [more]
#!/usr/bin/php -q
<?php
require_once "System/Daemon.php";
System_Daemon::setOption("appName","IMM15");
System_Daemon::setOption("appDescription","simple deamon by jatrn");
System_Daemon::setOption("authorName","xxx");
System_Daemon::setOption("authorEmail","xxx@yahoo.com");

System_Daemon::start();
System_Daemon::writeAutoRun();

while (true) {
$t=1;
sleep(10); }
System_Daemon::stop();
?>

#28. Kevin on 04 March 2009

Member avatar: Kevin@ Eduardo Jordan: Thanks!

#27. Eduardo Jordan on 03 March 2009

Gravatar.com: Eduardo JordanHere we go again. PHP sucks? Is it horrible language?
I wonder why facebook, a multibillon dolar company, uses it!
Kevin, I gonna use this class and will give you feedback.
About the perl guy. FYI, PHP has syntax similar to Perl, just simpler to understand.

... [more] I guess that for Perl programmers:

my @lines = <FILE>;
while (<FILE>) { print $_; }

that is nice, and this horrible

$handle = fopen('datafile.csv', "r");
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
echo $buffer;
}
fclose($handle);

I don't think so, second one is easier to read and understand.

#26. Kevin on 02 March 2009

Member avatar: Kevin@ Anon: Thanks for your comment. It is clear that you do not want to use PHP for this.

Let me just say: Nobody is forcing your hand here.

But I think your vision on this matter does not include factors like: time, resources, and company context.
... [more] For instance at my company, there is no (need for a) C programmer at all. There never was and there probably never will be. But we still have the need to automate a lot of thinks that are based on our PHP software.
Perl just doesn't play well with our LAMP environment. For instance we have many libraries & classes that the automating daemons need to interface with. And I was not planning to recreate & maintain all of our business-logic (no, this is not in CPAN unfortunately ;) in two different languages. I value DRY principles too much for that.

So I've been running PHP daemons for years now (it took a while before I submitted to PEAR) and without any problems. And so for me it's safe to say: it solves more problems than it creates.

#25. Anon on 01 March 2009

Gravatar.com: Anon@Kevin

Hey, Anon here (the one you responded to). I seemed to stumble upon this post again~

Let me reemphasize that it is interesting that PHP can do this, but for obvious reasons it should not. PHP is an awesome language in which quick websites can be easily created due to it's ease of use and availability.
... [more]
Other than than, to put it bluntly, it sucks. It is truly a horrible language. There is little point in debating that point here so please don't. (If you really want to debate, use a search engine to find essays about PHP and it's..erm..attributes.) (I am kind of cheating here; I go on to insult PHP and tell you not to argue :O)

I suppose when you have a hammer everything looks like a nail, but your three reasons to use PHP for creating daemons are not convincing at all.

"Reuse & connect existing code"
You should not be reinventing the wheel to begin with. If you must, there is Perl and CPAN.

"Deliver new applications very fast"
Yes, PHP has a lot of functions (too many), but Perl is better at creating applications quickly.

"Everyone knows PHP (right?)"
I already mentioned this point in my previous post, but I will do so here again (with a different approach). If your C programmer leaves, find a new C programmer. It is really that simple. If your Photoshopping person left would you resort to using Microsoft's paint? No, you would get a new Photoshopper. (Hypothetical situation is hypothetical, by the way.)

#24. Centerax on 13 February 2009

Gravatar.com: CenteraxWow!

This is great stuff, in conjunction with Monit this is very powerful and easy to implement.

Great job!

#23. Kevin on 11 February 2009

Member avatar: Kevin@ Chris: No I didn't. That's quite shameless. Most of the times they have the decency to at least add a small note as to who they got it from.

I've left a comment at that URL.. Let's see how long it stays there ;)

Thanks a lot for letting me know Chris!

#22. Chris on 10 February 2009

Gravatar.com: ChrisDid you know this has been published verbatim here: http://www.hurricanesoftwares.com/how-to-create-daemons-in-php/

#21. Ali on 10 February 2009

Gravatar.com: AliEpic post! I was looking for a way to use a daemon to create an automated backup system and this is just what we needed!

#20. Kevin on 01 February 2009

Member avatar: Kevin@ Tom: Yes, I haven't made anything with it yet, but it's very cool that's all possible :)

#19. Kevin on 01 February 2009

Member avatar: Kevin@ Strawp: Yes that's not really what this class was designed for. Maybe you could look into PHP_Fork (http://pear.php.net/package/PHP_Fork) which I believe does something similar. Good luck!

#18. Tom on 27 January 2009

Gravatar.com: TomReally cool! Did you also know about php-gtk? You can use PHP to make desktop application as well. PHP is really a great language for it's convenience. I wouldn't go and make Photoshop with it...but for small applications -- most definitely -- especially if people are accepting things like AIR as "applications". Great work, thanks!

#17. Strawp on 26 January 2009

Gravatar.com: StrawpThis looks great!

Quick stupid question though: In your example above, is everything from ::start(); to the end of the script the child process? Is the parent dead at this point?

If for example I wanted to fork a slow script as a child from the action page of a web app but keep the parent running so that it could finish up and redirect the user, how would that look?

#16. Kevin on 25 January 2009

Member avatar: Kevin@ all: thanks for the kind words

@ marek: That's not an issue.. Thats the solution TO the issue that you will lose the mysql connection if you don't.

#15. Douwe Kasemier on 23 January 2009

Gravatar.com: Douwe KasemierBrilliant pear plugin. Using it for multithreading!

#14. Zach Blank on 21 January 2009

Gravatar.com: Zach BlankThis looks amazing! I will be using it for some live data collection and visualization at a party in NYC this weekend. Thanks for sharing!

#13. Jason Moss on 20 January 2009

Gravatar.com: Jason MossGlad to see other people recognize the use of PHP for non-web use. We've been using it for some command-line based applications that work quite nicely. Development especially is nice, with the massive amount of built-in functionality and the outside resources available. I use PHP a ton, but I haven't done any web dev :P

#12. Dustin on 20 January 2009

Gravatar.com: DustinYeah first post stumble - sorry for being dumb - i like this post.

#11. marek on 20 January 2009

Gravatar.com: marekhow to fix issue "Connect to MySQL after you start() the daemon" ?

#10. Timothy on 19 January 2009

Gravatar.com: TimothyWow. That's awesome! Thank you!

#9. wiwo on 14 January 2009

Gravatar.com: wiwoThank you for your post. It is really interesting. I didn't know one can create deamon with php! Not yet tested but when I have the need I would know where to go :)

#8. Kevin on 13 January 2009

Member avatar: Kevin@ Matt Baker: Thank you for correcting me :)

I suppose I forgot to say the obligatory:
Sorry for my bad English, it's not my native language.

... [more] But really, thanks. I want to improve my English and I will definitely take a look at it.

#7. Kevin on 13 January 2009

Member avatar: Kevin@ crackgerbal: Thank you!

@ simeon: I agree PHP is not the best language for it, the article says something similar. But it's very well possible and even suitable in some cases.

From a design point-of-view you may be right about PHP. It probably wasn't meant to run that long, but:
... [more] - I haven't run into problems yet
- I believe PHP resource IDs are different from file descriptors

You do make in interesting point though: thank you.

#6. Kevin on 13 January 2009

Member avatar: Kevin@ Anon: Yes (because it's so easy) there are a LOT of PHP coders, and obviously there are a lot of rookies as well. And some may even never get passed that point.

But to do any real damage you would need root access to a Linux box in a production environment.
If you do: I hope you're at least responsible enough to know your own coding limits and not run your hobby projects on your company's main server. If you're not: your company's box is probably already destroyed, anyway. along with your job :)

#5. Matt Baker on 13 January 2009

Gravatar.com: Matt BakerYou use too many commas. Mostly in incorrect places.
http://owl.english.purdue.edu/handouts/grammar/g_comma.html

#4. simeon on 12 January 2009

Gravatar.com: simeonYour work to create the System_Daemon class is impressive. Unfortunately PHP is just not the best language to do this sort of thing. One recent article in my feeds noted that PHP leaks file descriptors (see http://gnuvince.wordpress.com/2008/10/28/php-wrong-for-long-running-processes-wrong-for-america/). This means that long running PHP processes eventually are unable to open files...

PHP's process model was is inherited from the cgi days and is built around loading, executing, and terminating (and don't get me wrong. There are definitely advantages to this process model for web development). Running a PHP script indefinitely is likely to push up against the edge cases in the runtime engine...

The alternative approach I've taken when I need something more sophisticated than cron jobs is to make sure I have command line interfaces to my PHP business logic and write the daemon in python or even bash. The daemon can be extremely simple (listen on a port, tail a file, etc) and merely invokes the appropriate PHP script to handle all the business logic... Just my 3 cents.

#3. Anon on 12 January 2009

Gravatar.com: AnonOh, err, I didn't mean that YOUR code was the horrible PHP code that was littered throughout the web, I just meant in general ;D

#2. Anon on 12 January 2009

Gravatar.com: AnonI read your article and it did a great job of explaining daemons and such in an easy to learn way. However,

"Everyone knows PHP (right?)"

Everyone knows how to grunt and point too. Sure, I find it interesting that PHP can do this, but please, there is horrible PHP code littered throughout the web. Please do not bring that to a lower level :(

#1. crackgerbal on 12 January 2009

Gravatar.com: crackgerbalAwesome blog! I went from not knowing what a daemon is to being able to write my own in php. great post!