标签归档:命令行

Symfony组件 之 Console

安装:

mkdir symfony-console
cd symfony-console
composer require symfony/console

创建一个命令行应用(命令在命令行应用中运行):

#新建命令行应用
vi console

#!/usr/bin/env php
<?php
if (file_exists(__DIR__.'/vendor/autoload.php')) {
    require __DIR__.'/vendor/autoload.php';
} else {
    require __DIR__.'/../../autoload.php';
}

$app = new Symfony\Component\Console\Application('Ifeeline Console', '1.0.0');

$app->run();

#添加权限
chmod +x console
#运行
./console
#运行结果
Console 1.0.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help  Displays help for a command
  list  Lists commands

从输出的结果可知,用法是commad [options] [arguments]。比如直接运行./console,那就是没有直接options和arguments。这里默认有两个命令(help和list), 直接运行命令行应用时,看起来是调用list命令。

在命令行中添加命令:

# 新建src目录,存放命令
mkdir src
cd src
vi TestCommand.php

<?php

namespace Ifeeline\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class TestCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('test')
            ->setDescription('Test')
            ->addArgument(
                'arg',
                InputArgument::OPTIONAL,
                'Test Command argument: arg'
            )
            ->addOption(
                'opt',
                null,
                InputOption::VALUE_NONE,
                'Test Command option: opt'
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $arg = $input->getArgument('arg');
        if ($arg) {
            $text = 'Test Command argument -> ' . $arg;
        } else {
            $text = 'Test Command';
        }

        if ($input->getOption('opt')) {
            $text = strtoupper($text);
        }

        $output->writeln($text);
    }
}

# 编辑composer.json文件,让命令类可以自动加载
{
    "require": {
        "symfony/console": "^4.0"
    },
    "autoload": {
        "psr-4": {
            "Ifeeline\\Console\\": "src"
        }
    }
}

# 最后composer update
composer update

添加命令到命令行应用:

#!/usr/bin/env php
<?php
if (file_exists(__DIR__.'/vendor/autoload.php')) {
    require __DIR__.'/vendor/autoload.php';
} else {
    require __DIR__.'/../../autoload.php';
}

$app = new Symfony\Component\Console\Application('Console', '1.0.0');

$app->add(new Ifeeline\Console\TestCommand);

$app->run();

运行:

./console test ifeeline
ifeeline

./console test --opt ifeeline
IFEELINE

Symfony的命令行工具包符合http://docopt.org/描述的规范。

总结来说,一个命令需要实现两个方法。一个配置命令的参数与选项,一个是命令执行主体。命令的输入与输出可用$input和$output控制。

分清楚Argument和Option:

Arguments使用空格分隔,跟在命令名称之后。它们是有顺序的,可以是可选(OPTIONAL)或者必填(REQUIRED)或者是数组(IS_ARRAY)。如果参数类型是IS_ARRAY类型,说明它可以接收多个值,由于参数是有顺序的,所以这个参数应该是最后一个定义的参数,因为参数本身是用空格分隔的

与参数不同,选项是没有顺序的(意味着你可以随意指定)并以为两个横杆开始(可以配置以一个横杆开头)。选项总是可选的,可以设置它接受一个值或没有值(让其简单作为一个布尔值,指定就是true,否则就是false)。

Option Value
InputOption::VALUE_IS_ARRAY This option accepts multiple values (e.g. --dir=/foo --dir=/bar)
InputOption::VALUE_NONE Do not accept input for this option (e.g. --yell)
InputOption::VALUE_REQUIRED This value is required (e.g. --iterations=5), the option itself is still optional
InputOption::VALUE_OPTIONAL This option may or may not have a value (e.g. --yell or --yell=loud)

在命令中运行命令:

protected function execute(InputInterface $input, OutputInterface $output)
{
    $command = $this->getApplication()->find('demo');

    $arguments = array(
        'command' => 'demo',
        'arg'    => 'ifeeline',
        '--opt'  => true,
    );

    $input = new ArrayInput($arguments);
    $returnCode = $command->run($input, $output);

    // ...
}

更多内容可参考官方文档:http://symfony.com/doc/current/components/console.html

Laravel – Redis实例

<?php
 
namespace eBay\Console\Commands;
 
use Illuminate\Console\Command;
use DB;
use Schema;
 
class TitleRepeat extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'title:repeat {--list=}';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '';
 
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
 
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $config = app('config');
        
        // Redis扩展不存在
        if (!extension_loaded('redis')) {
            echo "Redis do not exists";
            return;
        }
        
        // 链接Redis
        $redis = new \Redis();
        $redisServer = $config->get('app.redisServer');
        $redisPort = $config->get('app.redisPort');
        
        if(!$redis->connect($redisServer, $redisPort)) {
            echo 'Connect Redis Host:'.$redisServer." Port:".$redisPort." Fail";
            return;
        }
        
        // 单个  或  全量
        $pids = $this->option('list');
        if(!empty($pids)) {
            $pids = explode(',', preg_replace('/\s+/', '', $pids));
            $mainSkus = DB::table("ebay_item_master")->whereIn('id',$pids)->get();
        } else {
            $mainSkus = DB::table('ebay_item_master')->where('if_primary','>',0)
                ->select(['sku','title1','title2','title3','title4','title5','title6'])->get();
        }
        if(count($mainSkus) < 1){
            echo 'No Products...';
            return;
        }
        
        foreach($mainSkus as $mainSku){
            $sku = trim($mainSku->sku);
             
            for($i = 1; $i <= 6; $i++) {
                $title = 'title'.$i;
                // 不理会空的
                if(empty($mainSku->$title)){ continue; }
                 
                // 计算哈希
                $titleValueHash = $this->getStringHash($mainSku->$title);
                $hashHold = $sku.'_'.$title;
                
                $key = strtolower($sku."_".$title);
                if($redis->exists($key)) {
                    $keyValue = $redis->get($key);

                    // 不相等 换了标题
                    if($keyValue !== $titleValueHash) {
                        $oldKey = 'sku_hash_'.$keyValue;
                        $newKey = 'sku_hash_'.$titleValueHash;
                         
                        // 需要更新 或 删除
                        if($redis->exists($oldKey)) {
                            $oldKeyValue = $redis->get($oldKey);
                            // 只包含一个值
                            if(empty($oldKeyValue) || ($oldKeyValue === $hashHold)){
                                $redis->delete($oldKey);
                            } else {
                                $nowNewValue = str_replace([$hashHold.',',','.$hashHold],'',$oldKeyValue);
                                if(!empty($nowNewValue)) {
                                    $redis->set($oldKey,$nowNewValue);
                                } else {
                                    $redis->delete($oldKey);
                                }
                            }
                        }
                         
                        // 需要更新 或 添加
                        if($redis->exists($newKey)) {
                            $newKeyValue = $redis->get($newKey);
                            // 只包含一个值
                            if(empty($newKeyValue)){
                                $redis->set($newKey,$hashHold);
                            } else {
                                if($newKeyValue !== $hashHold){
                                    $newHashHold = str_replace([$hashHold.',', ','.$hashHold],'',$newKeyValue);
                                    if(!empty($newHashHold)) {
                                        $redis->set($newKey,$newHashHold.','.$hashHold);
                                    } else {
                                        $redis->set($newKey,$hashHold);
                                    }
                                }
                            }
                        } else {
                            $redis->set($newKey,$hashHold);
                        }
                        // 更新
                        $redis->set($key,$titleValueHash);
                    } else {
                        // 新旧Key对应的值相等,判断值对应的Key是否存在
                        $noExistKey = 'sku_hash_'.$keyValue;
                        if(!$redis->exists($noExistKey)){
                            $redis->set($noExistKey,$hashHold);
                        }
                    }
                } else {
                    $newKey = 'sku_hash_'.$titleValueHash;
                    if($redis->exists($newKey)) {
                        $newKeyValue = $redis->get($newKey);
                        // 只包含一个值
                        if(empty($newKeyValue)){
                            $redis->set($newKey,$hashHold);
                        } else {
                            if($newKeyValue !== $hashHold){
                                $newHashHold = str_replace([$hashHold.',', ','.$hashHold],'',$newKeyValue);
                                if(!empty($newHashHold)) {
                                    $redis->set($newKey,$newHashHold.','.$hashHold);
                                } else {
                                    $redis->set($newKey,$hashHold);
                                }
                            }
                        }
                    } else {
                        $redis->set($newKey,$hashHold);
                    }
                    
                    $redis->set($key,$titleValueHash);
                }
            } 
        }
         
    }
     
    public function getStringHash($str) {
        $titleValue = preg_split('/\s+/',trim($str));
        sort($titleValue);
        $titleValueHash = md5(implode('',$titleValue));
         
        return $titleValueHash;
    }
    
}

Laravel 命令行工具实例

大数据导入

<?php
 
namespace eBay\Console\Commands;
 
use Illuminate\Console\Command;
use DB;
use Schema;
 
class AmazonImport extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'amazon:import';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Import Amazon Data';
 
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
 
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
        $import = DB::table('amazon_imports')->where("has_import",'<',1)->orderBy('date')->first();
        if(!isset($import->id)) { return; }
         
        // 表名
        $tableName = 'amazon_import_'.$import->date;
        // 删表,如果存在
        Schema::dropIfExists($tableName);
        // 建表
        $this->builderTable($tableName);
         
        if (Schema::hasTable($tableName)){
            $s = microtime(true);
             
            // 插入数据
            $file = 'D:/11-201.txt';
            $row = 0; $dd = [];
            if (($handle = fopen($file, "r")) !== FALSE) {
                while (($data = fgetcsv($handle, 0, "\t")) !== FALSE) {
                    $row++;
                    if($row < 2) { continue; }
                    $d = [ 
                        'marketplace_id' => $data [0],
                        'gl_product_group' => $data [1],
                        'gl_product_group_desc' => $data [2],
                        'category_desc' => $data [3],
                        'subcategory_desc' => $data [4],
                        'asin' => $data [5],
                        'item_name' => $data [6],
                        'brand_name' => $data [7],
                        'ordered_gms_usd_tim' => $data [8],
                        'ordered_units_tim' => $data [9],
                        'fba_ordered_gms_usd_tim' => $data [10],
                        
                        'fba_ordered_units_tim' => $data [11],
                        'asp' => round($data [12],15),
                        
                        'gms_rank' => $data [13],
                        'gms_view_count_tim' => $data [14],
                        'units_conversion_rate' => round($data [15],15)
                    ];
                    
                    //if($row > 5000) { break; }
                    //continue;
                    
                    $dd [] = $d;
                     
                    // 合理调整,MySQL对于批量数据大小有限制
                    if (count ( $dd ) > 1999) {
                        DB::table ( $tableName )->insert ( $dd );
                        $dd = [ ];
                    }
                }
                if(!empty($dd)) {
                    DB::table ( $tableName )->insert ( $dd );
                    $dd = [ ];
                }
                $e = microtime(true);
                echo 'Total:'.(float)($e - $s)."\n";
                 
                fclose($handle);
                 
                // 
                DB::table('amazon_imports')->where('id',$import->id)->update(['has_import'=>1]);
            }
            
            // 插表完成后,更新类目
            DB::table('amazon_categories')->truncate();            
            $prefix = DB::connection()->getTablePrefix();  
            $query = "INSERT INTO ".$prefix."amazon_categories(product_group, product_group_desc, category_desc,subcategory_desc)
                SELECT gl_product_group, gl_product_group_desc, category_desc, subcategory_desc FROM `".$prefix.$tableName."` GROUP BY gl_product_group_desc,category_desc,subcategory_desc";  
            DB::statement($query);
             
            // 插表完成后,产生比对数据
            $compares = DB::table('amazon_imports')
                ->where("has_import",'>',0)
                ->where('id','!=',$import->id)
                ->orderBy('date','desc')->take(4)->get();
             
            $this->call('amazon:caculate', [
                'from' => $import->date
            ]);
        } else {
            return;
        }
    }
     
    public function builderTable($tableName)
    {
        Schema::create($tableName, function($table)
        {
            $table->increments('id');
             
            //
            $table->tinyInteger('marketplace_id')->unsigned();
            $table->smallInteger('gl_product_group')->unsigned();
            $table->string('gl_product_group_desc',32);
            $table->string('category_desc',64);
            $table->string('subcategory_desc',64);
            $table->string('asin',16);
            $table->string('item_name',128);
            $table->string('brand_name',64);
            $table->decimal('ordered_gms_usd_tim',10,2);
            $table->decimal('fba_ordered_gms_usd_tim',10,2);
            $table->smallInteger('ordered_units_tim');
            $table->smallInteger('fba_ordered_units_tim');
            $table->double('asp',20,15);
            $table->smallInteger('gms_rank');
            $table->mediumInteger('gms_view_count_tim');
            $table->double('units_conversion_rate',20,15);
            
            $table->index('asin');
        });
    }
}

执行批量计算

<?php
 
namespace eBay\Console\Commands;
 
use Illuminate\Console\Command;
use DB;
use Schema;
 
class AmazonCaculate extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'amazon:caculate {from}';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Caculate Amazon Data';
 
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
 
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // 取回参数
        $date = $this->argument('from');
        if(!empty($date)) {
            $has = DB::table("amazon_imports")->where('date',$date)->where('has_import','>',0)->first();
            if(!isset($has->id)) {
                echo 'Import Date:'.$date." Do Not Import.\n";
                return;
            }
        }
        
        // 取回需要比较的日期,当前记录的前4个
        $compares = DB::table('amazon_imports')
            ->where("has_import",'>',0)
            ->where('id','<', $has->id)
            ->orderBy('date','desc')->take(4)->get();
        $nowTable = "amazon_import_".$has->date;
        
        // 原表不存在,就不要比较了
        if (!Schema::hasTable($nowTable)){
            echo "Table ".$nowTable." DO NOT Existes\n";
            return;
        }

        foreach($compares as $compare) {
            $beforeTable = "amazon_import_".$compare->date;
            // 被比较的表不存在,就到此结束,后面的咔嚓
            if (!Schema::hasTable($beforeTable)){
                break;
            }
            
            $tableName = 'amazon_'.$has->date."_".$compare->date;
            // 如果结果表已经存在,不需要重复
            if (!Schema::hasTable($tableName)){
                // 删表,如果存在
                // Schema::dropIfExists($tableName);
                // 建表
                $this->builderTable($tableName);
            
                // 批量插入
                $prefix = DB::connection()->getTablePrefix();
                $query = "INSERT INTO `".$prefix.$tableName."`
                    (marketplace_id,
                    gl_product_group,
                    gl_product_group_desc,
                    category_desc,
                    subcategory_desc,
                    asin,
                    item_name,
                    brand_name,
                    
                    ordered_gms_usd_tim,
                    ordered_gms_usd_tim_diff,
                    
                    fba_ordered_gms_usd_tim,
                    fba_ordered_gms_usd_tim_diff,
                    
                    ordered_units_tim,
                    ordered_units_tim_diff,
                    
                    fba_ordered_units_tim,
                    fba_ordered_units_tim_diff,
                    
                    asp,
                    asp_diff,
                    
                    gms_rank,
                    gms_rank_diff,
                    
                    gms_view_count_tim,
                    gms_view_count_tim_diff,
                    
                    units_conversion_rate,
                    
                    is_new)
                SELECT 
                    n.marketplace_id, 
                    n.gl_product_group, 
                    n.gl_product_group_desc, 
                    n.category_desc,
                    n.subcategory_desc,
                    n.asin,
                    n.item_name,
                    n.brand_name,
                    n.ordered_gms_usd_tim,
                    (n.ordered_gms_usd_tim - b.ordered_gms_usd_tim) AS ordered_gms_usd_tim_diff,
                    
                    n.fba_ordered_gms_usd_tim,
                    (n.fba_ordered_gms_usd_tim - b.fba_ordered_gms_usd_tim) AS fba_ordered_gms_usd_tim_diff,
                    
                    n.ordered_units_tim,
                    (n.ordered_units_tim - b.ordered_units_tim) AS ordered_units_tim_diff,
                    
                    n.fba_ordered_units_tim,
                    (n.fba_ordered_units_tim - b.fba_ordered_units_tim) AS fba_ordered_units_tim_diff,
                    
                    n.asp,
                    (n.asp - b.asp) AS asp_diff,
                    
                    n.gms_rank,
                    (n.gms_rank - b.gms_rank) AS gms_rank_diff,
                    
                    n.gms_view_count_tim,
                    (n.gms_view_count_tim - b.gms_view_count_tim) AS gms_view_count_tim_diff,
                    
                    n.units_conversion_rate,
                    
                    IF(b.id IS NULL,1,0)
                FROM `".$prefix.$nowTable."` n LEFT JOIN `".$prefix.$beforeTable."` b ON n.asin = b.asin";
                
                DB::statement($query);
            } 
        }
    }
     
    public function builderTable($tableName)
    {
        Schema::create($tableName, function($table)
        {
            $table->increments('id');
             
            //
            $table->tinyInteger('marketplace_id')->unsigned();
            $table->smallInteger('gl_product_group')->unsigned();
            $table->string('gl_product_group_desc',32);
            $table->string('category_desc',64);
            $table->string('subcategory_desc',64);
            $table->string('asin',16);
            $table->string('item_name',128);
            $table->string('brand_name',64);
            $table->decimal('ordered_gms_usd_tim',10,2);
            $table->decimal('ordered_gms_usd_tim_diff',10,2);
            
            $table->decimal('fba_ordered_gms_usd_tim',10,2);
            $table->decimal('fba_ordered_gms_usd_tim_diff',10,2);
            
            $table->integer('ordered_units_tim');
            $table->integer('ordered_units_tim_diff');
            
            $table->integer('fba_ordered_units_tim');
            $table->integer('fba_ordered_units_tim_diff');
            
            $table->double('asp',20,15);
            $table->double('asp_diff',20,15);
            
            $table->integer('gms_rank')->unsigned();
            //$table->smallInteger('gms_rank_before')->unsigned();
            $table->integer('gms_rank_diff');
            
            $table->integer('gms_view_count_tim');
            $table->integer('gms_view_count_tim_diff');
            
            $table->double('units_conversion_rate',20,15);
            $table->tinyInteger('is_new')->default(0);
            
            $table->index('asin','asin_idx');
            
            //$table->index('gl_product_group_desc');
            //$table->index('category_desc');
            //$table->index('subcategory_desc');
            $table->index(['gl_product_group_desc','category_desc','subcategory_desc'],'category_idx');
        });
    }
}

命令行工具是非常便利的,数据库的操作相比之下也省了不少功夫。

Laravel 命令行工具使用

#查看所有的可用命令
php artisan list

#命令前可以加help显示用法详情
php artisan help migrate

#自定义命令(生成一个类app/Console/Commands/SendEmails.php)
#--command指定一个命令行可用的命令
php artisan make:console SendEmail --command=email:send

#然后在app/Console/Kernel.php的$commands中注册命令
protected $commands = [
        \App\Console\Commands\Inspire::class,
        \App\Console\Commands\SendEmails::class,
    ];

#之后可以这样调用
php artisan email:send

命令的结构:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SendEmails extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'email:send';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
    }
}

每个命令都继承自Illuminate\Console\Command,这个类又是继承Symfony\Component\Console\Command\Command,虽然是重用了这个东西,但是Laravel做很多扩展(可以看看源代码)。

这里的$signature就是命令的名称,就是在make:console时通过–command指定的命令名称,自然,如果没有通过–command指定,这里就要修改一下,在artisan list时,就可以列出来,当然也就可以直接使用了。变量$description就不解释。然后是handle()方法,它需要使用的依赖,可以通过构造函数带入。

文档建议,命令应该保持功能单一性。

命令行程序,往往需要接收参数:

/**
 * 控制台命令名称
 *
 * @var string
 */
protected $signature = 'email:send {user}';

// 选项参数...
email:send {user?}
// 带默认值的选项参数...
email:send {user=foo}

/**
 * 控制台命令名称
 *
 * @var string
 */
//--queue开关在调用Artisan命令的时候被指定。
//如果--queue开关被传递,其值是true,否则其值是false:
protected $signature = 'email:send {user} {--queue}';

// 等号
protected $signature = 'email:send {user} {--queue=}';
// 然后可以这样使用
php artisan email:send 1 --queue=default
// 可以给选项分配默认值
email:send {user} {--queue=default}

// 选项的描述
protected $signature = 'email:send
    {user : The ID of the user}
    {--queue= : Whether the job should be queued}';

//这个需要一个例子展示
php artisan email:send -h
Usage:
  email:send [options] [--] <user>

Arguments:
  user                  The ID of the user *********

Options:
      --queue[=QUEUE]   Whether the job should be queued *********
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under.
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output,
 2 for more verbose output and 3 for debug

Help:
 Command description.

命令中选项参数的获取:

// argument方法直接读取参数(user就是定义时指定的那个user)
public function handle(){
    $userId = $this->argument('user');
}
// 数组形式返回
$arguments = $this->argument();

// 获取指定选项...
$queueName = $this->option('queue');
// 获取所有选项...
$options = $this->option();

如果参数或选项不存在,返回null。

命令行交互:

// 询问输入
public function handle(){
    $name = $this->ask('What is your name?');
}

// 不回显输入
$password = $this->secret('What is the password?');

// 让用户确认,confirm默认返回false
if ($this->confirm('Do you wish to continue? [y|N]')) {
    //
}

// 提供选项
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], false);

控制台输出:

// 通常绿色显示
public function handle(){
    $this->info('Display this on the screen');
}

// 通常红色显示
$this->error('Something went wrong!');

// 表格
$headers = ['Name', 'Email'];
$users = App\User::all(['name', 'email'])->toArray();
$this->table($headers, $users);

// 进度条
$users = [1,2,3,4,5,6];
$this->output->progressStart(count($users));

foreach ($users as $user) {
    $this->output->progressAdvance();
    sleep(1);
}

$this->output->progressFinish();

大体上,命令行该有的,都有了。

最后的内容是关于外部如何调用命令:

//在我们的框架中,存在一个Artisan的facade
Route::get('/foo', function () {
    $exitCode = Artisan::call('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);
});

// 放入队列 Artisan::queue
Route::get('/foo', function () {
    Artisan::queue('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);
});

// 在命令中调用命令
public function handle(){
    $this->call('email:send', [
        'user' => 1, '--queue' => 'default'
    ]);
}

// 静音
$this->callSilent('email:send', [
    'user' => 1, '--queue' => 'default'
]);

以上的方法,看起来可能是在两一个PHP进程中执行。实际它不是,它在同一个PHP进程中(已经载入框架),然后载入命令类,执行这个命令而已。