PHP跨平台CLI应用开发实战教程插图

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工具应该具备清晰的文档、友好的错误提示和一致的跨平台行为。现在就去构建你自己的命令行工具吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。