远程异步执行服务器脚本(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,也可以在记录日志。。

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!