EasySwoole命令行Command分析

和阅读其他FPM框架源码一样需要从入口文件跟入. 在EasySwoole中启动方式是 php easyswoole start easyswoole其实就是php文件,只是省略了后缀

在开头定义了几个常量

// 判断是否在PHAR环境下运行, 当前返回False
defined('IN_PHAR') or define('IN_PHAR', boolval(\Phar::running(false)));
// 获取运行目录 /www/wwwroot/es_study
defined('RUNNING_ROOT') or define('RUNNING_ROOT', realpath(getcwd()));
// 获取easyswoole目录 /www/wwwroot/es_study
defined('EASYSWOOLE_ROOT') or define('EASYSWOOLE_ROOT', IN_PHAR ? \Phar::running() : realpath(getcwd()));

然后引入Composer自动加载

// 引用 Composer 自动加载
$file = EASYSWOOLE_ROOT.'/vendor/autoload.php';
if (file_exists($file)) {
    require $file;
}else{
    die("include composer autoload.php fail\n");
}

这里还有引入了一个bootstrap.php默认不存在需要自己新建,它允许用户在框架初始化之前执行自定义事件

if(file_exists(EASYSWOOLE_ROOT.'/bootstrap.php')){
    require_once EASYSWOOLE_ROOT.'/bootstrap.php';
}

最后接收Cli参数, 并弹出第一位. 只需要获取到 第二位之后的即可.

# $argv的内容
array(2) {
  [0]=>
  string(32) "/www/wwwroot/es_study/easyswoole"
  [1]=>
  string(7) "start"
}
$args = $argv;
//trim first command
array_shift($args);

当我们以 php easyswoole start 启动框架的时候,会将 cli参数 传入 CommandRunner::getInstance()->run($args);

类调用使用了 getInstance 方法,是一个单例模式. CommandRunner 也就是我们今天的主题了,继续跟入.

该类的文件在 vendor/easyswoole/easyswoole/src/Command 下. 顺便看下其他文件.可以知道DefaultCommand里放着的是命令参数.还实现了一个CommandContainer容器.

Utility类主要实现了一些辅助功能.

`easySwooleLog` 在终端打印出easySwoole的LOGO

`displayItem` 终端显示一些文本

`releaseResource` 复制文件

`opCacheClear` 清除opcache缓存和Apuc缓存

继续看我们的CommandRunner类.

class CommandRunner
{
    // 引用单例
    use Singleton;   
}

这里通过一个trait实现了一个单例的方法.

该类的构造方法注册了框架自带的命令

function __construct()
    {
        // 注册命令
        CommandContainer::getInstance()->set(new Help());
        CommandContainer::getInstance()->set(new Install());
        CommandContainer::getInstance()->set(new Start());
        CommandContainer::getInstance()->set(new Stop());
        CommandContainer::getInstance()->set(new Reload());
        CommandContainer::getInstance()->set(new PhpUnit());
        CommandContainer::getInstance()->set(new Restart());
        CommandContainer::getInstance()->set(new Config());
    }

该类还剩一个run方法

    // CommandRunner::getInstance()->run($args);
    function run(array $args):?string
    {
        // 弹出元素 `start`
        $command = array_shift($args);
        if(empty($command)){ // 如果为空就显示帮助信息
            $command = 'help';
        }else if($command != 'install'){ // 判断是否为安装模式,在composer下载完成后需要install才能正常使用
            //预先加载配置
            if(in_array('produce',$args)){
                Core::getInstance()->setIsDev(false); // 如果命令行参数中带有produce则是生产环境
            }
            Core::getInstance()->initialize(); // 初始化操作
        }
        if(!CommandContainer::getInstance()->get($command)){ // 判断是否有此命令,没有则为help帮助信息
            $command = 'help';
        }
        return CommandContainer::getInstance()->hook($command,$args); // 通过容器调用相应的类进行操作
    }
}

Core类这里是框架核心类. 我们之后在做具体介绍. 现在接着分析CommandContainer类.

container属性里面存着命令参数和对应类的实例化. CommandRunner类的构造方法里set出具体命令实例化对象.

然后通过CommandContainer::getInstance()->hook($command,$args)hook方法调用具体的类.

调用的所有命令类.需要实现CommandInterface接口.

interface CommandInterface
{
    public function commandName():string; // 命令名
    public function exec(array $args):?string ; // 操作
    public function help(array $args):?string ; // 帮助信息
}

那我们如何实现一个自定义的命令呢?

1.编一个类实现CommandInterface里的所有方法

2.在根目录的bootstrap里set出你新建的类

\EasySwoole\EasySwoole\Command\CommandContainer::getInstance()->set(new \App\Command\Test());

大功告成,你也可以使用自己的方法了. 例如php easyswoole test

框架自带的命令也不一一分析了.后面我们以start命令做深入讲解.毕竟这是我们的启动命令.