直接从代码中安排任务。

v1.4.9 2017-07-03 08:32 UTC

README

一次性安装cron作业,从代码中管理其余部分。

Crunz是一个框架无关的包,用于通过流畅的API在PHP中安排周期性任务(cron作业)。

Crunz能够执行任何类型的可执行命令以及PHP闭包。

安装

要安装它

composer require kwstanislav/crunz

如果安装成功,一个名为 crunz 的命令行工具将被符号链接到您项目的 vendor/bin 目录。

它是如何工作的?

想法非常简单:我们不是在crontab文件中安装cron作业,而是使用Crunz接口在单个或多个PHP文件中定义它们。

这里有一个基本示例

<?php
// tasks/backupTasks.php

use Crunz\Schedule;

$schedule = new Schedule();
$schedule->run('cp project project-bk')       
         ->daily();

return $schedule;

要运行任务,您只需要安装一个普通的cron作业(一个crontab条目),该作业每分钟运行一次,并将责任委托给Crunz的事件运行器。

* * * * * /project/vendor/bin/crunz schedule:run

命令 schedule:run 负责收集所有PHP任务文件并运行到期的任务。

任务文件

任务文件类似于crontab文件。就像crontab文件一样,它们可以包含一个或多个任务。

通常我们在项目的根目录下的 tasks/ 目录中创建我们的任务文件。

默认情况下,Crunz假定所有任务文件都位于项目根目录下的 tasks/ 目录中。

指定源目录有两种方式:1) 配置文件 2) 作为事件运行器命令的参数。

我们可以通过将源路径传递给事件运行器作为参数来显式设置源路径。

* * * * * /project/vendor/bin/crunz schedule:run /path/to/tasks/directory

创建一个简单的任务

在终端中,切换到您的项目根目录并运行以下命令

mkdir tasks && cd tasks
nano GeneralTasks.php

然后,添加以下任务

<?php
// tasks/FirstTasks.php

use Crunz\Schedule;

$schedule = new Schedule();

$schedule->run('cp project project-bk')       
         ->daily()
         ->description('Create a backup of the project directory.');

// ...

// IMPORTANT: You must return the schedule object
return $schedule; 

创建任务文件有一些约定,您需要遵守。首先,文件名应该以 Tasks.php 结尾,除非我们通过配置设置更改此设置。

除此之外,我们 必须 在每个文件的末尾返回 Schedule 类的实例,否则,事件运行器将跳过文件中的所有任务。

由于Crunz递归地扫描任务目录,我们可以将所有任务放在一个文件中,或者根据它们的用途跨不同的文件(或目录)放置,这有助于我们有一个良好的任务目录组织。

命令

我们可以使用 run() 运行 任何 命令或脚本。此方法接受两个参数:要执行的命令命令选项(如果有的话,作为关联数组)。

正常命令或脚本

<?php
use Crunz\Schedule;

$schedule = new Schedule();

$schedule->run('/usr/bin/php backup.php', ['--destination' => 'path/to/destination'])       
         ->everyMinute()
         ->description('Copying the project directory');
         
return $schedule;

在上面的示例中,--destinationbackup.php 脚本支持的选项。

闭包

我们还可以编写一个闭包来代替命令

<?php
use Crunz\Schedule;

$schedule = new Schedule();

$x = 12;
$schedule->run(function() use ($x) { 
   // Do some cool stuff in here 
})       
->everyMinute()
->description('Copying the project directory');
         
return $schedule;

执行频率

有多种方式可以指定任务应该运行的时间 频率。我们可以将这些方法组合起来以获得所需的频率。

时间单位

有一组方法指定时间单位(大于分钟)作为频率。它们通常以 ly 后缀结尾,如 hourly()daily()weeklymonthly()quarterly()yearly

<?php
// ...
$schedule->run('/usr/bin/php backup.php')       
         ->daily();
// ...

上述任务将在每天午夜运行。

这里有另一个任务,它会在每个月的第一天运行。

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->monthly();
// ...

使用这组方法安排的所有事件都发生在那个时间单位的开头。例如,weekly()将在星期日运行事件,而monthly()将在每月的第一天运行。

动态方法

动态方法允许我们即时选择广泛的频率选项。我们只需遵循以下模式

every[NumberInCamelCaseWords]Minute|Hour|Day|Months?

正如我们所看到的,方法应该以单词every开头,后跟驼峰式的数字,并以以下时间单位之一结束:分钟、小时、天和月

结尾的s是可选的,只是为了语法上的需要。

话虽如此,以下方法都是有效的

  • everyFiveMinutes()
  • everyMinute()
  • everyTwelveHours()
  • everyMonth
  • everySixMonths()
  • everyFifteenDays()
  • everyFiveHundredThirtySevenMinutes()
  • everyThreeThousandAndFiveHundredFiftyNineMinutes()
  • ...

这就是在任务文件中使用它的方式

<?php
// ...

$schedule->run('/usr/bin/php email.php')       
         ->everyTenDays();

$schedule->run('/usr/bin/php some_other_stuff.php')       
         ->everyThirteenMinutes();
// ...

return $schedule;

在特定时间运行事件

要安排一次性任务,您可以使用on()方法,如下所示

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->on('13:30 2016-03-01');
// ...

上述任务将在2016年3月1日下午1:30运行。

On()接受PHP的strtotime函数解析的任何日期格式。

要指定时间,我们使用at()方法

<?php
// ...
$schedule->run('/usr/bin/php script.php')       
         ->daily()
         ->at('13:30');
// ...

我们可以使用dailyAt()来得到相同的结果

<?php
// ...
$schedule->run('/usr/bin/php script.php')       
         ->dailyAt('13:30');
// ...

如果我们只向on()方法传递时间,它将具有与使用at()相同的效果

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->mondays()
         ->on('13:30');
         
// is the sames as
$schedule->run('/usr/bin/php email.php')       
         ->mondays()
         ->at('13:30');
// ...

工作日

Crunz还提供了一组方法,用于指定一周中的某一天。这些方法被设计为作为约束使用,不应单独使用。原因是工作日方法仅修改cron作业表达式的“星期几”字段。

考虑以下示例

<?php
// Cron equivalent:  * * * * 1
$schedule->run('/usr/bin/php email.php')       
         ->mondays();

乍一看,任务似乎会在每个星期一运行,但由于它只修改cron作业表达式的“星期几”字段,任务会在星期一每分钟运行。

这是使用工作日方法的正确方式

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->everyThreeHours()
         ->mondays();
// ...

在上面的任务中,我们使用mondays()作为约束,在星期一每三小时运行任务。

设置单个字段

Crunz的方法不仅限于前面解释的现成方法。我们还可以设置单个字段来组合我们自定义的频率。这些方法要么接受值数组,要么使用逗号分隔的参数列表

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->minute(['1-30', 45, 55])
         ->hour('1-5', 7, 8)
         ->dayOfMonth(12, 15)
         ->month(1);

或者

<?php
// ...
$schedule->run('/usr/bin/php email.php')       
         ->minute('30')
         ->hour('13')
         ->month([1,2])
         ->dayofWeek('Mon', 'Fri', 'Sat');

// ...

经典方法

我们也可以用老方法来安排,就像我们在crontab文件中做的那样

<?php
$schedule->run('/usr/bin/php email.php')
         ->cron('30 12 * 5-6,9 Mon,Fri');     
        

根据我们的用例,我们可以选择和组合适当的方法集合,这些方法更容易使用。

更改目录

您可以使用in()方法在运行命令之前更改目录

<?php

// ...

$schedule->run('./deploy.sh')
         ->in('/home')
         ->weekly()
         ->sundays()
         ->at('12:30')
         ->appendOutputTo('/var/log/backup.log');

// ...

return $schedule;

任务生命周期

在一个crontab条目中,我们无法轻松地指定任务的生存期(任务活跃的持续时间)。然而,在Crunz中已经使这变得容易。

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->from('12:30 2016-03-04')
         ->to('04:55 2016-03-10');
 //       

或者我们可以使用between()方法来得到相同的结果

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->between('12:30 2016-03-04', '04:55 2016-03-10');

 //       

如果我们不指定日期部分,任务将每天激活,但仅限于指定的持续时间

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->between('12:30', '04:55');

 //       

上述任务每天在下午12:30至下午4:55之间每五分钟运行。

运行条件

在传统的crontab文件中,我们无法非常容易地设置任务的运行条件。但是,通过when()skip()方法,这已经变得容易。

考虑以下代码

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->between('12:30 2016-03-04', '04:55 2016-03-10')
         ->when(function() {
           if ($some_condition_here) { return true; }
         });

 //       

方法when()接受一个回调函数,该回调函数必须返回TRUE才能运行任务。这在我们需要在执行资源密集型任务之前检查资源时非常有用。

在特定条件下,我们可以通过使用 skip() 方法跳过任务。如果传递的回调函数返回 TRUE,则任务将被跳过。

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->between('12:30 2016-03-04', '04:55 2016-03-10')
         ->skip(function() {
             if ($some_condition_here) { return true; }  
    });

 //       

对于单个任务,我们可以多次使用这些方法。它们按顺序进行评估。

配置

Crunz 以 YAML 格式提供了一些配置选项。为了修改配置设置,强烈建议您拥有自己的配置文件副本,而不是修改原始文件。

要创建配置文件的副本,首先需要发布配置文件

/project/vendor/bin/crunz publish:config
The configuration file was generated successfully

因此,配置文件副本将在我们项目的根目录中创建。

配置文件如下所示

# Crunz Configuration Settings

# This option defines where the task files and
# directories reside.
# The path is relative to the project's root directory,
# where the Crunz is installed (Trailing slashes will be ignored).
source: tasks

# The suffix is meant to target the task files inside the ":source" directory.
# Please note if you change this value, you need
# to make sure all the existing tasks files are renamed accordingly.
suffix: Tasks.php

# By default the errors are not logged by Crunz
# You may set the value to true for logging the errors
log_errors: false

# This is the absolute path to the errors' log file
# You need to make sure you have the required permission to write to this file though.
errors_log_file:

# By default the output is not logged as they are redirected to the
# null output.
# Set this to true if you want to keep the outputs
log_output: false

# This is the absolute path to the global output log file
# The events which have dedicated log files (defined with them), won't be
# logged to this file though.
output_log_file:

# This option determines whether the output should be emailed or not.
email_output: false

# This option determines whether the error messages should be emailed or not.
email_errors: false

# Global Swift Mailer settings
#
mailer:
    # Possible values: smtp, mail, and sendmail
    transport: smtp
    recipients:
    sender_name:
    sender_email:


# SMTP settings
#
smtp:
    host:
    port:
    username:
    password:
    encryption:

如您所见,有几个选项,如 source,用于指定源任务目录。其他选项用于错误/输出记录/电子邮件。

每次我们运行 Crunz 命令时,它都会查看项目的根目录,以查看是否存在任何用户修改的配置文件。如果配置文件不存在,它将使用随包提供的配置文件。

并行性和锁定机制

Crunz 以并行方式(在单独的进程)运行计划事件,因此具有相同执行频率的所有事件将异步同时运行。为了实现这一点,Crunz 使用 symfony/Process 库在子进程中运行任务。

如果一个任务的执行时间延长到下一个间隔甚至更长时间,我们说相同的任务实例是重叠的。在某些情况下,这并不是问题,但有时这些任务会修改数据库数据或文件,这可能会引起意外的行为,甚至数据丢失。

为了防止关键任务相互重叠,Crunz 通过 preventOverlapping() 方法提供锁定机制,该机制确保如果上一个实例正在运行,则不会运行任何任务。

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->preventOverlapping();
 //       

保留输出

cron 作业通常有输出,这通常通过电子邮件发送给 crontab 文件的拥有者或由 crontab 文件中的 MAILTO 环境变量设置的用户或用户组。

我们也可以使用 >>> 运算符将标准输出重定向到物理文件

* * * * * /command/to/run >> /var/log/crons/cron.log

这种操作已在 Crunz 中自动化。要自动将每个事件输出发送到日志文件,我们可以在配置文件中相应地设置 log_outputoutput_log_file 选项

# Configuration settings

## ...
log_output:      true
output_log_file: /var/log/crunz.log
## ...

这会将事件输出(如果执行成功)发送到 /var/log/crunz.log 文件。然而,我们需要确保我们有权写入该文件。

如果我们需要基于事件记录输出,我们可以像这样使用 appendOutputTo()sendOutputTo() 方法

<?php
//
$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->appendOutputTo('/var/log/crunz/emails.log');

 //       

方法 appendOutputTo() 追加 输出到指定的文件。要在每个运行后用新数据覆盖日志文件,我们使用 saveOutputTo() 方法。

我们还可以通过在配置文件中设置 email_outputmailer 设置,将错误作为电子邮件发送给一组收件人。

错误处理

Crunz 通过记录并允许在发生错误时添加一组回调函数来简化错误处理。

错误回调

您可以根据需要设置尽可能多的回调函数,以在发生错误时运行

<?php

use Crunz\Schedule;
$schedule = new Schedule();

$schedule->add('command/to/run')
         ->everyFiveMinutes();

$schedule
->onError(function(){
   // Send mail
})
->onError(function(){
   // Do something else
});

return $schedule;

如果有错误,将执行两个定义的回调函数。

错误记录

要记录每次运行期间可能发生的错误,我们可以在配置文件中设置 log_errorerror_log_file 设置,如下所示

# Configuration settings

# ...
log_errors:      true
errors_log_file: /var/log/error.log
# ...

结果,如果事件的执行由于某些原因而失败,错误消息将被追加到指定的错误日志文件。每个条目都提供了有用的信息,包括发生的时间、事件描述、导致错误的执行命令以及错误消息本身。

还可以通过在配置文件中设置 email_errormailer 配置来将错误作为电子邮件发送给一组收件人。

预处理和后处理钩子

有时候我们想在事件前后执行某些操作。通过将预处理和后处理回调附加到相应的事件,这是可能的。

为此,我们在 EventSchedule 对象上使用 before()after(),这意味着我们可以在基于事件和基于计划的基础上都有预和后钩子。绑定到计划的钩子将在所有事件之前运行,并在所有事件完成后运行。

<?php
// ...

$schedule = new Schedule();

$schedule->run('/usr/bin/php email.php')
         ->everyFiveMinutes()
         ->before(function() { 
             // Do something before the task runs
         })
         ->before(function() { 
                 // Do something else
         })
         ->after(function() {
             // After the task is run
         });
 
$schedule

->before(function () {
   // Do something before all events
})
->after(function () {
   // Do something after all events are finished
}
->before(function () {
   // Do something before all events
});

//  ...   

我们可以通过链式调用这些方法来使用这些方法,需要多少次就使用多少次。

只有当事件执行成功时,才会调用后执行回调。

其他有用的命令

我们已经使用了几个 crunz 命令,如 schedule:runpublish:config

要查看 crunz 的所有有效选项和参数,我们可以运行以下命令

vendor/bin/crunz --help

列出任务

这些命令中的一个就是 crunz schedule:list,它以表格格式列出定义的任务(在收集的 *.Tasks.php 文件中)。

vendor/bin/crunz schedule:list

+---+---------------+-------------+--------------------+
| # | Task          | Expression  | Command to Run     |
+---+---------------+-------------+--------------------+
| 1 | Sample Task   | * * * * 1 * | command/to/execute |
+---+---------------+-------------+--------------------+

生成任务

还有一个名为 make:task 的有用命令,它可以生成包含所有默认值的任务文件骨架,这样我们就不必从头开始编写它们。我们可以在以后根据我们的需求修改输出文件。

例如,要创建一个任务,该任务每小时在星期一运行 /var/www/script.php,我们运行以下命令

vendor/bin/crunz make:task exampleOne --run scripts.php --in /var/www --frequency everyHour --constraint mondays
Where do you want to save the file? (Press enter for the current directory)

运行此命令时,Crunz 会询问我们想要保存文件的路径。默认情况下,它是在我们的源任务目录。

结果,事件定义在指定的任务目录中的名为 exampleOneTasks.php 的文件中。

要查看事件是否已成功创建,我们列出事件

crunz schedule:list

+---+------------------+-------------+----------------+
| # | Task             | Expression  | Command to Run |
+---+------------------+-------------+----------------+
| 1 | Task description | 0 * * * 1 * | scripts.php    |
+---+------------------+-------------+----------------+

要查看带有所有默认值的 make:task 命令的所有选项,我们运行此

vendor/bin/crunz make:task --help

如果您需要帮助

请使用 GitHub 问题提交所有问题和疑问,我会尽力帮助您。

许可证

Crunz 是在 MIT 许可证条款下分发的免费软件。