创建
入口文件
对于Yii2而言,命令行应用和网页应用基本是类似的,添加一个命令行命令的过程,和添加一个action是一样的。网页应用的入口文件是web目录下的index.php,而命令行应用的入口文件在应用根目录下的yii(windows系统对应的是yii.bat),典型的入口文件如下:
#!/usr/bin/env php run();exit($exitCode);
与web相比,有两个不同的地方值得注意,一个是定义了标准输入输出,另外一个是引入了console.php配置文件,相对的网页应用引入的是web.php。因为命令行应用和网页应用的应用场景不同,所依赖的模块和组建也会差别很大,因此用不同的配置文件,尽量减少初始化不必要的组建所带来的性能损失。
文件结构
命令行应用同样遵循MVC结构,不同的是V通常没有啦,一般只有Controller和Model。每个Controller文件可以有多个action,调用方式和网页应用类似,例如:
class ExampleController extends \yii\console\Controller{ // 调用命令 "yii example/create test", 这里的$name会被赋值为test public function actionCreate($name) { ... } // 调用命令 "yii example/index city id" $category=city,$order=id public function actionIndex($category, $order = 'name') { ... } // 调用命令 "yii example/add test", 其中,$name = ['test'] // 调用命令 "yii example/add test1,test2" 其中,$name = ['test1', 'test2'] public function actionAdd(array $name) { ... }}
####模块内命令 为了简单耦合度,比较好的选择是讲命令行程序都封装到各个模块,而不是全部放在应用根目录下的commands目录下。那么怎么让命令行的各个controller也分部到各个模块里呢?关键就在于模块类的controllerNamespace这个成员变量。网页应用的模块,这个变量通过被设置成app\模块id\controllers,因此,如果是命令行的controller,通常放到模块内的commands目录下,因此这个命名空间就需要赋值成app\模块id\commands
###调用
####同步调用 php提供了好多调用命令行程序的方式,如exec,system,popen,等等。需要调用命令行程序的适合,只要组装相应地命令就行了。如 exec("php xxx/default/index abc"),就是调用xxx模块下地default控制器的index方法,参数传的是abc ####异步调用 需要涉及到命令行程序的场景,通常是执行时间较长的任务,比如发送邮件(如果邮件服务器繁忙,常常造成同步调用的情况下等待时间过长而30秒超时),爬虫抓取,或者执行一些大批量的数据库读写操作。这种场景下,网页程序只负责激活这个命令行程序,让其继续执行,页面不等待命令行执行结束,就返回了。代码如下:
if (substr(php_uname(), 0, 7) == "Windows"){ pclose(popen("start /B ". $cmd, "r"));}else { $php = 'php'; exec($php.' '. $cmd . " > /dev/null &"); }
windows服务器和linux服务器有着不同的处理方式。 ####socket调用 如果命令行程序和网页程序不在一个服务器上,远程调用,那么将命令行程序封装成socket服务,启动之后常驻内存中,网页端通过socket消息的方式,告诉socket服务程序执行相应地命令行程序,同样这里需要使用上一节提到的异步调用方式,socket服务器充当的是一个命令调度的角色,以多进程的方式,调用不同的命令行程序。