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.
Architecture fit. eluceo/ical is a pure, framework-agnostic PHP library built around a clean Domain / Presentation split: Domain\Entity\{Calendar, Event, TimeZone} plus value objects (Date, DateTime, SingleDay, MultiDay, Duration, Location, Geo, EmailAddress) describe what the calendar is; Presentation\Factory\CalendarFactory renders how it serializes as RFC 5545 output. This is effectively a hexagonal core in miniature and plays well with a Laravel codebase that already favours thin controllers, service classes, and testable domain objects. The package has zero runtime framework coupling — no service provider, no facade, no config — so it slots in as a library, not an opinionated module. That is a TPM advantage: low blast radius, easy to isolate behind an adapter, trivial to replace later.
Integration feasibility. Composer-installable with a single require. The canonical usage is four lines (build event → build calendar → factory → cast to string), which means a Laravel integration is genuinely a wrapper, not a project: a CalendarService that maps your internal models (e.g., Appointment, Shift, Subscription) into Eluceo domain objects, plus a controller returning text/calendar with Content-Disposition: attachment. No queue, no migration, no database touch. This is days of work, not sprints.
Technical risk. The dominant risk is the PHP support window, which is unusually aggressive. The changelog shows a pattern of removing support in the same release that adds support: 2.15.0 dropped PHP 7.4–8.2 to move to 8.3–8.4; 2.16.0 dropped 8.3 to support 8.4–8.5. A typical Laravel LTS estate on PHP 8.2/8.3 will therefore be pinned to an older minor (<2.15 / <2.16) and will not receive fixes — acceptable for a serializer, but it must be a conscious decision, not a composer update surprise. Secondary risks: the package writes ICS only (no parsing/validation), so inbound calendar ingestion is out of scope; and calendar-client quirks (Outlook, Google, Apple) are a real-world compatibility surface the library cannot fully own. Note the Dependents: 0 figure appears to be a metadata artifact — the README tracks monthly Packagist downloads — and should not be used to infer adoption or abandonment.
Key questions.
RRULE, BYSETPOS, multiples) in scope, or is this one-off event export?.ics — Outlook/Exchange, Google, Apple, or all three? Their tolerances differ on timezone and all-day handling.ATTENDEE/ORGANIZER/METHOD:REQUEST invite flow, or just a published feed?Stack fit. Works with any Laravel version that satisfies the Composer PHP constraint — no Laravel-specific pinning. The clean seam is a dedicated app/Calendar/ namespace containing a mapper (your model → Eluceo domain) and a renderer (domain → string/response). Bind it as a singleton service; expose a download() and a feed() method. Because it is a library rather than a package with a publishable config, there is nothing to vendor:publish and no upgrade ceremony beyond composer update.
Migration path. Two realistic starting points:
2.0.0 explicitly split domain and presentation; property classes were removed and the fluent builder API changed. There is an official upgrade guide — read it before estimating.kigkonsult/icalcreator or a Spatie generator): map your event constructs to the domain vocabulary first, then rebuild output. Expect the timezone and all-day semantics to be the fiddly part, not the data mapping.If migrating to something else later, the adapter boundary means only the mapper is rewritten.
Compatibility. Pin deliberately, e.g. "eluceo/ical": "^2.14" for a PHP 8.2 estate or ^2.16 for PHP 8.4+. Add a platform requirement check in CI so a future PHP bump prompts a review of the pin rather than silently breaking. Verify the mbstring extension is present — it is used for line folding and has been an explicit perf dependency since 0.13.
Sequencing.
BEGIN:VCALENDAR, DTSTART;TZID=, folding at 75 octets).echo).X-PUBLISHED-TTL, useful for feed refresh cadence), categories, and attendee/organizer fields if required.Maintenance. MIT licence, actively released (a 2026-07-12 release), documented changelog per version, CI + codecov + Psalm + Infection mutation testing badges — the quality signals are better than most single-maintainer PHP packages. The maintenance burden on us is genuinely low: no runtime services, no schema, no daemon. The one recurring chore is the PHP-version coupling described above; budget an hour per major PHP upgrade to re-evaluate the pin. Watch the deprecation noted in 2.1.0 around TimeZone::createFromPhpDateTimeZone defaults — it signals a future major-version break.
Support. Community-only: GitHub issues, Discussions Q&A, and a dedicated docs site (ical.poerschke.nrw). No vendor SLA. That is acceptable for a format serializer; it would not be for payments or auth. The practical mitigation is our own regression tests — they, not the upstream project, are what guarantee our output stays valid.
Scaling. Rendering is CPU/string-bound and linear in event count. For large feeds, the concerns are memory (whole calendar built then cast to string) and line-folding cost. Mitigations: cache the rendered ICS and serve it from cache with X-PUBLISHED-TTL set so clients re-poll on a schedule you control; stream generation for very large calendars; avoid rebuilding on every request. Calendar payloads are small relative to typical JSON APIs, so this rarely becomes the bottleneck — but a public "subscribe" feed can be polled by many clients and is a cheap CDN/cache win.
Failure modes. Expect problems in this order:
0.11–2.10, including UTC offset formatting and TZOFFSETFROM -0000). Test explicitly with DateTimeImmutable in non-UTC zones.DTEND is exclusive; off-by-one day is the classic bug (fixed back in 0.11.1 but easy to reintroduce in our mapper).\n escaping bug existed as recently as 2.14.0; long descriptions fold at 75 octets. Assert on raw output.Ramp-up. Low for a competent PHP developer: the README example is copy-paste runnable, the examples/ folder covers common cases, and the four-step mental model (event → calendar → factory → string) is learnable in an afternoon. The actual learning curve is RFC 5545 literacy — DTSTART/DTEND exclusivity, VTIMEZONE blocks, RRULE grammar — not the library. Front-load that knowledge in the mapper's tests and comments; it is the thing that will actually break in production.
How can I help you explore Laravel packages today?