/**
     * 创建所需的 proc_open 的描述符
     * @return array
     */
    private function getDescriptors()
    {// 获取描述符
        if ('\\' === DS) {// 如果是windows
            $this->processPipes = WindowsPipes::create($this, $this->input);// 创建 windows 管道系统
        } else {
            $this->processPipes = UnixPipes::create($this, $this->input);// 创建 unix 管道系统
        }
        $descriptors = $this->processPipes->getDescriptors($this->outputDisabled);// 获取描述信息

        if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {

            $descriptors = array_merge($descriptors, [['pipe', 'w']]);// 如果满足上述 的全部条件

            $this->commandline = '(' . $this->commandline . ') 3>/dev/null; code=$?; echo $code >&3; exit $code';
        }// 进行 命令行设置

        return $descriptors;// 返回描述,并且 执行 命令行工具
    }

    /**
     * 建立 wait () 使用的回调。
     * @param callable|null $callback
     * @return callable
     */
    protected function buildCallback($callback)
    {// wait 使用回调 建立 回调函数
        $out      = self::OUT;// 输出
        $callback = function ($type, $data) use ($callback, $out) {// 回调函数
            // 闭包 用法
            // 很经典的一个用法
            if ($out == $type) {// 如果 可以输出
                $this->addOutput($data);
            } else {// 否则添加错误数据
                $this->addErrorOutput($data);
            }

            if (null !== $callback) {// 回调
                call_user_func($callback, $type, $data);
            }
        };

        return $callback;// 返回一个回调函数
    }

    /**
     * 更新状态
     * @param bool $blocking
     */
    protected function updateStatus($blocking)
    {// 更新状态
        if (self::STATUS_STARTED !== $this->status) {// 当前状态不是启动状态 工作状态
            return;
        }// 如果状态不对,直接返回

        $this->processInformation = proc_get_status($this->process);// 获取进程状态信息
        $this->captureExitCode();// 获取执行代码

        $this->readPipes($blocking, '\\' === DS ? !$this->processInformation['running'] : true);// 读取管道信息

        if (!$this->processInformation['running']) {//如果当前信息 没有运行中
            $this->close();// 关闭
        }
    }// 在经历了3次外包之后,基本上就 没有 那个东西了是可以被看见的了

    /**
     * 是否开启 '--enable-sigchild'
     * @return bool
     */
    protected function isSigchildEnabled()
    {
        if (null !== self::$sigchild) {// 返回默认状态
            return self::$sigchild;
        }

        if (!function_exists('phpinfo')) {// 如果没有 phpinfo 这个函数
            return self::$sigchild = false;
        }

        ob_start();// 开启缓存
        phpinfo(INFO_GENERAL);// 信息 启动

        return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');// 很神奇的判断方式
    }

    /**
     * 验证是否超时
     * @param int|float|null $timeout
     * @return float|null
     */
    private function validateTimeout($timeout)
    {// 验证是否超时
        $timeout = (float)$timeout;// 设置时间

        if (0.0 === $timeout) {// 如果 为0
            $timeout = null;// 时间为空
        } elseif ($timeout < 0) {// 如果时间 小于0 异常
            throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
        }

        return $timeout;
    }

    /**
     * 读取pipes
     * @param bool $blocking
     * @param bool $close
     */
    private function readPipes($blocking, $close)
    {
        $result = $this->processPipes->readAndWrite($blocking, $close);// 读写数据

        $callback = $this->callback;// 回调
        foreach ($result as $type => $data) {// 对返回数据进行 处理
            if (3 == $type) {// 数据类型,返回错误代码
                $this->fallbackExitcode = (int)$data;
            } else {
                $callback($type === self::STDOUT ? self::OUT : self::ERR, $data);
            }
        }
    }

    /**
     * 捕获退出码
     */
    private function captureExitCode()
    {// 捕获退出码
        if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
            $this->exitcode = $this->processInformation['exitcode'];
        }
    }// 将系统运行命令的退出码,放到系统配置项里面

    /**
     * 关闭资源
     * @return int 退出码
     */
    private function close()
    {// 执行 资源 关闭
        $this->processPipes->close();// 关闭资源
        if (is_resource($this->process)) {// 是否有资源
            $exitcode = proc_close($this->process);// 执行进程关闭函数
        } else {
            $exitcode = -1;// 否则 退出码为1
        }

        $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
        $this->status   = self::STATUS_TERMINATED;
        // 退出码
        // 当前状态

        if (-1 === $this->exitcode && null !== $this->fallbackExitcode) {
            $this->exitcode = $this->fallbackExitcode;
        } elseif (-1 === $this->exitcode && $this->processInformation['signaled']
                  && 0 < $this->processInformation['termsig']
        ) {
            $this->exitcode = 128 + $this->processInformation['termsig'];
        }// 是否正常终止,还是异常中断

        return $this->exitcode;// 返回退出码
    }

    /**
     * 重置数据
     */
    private function resetProcessData()
    {// 重置全部数据
        $this->starttime                    = null;
        $this->callback                     = null;
        $this->exitcode                     = null;
        $this->fallbackExitcode             = null;
        $this->processInformation           = null;
        $this->stdout                       = null;
        $this->stderr                       = null;
        $this->process                      = null;
        $this->latestSignal                 = null;
        $this->status                       = self::STATUS_READY;
        $this->incrementalOutputOffset      = 0;
        $this->incrementalErrorOutputOffset = 0;
    }

    /**
     * 将一个 POSIX 信号发送到进程中。
     * @param int  $signal
     * @param bool $throwException
     * @return bool
     */
    private function doSignal($signal, $throwException)
    {// 信号发送到进程中
        if (!$this->isRunning()) {
            if ($throwException) {// 抛出异常

                throw new \LogicException('Can not send signal on a non running process.');
            }

            return false;
        }

        if ($this->isSigchildEnabled()) {
            if ($throwException) {
                throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
            }// 是否 子进程

            return false;
        }

        if (true !== @proc_terminate($this->process, $signal)) {
            if ($throwException) {
                throw new \RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
            }// 进程 中断

            return false;
        }

        $this->latestSignal = $signal;
// 最终 信号
        return true;// 返回值
    }

    /**
     * 确保进程已经开启
     * @param string $functionName
     */
    private function requireProcessIsStarted($functionName)
    {// 确认进程开启
        if (!$this->isStarted()) {
            throw new \LogicException(sprintf('Process must be started before calling %s.', $functionName));
        }
    }

    /**
     * 确保进程已经终止
     * @param string $functionName
     */
    private function requireProcessIsTerminated($functionName)
    {// 确认进程关闭
        if (!$this->isTerminated()) {
            throw new \LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
        }
    }
}