arara / process
为Unix-like系统上的进程工作提供更好的API
Requires
- php: >=5.4.0
- ext-pcntl: *
- ext-posix: *
- phpfluent/callback: ~1.0
Requires (Dev)
- ext-uopz: *
- phpmd/phpmd: ~2.2
- phpunit/phpunit: ~4.6
- squizlabs/php_codesniffer: ~2.3
README
此库通过PHP为Unix-like系统上的进程工作提供更好的API。
安装
该包可在Packagist上找到。您可以使用Composer进行安装。
composer require arara/process
依赖关系
- PHP 5.4+
- PCNTL
- POSIX
- PHPFluent\Callback (由Composer安装)
Arara\Process的版本1.6.0或更低在PHP 5.3上工作。
用法
除了本文档外,examples/ 目录中还有许多用法示例,这些示例可能被用作参考。
本文档中的所有示例都假设您在文件开头有以下声明
declare(ticks=1);
如果没有这个声明,无法保证PHP会处理信号;这对PCNTL的正常工作非常重要。自PHP 4.3.0版本以来就要求这样做,因此这不是库的要求,而是PHP语言本身的要求。
如果您想了解更多关于ticks的信息,我们建议您阅读https://php.ac.cn/declare#control-structures.declare.ticks。
动作接口
可以使用Arara\Process\Action\Action
接口封装Fork。实现此接口的所有类必须实现两个方法
execute(..)
:可能包含在后台执行的操作trigger(...)
:可能包含针对特定事件的特定操作
使用此接口,您可以创建自己的操作并在后台运行它们。
事件
如所写,Arara\Process\Action\Action::trigger(..)
方法将特定操作与事件关联。这些事件可以是
Action::EVENT_INIT
:在操作初始化时触发- 当操作附加到子对象时
Action::EVENT_FORK
:在操作Fork时触发- 在操作Fork后,在
父
进程中触发
- 在操作Fork后,在
Action::EVENT_START
:在执行execute()方法之前触发Action::EVENT_SUCCESS
:在操作成功完成后触发,即- 当操作没有遇到PHP错误时
- 当操作没有抛出异常时
- 当操作没有返回任何值时
- 当操作返回
Action::EVENT_SUCCESS
值时
Action::EVENT_ERROR
:在操作遇到错误时触发,即- 当操作遇到PHP错误时
- 当操作返回
Action:EVENT_ERROR
值时
Action::EVENT_FAILURE
:在操作完成并失败后触发,即- 当操作抛出异常时
- 当操作返回
Action::EVENT_FAILURE
值时
Action::EVENT_TIMEOUT
:在操作遇到超时时触发Action::EVENT_FINISH
:在execute()方法执行后触发。
回调操作
为了在不创建特定类执行后台操作的情况下执行Fork,有一个通用的实现允许在后台运行回调;唯一需要做的事情是将回调传递给此类的构造函数。
use Arara\Process\Action\Callback; $callback = new Callback(function () { echo "This will be executed in the background!" . PHP_EOL; });
回调动作提供了一种将回调绑定到特定事件触发的方法
$callback->bind(Callback::EVENT_SUCCESS, function () { echo "This will be executed if the action callback was successful!" . PHP_EOL; });
此外,还可以将回调绑定到多个事件
$callback->bind(Callback::EVENT_ERROR | Callback::EVENT_FAILURE, function () { echo "It is going to be executed if the action fails or get an error" . PHP_EOL; });
命令动作
如果您只想运行Linux命令,因此存在命令动作。
$command = new Command('whoami');
使用命令动作,您可以将参数定义为第二个参数
$command = new Command('cp', array('/path/to/source', '/path/to/destination'));
如果您希望通过键 => 值数组定义参数
$command = new Command( 'find', array( '/path/to/dir', '-name' => '*', '-type' => 'f', ) );
命令动作基于回调动作,因此您也可以绑定事件触发器。
守护进程动作
您可以使用Arara\Process\Action\Daemon
类创建守护进程
$daemon = new Daemon( function (Control $control, Context $context, Daemon $daemon) { while (! $daemon->isDying()) { // Do whatever you want =) } } );
此动作将
- 使进程会话与父进程分离
- 更新进程umask
- 更新进程工作目录
- 定义进程GID(如果已定义)
- 定义进程UID(如果已定义)
- 重新创建标准文件描述符(STDIN,STDOUT和STDERR)
- 创建Pidfile
- 运行定义的负载回调
守护进程动作基于回调动作,因此您也可以绑定事件触发器。
守护进程选项
守护进程动作类有一些选项,允许您更改某些行为
name
:用于pidfile的名称(默认arara)lock_dir
:pidfile的锁目录(默认/var/run)work_dir
:工作目录(默认/)umask
:默认umask值(默认0)user_id
:当定义时更改守护进程UID(默认NULL)group_id
:当定义时更改守护进程GID(默认NULL)stdin
:用作STDIN
的文件(默认/dev/null)stdout
:用作STDOUT
的文件(默认/dev/null)stderr
:用作STDERR
的文件(默认/dev/null)
您可以通过在类构造函数中定义来更改默认的守护进程选项
$daemon = new Daemon( $callback, array( 'name' => 'mydaemonname', 'lock_dir' => __DIR__, ) );
在对象创建之后,您还可以更改所有选项
$daemon->setOptions( array( 'stdout' => '/tmp/daemon.stdout', 'stderr' => '/tmp/daemon.stderr', ) );
此外,您还可以更改单个选项
$daemon->setOption('work_dir', __DIR__);
在后台启动进程
类Arara\Process\Child
允许您在后台执行任何动作。
$child = new Child( new Daemon(function () { // What my daemon does... }), new Control() ); $child->start(); // Runs the callback in the background
上面的例子在后台运行守护进程动作,但也可以使用任何实现Arara\Process\Action\Action
接口的类,如回调动作。
检查进程是否在运行
检查进程是否在运行是一个非常常见的操作;要使用此库执行此操作,您可能需要调用
$child->isRunning(); // Returns TRUE if it is running or FALSE if it is not
此方法不仅检查对象的状态,而且还检查进程是否已经在系统上运行。
终止进程
如果进程已经启动,这将告诉进程终止,但不会强制它。
$child->terminate(); // Sends a SIGTERM to the process
杀死进程
如果它已经启动,这将强制进程立即终止。
$child->kill(); // Sends a SIGKILL to the process
等待进程
如果您想等待进程完成,而不是仅在后台启动进程,您可以调用
$child->wait();
下一行代码将在进程完成后执行。
获取进程状态
在等待进程完成后,可以获取进程的状态。类Arara\Process\Child
有一个名为getStatus()
的方法,允许您检查进程的状态。
$child->getStatus(); // Returns an Arara\Process\Control\Status instance
内部,这调用wait()
方法,以便等待进程完成 - 然后获取其状态。
获取进程的退出码
$child->getStatus()->getExitStatus();
获取导致进程停止的信号
$child->getStatus()->getStopSignal();
获取导致进程终止的信号
$child->getStatus()->getTerminateSignal();
检查状态码是否表示正常退出
$child->getStatus()->isExited();
检查状态码是否表示由于信号而终止
$child->getStatus()->isSignaled();
检查进程是否已停止
$child->getStatus()->isStopped();
检查进程是否成功完成
$child->getStatus()->isSuccessful();
孵化
由于您正在处理fork,因此您还可以使用spawn。类Arara\Process\Pool
提供了一个简单的方法来处理它。
该类动态处理进程队列,您需要做的只是在其构造函数中提供您想要的子进程数量限制,然后附加子进程。
$maxConcurrentChildren = 2; $pool = new Pool($maxConcurrentChildren); $pool->start(); $pool->attach(new Child(/* ... */)); $pool->attach(new Child(/* ... */)); $pool->attach(new Child(/* ... */)); $pool->attach(new Child(/* ... */)); // ...
它拥有的子进程数量无关紧要;它只会同时运行2个进程;当其中一个进程完成时,它将从队列中移除,并打开一个新槽。
Arara\Process\Pool
类包含大部分 Arara\Process\Child
类的方法
isRunning()
kill()
start()
terminate()
wait()
所有方法的行为都类似。
控制类
您也可以不使用池、子进程或操作类来处理进程。
我们提供了一个简单的API来与 pcntl_*
和 posix_*
函数一起工作。您可以通过阅读 Arara\Process\Control
及其依赖项的代码来了解更多信息,但这里有一个示例
$control = new Control(); $pid = $control->fork();// Throws RuntimeException when pcntl_fork() returns -1 if ($pid > 0) { echo 'Waiting on child...' . PHP_EOL; $control->waitProcessId($pid); echo 'Child finished' . PHP_EOL; $control->quit(); } echo 'Child process has PID ' . $control->info()->getId() . PHP_EOL; echo 'Child process has parent PID ' . $control->info()->getParentId() . PHP_EOL; $control->flush(2.5); // Will try to flush current process memory and sleep by 2 and a half seconds $control->signal()->send('kill'); // Will send SIGKILL to the current process (the child)
进程ID文件类
如果您正在处理后台任务,您可能想要创建一个锁来避免人们重复运行您的脚本。为此,有 Arara\Process\Pidfile
类。
$control = new Control(); $applicationName = 'my_app'; $pidfile = new Pidfile($control, $applicationName); $pidfile->initialize(); // Whatever you need here... $pidfile->finalize();
当有人第二次运行它时,将抛出异常。我们建议您将此代码放入 try..catch
语句中。