Page 1 of 2

fake cron

Posted: Wed Aug 15, 2007 10:45 pm
by s.dot
Some topic earlier referred to the usage of crons, and it was doubted weather or not he/she had access to cron jobs.

And I know some people don't, or some people can create a limited number of cron jobs when they need more. So I made this "fake cron" class.. maybe it might work. I haven't tested it, but theoretically in my head it should work. I have doubts about the usage of include(). You guys can tell me what you think. =]

fakecron.class.php

Code: Select all

<?php

class fakeCron
{
	/*
	** Container for cronjobs stored in the format [time] => script
	*/
	private $_cronJobs = array();

	/*
	** The amount of time to wait before checking if a script needs to be run
	*/
	private $_timeout = 60;

	/*
	** Method to set the number of seconds between checking if the script needs
	** to run.
	** @param int $time (in seconds)
	** @access public
	*/
	public function setTimeout($time)
	{
		$this->_timeout = $time;
	}

	/*
	** Method to add a script and time to run to the cronJobs array.
	** @param string $script (script to be executed)
	** @param mixed $timeToRun (any time format that can be converted to a timestamp
	** using strtotime())
	** @access public
	*/
	public function addCron($script, $timeToRun)
	{
		if (is_array($timeToRun)) {
			foreach ($timeToRun AS $timeEach)
			{
				$this->_addCron($script, $timeEach);
			}
		} else
		{
			$this->_addCron($script, $timeToRun);
		}
	}
	
	/*
	** Internal method to add to $this->_cronJobs array
	** @access private
	*/
	private function _addCron($script, $timeToRun)
	{
		if (strtotime($timeToRun) !== false) {
			$this->_cronJobs[strtotime($timeToRun)] = $script;
		} else {
			trigger_error('FakeCron: Could not parse time (' . $timeToRun . ') given for script ('
				 . $script . ') into timestamp', E_USER_ERROR);
		}
	}

	/*
	** This method sets the script so it will run hypothetically forever and
	** starts an infinite loop, checking if any scripts need to be run and then
	** sleep()ing.
	** @access public
	*/
	public function run()
	{
		ignore_user_abort(true);
		set_time_limit(0);

		while (true)
		{
			foreach ($this->_cronJobs AS $k => $v)
			{
				if (($k <= time()) && ($k >= time()-$this->_timeout)) {
					include $v;
				}
			}

			$this->checkKill();
			
			sleep($this->_timeout);
		}
	}
	
	/*
	** Method to check for existence of kill.txt
	** If found, the script will exit.
	*/
	private function _checkKill()
	{
		if (file_exists('kill.txt')) {
			if (!unlink('kill.txt')) {
				die('Fake Cron: kill.txt was found and could not be deleted.  Fake Cron '
					. 'was successfully killed, but will not be able to run in the future.  Change '
					. 'permissions on kill.txt or manually delete it.');
			}
			
			exit;
		}
	}
}
Example

Code: Select all

$fakeCron = new fakeCron();
$fakeCron->addCron('updatestats.php', '12:30 AM');  //runs at 12:30 AM every day
$fakeCron->addCron('sendmailinglist.php', '1 PM');  //runs at 1 PM every day

//runs on specific time on specific date
$fakeCron->addCron('somecron.php', 'Saturday Aug 26, 2007 3:15 AM');

//runs on each time of array of times every day
$fakeCron->addCron('updatestatsagain.php', array('1 PM', '2 PM', '3 PM', '1 AM', '2 AM', '3 AM'));

$fakeCron->run();
To kill fake cron (to update cronjobs list). Manually create kill.txt or write a script to create it. Fake cron will detect it, kill the script, and then delete the file so fake cron can run again.

Posted: Wed Aug 15, 2007 10:53 pm
by VladSun
It would be nice if it accepts crontab time format ;)

Posted: Wed Aug 15, 2007 11:09 pm
by VladSun
Also, one should be able to add/remove cron jobs in real time.

Posted: Wed Aug 15, 2007 11:11 pm
by s.dot
Not sure what you mean in real time?
Also, do you presume it would work? I'd test it.. but I really have nothing to test it on. Guess I could make some stuff to test it on.

Posted: Wed Aug 15, 2007 11:19 pm
by VladSun
scottayy wrote:Not sure what you mean in real time?
I mean that after you request this PHP file and run() is called there is no way to add new cron jobs, neither to remove old one.
One way of making this possible is to read a DB (maybe flat DB - a simple text file) let's say every 1 minute and to update the list of the cron jobs according to DB's content.

Posted: Thu Aug 16, 2007 1:19 am
by s.dot
Thanks for the suggestion, but I would rather keep this in one file.

I would like to implement a kill() method, so a user wouldn't have to restart apache to stop the script. But I don't know how to go about that.

Posted: Thu Aug 16, 2007 3:24 am
by onion2k
scottayy wrote:I would like to implement a kill() method, so a user wouldn't have to restart apache to stop the script. But I don't know how to go about that.
The script would need to listen for something. You could have it create a socket and listen to that, but that's a lot of work. A simpler solution would be to check for the existence of a file. So your script would do something like..

Code: Select all

if (file_exists("kill.txt")) {
  unlink("kill.txt");
  exit;
}
To kill the script you'd just call a script that creates the kill.txt file.

Posted: Thu Aug 16, 2007 10:13 am
by VladSun
Waht about a pure system call to kill?

Code: Select all

kill -s 9 `pgrep fakecron.php`
The only issue here is that is not going to work under Windows OS - but that not my concern ;)

Posted: Thu Aug 16, 2007 11:23 am
by s.dot
Okay, I edited the script.

Took up Onion2k's suggestion to check for kill.txt, which can be manually created or a script can be created to kill the script. Fake Cron will then attempt to delete the file, then exit, so Fake Cron can be ran again.

Also, I added the ability to pass an array of times to the addCron method to run a script at multiple times each day.

There is one problem I see. If you tell it to run a script at 1 PM, it will run successfully for the first day, but every day after that the timestamp will be old (because the script is still running). I need a proposal on how to update the timestamp if the script is to be ran every day.

Posted: Thu Aug 16, 2007 11:29 am
by VladSun
1. There should be some check before executing the script - if the script is already runing ...
2. crontabs has already every functionality one can ask for - just follow its model...

Posted: Thu Aug 16, 2007 11:31 am
by s.dot
VladSun wrote:1. There should be some check before executing the script - if the script is already runing ...
Thought about that. I need to think of a way to do this.

Posted: Thu Aug 16, 2007 11:34 am
by feyd
Generate another file like a .pid.

Posted: Thu Aug 16, 2007 11:38 am
by VladSun

Code: Select all

if (system('pgrep fakecron.php') === '')
{
    // allow runing the script
}
Beware of lock-file aproach - if for some reason it is not deleted (i.e. abnormal exit of fakecron.php) you will not be able to run this script and you will have to manually delete the lock-file.

Posted: Thu Aug 16, 2007 11:39 am
by VladSun
feyd wrote:Generate another file like a .pid.
That is exactly what you must beware of ;)

Posted: Thu Aug 16, 2007 12:04 pm
by s.dot
Actually I believe if you lock a file, the file will remain locked until the script is finished executing or no longer running. So if I create a .pid file, and lock it. I can check if i can read/write to the .pid file, and this will tell me if the script is already running.

As far as I know, file locks are automatically unlocked by the operating system, right?