Skip to content

Commit

Permalink
NEW: binary() + clipath renamed to script
Browse files Browse the repository at this point in the history
  • Loading branch information
xfra35 committed Dec 23, 2015
1 parent eb504bb commit a35ce54
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 33 deletions.
61 changes: 51 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ This plugin for [Fat-Free Framework](http://github.com/bcosca/fatfree) helps you
* [Options](#options)
* [Logging](#logging)
* [Web interface](#web-interface)
* [CLI path](#cli-path)
* [Script path](#script-path)
* [PHP binary path](#php-binary-path)
* [Ini configuration](#ini-configuration)
* [Asynchronicity](#asynchronicity)
* [API](#api)
Expand Down Expand Up @@ -146,7 +147,13 @@ By default, the routes `GET /cron` and `GET cron/@job` are available in CLI mode

You can enable web routes by setting `$cron->web=TRUE`.

### CLI path
In that case, `/cron` can be triggered via HTTP on a periodic basis, for example by your web app, or by a web cron service, or even by your own crontab:

```cron
* * * * * curl http://mydomain.tld/cron
```

### Script path

By default, the script called asynchronously is `index.php` located in the current working directory.

Expand All @@ -158,8 +165,21 @@ You may need to tweak this value if:
Examples:

```php
$cron->clipath='htdocs/index.php';//relative to app root
$cron->clipath=__DIR__.'/cron.php';//absolute path
$cron->script='htdocs/index.php';//relative to app root
$cron->script=__DIR__.'/cron.php';//absolute path
```

### PHP binary path

By default, the PHP binary used to trigger asynchronous job executions is either `php` or `php-cli` (smart guess).

You may need to tweak this value if none of these values correspond to an executable PHP CLI binary
or if they are not in the path.

Example:

```php
$cron->binary('/home/myphp-cli');
```

## Ini configuration
Expand All @@ -170,7 +190,7 @@ Configuration is possible from within an .ini file, using the `CRON` variable. E
[CRON]
log = TRUE
web = FALSE
clipath = cron.php
script = cron.php

[CRON.presets]
lunch = 0 12 * * *
Expand All @@ -195,8 +215,9 @@ $f3->run();

If you want tasks to be run asynchronously, you'll need:
* `exec()` to be enabled on your hosting
* the `php` executable to be executable and in the path of your hosting user

* the [script path](#script-path) to be configured correctly
* the [PHP CLI binary](#php-binary-path) to be executable and in the path of your hosting user

**NB1:** The plugin will detect automatically if jobs can be run asynchronously.
If not, jobs will be executed synchronously, which may take longer and add a risk of queue loss in case of a job failure.

Expand Down Expand Up @@ -224,15 +245,35 @@ $cron->log=TRUE;// enable logging
$cron->web=TRUE;// enable web interface
```

### clipath
### script

**Path of the script to call asynchronously (default='index.php')**

Defaults to `index.php` in the current working directory.

```php
$cron->clipath='htdocs/index.php';//relative to app root
$cron->clipath=__DIR__.'/cron.php';//absolute path
$cron->script='htdocs/index.php';//relative to app root
$cron->script=__DIR__.'/cron.php';//absolute path
```

### clipath

**Alias for script [deprecated]**

### binary

**Path of the PHP CLI binary (default='php' or 'php-cli')**

```php
echo $cron->binary;// php
```

### binary( $path )

**Set PHP CLI binary path**

```php
$cron->binary('/home/myphp-cli');
```

### silent
Expand Down
50 changes: 34 additions & 16 deletions lib/cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ class Cron extends \Prefab {
/** @var bool */
public $web=FALSE;

/** @var string */
public $clipath='index.php';

/** @var bool */
public $silent=TRUE;

/** @var string Script path */
public $script='index.php';

/** @var string PHP CLI path */
protected $binary;

/** @var array */
protected $jobs=array();

/** @var bool */
protected $async=FALSE;

/** @var array */
protected $presets=array(
'yearly'=>'0 0 1 1 *',
Expand All @@ -43,6 +43,20 @@ class Cron extends \Prefab {
'hourly'=>'0 * * * *',
);

/**
* Set binary path after checking that it can be executed and is CLI
* @param string $path
* @return string
*/
function binary($path) {
if (function_exists('exec')) {
exec($path.' -v 2>&1',$out,$ret);
if ($ret==0 && preg_match('/cli/',@$out[0],$out))
$this->binary=$path;
}
return $this->binary;
}

/**
* Schedule a job
* @param string $job
Expand Down Expand Up @@ -93,15 +107,15 @@ function execute($job,$async=TRUE) {
$func=$f3->grab($func);
if (!is_callable($func))
user_error(sprintf(self::E_Callable,$job),E_USER_ERROR);
if ($async && $this->async) {
if ($async && isset($this->binary)) {
// PHP docs: If a program is started with this function, in order for it to continue running in the background,
// the output of the program must be redirected to a file or another output stream.
// Failing to do so will cause PHP to hang until the execution of the program ends.
$dir=dirname($this->clipath);
$file=basename($this->clipath);
$dir=dirname($this->script);
$file=basename($this->script);
if (@$dir[0]!='/')
$dir=getcwd().'/'.$dir;
exec(sprintf('cd "%s";php %s /cron/%s > /dev/null 2>/dev/null &',$dir,$file,$job));
exec(sprintf('cd "%s";%s %s /cron/%s > /dev/null 2>/dev/null &',$dir,$this->binary,$file,$job));
return FALSE;
}
$start=microtime(TRUE);
Expand Down Expand Up @@ -201,20 +215,24 @@ function parseExpr($expr) {

//! Read-only public properties
function __get($name) {
if (in_array($name,array('jobs','async','presets')))
if (in_array($name,array('binary','jobs','presets')))
return $this->$name;
if ($name=='clipath') // alias for script [deprecated]
return $this->script;
trigger_error(sprintf(self::E_Undefined,__CLASS__,$name));
}

//! Constructor
function __construct() {
$f3=\Base::instance();
$config=(array)$f3->get('CRON');
foreach(array('log','web','clipath','silent') as $k)
foreach(array('log','web','script','silent') as $k)
if (isset($config[$k])) {
settype($config[$k],gettype($this->$k));
$this->$k=$config[$k];
}
if (isset($config['binary']))
$this->binary($config['binary']);
if (isset($config['jobs']))
foreach($config['jobs'] as $job=>$arr) {
$handler=array_shift($arr);
Expand All @@ -223,10 +241,10 @@ function __construct() {
if (isset($config['presets']))
foreach($config['presets'] as $name=>$expr)
$this->preset($name,is_array($expr)?implode(',',$expr):$expr);
if (function_exists('exec')) {
exec('php -v 2>&1',$out,$ret);
$this->async=$ret==0;// check if the `php` binary is in the path and can be executed
}
if (!isset($this->binary))
foreach(array('php','php-cli') as $path) // try to guess the binary name
if ($this->binary($path))
break;
$f3->route(array('GET /cron','GET /cron/@job'),array($this,'route'));
}

Expand Down
12 changes: 12 additions & 0 deletions tests/jobs.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ function jobC($f3) {
$f3->concat('job','C');
}

function jobD($f3) {
$f3->concat('job','D');
}

function jobE($f3) {
$f3->concat('job','E');
}

function jobF($f3) {
$f3->concat('job','F');
}

function test1($f3) {
$this->write('A');
usleep(50000);
Expand Down
27 changes: 20 additions & 7 deletions tests/tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ function run($f3) {
'log'=>FALSE,
'cli'=>TRUE,
'web'=>FALSE,
'clipath'=>'index.php',
'script'=>'index.php',
),'CRON.');
$cron=Cron::instance();
$test->expect(
!$cron->log && !$cron->web && count($cron->jobs)==3 && $cron->clipath=='index.php',
!$cron->log && !$cron->web && count($cron->jobs)==3 && $cron->script=='index.php',
'Initial config'
);
$test->expect(
Expand All @@ -26,11 +26,24 @@ function run($f3) {
isset($f3->ROUTES['/cron']),
'Route automatically defined'
);
//async auto-detection
$async=function_exists('exec') && exec('php -r "echo 1+3;"')=='4';
//binary auto-detection
$binary=NULL;
if (function_exists('exec'))
foreach(array('php','php-cli') as $bin) {
exec($bin.' -v 2>&1',$out,$ret);
if ($ret==0) {
$binary=$bin;
break;
}
}
$test->expect(
$cron->binary===$binary,
'Binary auto-detection: '.($binary?:'none')
);
$cron->binary('foobarbaz');
$test->expect(
$cron->async===$async,
'Async auto-detection: '.($async?'ON':'OFF')
$cron->binary===$binary,
'Binary existence check'
);
//expression parsing
$test->expect(
Expand Down Expand Up @@ -111,7 +124,7 @@ function run($f3) {
'Run scheduler, i.e executes all due jobs'
);
//async job execution
if ($async) {
if ($binary) {
$cron->set('test1','Jobs->test1','* * * * *');
$cron->set('test2','Jobs->test2','* * * * *');
@unlink($testfile=$f3->TEMP.'cron-test.txt');
Expand Down

0 comments on commit a35ce54

Please sign in to comment.