本文目录导读:

- 方案一:在读取时判断(无需定时任务,最常用)
- 方案二:使用 Linux Crontab + PHP CLI(标准生产方案)
- 方案三:使用消息队列(异步、高性能、高精度)
- 方案四:使用任务调度器(如:Laravel Task Scheduler)
- 总结与选择建议
在PHP项目中实现内容定时发布,通常有几种主流方案,由于PHP本身是同步执行的(每次请求结束后进程退出),所以需要借助外部机制来实现“定时”操作。
以下是几种从简单到复杂的实现方案:
在读取时判断(无需定时任务,最常用)
这是推荐的最佳实践,利用数据库字段判断是否到达发布时间。
原理:不修改数据库里的 publish_time 字段,而是在查询时设置条件。
数据库表结构(假设 articles 表):
CREATE TABLE articles (
id INT PRIMARY KEY,VARCHAR(255),
content TEXT,
status TINYINT DEFAULT 0, -- 0: 草稿, 1: 已发布
publish_time DATETIME, -- 预定的发布时间
created_at TIMESTAMP
);
查询代码(用户看到已发布的文章):
// 获取当前可展示的文章(已到发布时间且状态为已发布)
$sql = "SELECT * FROM articles
WHERE status = 1
AND publish_time <= NOW()
ORDER BY publish_time DESC";
$result = $db->query($sql);
优点:
- 无需任何定时任务或外部服务。
- 实时生效。
- 服务器不需要额外配置 Cron。
缺点:
- 文章在发布前可能会被提前知道 URL(但看不到内容,因为查不到)。
使用 Linux Crontab + PHP CLI(标准生产方案)
适合需要在特定时间做某些操作的场景(如:发送通知、生成静态页面、发布文章)。
步骤:
-
编写 PHP 脚本(CLI 模式):
<?php // /var/www/cron/publish_article.php require_once 'bootstrap.php'; // 包含数据库连接等 // 查询所有已到发布时间但尚未标记为“已发布”的文章 $stmt = $db->prepare("UPDATE articles SET status = 1 WHERE status = 0 AND publish_time <= NOW()"); $stmt->execute(); echo date('Y-m-d H:i:s') . " Published " . $stmt->rowCount() . " articles.\n"; -
设置 Crontab(需要服务器 root 或 www 用户权限):
# 编辑 crontab crontab -e # 添加任务:每分钟执行一次PHP脚本 * * * * * /usr/bin/php /var/www/cron/publish_article.php >> /var/log/article_publish.log 2>&1
优点:
- 非常稳定,几乎所有 Linux 服务器都支持。
- 可以执行复杂的逻辑(如:生成 PDF、清理缓存)。
缺点:
- 需要 SSH 权限。
- 精度受限于 Cron 的最小频率(通常是 1 分钟)。
- 虚拟主机用户可能无法使用 Cron。
使用消息队列(异步、高性能、高精度)
适合高并发或秒级精度要求(如:抢购开始、倒计时活动)。
工作流:
- 用户发布文章时,将任务写入 Redis 或 RabbitMQ(延迟队列)。
- 后台 Worker 进程监听队列,时间到了自动取出并执行。
代码示例(使用 Redis + 延时队列):
// 1. 发布时,将任务加入 Redis ZSet
$redis->zAdd('article:publish:delay', strtotime('2024-12-01 10:00:00'), $article_id);
// 2. Worker 循环(CLI 常驻脚本)
while (true) {
$now = time();
// 取出所有已到时间的任务
$tasks = $redis->zRangeByScore('article:publish:delay', 0, $now);
if ($tasks) {
foreach ($tasks as $article_id) {
// 执行发布:更新数据库
$db->update('articles', ['status' => 1], ['id' => $article_id]);
// 从队列中移除
$redis->zRem('article:publish:delay', $article_id);
}
}
usleep(500000); // 0.5秒循环一次
}
优点:
- 精度高(毫秒级、秒级)。
- 适合分布式系统,不会因为多个 Worker 同时运行而出错(利用 Redis 原子操作)。
缺点:
- 架构复杂,需要维护 Worker 进程(可以使用 Supervisor 守护)。
- 需要额外安装 Redis 或 RabbitMQ。
使用任务调度器(如:Laravel Task Scheduler)
如果你使用 Laravel、Symfony 等框架,它们内置了任务调度功能,简化了 Cron 的管理。
Laravel 示例:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// 每分钟检查一次待发布文章
$schedule->call(function () {
Article::where('status', 0)
->where('publish_time', '<=', now())
->update(['status' => 1]);
})->everyMinute();
}
你只需要在服务器设置一条 Cron 即可:
* * * * * cd /your-project && php artisan schedule:run >> /dev/null 2>&1
总结与选择建议
| 方案 | 适用场景 | 难度 | 是否需要 Cron | 实时性 |
|---|---|---|---|---|
| 查询时判断 | 95% 的通用场景(博客、新闻、CMS) | 低 | 否 | 实时 |
| Cron + PHP CLI | 需要定时执行脚本(如:清理、通知、生成缓存) | 中 | 是 | 1 分钟 |
| 消息队列 | 高并发、秒级需求(抢购、倒计时) | 高 | 可选 | 秒级 |
| 框架调度器 | 使用 Laravel/Symfony 框架 | 低 | 是(仅一条) | 1 分钟 |
最推荐的做法:
- 如果你只是做普通的内容发布(如:文章、资讯、商品上架),用方案一就够了,不需要写任何定时任务,代码最简洁。
- 如果需要在发布时间触发动作(如:发邮件、推送),用方案二或方案四。
- 如果要求秒级启动活动,用方案三。