devdot/cli
Composer 安装命令:
composer require devdot/cli
包简介
Tools for CLI projects, on top of symfony/console.
README 文档
README
Tools for CLI projects, on top of symfony/console.
Features
- Intuitive application layout
- Container / Dependency Injection
- File-based command routing by default
- Building companion utility
- Fast production application
- Powerful PHP traits for command classes
Installation
Get started with a new project:
composer create-project devdot/cli-project
Or install the framework into an existing project like this:
composer require devdot/cli
composer require devdot/cli-builder --dev
vendor/bin/cli-builder init
Documentation
CLI Builder
Using devdot/cli-builder, cli applications can be built in minutes.
Create new commands like this:
vendor/bin/cli-builder make:command Testing/Example
This will create a new command class at src/Commands/Testing/Example that is wired to testing:example. This generated class will be fully registered in you application and it extends your projects's custom base command class:
namespace App\Commands\Testing; use App\Commands\Command; class Example extends Command { protected function handle(): int { // return self::SUCCESS; } }
Run vendor/bin/cli-builder list to see an overview of available commands.
Application Layout
Take a look at devdot/cli-project to see the default application layout.
⊢ bin/
⊢ build // build script for production
⊢ dev // application in development mode
⊢ prod // application in production mode
⊢ src/
⊢ Commands/
⊢ Example/
⊢ SendMessage.php // command "example:send-message"
⊢ About.php // command "about"
⊢ Command.php // project base command class (optional)
⊢ Kernel.php // application kernel, called by binaries
Binaries
If you want to provide your production application through your composer project package, you may call cli-builder composer:add-binary or add it to composer.json yourself, (see more).
The provided binaries are described as follows:
| Binary | cli-builder command | Description |
|---|---|---|
bin/dev |
run:dev |
Execute your application in development mode. The container will be rebuild on every call, by default no caching is active. |
bin/prod |
run:prod |
Execute your application in production mode. This will assume a built container and use all available caching. |
bin/build |
run:build <version> <name> |
Build and cache the production container. This is required for autowiring to work in production. |
For more details on the build process, see Production Build.
File based routing
Command names are generated automatically based on their location in the App\\Commands namespace. See examples for generated names:
| Command | Path |
|---|---|
| about | src/Commands/About.php |
| load-config | src/Commands/LoadConfig.php |
| cache:clear | src/Commands/Cache/Clear.php |
| app-config:reload-data | src/Commands/AppConfig/ReloadData.php |
| routing:manager:reset | src/Commands/Routing/Manager/Reset.php |
Upon building the container, all classes in the namespace App\\Commands that are commands (extending Devdot\\Cli\\Command) are added to the CLI application. No manual registration is necessary, command names do not need to be set. Constructors and Trait Constructors are autowired with the container.
If you want to change a commands name, you may do so like this:
class SomeCustomCommand extends Command { public static function getGeneratedName(): string { return 'custom_command_name'; } }
If you want to use a new or changed command in the production application, make sure to run bin/build first. The container will store command names (Command::getGeneratedName) and cache them for production.
To increase performance, command classes will only be created when necessary (by using Symfony's ContainerCommandLoader). Therefore, __construct and configure are only called when the command is executed or when the commands are list-ed.
Exceptions as Exit Codes
Exceptions may be used to find a direct exit from nested control flow. Any uncaught exception during Command::handle will cause the process to exit with the exception's error code. The exception message will be displayed (the stack trace is only shown in development).
You may use exceptions as exit codes like this:
namespace App\Commands; use Devdot\Cli\Exceptions\CommandFailedException; class ExampleCommand extends Command { protected function handle(): int { $this->handleReadingFiles(); $this->handleProcessingFiles() $this->handleWritingFiles(); return self::SUCCESS; } private function handleReadingFiles(): void { if ($this->fileReader->get() === '') { // exit the command throw new CommandFailedException('File is empty'); // the command will return code 1 } } // ... }
Kernel
The Kernel.php file is required as it anchors the container in your namespace and allows auto-detection of commands.
The container is invoked by binaries through Kernel::run, with the first param set to true on development builds (which will build a fresh container on every call).
You may change the application name and version by overwriting Kernel::getName and Kernel::getVersion respectively. By default, the name is assumed from the directory name and the version is pulled from the latest git tag.
Services for the container may be added as class-string in the Kernel::$services array. For more on services and providers, see Service Providers.
Command methods for overwriting
There are some methods in the Command class that are ready for being overwritten by extending classes:
class ExampleCommand extends Command { protected function beforeHandle(): void { } protected function afterHandle(int $code): void { } protected function handleException(Throwable $t): int { if ($t instanceof MyException) { // } return parent::handleException($t); } }
Customization
Follow these steps to change the default namespace or directory:
- change namespace in
composer.json - regenerate the Kernel using
cli-builder make:kernel - change namespace in entrypoints (files in
bin) - dump composer autoload
You may use the cli-builder to automatically rename the namespace:
vendor/bin/cli-builder composer:rename-namespace New\\Namespace
Traits
There are some traits that provide common functionality to commands as you require them. Thanks to Trait Constructors, they require no setup in Command::__construct or Command::configure.
| Trait | Description |
|---|---|
Devdot\Cli\Traits\ForceTrait |
Adds--force (-f) option to a command. Check input like this: $this->input->getOption('force'). |
Devdot\Cli\Traits\RunProcessTrait |
Add theCommand::runProcess utility. Execute on the command line or call other internal commands with a nice wrapper. Works on top of symfony/process. |
Container
All parts of a devdot/cli application are connected through the container's dependency injection. The container registers commands and constructs commands, and autowires all services as needed.
Register a service in through the Kernel and have it automatically injected in your command:
final class Kernel extends BaseKernel { protected array $services = [ // register your service classes here \App\Services\YourService::class, ]; // ... } class ExampleCommand extends Command { public function __construct( private \App\Services\YourService $service, // automatically injected ) { // ... } protected function handle(): int { $this->service->doSomething(); // ... } }
Services are autowired to other services, so you may use dependency injection on service constructors too.
The container is build before commands are created. In production mode, the container is only loaded from cache. Therefore, unused services cause no overhead in production. See more at Production Build.
Service Providers
If you have services that require complex setup, you need to use a service provider. Create a new service provider using cli-builder make:provider YourProvider and add it to Kernel::$providers.
// src/Providers/YourProvider.php namespace App\Providers; use Devdot\Cli\Container\ServiceProvider; use Symfony\Component\DependencyInjection\ContainerBuilder; class YourProvider extends ServiceProvider { /** @var class-string[] */ protected array $services = [ // you may add additional service class names here ]; public function booting(ContainerBuilder $container): void { parent::booting($container); // this will register the services in $this->services // this is called during the container build phase $container->autowire(SomeService::class)->setFactory([SomeFactory::class, 'make']); } } // src/Kernel.php namespace App; final class Kernel extends Devdot\Cli\Kernel { /** @var class-string<\Devdot\Cli\Container\ServiceProvider>[] */ protected array $providers = [ // add the new provider here \App\Providers\YourProvider::class, ]; // ... }
In production mode, service providers are never invoked, because the entire container is cached (see more at Production Build).
The Kernel acts as a service provider too, so you may overwrite Kernel::booting just like in a regular service provider.
Command Trait Constructors
Traits that are used in commands may have their own constructors that are called automatically. Trait constructors can be used to setup the command:
namespace Devdot\Cli\Traits; trait ForceTrait { public function __constructForceTrait(): void { // this method will be called by the container after Command::__construct on each command that uses this trait $this->addOption('force', 'f', null, 'Force run the command.'); } }
Trait constructors can receive services through dependency injection like this:
trait DataTrait { protected Data $data public function __constructForceTrait(Data $data): void { // $data will be provided by the container if the service Data is registered $this->data = $data; } }
Trait constructors are called on the commands that implement those traits. Traits may be inherited both by other traits or in the command classes.
Production Build
It is recommended to deploy the application based on the production binary. Use bin/build to build the container that is used in production.
Some important hints:
- Command names are generated through
Command::getGeneratedNameand cached in the container. Dynamic names will not work in production. - Application name and version are cached in the container and will not behave dynamically in production.
- Unused services will be optimized out of the production container. Only accessing them without dependency injection (i.e. through
$container->get('service')may cause them to be unavailable in production. - Service Providers are only called during the container build process. Therefore, these classes will not even be initialized in production.
By default, the cached container is stored in src/ProductionContainer.php. The class can be changed through overwriting const Kernel::CACHED_CONTAINER_NAME.
You may use cli-builder build:phar to create an executable PHAR from your production application.
devdot/cli 适用场景与选型建议
devdot/cli 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 53 次下载、GitHub Stars 达 0, 最近一次更新时间为 2024 年 08 月 15 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 devdot/cli 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 devdot/cli 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 53
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 7
- 依赖项目数: 3
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2024-08-15