creating schedule task without Cron job

24,661

Solution 1

There are multiple ways of doing repetitive jobs. Some of the ways that I can think about right away are:

  1. Using: https://www.setcronjob.com/

Use an external site like this to fire off your url at set intervals

  1. Using meta refresh. More here. You'd to have to open the page and leave it running.

  2. Javascript/Ajax refresh. Similar to the above example.

  3. Setting up a cron job. Most shared hosting do provide a way to set up cron jobs. Have a look at the cPanel of your hosting.

Solution 2

if you have shell access you could execute a php script via the shell

something like this would be an endless loop, that would sleep 60 seconds execute, collect garbage and repeat until the end of time.

while(true) {
    sleep(60);
    //script here


    //end your script
}

or you could do a "poor mans cron" with ajax or meta refresh. i've done it before. basically, you just place a redirect with either javascript or html's meta refresh at the beggining of your script. access this script from your browser, and just leave it open. it'll refresh every 60 seconds, just like a cronjob.

yet another alternative to a cronjob, would be a bash script such as:

#!/bin/bash
while :

do
sleep 60
 wget http://127.0.0.1/path/to/cronjob.php -O Temp --delete-after

done

all this being said, you probably will get caught by the host and get terminated anyway.

So your best solution:

go and sign up for a 5-10 dollar a month vps, and say good bye to shared hosting and hello to running your own little server.

if you do this, you can even stop using crappy php and use facebook's hhvm instead and enjoy its awesome performance.

Solution 3

There's a free service at

http://cron-job.org

That lets you set up a nice little alternative.

Solution 4

Option A

An easy way to realize it would be to create a file/database entry containing the execution time of your php script:

<?php
// crons.php
return [
    'executebackup.php' => 1507979485,
    'sendnewsletter.php' => 1507999485
];
?>

And on every request made through your visitors you check the current time and if its higher you include your php script:

<?php
// cronpixel.php
$crons = @include 'cache/crons.php';
foreach ($crons as $script => $time) {
    if ($time < time()) {
        // create lock to avoid race conditions
        $lock = 'cache/' . md5($script) . '.lock';
        if (file_exists($lock) || !mkdir($lock)) {
            continue;
        }
        // start your php script
        include($script);
        // now update crons.php
        $crons[ $script ] += 86400; // tomorrow
        file_put_contents('cache/crons.php', '<?php return ' . var_export($crons, true) . '; ?' . '>')
        // finally delete lock
        rmdir($lock);
    }
}
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
// image data
$im = imagecreate(1, 1);
$blk = imagecolorallocate($im, 0, 0, 0);
imagecolortransparent($im, $blk);
// image output
header("Content-type: image/gif");
imagegif($im);
// free memory
imagedestroy($im);
?>

Note: It will be rarily called on the exact second, because you do not know when your visitor will open your page (maybe 2 seconds later). So it makes sense to set the new time for the next day not through adding 86400 seconds. Instead use mktime.

Option B

This is a little project I realized in the past, that is similar to @r3wt 's idea, but covers race conditions and works on exact times like a cronjob would do in a scheduler without hitting the max_execution_time. And it works most of the time without the need to resurrect it (as done through visitors in Option A).

Explanation: The script writes a lock file (to avoid race conditions) for the 15th, 30th, 45th and 60th second of a minute:

// cron monitoring
foreach ($allowed_crons as $cron_second) {
    $cron_filename = 'cache/' . $cron_second . '_crnsec_lock';
    // start missing cron requests
    if (!file_exists($cron_filename)) {
        cron_request($cron_second);
    }
    // restart interrupted cron requests
    else if (filemtime($cron_filename) + 90 < time()) {
        rmdir($cron_filename);
        cron_request($cron_second);
    }
}

Every time a lock file is missing the script creates it and uses sleep() to reach the exact second:

if (file_exists($cron_filename) || !mkdir($cron_filename)) {
    return;
}
// add one minute if necessary
$date = new DateTime();
$cron_date = new DateTime();
$cron_date->setTime($cron_date->format('H'), $cron_date->format('i'), $sec);
$diff = $date->diff($cron_date);
if ($diff->invert && $diff->s > 0) {
    $cron_date->setTime($cron_date->format('H'), $cron_date->format('i') + 1, $sec);
}
$diff = $date->diff($cron_date);
// we use sleep() as time_sleep_until() starts one second to early (https://bugs.php.net/bug.php?id=69044)
sleep($diff->s);

After waking up again, it sends a request to itself through fopen():

// note: filter_input returns the unchanged SERVER var (http://php.net/manual/de/function.filter-input.php#99124)
// note: filter_var is unsecure (http://www.d-mueller.de/blog/why-url-validation-with-filter_var-might-not-be-a-good-idea/)
$url = 'http' . isSecure() . '://' . filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_URL) . htmlspecialchars($request_uri, ENT_QUOTES, 'UTF-8');
$context = stream_context_create(array(
    'http' => array(
        'timeout' => 1.0
    )
));
// note: return "failed to open stream: HTTP request failed!" because timeout < time_sleep_until
if ($fp = @fopen($url, 'r', false, $context)) {
    fclose($fp);
}
rmdir($cron_filename);

By that it calls itself infinitely and you are able to define different starting times:

if (isset($_GET['cron_second'])) {
    if ($cron_second === 0 && !(date('i') % 15)) {
        mycron('every 15 minutes');
    }
    if ($cron_second === 0 && !(date('i') % 60)) {
        mycron('every hour');
    }
}

Note: It produces 5760 requests per day (4 per minute). Not much, but a cronjob uses much less ressources. If your max_execution_time is high enough you could change it to calling itself only once per minute (1440 requests/day).

Solution 5

I understand that this question is bit old but I stumbled on it a week ago with this very question and the best and secure option we found was using a Web Service.

Our context:

We have our system in both shared hosting and private clouds.

We need that a script is activated once in a month (there are plans to create more schedules and to allow users to create some predetermined actions)

Our system provides access to many clients, so, when anyone uses the system it calls for a Web Service via Ajax and doesn't care about the response (after all everything is logged in our database and must run without user interaction)

What we've done is:

1 - An ajax call is called upon access in any major screen.

2 - The Web Service reads a schedule table on our database and calls whatever needs calling

3 - To avoid many stacked Web Service calls we check datetime with an interval of 10 mins before actually performing any actions

That's also a way to distribute the load balance and the schedules doesn't affect the system with user interaction.

Share:
24,661
Marko
Author by

Marko

Updated on October 15, 2020

Comments