本文目录导读:

在 PHP 中组织命令行脚本,我建议采用以下最佳实践架构:
基础目录结构
project/
├── bin/ # 可执行脚本入口
│ └── app.php
├── src/ # 业务逻辑代码
│ ├── Command/ # 命令类
│ ├── Service/ # 服务层
│ └── Helper/ # 辅助函数
├── config/ # 配置文件
├── vendor/ # 依赖包
└── tests/ # 测试目录
使用 Symfony Console 组件
推荐使用 symfony/console,这是最流行的 PHP CLI 框架:
// bin/app.php
#!/usr/bin/env php
<?php
require __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Console\Application;
use App\Command\HelloCommand;
$app = new Application('My CLI App', '1.0.0');
$app->add(new HelloCommand());
$app->run();
// src/Command/HelloCommand.php
namespace App\Command;
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;
class HelloCommand extends Command
{
protected function configure()
{
$this->setName('hello')
->setDescription('Say hello')
->addArgument('name', InputArgument::REQUIRED, 'Your name')
->addOption('greeting', 'g', InputOption::VALUE_OPTIONAL, 'Custom greeting', 'Hello');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$greeting = $input->getOption('greeting');
$output->writeln("<info>{$greeting}, {$name}!</info>");
return Command::SUCCESS;
}
}
轻量级脚本 - 单文件组织
对于简单任务,可以组织为:
#!/usr/bin/env php
<?php
// 手动加载
require_once __DIR__ . '/vendor/autoload.php';
// 参数解析辅助函数
function parseArgs(array $argv): array {
$args = [];
foreach (array_slice($argv, 1) as $arg) {
if (str_starts_with($arg, '--')) {
$parts = explode('=', ltrim($arg, '--'), 2);
$args[$parts[0]] = $parts[1] ?? true;
} elseif (str_starts_with($arg, '-')) {
$args[ltrim($arg, '-')] = true;
} else {
$args[] = $arg;
}
}
return $args;
}
// 主逻辑
function main(array $args): void {
// 你的业务逻辑
}
// 入口
if (PHP_SAPI === 'cli') {
$args = parseArgs($argv);
main($args);
}
编写健壮的 CLI 脚本
1 颜色输出辅助类
// src/Helper/Output.php
namespace App\Helper;
class Output {
const COLORS = [
'red' => '31',
'green' => '32',
'yellow' => '33',
'blue' => '34',
'magenta' => '35',
'cyan' => '36'
];
public static function color(string $text, string $color): string {
return "\033[" . self::COLORS[$color] . "m{$text}\033[0m";
}
public static function success(string $msg): void {
echo self::color($msg, 'green') . PHP_EOL;
}
public static function error(string $msg): void {
fwrite(STDERR, self::color($msg, 'red') . PHP_EOL);
}
}
2 进度条和交互
// 进度条示例
class ProgressBar {
private $total;
private $current = 0;
public function __construct(int $total) {
$this->total = $total;
}
public function advance(int $step = 1): void {
$this->current += $step;
$percent = round(($this->current / $this->total) * 100);
printf("\rProgress: [%s] %d%%",
str_repeat('=', $percent/2) . str_repeat(' ', 50 - $percent/2),
$percent
);
}
}
环境检测和依赖验证
// bin/app.php
<?php
// 检查运行环境
if (PHP_SAPI !== 'cli') {
die('This script can only be run from command line');
}
// 检查 PHP 版本
if (version_compare(PHP_VERSION, '8.0.0', '<')) {
die('PHP 8.0 or higher is required');
}
// 自动加载
require __DIR__ . '/../vendor/autoload.php';
// 错误处理
set_error_handler(function($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function($e) {
fwrite(STDERR, "Unhandled exception: " . $e->getMessage() . PHP_EOL);
exit(1);
});
配置管理
// config/app.php
return [
'app_name' => 'My CLI Tool',
'version' => '1.0.0',
'debug' => getenv('APP_DEBUG') ?: false,
'database' => [
'host' => getenv('DB_HOST') ?: 'localhost',
'port' => getenv('DB_PORT') ?: 3306,
]
];
测试组织
// tests/Command/HelloCommandTest.php
namespace Tests\Command;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use App\Command\HelloCommand;
class HelloCommandTest extends TestCase {
public function testExecute() {
$application = new Application();
$application->add(new HelloCommand());
$command = $application->find('hello');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'name' => 'World'
]);
$output = $commandTester->getDisplay();
$this->assertStringContainsString('Hello, World!', $output);
}
}
实际使用示例
# 查看帮助 php bin/app.php list # 运行命令 php bin/app.php hello John --greeting="Hi" # 后台执行 nohup php bin/app.php long-running-task > /var/log/app.log 2>&1 & # 定时执行 0 * * * * /usr/bin/php /path/to/bin/app.php backup-database
关键最佳实践:
- 参数解析:使用专业的参数解析器(Symfony Console 或 GetOpt)
- 错误处理:使用异常和 exit codes
- 日志记录:输出到 STDOUT/STDERR,需要时才写文件
- 依赖注入:使用容器管理服务
- 环境配置:使用环境变量或配置文件
- 严格输入验证:验证所有输入参数
- 脚本权限:设置可执行权限
chmod +x bin/app.php
这样的组织方式既适合简单的单文件脚本,也适合复杂的命令行应用程序。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播