定制 ez-php/scheduler 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ez-php/scheduler

最新稳定版本:1.11.1

Composer 安装命令:

composer require ez-php/scheduler

包简介

Cron-based job scheduler with mutex-backed overlap prevention for ez-php applications

README 文档

README

Cron-based job scheduler for ez-php applications. Register commands with a fluent frequency API, prevent overlapping runs via pluggable mutex drivers (File, Database), and execute due jobs from a single cron entry.

Installation

composer require ez-php/scheduler

Quick Start

Create a schedule definition (e.g. app/schedule.php):

use EzPhp\Scheduler\Mutex\DatabaseMutex;
use EzPhp\Scheduler\Scheduler;

$pdo = $app->make(\PDO::class); // or any PDO instance
$scheduler = new Scheduler(new DatabaseMutex($pdo));

$scheduler->command('queue:work')->everyMinute()->withoutOverlapping();
$scheduler->command('cache:prune')->hourly();
$scheduler->command('reports:generate')->daily();

Run from a cron entry (once per minute):

* * * * * php /var/www/html/ez schedule:run

In the schedule:run command, pass a callable executor that dispatches to your console:

$scheduler->run(new DateTimeImmutable(), static function (string $command) use ($console): void {
    $console->call($command);
});

Frequency Methods

All methods are fluent and return ScheduleEntry for chaining:

Method When due
everyMinute() Every cron invocation
everyFiveMinutes() When minute % 5 === 0
hourly() At :00 of every hour
daily() At 00:00
weekly() On Sunday at 00:00
monthly() On the 1st of the month at 00:00

An entry without a frequency set is never due.

Overlap Prevention

Call withoutOverlapping() to skip a command if a previous invocation is still running:

$scheduler->command('queue:work')->everyMinute()->withoutOverlapping();

Requires a MutexInterface passed to the Scheduler constructor. A SchedulerException is thrown at runtime if withoutOverlapping() is used without a mutex configured.

Mutex Drivers

FileMutex

Uses PHP's flock(LOCK_EX|LOCK_NB) on per-command lock files in a configurable directory.

use EzPhp\Scheduler\Mutex\FileMutex;

$mutex = new FileMutex('/var/run/ez-php/locks');
$scheduler = new Scheduler($mutex);
  • The lock directory is created automatically if it does not exist.
  • Lock files are never deleted — their inodes remain stable across runs.
  • The lock is tied to the file handle, so a crashed process releases it automatically on the next cron run.
  • Suitable for single-server deployments.

DatabaseMutex

Uses a scheduler_locks table (created automatically via CREATE TABLE IF NOT EXISTS). Acquiring a lock inserts a row; releasing it deletes the row. A duplicate-key violation signals the lock is already held.

use EzPhp\Scheduler\Mutex\DatabaseMutex;

$mutex = new DatabaseMutex($pdo); // any PDO instance
$scheduler = new Scheduler($mutex);
  • Compatible with MySQL and SQLite.
  • No automatic TTL/expiry — stale rows from crashed processes must be cleaned manually.
  • Suitable for multi-server deployments sharing the same database.

API Reference

Scheduler

new Scheduler(?MutexInterface $mutex = null)
Method Description
command(string $name): ScheduleEntry Register a command and return its entry for chaining
all(): list<ScheduleEntry> Return all registered entries
dueEntries(DateTimeInterface $time): list<ScheduleEntry> Return entries whose predicate matches $time
run(DateTimeInterface $time, callable $executor): void Execute all due entries via the callable

ScheduleEntry

Method Description
everyMinute(): self Due on every invocation
everyFiveMinutes(): self Due at minute :00, :05, :10, …
hourly(): self Due at minute :00
daily(): self Due at 00:00
weekly(): self Due on Sunday at 00:00
monthly(): self Due on the 1st at 00:00
withoutOverlapping(bool $enabled = true): self Enable mutex-based skip
isDue(DateTimeInterface $time): bool Evaluate the frequency predicate
getCommand(): string Return the registered command name
getMutexKey(): string Return a stable sha1-derived lock key

MutexInterface

interface MutexInterface
{
    public function acquire(string $key): bool;
    public function release(string $key): void;
}

Implement this interface to add custom mutex backends (e.g. Redis, Memcached).

Custom Mutex

use EzPhp\Scheduler\MutexInterface;

final class RedisMutex implements MutexInterface
{
    public function __construct(private readonly \Redis $redis) {}

    public function acquire(string $key): bool
    {
        return (bool) $this->redis->set($key, 1, ['NX', 'EX' => 300]);
    }

    public function release(string $key): void
    {
        $this->redis->del($key);
    }
}

Exceptions

SchedulerException (extends RuntimeException) is thrown when:

  • withoutOverlapping() is used but no MutexInterface was passed to Scheduler
  • FileMutex cannot create the lock directory or open a lock file

Exceptions from the executor callable propagate up after the mutex lock is released (guaranteed via finally).

统计信息

  • 总下载量: 26
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-23

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固