Long Live PHP Command Line as Daemon

General discussion on PHP
Post Reply
rampog
Posts: 56
Joined: Mon May 04, 2009 12:15 pm

Long Live PHP Command Line as Daemon

Post by rampog » Fri Dec 28, 2012 2:40 am

Hi All,
anyone has been ever develop service might be for realtime email notification in command line & queueing in such as storage(db,file,mqueue).
sample architecture
while(true){
//do send mail
}
is there any concern that i've to consider for doing this,i prefer building this, instead of i build with OS/Linux facilities not long live which is
there is limitation timing max every one minute in scheduler could not be every second.
please advice

tedtiger
Posts: 244
Joined: Thu Mar 05, 2009 4:59 pm
Location: Germany
Contact:

Re: Long Live PHP Command Line as Daemon

Post by tedtiger » Fri Dec 28, 2012 9:53 am

Hi,

we used kevin von zonefelds script. Link is included in the source code.
The script spawns a main process with several childs (number can be adjusted but take ram usage in mind). Included in the example is how to open a socket connection to another server.
If you wanto to use a MQservervice there you should implement it...

Code: Select all

#!/usr/local/php5/bin/php -d memory_limit=512M
<?php
error_reporting( E_ALL ^ E_NOTICE ^ E_DEPRECATED );

ini_set( "track_errors", true );
chdir( dirname( __FILE__ ) ); // we're working relativly from the script


// Include Class
/// https: // github . com /kvz/system_daemon/blob/master/System/Daemon.php
include_once 'System/Daemon.php';

function testConnection( $str_dispatcher )
{
	$sock = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
	if ( $sock === false )
	{
		return - 1;
	}
	else
		if ( socket_connect( $sock, gethostbyname( $str_dispatcher ), 49510 ) )
		{
			return true;
		}
		else
		{
			socket_close( $sock );
			return false;
		}
}

$user = "apache";
$uid_name = posix_getpwnam( $user );
$uid_number = $uid_name[ 'uid' ];
$gid_number = $uid_name[ 'gid' ];

// Setup
$options = array(
	"usePEAR" => false,
	"appName" => "deamonsample",
	"appDir" => dirname( __FILE__ ),
	"appDescription" => "",
	"authorName" => "TD",
	"authorEmail" => "",
	"sysMaxExecutionTime" => "0",
	"sysMaxInputTime" => "0",
	"sysMemoryLimit" => "1024M",
	"appRunAsGID" => $gid_number,
	"appRunAsUID" => $uid_number
);

$is_daemon = true;
$int_maxChilds = 6;
$str_dispatcher = "yourqueueserver.lan";

// Scan command line attributes for allowed arguments
foreach ( $argv as $k => $arg )
{
	if ( preg_match( "/--(.*?)($|=('(.*)'|\"(.*)\"|(.*)))/", $arg ) == 1 )
	{
		$str_name = preg_replace( "/--(.*?)($|=(.*))/", "$1", $arg );
		$str_value = preg_replace( "/--.*?=('(.*)'|\"(.*)\"|(.*))/", "$2$3$4", $arg );
		if ( $str_value == $arg )
		{
			$str_value = null;
		}

		switch ( $str_name )
		{
			case 'name' :
				$frmstr_Client = $str_value ? $str_value : $frmstr_Client;
				break;
			case 'no-daemon' :
				$is_daemon = false;
				break;
			case 'dispatcher' :
				$str_dispatcher = $str_value;
				$result = testConnection( $str_dispatcher );
				if ( $result === - 1 )
				{
					echo "Could not create Socket.\n";
					exit( ) - 1;
				}
				else
					if ( $result === false )
					{
						echo "Could not connect to dispatcher on '{$str_dispatcher}'\n";
						exit( ) - 1;
					}
				break;
			case 'childs' :
				$int_maxChilds = is_numeric( $str_value ) && $str_value > 0 ? $str_value : $int_maxChilds;
				break;
		}
	}
}

System_Daemon::setOptions( $options );

$str_pidFile = "/var/run/{$options['appName']}/{$options['appName']}.pid";

switch ( $argv[ $argc - 1 ] )
{
	case 'force-start' :
		if ( file_exists( $str_pidFile ) )
		{
			unlink( $str_pidFile );
		}
		$argv[ $argc - 1 ] = 'start';
	case 'start' :
		if ( file_exists( $str_pidFile ) )
		{
			echo "Deamonsample allready running!\n";
			exit( 0 );
		}
		else
			if ( $is_daemon )
			{
				System_Daemon::start( );
			}
		break;
	case 'stop' :
		if ( file_exists( $str_pidFile ) )
		{
			exec( "kill -KILL `cat {$str_pidFile}`" );
			unlink( $str_pidFile );
		}
		exit( 0 );
		break;
	case 'restart' :
		$str_pidFile = "/var/run/{$options['appName']}/{$options['appName']}.pid";
		if ( file_exists( $str_pidFile ) )
		{
			exec( "kill -KILL `cat {$str_pidFile}`" );
			unlink( $str_pidFile );
		}
		if ( $is_daemon )
		{
			System_Daemon::start( );
		}
		break;
	case 'help' :
	default :
		echo "Deamonsample\n";
		echo "\n";
		echo "usage: deamonsample.php [options] (start|force-start|restart|stop|help)\n";
		echo "\n";
		echo "Arguments:\n";
		echo "   --name=NAME			Sets the name for the service.\n";
		echo "   --childs=NUMBER		Defines how many childs will be created.\n";
		echo "   --no-daemon			Runs the service in the foreground.\n";
		echo "   --dispatcher=DOMAIN			Domain of the dispatcher.\n";
		exit( 0 );
		break;
}

if ( $argv[ $argc - 1 ] != 'start' && $argv[ $argc - 1 ] != 'restart' )
{
	exit( 0 );
}

$int_actChilds = 0;
$children = Array();
$runningOkay = true;

$int_startTime = time( );

function getNextinQueue( $str_dispatcher )
{
	$sock = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
	if ( $sock === false )
	{
		return - 1;
	}
	else
		if ( socket_connect( $sock, gethostbyname( $str_dispatcher ), 49510 ) )
		{
			@socket_write( $sock, "whateveryouneedtoget\n" );
			$result = @socket_read( $sock, 2048 );
			$result = trim( $result );
			socket_close( $sock );
			$result = is_numeric( $result ) ? $result : - 1;
			return $result;
		}
		else
		{
			socket_close( $sock );
			return - 1;
		}
}

while ( ! System_Daemon::isDying( ) && $runningOkay )
{

	while ( pcntl_wait( $status, WNOHANG or WUNTRACED ) > 0 )
	{
		usleep( 5000 );
	}
	while ( list( $key, $val ) = each( $children ) )
	{
		if ( ! posix_kill( $val, 0 ) )
		{
			unset( $children[ $key ] );
			$int_actChilds --;
		}
	}
	$children = array_values( $children );

	$int_waitTime = 5;

	if ( $int_actChilds < $int_maxChilds )
	{
		$int_journalId = getNextinQueue( $str_dispatcher );
		if ( $int_journalId > 0 )
		{

			$int_actChilds ++;
			$pid = pcntl_fork( );
			if ( $pid == - 1 )
			{
				exit( 0 );
			}
			else
				if ( $pid )
				{
					$children[] = $pid;
					$int_waitTime = 1;
				}
				else
				{
					// DO PROCESS CODE IN HERE
					mysql_connect( $gblstr_Site, $gblstr_UserName, $gblstr_PassWord, true );
					mysql_select_db( $gblstr_dbName );

					$sql_Job = "SELECT SQL_NO_CACHE * FROM foo";
					$sqr_Job = mysql_query( $sql_slideShowJob );
					$sqw_Job = mysql_fetch_assoc( $sqr_slideShowJob );

					exit( );
				}
		}
	}
	System_Daemon::iterate( $int_waitTime );
}
System_Daemon::stop( );

rampog
Posts: 56
Joined: Mon May 04, 2009 12:15 pm

Re: Long Live PHP Command Line as Daemon

Post by rampog » Mon Dec 31, 2012 1:07 pm

Hi tedtiger,
that is awesome tips could you please explain differences with "php-cli simpleDaemon.php &" execute as root,
what's the benefit of using pcntl ?

tedtiger
Posts: 244
Joined: Thu Mar 05, 2009 4:59 pm
Location: Germany
Contact:

Re: Long Live PHP Command Line as Daemon

Post by tedtiger » Tue Jan 01, 2013 8:26 pm

For security reasons I wouldn't even consider running a script as the root user. pcntl is simply for process control. Your PHP script behaves like a normal Linux background deamon.
http : // php net /manual/en/intro.pcntl.php

Post Reply