php popen 和 pclose实现多进程运行

1,089 阅读1分钟

popen pclose 概念

  • popen(command,mode) 打开一个指向进程的管道,该进程由派生指定的 command 命令执行而产生
    • command 必需。规定要执行的命令。
    • mode 必需。规定连接模式 , 可能的值:
      • r: 只读。
      • w: 只写 (打开并清空已有文件或创建一个新文件)
  • pclose(pipe) 关闭由 popen() 打开的管道
    • pipe 必需。规定由 popen() 打开的管道。

创建类

<?php

/**
 * MultiProgress 多进程运行类
 * @author Terrence
 */
class MProgress
{
    /** error_msg */
    const ERROR_MSG = array('status' => 'error', 'message' => 'popen is error');

    public function __construct() {
        /** 初始化进程池 */
        $this->pids = array();
    }

    /**
     * set 在进程池中进行任务设置
     * @author Terrence
     * @param any 进程名字
     * @param string task任务路径
     */
    public function set($taskName = '', $taskPath){
        if(empty($taskName)){
            $this->pids[] = popen($taskPath,'r');
        }else{
            $this->pids[$taskName] = popen($taskPath,'r');
        }
    }

    /**
     * get 获取进程执行结果
     * @author Terrence
     * @param any $taskName 进程名字
     * @param boolean $isJson 返回的结果是否进行解json操作
     * @return array 执行结果
     */
    public function get($taskName , $isJson = false){
        try {
            // 读取进程的执行结果 (读取进程中 echo 出来的信息)
            $result = fgets($this->pids[$taskName]);
            if( $isJson ){
                try {
                    $result = json_decode($result,true);
                } catch (Exception $th) {
                    $result = [];
                }
            }
        } catch (Exception $th) {
            $result = $isJson ? [] : '' ;
        }
        // 杀死进程
        pclose($this->pids[$taskName]);
        unset($this->pids[$taskName]);
        return $result;
    }

    /**
     * getPids 获取当前的进程池里的进程名称
     * @author Terrence
     */
    public function getPids(){
        return array_keys($this->pids);
    }

    /**
     * clear 清空进程池
     * @author Terrence
     */
    public function clear(){
        foreach ($this->pids as &$pid) {
            try {
                $single = fgets($pid);
                pclose($pid);
            } catch (Exception $th) { }
        }
        $this->pids = [];
    }

}

使用

    /** 创建进程池 */
    $pids = new MProgress();
    
    /** 在进程池中创建进程任务 (进程名字为空,以数组下标的方式直接push到进程池) */
    /** $taskName 是要执行的任务,一般为命令行操作 */
    $pids->set('', $taskName );
     
    /** 遍历进程结果 $result 是进程池的运行结果集,是一个 Array */
    $result = array();
    foreach (array_reverse( $pids->getPids() ) as $pid) {
        $single = $pids->get($pid, true);
        /**将结果进行合并 */
        if(!empty($single )) $result = array_merge($result, $single);
    }
    
    /** 关闭进程 */
    $pids->clear();