当前位置: 首页 > news >正文

PHP命令行脚本开发实战

PHP命令行脚本开发实战

PHP不仅用来做Web开发,写命令行脚本也很方便。定时任务、数据处理、队列消费,这些场景用PHP命令行模式再合适不过了。

先说说命令行参数的处理。$argc是参数个数,$argv是参数数组。getopt可以解析选项参数。

```php
// 命令行参数解析
echo "脚本名: {$argv[0]}\n";
echo "参数个数: $argc\n";

// 手动解析参数
for ($i = 1; $i < $argc; $i++) {
echo "参数{$i}: {$argv[$i]}\n";
}

// 使用getopt解析选项
$options = getopt('u:p:h:', ['host:', 'port:', 'help', 'verbose']);

if (isset($options['help'])) {
echo "用法: php script.php -u <用户名> -p <密码> [--host=<主机>]\n";
echo "选项:\n";
echo " -u 用户名\n";
echo " -p 密码\n";
echo " -h 主机\n";
echo " --port 端口\n";
echo " --verbose 详细输出\n";
exit(0);
}

$username = $options['u'] ?? null;
$password = $options['p'] ?? null;
$host = $options['h'] ?? $options['host'] ?? 'localhost';
$port = $options['port'] ?? 3306;
$verbose = isset($options['verbose']);

if ($verbose) {
echo "详细模式: 连接 $host:$port\n";
}
?>
```

命令行输出可以加上颜色,让信息更醒目。

```php
class Console
{
const COLORS = [
'red' => "\033[31m",
'green' => "\033[32m",
'yellow' => "\033[33m",
'blue' => "\033[34m",
'magenta' => "\033[35m",
'cyan' => "\033[36m",
'white' => "\033[37m",
'reset' => "\033[0m",
];

const STYLES = [
'bold' => "\033[1m",
'dim' => "\033[2m",
'italic' => "\033[3m",
'underline' => "\033[4m",
'blink' => "\033[5m",
];

public static function text(string $text, string $color = 'white'): string
{
$colorCode = self::COLORS[$color] ?? self::COLORS['white'];
return $colorCode . $text . self::COLORS['reset'];
}

public static function info(string $message): void
{
echo self::COLORS['green'] . '[INFO] ' . self::COLORS['reset']
. $message . "\n";
}

public static function error(string $message): void
{
echo self::COLORS['red'] . '[ERROR] ' . self::COLORS['reset']
. $message . "\n";
}

public static function warning(string $message): void
{
echo self::COLORS['yellow'] . '[WARNING] ' . self::COLORS['reset']
. $message . "\n";
}

public static function success(string $message): void
{
echo self::COLORS['green'] . '[SUCCESS] ' . $message . self::COLORS['reset'] . "\n";
}

public static function table(array $headers, array $rows): void
{
// 计算列宽
$widths = [];
foreach ($headers as $i => $header) {
$widths[$i] = strlen($header);
foreach ($rows as $row) {
$widths[$i] = max($widths[$i], strlen((string)($row[$i] ?? '')));
}
}

// 输出表头
$separator = '+';
foreach ($widths as $w) {
$separator .= str_repeat('-', $w + 2) . '+';
}
echo $separator . "\n";

echo '|';
foreach ($headers as $i => $header) {
echo ' ' . str_pad($header, $widths[$i]) . ' |';
}
echo "\n";

echo $separator . "\n";

// 输出数据行
foreach ($rows as $row) {
echo '|';
foreach ($row as $i => $cell) {
echo ' ' . str_pad((string)$cell, $widths[$i]) . ' |';
}
echo "\n";
}

echo $separator . "\n";
}

public static function progressBar(int $current, int $total, int $width = 50): void
{
$percent = round($current / $total * 100);
$filled = round($width * $current / $total);
$bar = str_repeat('=', $filled) . str_repeat(' ', $width - $filled);
printf("\r进度: [%s] %d%% (%d/%d)", $bar, $percent, $current, $total);

if ($current === $total) {
echo "\n";
}
}

public static function confirm(string $message): bool
{
echo self::COLORS['yellow'] . "$message (y/N): " . self::COLORS['reset'];
$input = trim(fgets(STDIN));
return strtolower($input) === 'y' || strtolower($input) === 'yes';
}

public static function input(string $prompt, string $default = ''): string
{
$defaultText = $default ? " [$default]" : '';
echo "$prompt$defaultText: ";
$input = trim(fgets(STDIN));
return $input ?: $default;
}
}

Console::info('系统启动');
Console::warning('磁盘空间不足');
Console::error('连接失败');
Console::success('操作完成');

echo Console::text('红色文字', 'red') . "\n";
echo Console::text('蓝色粗体', 'blue') . "\n";

Console::table(
['ID', '姓名', '年龄', '城市'],
[
[1, '张三', 28, '北京'],
[2, '李四', 35, '上海'],
[3, '王五', 22, '广州'],
]
);

for ($i = 1; $i <= 100; $i++) {
Console::progressBar($i, 100);
usleep(30000);
}
?>
```

管线和重定向让命令行脚本可以组合使用。

```php
// 从标准输入读取
$input = file_get_contents('php://stdin');
$lines = explode("\n", trim($input));

echo "读取了 " . count($lines) . " 行数据\n";

// 处理每行数据
$processed = array_map(function ($line) {
$line = trim($line);
if (empty($line)) return null;
return strtoupper($line);
}, $lines);

$processed = array_filter($processed);

// 输出到标准输出
foreach ($processed as $item) {
echo $item . "\n";
}

// 输出到标准错误
fwrite(STDERR, "处理完成\n");
?>
```

命令行脚本的生命周期管理和错误处理比Web脚本更重要。

```php
// 信号处理
declare(ticks = 1);

pcntl_signal(SIGTERM, 'signalHandler');
pcntl_signal(SIGINT, 'signalHandler');

$running = true;

function signalHandler(int $signal): void
{
global $running;
echo "\n收到信号: $signal\n";
echo "正在清理...\n";
$running = false;
}

// 内存管理和超时控制
set_time_limit(0); // CLI模式通常不限执行时间

$startTime = time();
$maxRuntime = 3600; // 最长运行1小时

echo "开始处理...\n";

$count = 0;
while ($running) {
$count++;

// 检查运行时间
if (time() - $startTime > $maxRuntime) {
echo "运行超时\n";
break;
}

// 检查内存
if (memory_get_usage(true) > 100 * 1024 * 1024) {
echo "内存超限\n";
break;
}

// 模拟处理
if ($count > 1000) {
$running = false;
echo "处理完成\n";
}
}

echo "总共处理: $count 条\n";
?>
```

定时任务也是命令行脚本的常见用途。

```php
// 简单定时任务调度器
class Scheduler
{
private array $tasks = [];

public function addTask(string $name, callable $task, int $interval): void
{
$this->tasks[] = [
'name' => $name,
'task' => $task,
'interval' => $interval,
'last_run' => 0,
];
}

public function run(): void
{
echo "调度器启动\n";

while (true) {
$now = time();

foreach ($this->tasks as &$task) {
if ($now - $task['last_run'] >= $task['interval']) {
$task['last_run'] = $now;
echo "执行任务: {$task['name']}\n";
try {
($task['task'])();
} catch (Exception $e) {
echo "任务失败: {$task['name']}: {$e->getMessage()}\n";
}
}
}
unset($task);

sleep(1);
}
}
}

$scheduler = new Scheduler();
$scheduler->addTask('数据库备份', function () {
echo "备份完成\n";
}, 3600);
$scheduler->addTask('清理日志', function () {
echo "日志清理完成\n";
}, 7200);
?>
```

命令行脚本在PHP开发中占有一席之地。定时任务用cron加PHP脚本,数据处理用管道重定向,队列消费用守护进程模式。掌握了命令行开发,PHP的应用场景就不局限于Web了。

http://www.jsqmd.com/news/945319/

相关文章:

  • 2026年HRM系统GEO服务商哪家好?成功案例复盘与效果展示 - GEO优化
  • Vatee:把平台稳定性做扎实,新手更容易感受到的清单
  • Windows 11终极优化指南:用开源工具彻底解放你的系统性能
  • 上海付杰装潢新房装修怎么样? - mypinpai
  • 3步轻松搞定Windows顽固窗口:WindowResizer终极解决方案指南
  • Endothelin-3 (human, rat, porcine, rabbit) ;CTCFTYKDKECVYYCHLDIIW
  • 【Java杂项】为什么 long 可以自动转 float?宽化基本类型转换与精度丢失详解
  • Windows系统优化终极指南:5个专业技巧一键配置你的完美工作环境
  • Qt实战:手把手教你打造一个酷炫的IMU传感器数据可视化上位机(含完整源码)
  • WSL2下搞定CUDA 11.1与12.0版本切换,成功编译diff-gaussian-rasterization的踩坑实录
  • 2026 宁波卫生间漏水、外墙、楼顶、地下室、阳光房渗漏维修师傅推荐|同城附近上门防水补漏公司测评 - 防水百科
  • Vatee:把长期一致性做扎实,长期观察者更容易感受到的逻辑
  • 2026年京鑫天伟,旧大型空调机组回收靠谱吗? - mypinpai
  • GitHub Actions 静态合规校验:PR 阶段风险拦截实践
  • XInputTest终极指南:Windows游戏手柄延迟与轮询率测试的完整解决方案
  • Pearcleaner:macOS应用彻底卸载的3步完整指南
  • 行政中台进化论:融合RPA、NLP与知识图谱的智能引擎搭建实录(含3家世界500强脱敏架构图)
  • 2026 温州卫生间漏水、外墙、楼顶、地下室、阳光房渗漏维修师傅推荐|同城附近上门防水补漏公司测评 - 防水百科
  • ROS 2 YOLOv8目标检测系统:突破性的机器人视觉感知框架
  • 从冷启动到千人千面,AI工具与推荐系统深度耦合的7个关键接口设计,附GitHub可运行Demo
  • 树莓派智能温控系统:从传感器到物联网的STEM教育实践
  • 用数据驱动交付决策:多阶段镜像构建与Grafana看板配置加速容器交付
  • 2026年大型空调配件二手交易回收靠谱吗,怎么选择? - mypinpai
  • DIY多节18650电池组:从串联原理到平衡充电的完整制作指南
  • 探索AntiDupl:智能图片去重工具如何拯救你的数字空间
  • AI工具×智能签到系统深度耦合实战:7步完成企业级无缝对接(附2024最新API兼容矩阵)
  • 2026南京卫生间漏水哪家好|本地正规防水补漏维修公司推荐 - 苏易修缮
  • 2026北京屋顶防水补漏多少钱|2026楼顶阳台维修价格明细与避坑技巧 - 苏易修缮
  • 环境配置与基础教程:日志系统升级:结合 Loguru 与结构化 JSON 日志,实现训练异常的自动告警推送
  • 终极宝可梦存档管理指南:5个步骤学会PKSM跨版本精灵编辑