本文目录导读:

我来介绍几种在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();
使用建议
- 生产环境推荐使用系统Cron
- 如果无法使用系统Cron,考虑使用PHP实现
- 注意设置合适的执行超时时间
- 添加日志记录便于排查问题
- 使用数据库或文件存储任务状态
需要根据实际应用场景选择最适合的方案,对于大多数Web应用,使用系统Cron + PHP脚本是最简单可靠的方案。