Skip to content

Commit 14bcafd

Browse files
committed
Merge pull request jobbyphp#46 from CarsonF/feature/closure-updates
Closure updates
2 parents 33a63f4 + dcd6eeb commit 14bcafd

File tree

11 files changed

+173
-159
lines changed

11 files changed

+173
-159
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
language: php
22

3+
sudo: false
4+
35
php:
46
- 5.4
57
- 5.5
@@ -9,7 +11,6 @@ php:
911

1012
matrix:
1113
allow_failures:
12-
- php: 7.0
1314
- php: hhvm
1415

1516
before_script:

README.md

Lines changed: 78 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
1-
[![Build Status](https://secure.travis-ci.org/hellogerard/jobby.png)](http://travis-ci.org/hellogerard/jobby)
1+
# Jobby, a PHP cron job manager #
2+
[![Total Downloads](https://img.shields.io/packagist/dt/hellogerard/jobby.svg)](https://packagist.org/packages/hellogerard/jobby)
3+
[![Latest Version](https://img.shields.io/packagist/v/hellogerard/jobby.svg)](https://packagist.org/packages/hellogerard/jobby)
4+
[![Build Status](https://img.shields.io/travis/hellogerard/jobby.svg)](https://travis-ci.org/hellogerard/jobby)
5+
[![MIT License](https://img.shields.io/packagist/l/hellogerard/jobby.svg)](https://github.com/hellogerard/jobby/blob/master/LICENSE)
26

3-
`Jobby` is a PHP cron job manager. Install the master jobby cron job, and it will
4-
manage all your offline tasks. Add jobs without modifying crontab. Jobby can
5-
handle logging, locking, error emails and more.
7+
Install the master jobby cron job, and it will manage all your offline tasks. Add jobs without modifying crontab.
8+
Jobby can handle logging, locking, error emails and more.
69

7-
## Install ##
8-
9-
1. Install [`composer`](<http://getcomposer.org>).
10-
2. Add `jobby` to your `composer.json`.
11-
12-
`'hellogerard/jobby': 'dev-master'`
13-
14-
3. Run `composer install`.
15-
4. Add the following line to your (or whomever's) crontab:
16-
17-
`* * * * * cd /path/to/project && php jobby.php 1>> /dev/null 2>&1`
18-
19-
After `jobby` installs, you can copy an example jobby file to the project root.
20-
21-
`% cp vendor/hellogerard/jobby/resources/jobby.php .`
22-
23-
## Usage ##
24-
25-
### Features ###
10+
## Features ##
2611

2712
- Maintain one master crontab job.
2813
- Jobs run via PHP, so you can run them under any programmatic conditions.
@@ -33,87 +18,105 @@ After `jobby` installs, you can copy an example jobby file to the project root.
3318
- Run only on certain hostnames (handy in webfarms).
3419
- Theoretical Windows support (but not ever tested)
3520

36-
### Currently Supported Options ###
37-
38-
Global options can be given to the `Jobby` object constructor. These will be
39-
used as a default for all subsequent jobs. Individual jobs can override a
40-
particular option when the job is `added`.
41-
42-
<pre>
43-
Option | Default | Required | Description
44-
===============+=====================================+==========+============
45-
| | |
46-
recipients | null | No | Comma-separated string of email addresses
47-
mailer | sendmail | No | Email method: sendmail or smtp or mail
48-
smtpHost | null | No | SMTP host, if `mailer` is smtp
49-
smtpPort | 25 | No | SMTP port, if `mailer` is smtp
50-
smtpUsername | null | No | SMTP user, if `mailer` is smtp
51-
smtpPassword | null | No | SMTP password, if `mailer` is smtp
52-
smtpSecurity | null | No | SMTP security option (ssl|tls), if `mailer` is smtp
53-
smtpSender | jobby@&lt;hostname&gt; | No | The sender and from addresses used in SMTP notices
54-
smtpSenderName | Jobby | No | The name used in the from field for SMTP messages
55-
runAs | null | No | Run as this user, if crontab user has `sudo` privileges
56-
environment | null or `getenv('APPLICATION_ENV')` | No | Development environment for this job
57-
runOnHost | `gethostname()` | No | Run jobs only on this hostname
58-
maxRuntime | null | No | Maximum execution time for this job (in seconds)
59-
output | /dev/null | No | Redirect `stdout` and `stderr` to this file
60-
dateFormat | 'Y-m-d H:i:s' | No | Format for dates on `jobby` log messages
61-
enabled | true | No | Run this job at scheduled times
62-
haltDir | null | No | A job will not run if this directory contains a file bearing the job's name
63-
debug | false | No | Send `jobby` internal messages to 'debug.log'
64-
command | none | Yes | The job to run (either a shell command or anonymous PHP function)
65-
schedule | none | Yes | Crontab schedule format (`man -s 5 crontab`) or Datetime format
66-
</pre>
67-
68-
### Example `jobby.php` File ###
21+
## Example ##
6922

7023
```php
7124
<?php
7225

73-
require(__DIR__ . '/vendor/autoload.php');
26+
require_once __DIR__ . '/vendor/autoload.php';
7427

75-
$jobby = new \Jobby\Jobby();
28+
$jobby = new Jobby\Jobby();
7629

7730
// Every job has a name
78-
$jobby->add('CommandExample', array(
79-
// Commands are either shell commands or anonymous functions
80-
'command' => 'ls',
31+
$jobby->add('CommandExample', [
32+
// Run a shell commands
33+
'command' => 'ls',
8134

8235
// Ordinary crontab schedule format is supported.
8336
// This schedule runs every hour.
84-
// You could also insert Datetime string.
37+
// You could also insert DateTime string in the format of Y-m-d H:i:s.
8538
'schedule' => '0 * * * *',
8639

8740
// Stdout and stderr is sent to the specified file
88-
'output' => 'logs/command.log',
41+
'output' => 'logs/command.log',
8942

9043
// You can turn off a job by setting 'enabled' to false
91-
'enabled' => true,
92-
));
44+
'enabled' => true,
45+
]);
9346

94-
$jobby->add('ClosureExample', array(
95-
// Commands can be PHP closures
96-
'command' => function() {
47+
$jobby->add('ClosureExample', [
48+
// Invoke PHP closures
49+
'closure' => function() {
9750
echo "I'm a function!\n";
9851
return true;
9952
},
10053

10154
// This function will run every other hour
10255
'schedule' => '0 */2 * * *',
10356

104-
'output' => 'logs/closure.log',
105-
'enabled' => true,
106-
));
57+
'output' => 'logs/closure.log',
58+
]);
10759

10860
$jobby->run();
10961
```
11062

111-
### Paid Support
63+
## Installation ##
11264

113-
[![Support and Consulting Services](https://s3-us-west-2.amazonaws.com/supportedsourceassets/buttons/supportandservices1.png)](http://supportedsource.org/consulting-services-and-support/jobby)
65+
The recommended way to install Jobby is through [Composer](http://getcomposer.org):
66+
```
67+
$ composer require hellogerard/jobby
68+
```
11469

115-
### Credits ###
70+
Then add the following line to your (or whomever's) crontab:
71+
```
72+
* * * * * cd /path/to/project && php jobby.php 1>> /dev/null 2>&1
73+
```
74+
75+
After Jobby installs, you can copy an example file to the project root.
76+
```
77+
$ cp vendor/hellogerard/jobby/resources/jobby.php .
78+
```
11679

117-
Developed before, but since inspired by [`whenever`](<https://github.com/javan/whenever>).
80+
## Supported Options ##
81+
82+
Each job requires these:
83+
84+
Key | Type | Description
85+
:-------- | :------ | :------------------------------------------------------------------------------
86+
schedule | string | Crontab schedule format (`man -s 5 crontab`) or DateTime format (`Y-m-d H:i:s`)
87+
command | string | The shell command to run (exclusive-or with `closure`)
88+
closure | Closure | The anonymous PHP function to run (exclusive-or with `command`)
89+
90+
91+
The options listed below can be applied to an individual job or globally through the `Jobby` constructor.
92+
Global options will be used as default values, and individual jobs can override them.
93+
94+
Option | Type | Default | Description
95+
:------------- | :-------- | :---------------------------------- | :--------------------------------------------------------
96+
runAs | string | null | Run as this user, if crontab user has `sudo` privileges
97+
debug | boolean | false | Send `jobby` internal messages to 'debug.log'
98+
_**Filtering**_| | | _**Options to determine whether the job should run or not**_
99+
environment | string | null or `getenv('APPLICATION_ENV')` | Development environment for this job
100+
runOnHost | string | `gethostname()` | Run jobs only on this hostname
101+
maxRuntime | integer | null | Maximum execution time for this job (in seconds)
102+
enabled | boolean | true | Run this job at scheduled times
103+
haltDir | string | null | A job will not run if this directory contains a file bearing the job's name
104+
_**Logging**_ | | | _**Options for logging**_
105+
output | string | /dev/null | Redirect `stdout` and `stderr` to this file
106+
dateFormat | string | Y-m-d H:i:s | Format for dates on `jobby` log messages
107+
_**Mailing**_ | | | _**Options for emailing errors**_
108+
recipients | string | null | Comma-separated string of email addresses
109+
mailer | string | sendmail | Email method: _sendmail_ or _smtp_ or _mail_
110+
smtpHost | string | null | SMTP host, if `mailer` is smtp
111+
smtpPort | integer | 25 | SMTP port, if `mailer` is smtp
112+
smtpUsername | string | null | SMTP user, if `mailer` is smtp
113+
smtpPassword | string | null | SMTP password, if `mailer` is smtp
114+
smtpSecurity | string | null | SMTP security option: _ssl_ or _tls_, if `mailer` is smtp
115+
smtpSender | string | jobby@&lt;hostname&gt; | The sender and from addresses used in SMTP notices
116+
smtpSenderName | string | Jobby | The name used in the from field for SMTP messages
117+
118+
## Credits ##
119+
120+
Developed before, but since inspired by [whenever](<https://github.com/javan/whenever>).
118121

119122
[Support this project](https://cash.me/$hellogerard)

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"php": ">=5.4",
1919
"mtdowling/cron-expression": "^1.0",
2020
"swiftmailer/swiftmailer": "^5.4",
21-
"jeremeamia/superclosure": "^2.1",
21+
"jeremeamia/superclosure": "^2.2",
2222
"symfony/process": "^2.7"
2323
},
2424
"require-dev": {

resources/jobby-pdo.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@
6868
echo "I'm a function (" . date('Y-m-d H:i:s') . ')!' . PHP_EOL;
6969
return true;
7070
};
71-
$secondJobFnSerializable = new \SuperClosure\SerializableClosure($secondJobFn);
72-
$secondJobFnSerialized = serialize($secondJobFnSerializable);
71+
$serializer = new SuperClosure\Serializer();
72+
73+
$secondJobFnSerialized = $serializer->serialize($secondJobFn);
7374
$insertCronJobConfiguration->execute(
7475
['ClosureExample', $secondJobFnSerialized, '* * * * *', 'logs/closure-pdo.log']
7576
);
@@ -89,11 +90,10 @@
8990
// Filter out each value, which is not set (for example, "maxRuntime" is not defined in the job).
9091
$job = array_filter($job);
9192

92-
$commandUnserialized = @unserialize($job['command']);
93-
if (false !== $commandUnserialized) {
94-
assert($commandUnserialized instanceof \SuperClosure\SerializableClosure);
95-
96-
$job['command'] = $commandUnserialized;
93+
try {
94+
$job['closure'] = $serializer->unserialize($job['command']);
95+
unset($job['command']);
96+
} catch (SuperClosure\Exception\ClosureUnserializationException $e) {
9797
}
9898

9999
$jobName = $job['name'];

src/BackgroundJob.php

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
namespace Jobby;
44

55
use Cron\CronExpression;
6-
use SuperClosure\SerializableClosure;
76

87
class BackgroundJob
98
{
9+
use SerializerTrait;
10+
1011
/**
1112
* @var Helper
1213
*/
@@ -83,7 +84,7 @@ public function run()
8384
$this->helper->acquireLock($lockFile);
8485
$lockAcquired = true;
8586

86-
if ($this->isFunction()) {
87+
if (isset($this->config['closure'])) {
8788
$this->runFunction();
8889
} else {
8990
$this->runFile();
@@ -227,24 +228,9 @@ protected function log($message)
227228
}
228229
}
229230

230-
/**
231-
* @return bool
232-
*/
233-
protected function isFunction()
234-
{
235-
$cmd = @unserialize($this->config['command']);
236-
237-
if ($cmd === false) {
238-
return false;
239-
}
240-
241-
return is_object($cmd) && $cmd instanceof SerializableClosure;
242-
}
243-
244231
protected function runFunction()
245232
{
246-
/** @var SerializableClosure $command */
247-
$command = unserialize($this->config['command']);
233+
$command = $this->getSerializer()->unserialize($this->config['closure']);
248234

249235
ob_start();
250236
$retval = $command();

src/Helper.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<?php
22
namespace Jobby;
33

4-
use SuperClosure\SerializableClosure;
5-
64
class Helper
75
{
86
/**
@@ -225,18 +223,6 @@ public function getPlatform()
225223
}
226224
}
227225

228-
/**
229-
* @param \Closure $fn
230-
*
231-
* @return string
232-
*/
233-
public function closureToString(\Closure $fn)
234-
{
235-
$code = new SerializableClosure($fn);
236-
237-
return serialize($code);
238-
}
239-
240226
/**
241227
* @param string $input
242228
*

src/Jobby.php

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
namespace Jobby;
44

5+
use Closure;
56
use SuperClosure\SerializableClosure;
67
use Symfony\Component\Process\PhpExecutableFinder;
78

89
class Jobby
910
{
11+
use SerializerTrait;
12+
1013
/**
1114
* @var array
1215
*/
@@ -104,9 +107,25 @@ public function getConfig()
104107
*/
105108
public function add($job, array $config)
106109
{
107-
foreach (['command', 'schedule'] as $field) {
108-
if (empty($config[$field])) {
109-
throw new Exception("'$field' is required for '$job' job");
110+
if (empty($config['schedule'])) {
111+
throw new Exception("'schedule' is required for '$job' job");
112+
}
113+
114+
if (!(isset($config['command']) xor isset($config['closure']))) {
115+
throw new Exception("Either 'command' or 'closure' is required for '$job' job");
116+
}
117+
118+
if (isset($config['command']) &&
119+
(
120+
$config['command'] instanceof Closure ||
121+
$config['command'] instanceof SerializableClosure
122+
)
123+
) {
124+
$config['closure'] = $config['command'];
125+
unset($config['command']);
126+
127+
if ($config['closure'] instanceof SerializableClosure) {
128+
$config['closure'] = $config['closure']->getClosure();
110129
}
111130
}
112131

@@ -170,17 +189,9 @@ protected function runWindows($job, array $config)
170189
*/
171190
protected function getExecutableCommand($job, array $config)
172191
{
173-
if ($config['command'] instanceof SerializableClosure) {
174-
$config['command'] = serialize($config['command']);
175-
176-
} else if ($config['command'] instanceof \Closure) {
177-
// Convert closures to its source code as a string so that we
178-
// can send it on the command line.
179-
$config['command'] = $this->getHelper()
180-
->closureToString($config['command'])
181-
;
192+
if (isset($config['closure'])) {
193+
$config['closure'] = $this->getSerializer()->serialize($config['closure']);
182194
}
183-
184195
return sprintf('"%s" "%s" "%s"', $this->script, $job, http_build_query($config));
185196
}
186197

0 commit comments

Comments
 (0)