eluceo/ical
eluceo/ical is a PHP library that lets you create RFC 5545-compliant .ics calendar files through a clean domain abstraction, with a separate presentation layer handling output. Install via Composer.
Build vs. buy — calendar output is a "buy." Any feature that emails a .ics file, serves a "subscribe to this calendar" URL, or adds an "Add to calendar" action needs RFC 5545-compliant output: line folding at 75 octets, text escaping, VFREQUENCY/RRULE syntax, VTIMEZONE blocks, and DST-correct DTSTART/DTEND handling. This package abstracts all of it behind domain objects. Hand-rolling it is a 2–4 week build plus a permanent tail of "event appears an hour off in Outlook" tickets. MIT license, no procurement friction, no legal review.
Feature bets this unlocks or de-risks:
CalendarFactory output can be dropped straight into a Laravel Mailable attachment with Content-Type: text/calendar.setPublishedTTL() (X-PUBLISHED-TTL) lets you steer how aggressively Outlook/Apple Calendar re-poll, which is a real load-management lever on your own endpoint..ics is a standard growth experiment; this package makes the download half a one-day task, not a sprint.ORGANIZER, ATTENDEE, STATUS (CONFIRMED/TENTATIVE/CANCELLED), CATEGORIES, URL, LAST-MODIFIED, GEO, ATTACH. Cancellation/status semantics are what make "your appointment was cancelled" actually propagate into the user's calendar rather than leaving a zombie event.BYSETPOS (e.g. "last weekday of the month"). Doing recurrence in-house is where naive implementations quietly break for months.Roadmap / sequencing signal: the domain-vs-presentation split in v2 is the important architectural fact. Domain objects (Event, Calendar, SingleDay, Date, DateTime) describe what happened; Presentation\Factory\CalendarFactory decides how it's serialized. That means "generate an ICS" and "render the same schedule as a web page / PDF / push payload" can share one domain model. It also means you can unit-test event construction without touching string output — cheap coverage for QA.
Where it should not change the roadmap: it is a writer only. It will not read, parse, or ingest .ics. If the roadmap contains inbound calendar sync, CalDAV/CardDAV, two-way Google Calendar integration, or "import from Outlook," this package is at most 20% of that story and should not be scoped as if it's the solution.
Planning note for engineering capacity: this is a thin, boring dependency — days, not weeks. The actual work in any calendar feature is timezone correctness in your data model, the delivery path (attachment vs. hosted URL vs. both), and cache/ETag handling on the feed endpoint. Budget accordingly; don't let the calendar ticket get sized by the library.
Adopt when all of these hold:
.ics for download, email attachment, or a hosted subscription URL.TZOFFSETTO/-0000, all-day end-date rollover, \n escaping regression in 2.14, UTC formatting in 2.10). That institutional memory is the thing you'd otherwise rediscover in production.RRULE later is the expensive path.Look elsewhere (or scope down) when:
RRULE expansion logic (i.e. "give me every occurrence between date X and Y"). This package emits the rule; it does not evaluate it. Expand recurrence on your side or via a dedicated library.Two adoption risks a PM should price in — both are schedule risks, not capability risks:
>=2.16 targets PHP 8.4–8.5 and removed 8.3; 2.15 covered 8.3–8.4; <2.14 covers 7.4–8.3. If a Laravel LTS on PHP 8.2/8.3 is in the estate, you must pin eluceo/ical in Composer and treat the PHP upgrade and the library upgrade as one coupled workstream. Put a standing line item in the platform-upgrade checklist so this doesn't ambush a release.Version decision to make explicitly: v2 is a complete rewrite and not compatible with 0.*. Greenfield projects should start on v2. If any internal package or legacy service is on 0.x, treat the migration as its own scoped task using the published upgrade guide — do not assume a drop-in bump.
For executives — one paragraph: Calendar files are a small feature with a disproportionate correctness bar: an event that lands an hour wrong, or a cancelled meeting that stays on someone's calendar, damages trust in the product more than the feature ever earned. eluceo/ical is a free (MIT), ten-year-old PHP library that gets those details right — timezones, all-day events, recurrence, cancellation status — and lets us ship booking confirmations and subscription calendar feeds in days instead of weeks. It is a write-only library: it produces calendar files and does not read them, so it is not a path to two-way Google/Outlook sync. The decision is low-cost and reversible; the main thing to manage is a tight PHP-version support window, which we handle by pinning the version and bundling its upgrade into our existing PHP upgrade cycle.
For engineering — the short version: add eluceo/ical via Composer. Build Domain\Entity\Event objects (setSummary, setDescription, setOccurrence, setOrganizer, setStatus, addRecurrenceRule), collect them in Domain\Entity\Calendar, then serialize with Presentation\Factory\CalendarFactory::createCalendar(). Cast to string; file_put_contents for a feed, or attach to a Laravel Mailable / return with Content-Type: text/calendar; charset=utf-8 and a Content-Disposition: attachment; filename="cal.ics" header. No service provider, no config, no facade needed — a thin wrapper class is enough. It's framework-agnostic and stateless. Pin the version against your PHP runtime (2.16 requires PHP 8.4–8.5 and drops 8.3; use 2.15 if you're on 8.3). Do not use it for parsing or CalDAV.
Success metrics to instrument: click-through on "add to calendar," attachment open/add rate on confirmation emails, subscription-feed adoption (unique authenticated ICS URLs fetched per week), and — the real one — a decline in support tickets matching "wrong time," "wrong day," or "event won't update." If those tickets don't fall after launch, the problem is almost certainly in our timezone data or feed caching, not in this library.
How can I help you explore Laravel packages today?