远程异步执行服务器脚本(popen),脚本运行中遇到(echo)中止的错误原因?
需求:通过远程访问地址,异步执行一段服务器上的脚本
框架版本
php artisan --version
Laravel Framework 5.2.45
定义脚本
app/Console/Commands/Out.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Log;
class Out extends Command
{
protected $signature = 'laravel_out {a=0}';
protected $description = 'Out number from a.';
public function handle()
{
$a = $this->argument('a');
if (!is_numeric($a) || $a >= 10) {
Log::info('out', ["{$a} format error."]);
echo "{$a} format error.";
}
for ($i = $a; $i < 10; $i++) {
Log::info('out', ["{$i}"]);
echo $i.PHP_EOL;
}
Log::info('out', ["Out from {$a} to 9."]);
echo "Out from {$a} to 9.".PHP_EOL;
return;
}
}
定义路由
app/Http/routes.php
<?php
Route::get('/out', ['uses' => 'OutController@out']);
定义控制器
app/Http/Controllers/OutController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class OutController extends Controller
{
public function out(Request $request)
{
$a = $request->input('a', 0);
$handle = popen("/usr/local/php/bin/php /laravel/artisan laravel_out {$a}", 'r');
dump($handle);
pclose($handle);
exit('url over');
}
}
现在在服务器执行命令
php artisan laravel_out
storage/logs/laravel.log
[2017-12-19 11:36:24] laravel.INFO: out ["0"]
[2017-12-19 11:36:24] laravel.INFO: out ["1"]
[2017-12-19 11:36:24] laravel.INFO: out ["2"]
[2017-12-19 11:36:24] laravel.INFO: out ["3"]
[2017-12-19 11:36:24] laravel.INFO: out ["4"]
[2017-12-19 11:36:24] laravel.INFO: out ["5"]
[2017-12-19 11:36:24] laravel.INFO: out ["6"]
[2017-12-19 11:36:24] laravel.INFO: out ["7"]
[2017-12-19 11:36:24] laravel.INFO: out ["8"]
[2017-12-19 11:36:24] laravel.INFO: out ["9"]
[2017-12-19 11:36:24] laravel.INFO: out ["Out from 0 to 9."]
对比浏览器访问
xxx.app/out
storage/logs/laravel.log
[2017-12-19 11:46:43] ticket_five.INFO: out ["1"]
仅轮询了一次
现在有两个解决方案:
1.通过使用system()函数替用popen()和pclose(),但是达不到异步的效果。
2.通过直接打印输出
修改控制器
app/Http/Controllers/OutController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class OutController extends Controller
{
public function out(Request $request)
{
$a = $request->input('a', 0);
$handle = popen("/usr/local/php/bin/php /laravel/artisan laravel_out {$a}", 'r');
dump($handle);
// 添加
// 直接打印输出
while (!feof($handle)) {
$out = fgets($handle, 4096);
echo $out;
}
pclose($handle);
exit('url over');
}
}
通过浏览器访问,日志记录如下
[2017-12-19 13:44:36] laravel.INFO: out ["1"]
[2017-12-19 13:44:36] laravel.INFO: out ["2"]
[2017-12-19 13:44:36] laravel.INFO: out ["3"]
[2017-12-19 13:44:36] laravel.INFO: out ["4"]
[2017-12-19 13:44:36] laravel.INFO: out ["5"]
[2017-12-19 13:44:36] laravel.INFO: out ["6"]
[2017-12-19 13:44:36] laravel.INFO: out ["7"]
[2017-12-19 13:44:36] laravel.INFO: out ["8"]
[2017-12-19 13:44:36] laravel.INFO: out ["9"]
[2017-12-19 13:44:36] laravel.INFO: out ["Out from 1 to 9."]
3.重新定义PHP的标准输出,代码如下
修改脚本
app/Console/Commands/Out.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Log;
class Out extends Command
{
protected $signature = 'laravel_out {a=0}';
protected $description = 'Out number from a.';
public function handle()
{
// 添加
// 重新定义PHP的标准输出
$outPath = '/laravel/storage/logs/out.log';
fclose(STDOUT);
$GLOBALS['stdout'] = fopen($outPath, 'ab');
$a = $this->argument('a');
if (!is_numeric($a) || $a >= 10) {
Log::info('out', ["{$a} format error."]);
echo "{$a} format error.";
}
for ($i = $a; $i < 10; $i++) {
Log::info('out', ["{$i}"]);
echo $i.PHP_EOL;
}
Log::info('out', ["Out from {$a} to 9."]);
echo "Out from {$a} to 9.".PHP_EOL;
return;
}
}
通过浏览器访问,日志记录如下
[2017-12-19 13:39:16] laravel.INFO: out ["1"]
1
[2017-12-19 13:39:16] laravel.INFO: out ["2"]
2
[2017-12-19 13:39:16] laravel.INFO: out ["3"]
3
[2017-12-19 13:39:16] laravel.INFO: out ["4"]
4
[2017-12-19 13:39:16] laravel.INFO: out ["5"]
5
[2017-12-19 13:39:16] laravel.INFO: out ["6"]
6
[2017-12-19 13:39:16] laravel.INFO: out ["7"]
7
[2017-12-19 13:39:16] laravel.INFO: out ["8"]
8
[2017-12-19 13:39:16] laravel.INFO: out ["9"]
9
[2017-12-19 13:39:16] laravel.INFO: out ["Out from 1 to 9."]
Out from 1 to 9.
问题来了,第二种,第三种方案确实可以满足我的需求,但是报错的原因和解决的原理是什么呢?
对比我直接使用命令
/usr/local/php/bin/php /laravel/artisan laravel_out
既可以在页面输出echo,也可以在记录日志。。