自定义 Laravel 的默认 Artisan 命令
Laravel 自带功能强大的Artisan命令行 , 可以给开发带来很多的便利 . 但有一些命令行的命令功能太强大 , 例如migration , 可以直接销毁数据库 . 尽管默认了运行环境( 在生产环境下只有使用 '--force' 才能运行 ) , 仍然会带来意想不到的安全隐患 .
希望对项目的控制权更强一些 , 自然想要有一个可以自己配置的 artisan 命令行 . 这该怎么做呢 ?
为了解决这个问题 , 首先花时间弄明白了 laravel 命令是如何加载的 .
以Migration为例 . 所有的migration相关命令 , 其实是由 Illuminate\Database\MigrationServiceProvider
加载的 . 加载代码如下:
/**
* Register all of the migration commands.
*
* @return void
*/
protected function registerCommands()
{
$commands = ['Migrate', 'Rollback', 'Reset', 'Refresh', 'Install', 'Make', 'Status'];
// We'll simply spin through the list of commands that are migration related
// and register each one of them with an application container. They will
// be resolved in the Artisan start file and registered on the console.
foreach ($commands as $command) {
$this->{'register'.$command.'Command'}();
}
// Once the commands are registered in the application IoC container we will
// register them with the Artisan start event so that these are available
// when the Artisan application actually starts up and is getting used.
$this->commands(
'command.migrate', 'command.migrate.make',
'command.migrate.install', 'command.migrate.rollback',
'command.migrate.reset', 'command.migrate.refresh',
'command.migrate.status'
);
}
/**
* Register the package's custom Artisan commands.
*
* @param array|mixed $commands
* @return void
*/
public function commands($commands)
{
$commands = is_array($commands) ? $commands : func_get_args();
// To register the commands with Artisan, we will grab each of the arguments
// passed into the method and listen for Artisan "start" event which will
// give us the Artisan console instance which we will give commands to.
$events = $this->app['events'];
$events->listen(ArtisanStarting::class, function ($event) use ($commands) {
$event->artisan->resolveCommands($commands);
});
}
/**
* Register the "migrate" migration command.
*
* @return void
*/
protected function registerMigrateCommand()
{
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator']);
});
}
简单来说 , laravel 加载命令行的流程有这么几步:
- bootstrap/app.php中将IoC容器初始化 , artisan调用App\Console\Kernerl
Illuminate\Foundation\Console\Kernel
启动时自动加载artisan相关的provider- 命令行相关的provider,为所有预加载的命令行创建单例, 例如
registerMigrateCommand()
- 命令行相关的provider,会把注册命令的闭包作为监听者 , 监听
Events\ArtisanStarting
事件 - laravel 执行artisan的时候 ,IoC容器会自动生成
Illuminate\Console\Application
单例 , 用来执行命令 - 实例化
Illuminate\Console\Application
时会触发事件$events->fire(new Events\ArtisanStarting($this));
- 触发事件后 ,
Illuminate\Console\Application
先通过监听者加载所有命令 , 然后再执行命令
看源代码 , Illuminate\Console\Application
只能加载命令行 , 不能移除已经加载的命令行 . 所以要修改laravel默认的命令行 , 只能通过Provider加载的环节进行修改 .
那命令行相关的provider是怎么加载的呢? 通过代码追踪后可见 , 关键在于config/app.php
中默认加载的provider , 其中之一 Illuminate\Foundation\Providers\ConsoleSupportServiceProvider
. 打开来会发现有个数组:
/**
* The provider class names.
*
* @var array
*/
protected $providers = [
'Illuminate\Foundation\Providers\ArtisanServiceProvider',
'Illuminate\Console\ScheduleServiceProvider',
'Illuminate\Database\MigrationServiceProvider',
'Illuminate\Database\SeedServiceProvider',
'Illuminate\Foundation\Providers\ComposerServiceProvider',
'Illuminate\Queue\ConsoleServiceProvider',
];
显然这个数组负责加载所有命令行相关的Provider . 在这个数组里添加或者移除Provider , 就能添加或删除一些默认的命令行.
再进一步看Illuminate\Foundation\Providers\ArtisanServiceProvider
, 有这样的属性:
/**
* The commands to be registered.
*
* @var array
*/
protected $commands = [
'ClearCompiled' => 'command.clear-compiled',
'ClearResets' => 'command.auth.resets.clear',
'ConfigCache' => 'command.config.cache',
'ConfigClear' => 'command.config.clear',
'Down' => 'command.down',
'Environment' => 'command.environment',
'KeyGenerate' => 'command.key.generate',
'Optimize' => 'command.optimize',
'RouteCache' => 'command.route.cache',
'RouteClear' => 'command.route.clear',
'RouteList' => 'command.route.list',
'Tinker' => 'command.tinker',
'Up' => 'command.up',
'ViewClear' => 'command.view.clear',
];
/**
* The commands to be registered.
*
* @var array
*/
protected $devCommands = [
'AppName' => 'command.app.name',
'AuthMake' => 'command.auth.make',
'CacheTable' => 'command.cache.table',
'ConsoleMake' => 'command.console.make',
'ControllerMake' => 'command.controller.make',
'EventGenerate' => 'command.event.generate',
'EventMake' => 'command.event.make',
'JobMake' => 'command.job.make',
'ListenerMake' => 'command.listener.make',
'MiddlewareMake' => 'command.middleware.make',
'ModelMake' => 'command.model.make',
'PolicyMake' => 'command.policy.make',
'ProviderMake' => 'command.provider.make',
'QueueFailedTable' => 'command.queue.failed-table',
'QueueTable' => 'command.queue.table',
'RequestMake' => 'command.request.make',
'SeederMake' => 'command.seeder.make',
'SessionTable' => 'command.session.table',
'Serve' => 'command.serve',
'TestMake' => 'command.test.make',
'VendorPublish' => 'command.vendor.publish',
];
很明显 , 修改这些数组 , 能够开启或关闭默认的命令 .
所以结论就很明显了 , 需要自定义laravel默认的命令行 , 我们只需要自己创建若干个Providers , 继承Illuminate\Foundation\Providers\ConsoleSupportServiceProvider
以及它所加载的各个Provider , 重写这些provider的部分数组和方法 , 最后把config/app.php 文件中的ConsoleSupportServiceProvider 替换成我们自己定义的Provider 就可以了.
推荐文章: