
PHP跨平台CLI应用开发实战:从零构建命令行工具
作为一名长期使用PHP的开发者,我最初接触PHP CLI开发时也经历了不少困惑。但经过多个项目的实践,我发现PHP在命令行应用开发方面有着惊人的潜力。今天我就带大家从零开始,构建一个真正实用的跨平台CLI应用。
环境准备与基础配置
首先确保你的PHP环境支持CLI模式。打开终端,输入:
php -v
如果看到版本信息,说明CLI模式已就绪。接下来创建项目目录:
mkdir my-cli-app
cd my-cli-app
创建composer.json文件来管理依赖:
{
"name": "myapp/cli-tool",
"description": "A cross-platform CLI tool",
"require": {
"symfony/console": "^6.0"
},
"autoload": {
"psr-4": {
"MyApp": "src/"
}
},
"bin": ["bin/myapp"]
}
构建第一个CLI命令
使用Symfony Console组件能大大简化开发。首先安装依赖:
composer install
创建bin/myapp文件(记得添加执行权限):
#!/usr/bin/env php
add(new HelloCommand());
$application->run();
接着创建src/Commands/HelloCommand.php:
setDescription('Say hello to someone')
->addArgument('name', InputArgument::OPTIONAL, 'Your name', 'World');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$name = $input->getArgument('name');
$output->writeln("Hello, {$name}!");
return Command::SUCCESS;
}
}
处理用户交互与输入验证
在实际项目中,我们需要更复杂的交互。下面是一个文件处理命令的示例:
setDescription('Process a file')
->addArgument('file', InputArgument::REQUIRED, 'File to process');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$filename = $input->getArgument('file');
// 文件存在性检查
if (!file_exists($filename)) {
$io->error("File {$filename} does not exist!");
return Command::FAILURE;
}
// 交互式确认
if (!$io->confirm("Are you sure you want to process {$filename}?")) {
$io->warning('Operation cancelled.');
return Command::SUCCESS;
}
// 进度条演示
$io->progressStart(100);
for ($i = 0; $i progressAdvance();
}
$io->progressFinish();
$io->success("File {$filename} processed successfully!");
return Command::SUCCESS;
}
}
跨平台兼容性处理
在开发跨平台CLI应用时,我踩过不少坑。这里分享几个关键点:
路径分隔符处理:
// 错误的做法
$path = "dirfile.txt";
// 正确的做法
$path = DIRECTORY_SEPARATOR . 'dir' . DIRECTORY_SEPARATOR . 'file.txt';
// 或者使用更简洁的方式
$path = '/dir/file.txt'; // PHP会自动处理路径分隔符
换行符处理:
// 跨平台换行符
$content = "Line 1" . PHP_EOL . "Line 2" . PHP_EOL;
颜色输出(ANSI转义序列):
// 使用Symfony Console的颜色支持
$output->writeln('Success message');
$output->writeln('Error message');
$output->writeln('Warning message');
// 手动处理
if (DIRECTORY_SEPARATOR !== '') { // 非Windows系统
echo "33[32mGreen text33[0m" . PHP_EOL;
}
实战:构建一个文件搜索工具
让我们构建一个实用的文件搜索工具:
setDescription('Search files by pattern')
->addArgument('pattern', InputArgument::REQUIRED, 'Search pattern')
->addOption('dir', 'd', InputOption::VALUE_REQUIRED, 'Search directory', '.')
->addOption('case-sensitive', 'c', InputOption::VALUE_NONE, 'Case sensitive search');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$pattern = $input->getArgument('pattern');
$directory = $input->getOption('dir');
$caseSensitive = $input->getOption('case-sensitive');
if (!is_dir($directory)) {
$io->error("Directory {$directory} does not exist!");
return Command::FAILURE;
}
$files = $this->searchFiles($directory, $pattern, $caseSensitive);
if (empty($files)) {
$io->warning('No files found matching the pattern.');
return Command::SUCCESS;
}
$io->section('Found Files:');
$io->listing($files);
$io->success(sprintf('Found %d file(s)', count($files)));
return Command::SUCCESS;
}
private function searchFiles(string $directory, string $pattern, bool $caseSensitive): array
{
$flags = $caseSensitive ? 0 : GLOB_FLAG_NOCASE;
$searchPattern = $directory . DIRECTORY_SEPARATOR . $pattern;
$files = glob($searchPattern, $flags);
if ($files === false) {
return [];
}
// 递归搜索子目录
$subdirs = glob($directory . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
foreach ($subdirs as $subdir) {
$files = array_merge($files, $this->searchFiles($subdir, $pattern, $caseSensitive));
}
return $files;
}
}
调试与错误处理技巧
在CLI开发中,良好的错误处理至关重要:
// 设置错误处理
set_error_handler(function($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
// 异常处理
try {
// 业务逻辑
} catch (Exception $e) {
$io->error('Error: ' . $e->getMessage());
if ($output->isVerbose()) {
$io->writeln('Stack trace:');
$io->writeln($e->getTraceAsString());
}
return Command::FAILURE;
}
打包与分发
最后,让我们将应用打包为PHAR文件:
buildFromDirectory(__DIR__);
$phar->setStub($phar->createDefaultStub('bin/myapp'));
或者使用Box工具(推荐):
composer require humbug/box --dev
./vendor/bin/box compile
通过这个教程,相信你已经掌握了PHP CLI应用开发的核心技能。记住,好的CLI工具应该具备清晰的文档、友好的错误提示和一致的跨平台行为。现在就去构建你自己的命令行工具吧!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)