你清楚如何用PHP实现一个简单的任务计划(定时脚本)吗

wen PHP项目 48

本文目录导读:

你清楚如何用PHP实现一个简单的任务计划(定时脚本)吗

  1. 使用系统Cron(最常用)
  2. PHP纯代码实现(无需系统Cron)
  3. 使用时间间隔的精确实现
  4. 使用文件锁防止重复执行
  5. 使用第三方库(推荐)
  6. 完整的任务管理器示例
  7. 使用建议

我来介绍几种在PHP中实现定时任务/计划任务的方法:

使用系统Cron(最常用)

Linux/Mac系统

# 编辑crontab
crontab -e
# 每分钟执行一次
* * * * * /usr/bin/php /path/to/your/script.php
# 每天凌晨2点执行
0 2 * * * /usr/bin/php /path/to/your/script.php
# 每小时执行
0 * * * * /usr/bin/php /path/to/your/script.php

Windows系统

使用任务计划程序,或创建批处理文件:

@echo off
php C:\path\to\your\script.php

PHP纯代码实现(无需系统Cron)

<?php
// simple_cron.php
class SimpleCron {
    private $tasks = [];
    private $running = true;
    public function addTask($name, $interval, $callback) {
        $this->tasks[$name] = [
            'interval' => $interval,
            'last_run' => 0,
            'callback' => $callback
        ];
    }
    public function run() {
        echo "Cron Job Started at: " . date('Y-m-d H:i:s') . "\n";
        while ($this->running) {
            $current_time = time();
            foreach ($this->tasks as $name => &$task) {
                if ($current_time - $task['last_run'] >= $task['interval']) {
                    echo "Running task: $name\n";
                    try {
                        call_user_func($task['callback']);
                        $task['last_run'] = $current_time;
                    } catch (Exception $e) {
                        echo "Error in task $name: " . $e->getMessage() . "\n";
                    }
                }
            }
            sleep(1); // 每秒检查一次
        }
    }
    public function stop() {
        $this->running = false;
    }
}
// 使用示例
$cron = new SimpleCron();
// 添加任务:每5秒执行一次
$cron->addTask('task1', 5, function() {
    echo "Task 1 executed at: " . date('H:i:s') . "\n";
});
// 添加任务:每10秒执行一次
$cron->addTask('task2', 10, function() {
    echo "Task 2 executed at: " . date('H:i:s') . "\n";
});
// 启动计划任务
$cron->run();

使用时间间隔的精确实现

<?php
// precise_cron.php
class PreciseCron {
    private $tasks = [];
    public function schedule($expression, $callback) {
        $this->tasks[] = [
            'expression' => $expression,
            'callback' => $callback
        ];
    }
    private function shouldRun($expression) {
        // 解析cron表达式(简化版本)
        $parts = explode(' ', $expression);
        $now = getdate();
        if (count($parts) < 5) {
            return false;
        }
        // 匹配分钟 (0-59)
        if ($parts[0] != '*' && $now['minutes'] != (int)$parts[0]) {
            return false;
        }
        // 匹配小时 (0-23)
        if ($parts[1] != '*' && $now['hours'] != (int)$parts[1]) {
            return false;
        }
        // 匹配日 (1-31)
        if ($parts[2] != '*' && $now['mday'] != (int)$parts[2]) {
            return false;
        }
        // 匹配月 (1-12)
        if ($parts[3] != '*' && $now['mon'] != (int)$parts[3]) {
            return false;
        }
        // 匹配星期 (0-7, 0和7都代表周日)
        if ($parts[4] != '*') {
            $weekday = $now['wday'] == 0 ? 7 : $now['wday'];
            if ($weekday != (int)$parts[4]) {
                return false;
            }
        }
        return true;
    }
    public function run() {
        while (true) {
            foreach ($this->tasks as $task) {
                if ($this->shouldRun($task['expression'])) {
                    echo "[" . date('Y-m-d H:i:s') . "] " . 
                         "Executing: " . $task['expression'] . "\n";
                    call_user_func($task['callback']);
                }
            }
            sleep(60); // 每分钟检查一次
        }
    }
}
// 使用示例
$cron = new PreciseCron();
// 每天凌晨2点执行
$cron->schedule('0 2 * * *', function() {
    echo "每天的数据清理任务\n";
});
// 每小时执行
$cron->schedule('0 * * * *', function() {
    echo "每小时的数据同步\n";
});
// 每分钟执行
$cron->schedule('* * * * *', function() {
    echo "每分钟的检查\n";
});
$cron->run();

使用文件锁防止重复执行

<?php
// cron_with_lock.php
class CronWithLock {
    private $lockFile;
    public function __construct($lockFile = '/tmp/cron.lock') {
        $this->lockFile = $lockFile;
    }
    public function execute($taskName, $callback, $maxExecutionTime = 3600) {
        $lockHandle = fopen($this->lockFile . '.' . $taskName, 'w');
        if (!$lockHandle) {
            echo "无法创建锁文件\n";
            return false;
        }
        // 尝试获取独占锁
        if (!flock($lockHandle, LOCK_EX | LOCK_NB)) {
            echo "任务 $taskName 已经在运行中\n";
            return false;
        }
        echo "开始执行任务: $taskName\n";
        try {
            // 设置执行超时时间
            set_time_limit($maxExecutionTime);
            // 执行任务
            call_user_func($callback);
            echo "任务 $taskName 执行完成\n";
        } catch (Exception $e) {
            echo "任务 $taskName 执行失败: " . $e->getMessage() . "\n";
        } finally {
            // 释放锁
            flock($lockHandle, LOCK_UN);
            fclose($lockHandle);
        }
        return true;
    }
}
// 使用示例
$cron = new CronWithLock();
// 在cron中调用
$cron->execute('daily_backup', function() {
    // 备份数据库
    exec('mysqldump -u root database > backup.sql');
    echo "数据库备份完成\n";
}, 600); // 10分钟超时

使用第三方库(推荐)

使用 cron-expression 库

composer require dragonmantank/cron-expression
<?php
require 'vendor/autoload.php';
use Cron\CronExpression;
class CronScheduler {
    private $jobs = [];
    public function addJob($cronExpression, $callback) {
        $this->jobs[] = [
            'expression' => new CronExpression($cronExpression),
            'callback' => $callback
        ];
    }
    public function run() {
        while (true) {
            foreach ($this->jobs as $job) {
                if ($job['expression']->isDue()) {
                    echo "执行任务: " . $job['expression']->getExpression() . "\n";
                    call_user_func($job['callback']);
                }
            }
            sleep(60);
        }
    }
}
$scheduler = new CronScheduler();
$scheduler->addJob('*/5 * * * *', function() {
    echo "每5分钟执行\n";
});
$scheduler->run();

完整的任务管理器示例

<?php
// task_manager.php
class TaskManager {
    private $db;
    private $tasks = [];
    public function __construct($dbPath = 'tasks.db') {
        $this->db = new SQLite3($dbPath);
        $this->initDatabase();
        $this->loadTasks();
    }
    private function initDatabase() {
        $this->db->exec("
            CREATE TABLE IF NOT EXISTS tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                interval_seconds INTEGER NOT NULL,
                command TEXT NOT NULL,
                last_run INTEGER DEFAULT 0,
                status TEXT DEFAULT 'active',
                created_at INTEGER DEFAULT (strftime('%s', 'now'))
            )
        ");
    }
    public function addTask($name, $interval, $command) {
        $stmt = $this->db->prepare("
            INSERT INTO tasks (name, interval_seconds, command)
            VALUES (:name, :interval, :command)
        ");
        $stmt->bindValue(':name', $name, SQLITE3_TEXT);
        $stmt->bindValue(':interval', $interval, SQLITE3_INTEGER);
        $stmt->bindValue(':command', $command, SQLITE3_TEXT);
        return $stmt->execute();
    }
    public function removeTask($id) {
        $stmt = $this->db->prepare("DELETE FROM tasks WHERE id = :id");
        $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
        return $stmt->execute();
    }
    private function loadTasks() {
        $result = $this->db->query("SELECT * FROM tasks WHERE status = 'active'");
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $this->tasks[] = $row;
        }
    }
    public function run() {
        echo "Task Manager Started at: " . date('Y-m-d H:i:s') . "\n";
        while (true) {
            $currentTime = time();
            foreach ($this->tasks as &$task) {
                if ($currentTime - $task['last_run'] >= $task['interval_seconds']) {
                    echo "[" . date('Y-m-d H:i:s') . "] " . 
                         "Running task: {$task['name']}\n";
                    // 执行命令
                    $output = [];
                    $returnVar = 0;
                    exec($task['command'], $output, $returnVar);
                    // 更新最后执行时间
                    $stmt = $this->db->prepare("
                        UPDATE tasks SET last_run = :last_run 
                        WHERE id = :id
                    ");
                    $stmt->bindValue(':last_run', $currentTime, SQLITE3_INTEGER);
                    $stmt->bindValue(':id', $task['id'], SQLITE3_INTEGER);
                    $stmt->execute();
                    $task['last_run'] = $currentTime;
                    echo "Task {$task['name']} completed. Output: " . 
                         implode("\n", $output) . "\n";
                }
            }
            sleep(1);
        }
    }
}
// 使用示例
$manager = new TaskManager();
// 添加任务
$manager->addTask('Check Server', 300, 'curl -s http://localhost/health');
$manager->addTask('Clear Cache', 3600, 'rm -rf /tmp/cache/*');
// 启动管理器
$manager->run();

使用建议

  1. 生产环境推荐使用系统Cron
  2. 如果无法使用系统Cron,考虑使用PHP实现
  3. 注意设置合适的执行超时时间
  4. 添加日志记录便于排查问题
  5. 使用数据库或文件存储任务状态

需要根据实际应用场景选择最适合的方案,对于大多数Web应用,使用系统Cron + PHP脚本是最简单可靠的方案。

抱歉,评论功能暂时关闭!