fake cron - revised

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

fake cron - revised

Post by s.dot »

[edit]
Last edited by s.dot on Wed Aug 22, 2007 8:42 pm, edited 2 times in total.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
roast
Forum Newbie
Posts: 1
Joined: Thu Nov 03, 2005 6:48 am

Post by roast »

Thanks fro your wonderful idea. :D

But,it seems doesn't work as anticipation,and I get the error message as below:

Code: Select all

Fatal error: Call to undefined method fakeCron::_evaluateCronFormatTime() in D:\www\cron\fakecron.class.php on line 231
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Yes. That is the method that I have not written yet. It's the last one I have to write. I didn't want to complete the piece before it was critiqued. However, if you pass an english time to it like '4 PM', or an array of times, you will see that it works. ;)

EDIT| Which I will write in the morning, because I'm pooped right now (2:30 AM). I'll then probably refactor a little bit, write some documentation, do some tests, and call it a release!
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Post by VladSun »

1. Fork process for every item in the cron list. Otherwise one zombie cron job process will make your fakecron a zombie and no more cron jobs will be executed ;)
2. I repeat myself - still there is a need to add/remove cron jobs in real time.
3. In you current version - why sleep period is 1min, when the only available cron job period is 1 hour?
4. Better error/warning logging and log format.

PS:

5. I think it would be much better if you separate the daemon and the cron job list by using config file instead. Then you will need one additional method - to check if the config file has been updated. It will the "add/remove cron job in real time" verryyy easy :)
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

VladSun wrote:1. Fork process for every item in the cron list. Otherwise one zombie cron job process will make your fakecron a zombie and no more cron jobs will be executed ;)
2. I repeat myself - still there is a need to add/remove cron jobs in real time.
3. In you current version - why sleep period is 1min, when the only available cron job period is 1 hour?
4. Better error/warning logging and log format.

PS:

5. I think it would be much better if you separate the daemon and the cron job list by using config file instead. Then you will need one additional method - to check if the config file has been updated. It will the "add/remove cron job in real time" verryyy easy :)
1. I'm not sure how to fork processes.
2. I may do this. Load a text file of tasks. md5_file() the contents and check for changes during each iteration.
3. Available cron job period is every minute by using * * * * *. The only thing the hour is for, is to generate a list of tasks to run every hour -- has nothing to do with how often tasks can be run. If you set a task to run every minute, the script will stores this to run 60 times during this hour.
4. What do you suggest?
5. I'm starting to agree. I'm going to finish this version first, then work on that.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Post by VladSun »

1. Well, if you refer back to the original topic you will see how to do it ;) viewtopic.php?t=72201. You need two .php files - master and child. The "master" PHP will iterate through your cron list and execute the "child" PHP with arguments according to the cron job item.

main.php

Code: Select all

<?php
while (true)
{
       if ($cion_job_1)
       {
              shell_exec("php -q child.php '$params_1' &");
       }
       sleep($sleep_time);
}
?>
child.php

Code: Select all

<?php
        $err = shell_exec("php -q $params");
?>
2. Refer to (5)
3. If it's so - OK :)
4. Instead of using "die" you may use a function to write to a specified file - e.g. /etc/fakecron.log.
5. :) I am glad that you agree :) My suggestion is NOT to use XML (or similar) file format. Tab-delimited config would be enough.

PS: Read the crontab/crond manual - there you will find a lot usefull things :)
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Alrighty, I've got the script completely working now, even with crontab style formats.
It feels really ugly. So I'm going to write a "final" version, including the real time add/edit task feature. That will be nice because there won't need to be starting/stopping of the daemon. Logging could be added too.

I have spent lots of time on this. I hope it will be useful. :)
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

I am almost done writing the "final" version of this. I have logging enabled, realtime add/edit scheduled task list. I'm stuck on the part on how to fork a new process to avoid a zombie task from zombie'ing the fakeCron daemon.

Would I put it in a new class and call it like this?

Code: Select all

if (taskToRun)
{
    $output = new runScript(taskToRun);
}
Would that make it fork a new process?

I can't really make sense of how the main.php and child.php code posted by Vladsun works?
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

http://php.net/ref.pcntl may be of interest.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Thanks for that link. I did some quick reading and it seems it's not what I'm looking for.
Process Control support in PHP is not enabled by default. You have to compile the CGI or CLI version of PHP with --enable-pcntl configuration option when compiling PHP to enable Process Control support.
A main target for this script would be people using shared servers, who don't have control over the php configuration.
Note: Currently, this module will not function on non-Unix platforms (Windows).
Also, I want it to work on windows, for users not having access to crontab and don't know how to (or want to) run windows task scheduler.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Windows comes with the "at" command. Executing a command via system() et al can be redirected, this effectively separates the process too.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Post by VladSun »

@feyd
Installation

Process Control support in PHP is not enabled by default. You have to compile the CGI or CLI version of PHP with --enable-pcntl configuration option when compiling PHP to enable Process Control support.

Note: Currently, this module will not function on non-Unix platforms (Windows).
Recently, I found a script which should work on *nix and Window OS:

Code: Select all

<?php

  $runCommand = "YOUR COMMAND AND ITS PARAMS HERE";

  if(isset($_SERVER['PWD'])//*nix (aka NOT windows)
  {
    $nullResult = `$runCommand > /dev/null &`;
  }
  else //windowz
  {
    $WshShell = new COM("WScript.Shell");
    $oExec = $WshShell->Run($runCommand, 7, false);
  }

?>
You suggested to check PHP_OS (it's a constant) over a $_SERVER variable.

@scottayy

The contents of main.php should be a part of your class. It should pass as argument to child.php a string containing cron job php file name and its arguments. This should replace the following line code (and its related) in your method "public function run()"

Code: Select all

$cronOutput = shell_exec(escapeshellcmd('php -q -f ' . $cronToRun['script']));
You have to replace it with something like this:

Code: Select all

if(isset($_SERVER['PWD'])//*nix (aka NOT windows)
  {
    $nullResult = escapeshellcmd('php -q -f child.php "' . $cronToRun['script']. '" > /dev/null &');
  }
  else //windowz
  {
    $WshShell = new COM("WScript.Shell");
    $oExec = $WshShell->Run(escapeshellcmd('php -q -f child.php "' . $cronToRun['script']) . '"', 7, false);
  }
In child.php you have to implement the real execution of $cronToRun['script']. You can get the arguments passed to child.php by using $argv[].
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

This script is killing me :evil:
break time
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Post by VladSun »

:) Let me help you:

fakeCron.class.php

Code: Select all

<?php
public function run()
    {
        //set script to run "forever"
        ignore_user_abort(true);
        set_time_limit(0);
       
        //infinite loop
        while (true) {
           
            //check if new hourly cron list needs to be generated
            $this->_previousHour = $this->_currentHour;
            $this->_currentHour = date("G");
           
            if (($this->_previousHour == null) || ($this->_previousHour != $this->_currentHour)) {
                $this->_generateHourlyCronList();
            }
           
            //if there are scripts to be run this hour, loop through them
            if(!empty($this->_cronsToRun)) {
                foreach ($this->_cronsToRun AS $cronToRun) {
                   
                    //see if script should be run now
                    if (($cronToRun['time'] <= time()) && ($cronToRun['time'] >= (time()-$this->_timeout))) {
                       
                        //check if script exists
                        if (file_exists($cronToRun['script'])) {
							if(isset($_SERVER['PWD']) { //*nix (aka NOT windows)
								$nullResult = escapeshellcmd('php -q -f forkCron.php "' . $cronToRun['script']. '" > /dev/null &');
							}
							else { //windowz 
								$WshShell = new COM("WScript.Shell");
								$nullResult = $WshShell->Run(escapeshellcmd('php -q -f forkCron.php "' . $cronToRun['script']) . '"', 7, false);
							}                             
                        } else {
                            //script could not be found -- send error email?
                            if ($this->emailErrors == true) {
                                $this->_sendErrorMail($cronToRun['script'], time(), 'Could not locate script on server.');
                            }
                        }
                    }
                }
            }
           
            //check if fakeCron has been killed
            $this->_checkKill();
           
            //update file, so fakecron can be viewed as "running"
            $this->_updateFile();
           
            //sleep to avoid CPU hogging, never ending, evil loop =]
            sleep($this->_timeout);
        }
    } 

?>
forkCron.php

Code: Select all

//run the script and capture the output
	if (!empty($argv[1]))
	{
		$cronOutput = shell_exec(escapeshellcmd('php -q -f ' . $argv[1]));
		echo $cronOutput;
	}
This code will break your checks for "execution time" > "timeout", but it gives you the idea. If you want these check to be performed you have to implement a "feedback" messaging from forkCron to fakeCron. That's why there are two files. If you don't want to have these checks you may use only fakeCron.class.php by removing the "forkCron.php" string from the escapeshellcmd's arguments.
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Just wondering... does anyone see a need for this? Or am I being overly optimistic in the hopes that it will be useful? :P

Something inside of me wants to write something great & open source. But the complexities in this makes me think this isn't it. :P

/end cheesyness
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply