Bridge
A personal operations dashboard managing chores, fitness, plants, and job applications. Deployed on a home server.
A standalone PHP 8.2+ library that evaluates whether a recurring rule string is due on a given date — extracted from Bridge and published on Packagist.
Live Demo
Overview
antnavdev/recurrence is a small, framework-agnostic PHP 8.2 library with a single job: given a rule string and a date, tell you whether something is due. It was extracted from Bridge's chore scheduler, where the same logic lived directly on the Chore model. Publishing it as a standalone package made it independently testable and reusable across projects.
The problem it solves
Bridge generates the day's chore list at midnight via a scheduled command. Every chore stores a plain VARCHAR rule string — daily, weekly:mon,wed, biweekly:mon, monthly:15, monthly:last, interval:7 — and the scheduler needs to decide which ones are due on today's date. The logic has to be stateless: a missed or late chore must never cause the schedule to drift.
Design
The entire library is one class, Recurrence, with one public method: isDueOn(Carbon $date): bool. All six rule variants are parsed with str_starts_with and explode — no regex, no pattern objects, no visitor hierarchy. The implementation is intentionally flat.
Rules that require a reference point (biweekly and interval) accept an $anchorDate as a constructor argument. Biweekly chores are anchored to ISO week boundaries: the algorithm counts full weeks between the anchor's Monday and the target's Monday using Carbon's diffInWeeks(), then checks whether the result is even. This means the anchor date itself is always an "on" week, and the schedule never drifts regardless of when the chore was last completed.
Interval rules compute the day difference between anchor and target with diffInDays() and check divisibility. An interval of zero or negative is treated as "never due" rather than an exception — a deliberate choice to handle misconfigured records gracefully at runtime.
Error handling
Two exception types: InvalidRuleException (extends \InvalidArgumentException) for unrecognisable rule strings, and \LogicException when biweekly or interval is evaluated without an anchor date. Invalid monthly sub-values (e.g. monthly:foo) also raise InvalidRuleException.
Testing
The test suite covers all six rule variants across normal cases, boundary conditions (last day of month, leap year February 29, zero/negative intervals, anchor-week alignment), wrong-day-of-week rejections, and both exception paths. All tests pass against PHP 8.2+ and PHPUnit 11.
Dependencies
The only runtime dependency is nesbot/carbon ^3.0. No Laravel, no Symfony, no service providers.
Explore
A personal operations dashboard managing chores, fitness, plants, and job applications. Deployed on a home server.
A recipe and cookbook manager with fraction-accurate ingredient scaling, star ratings, and cookbook collections.