使用PHP exec调用Node脚本并在finally方法之前将数据返回给PHP

tur*_*_34 3 javascript php node.js es6-promise

我有一个 PHP 脚本,它使用该exec函数来执行 Node 脚本并将一些数据返回到同一 PHP 脚本。

我的问题是我需要将数据返回给 PHP,而不必等待清理代码完成finally

我在下面编写了一些示例代码,向您展示代码的流程并说明我的问题。该代码示例不使用任何节点模块,但如果它们有帮助,请随意使用它们。

示例.php

$data = 'hello world';

$exec = 'node example.js ' . $data;
$escaped_command = escapeshellcmd($exec);
$data = exec($escaped_command);

// waits 10 seconds then displays returned data
// I need the code to return before finally executes
var_dump($data); // string(31) "Final data is final hello world"
Run Code Online (Sandbox Code Playgroud)

示例.js

   let data = process.argv[2];
// let data = "hello world";

async function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const doSomething = data => new Promise((resolve, reject) => {

    if (data) {
        resolve("new hello world");
    } else {
        reject();
    }
});

const doSomethingElse = newData => new Promise((resolve, reject) => {

    if (newData) {
        resolve("final hello world");
    } else {
        reject();
    }
});


(async() => {

    try {

        let newData = await doSomething(data);

        let finalData = await doSomethingElse(newData);

        // return this to server/php right now. 
        // Do not wait until finally script finishes
        console.log("Final data is " + finalData);

    } catch (err) {

        // if error return this to server/php right now. 
        // Do not wait until finally script finishes
        console.log(err);

    } finally {

        // does cleanup/closes node app
        await timeout(10000);
    }

})();
Run Code Online (Sandbox Code Playgroud)

Pet*_*ter 5

使用proc_open异步运行进程。让节点尽快输出数据。无需等待节点完成即可读取此数据:

// foo.js
process.stdout.write(JSON.stringify({"foo":"bar"})+"\n");

// simulate cleanup
setTimeout(() => {}, 3000);  


// foo.php
<?php

$io = [
    0 => ['pipe', 'r'], // node's stdin
    1 => ['pipe', 'w'], // node's stdout
    2 => ['pipe', 'w'], // node's stderr
];

$proc = proc_open('node foo.js', $io, $pipes);

$nodeStdout = $pipes[1]; // our end of node's stdout
echo date('H:i:s '), fgets($nodeStdout);

proc_close($proc);
echo date('H:i:s '), "done\n";
Run Code Online (Sandbox Code Playgroud)

示例输出:

$ php foo.php 
14:59:03 {"foo":"bar"}
14:59:06 done
Run Code Online (Sandbox Code Playgroud)

proc_close 将等待节点进程退出,但您可以推迟等待,直到完成 PHP 中的其他操作。

如果您不调用 proc_close,则可能会出现僵尸进程,具体取决于您的环境。Docker 和 PID 1 僵尸收割问题包含了很好的解释。很多时候都是指docker,因为这里是僵尸经常出现的地方,但是还是普遍适用的。

某些服务器 API 具有为客户端“隐形”运行代码的规定。例如,fcgi 有fastcgi_finish_request,您可以在 proc_close 之前调用它。这样 HTTP 客户端就不会注意到等待时间。

如果您不能容忍最终等待节点,请考虑将节点事物改为 Web 服务,它独立于 PHP 进行管理。