Download the PHP package erenav/icalendar without Composer

On this page you can find all versions of the php package erenav/icalendar. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package icalendar

erenav/icalendar

Latest Version Tests PHP Version Total Downloads

A modern, strongly-typed, immutable iCalendar library for PHP 8.3+.

Models the package's documented portions of RFC 5545 (iCalendar), RFC 7986 (new properties), and RFC 5546 (iTIP scheduling).

No stringly-typed array access, no $event['VEVENT']['SUMMARY']. Fluent builders, immutable value objects, typed getters, and semantic preservation of supported external data the library does not model directly.


📖 New here? The Recipes page has short, copy-paste examples for the most common tasks — start there.

Table of contents


Why this library

sabre/vobject is the established option, but it leans on stringly-typed array access and mutable objects. erenav/icalendar aims for:

Requirements

Installation

Quick start

Building calendars

Calendar::build(), Event::build() and Alarm::build() return mutable builders. Calling ->get() produces the immutable component.

Serializing to .ics

The serializer handles CRLF line endings, 75-octet line folding (UTF-8 safe), TEXT escaping, RFC 6868 parameter encoding, and derives TZID / VALUE / ENCODING parameters from the values themselves. Parameter carriage returns and line feeds are normalized to RFC 6868 ^n; non-TEXT values containing either character are rejected so they cannot inject another content line. A manually assembled multi-value property is also rejected when its values require incompatible controlling parameters, as is an explicit VALUE, TZID, or ENCODING parameter that contradicts its typed value.

Parsing .ics

Parsing provides Level-1 semantic preservation for supported RFC input: unknown properties, unknown components, and unrecognized parameter values are retained rather than discarded. Serialization is canonical, not byte-identical, and malformed recovery has explicit limits (see Gotchas).

Strict parsing rejects duplicate or comma-multivalued controlling TZID, VALUE, and ENCODING parameters, impossible DATE/DATE-TIME fields, contradictory TZID on DATE or UTC DATE-TIME or non-temporal typed values, contradictory standard encodings, and invalid TEXT escapes. Lenient parsing keeps the affected value and its controlling parameters raw instead of choosing an interpretation. Structured REQUEST-STATUS values are likewise retained raw so their status-code/description/data semicolons are not mistaken for TEXT that needs escaping.

Reading data

Typed getters read from the underlying model. Optional properties return null.

Anything without a dedicated getter is still reachable:

Editing immutably

Components are readonly. To change one, get a builder back, tweak it, and rebuild — the original is untouched.

Dates, times & time zones

iCalendar distinguishes four date/time forms. The DateTimeValue value object models all of them, and is the single source of truth for the TZID / VALUE=DATE parameters.

date() and floating() copy the supplied calendar fields into a timezone-neutral backing value; the source object's timezone does not turn them into instants. Likewise, zoned($dateTime, $tzid) interprets the supplied fields in the explicit TZID instead of converting an instant from the source timezone. For an ambiguous local time, the first occurrence is selected. For a nonexistent local time during a forward transition, the pre-transition UTC offset is used, as required by RFC 5545.

An elapsed DURATION can end at the second occurrence of a folded wall time, which a TZID/local literal cannot distinguish from the RFC-selected first occurrence. Event::end() returns UTC for that derived end so its instant stays exact. Calendar-level range materialization rejects a cross-zone conversion that would otherwise change the instant at such a fold.

DateTimeValue::adding($duration) applies day/week components in wall-clock coordinates before exact hour/minute/second components, so P1D and PT24H correctly differ across DST. For a custom or embedded TZID, pass its calendar resolver to $event->end($resolver) / $event->effectiveEnd($resolver), or call $resolver->addDuration($start, $duration) directly.

The builder's date setters accept any DateTimeInterface (so Carbon works), or a DateTimeValue when you need an explicit form:

Durations

Duration is a dedicated value object (not DateInterval) because the iCalendar DURATION type forbids months/years, has a distinct week form, and must be immutable. It bridges to native PHP both ways:

For backward compatibility, Duration::equals() compares a context-free normalized second count, so P1D and PT24H compare equal. RFC duration application can distinguish them across a daylight-saving transition. Do not use equals() to infer equal event ends in a named zone; apply each duration to its DTSTART and compare the resolved ends.

Attendees & organizer

addAttendee() builds the ATTENDEE property and its parameters. attendees() returns typed Attendee views; each exposes its complete underlying Property.

The organizer builder accepts either an email address or mailto: URI for sentBy and stores a canonical calendar address. Because this builder creates VEVENTs, addAttendee() rejects the VTODO-only PARTSTAT values COMPLETED and IN-PROCESS.

Typed URI/calendar-address parameter accessors return null (or omit an invalid list entry) when lenient input retained a malformed value. The complete raw parameter remains available through the underlying Property.

The concise organizer() and addAttendee() builder methods intentionally cover common parameters without a long positional API. When copying an external property, use the complete-property methods so IANA, experimental, and less-common scheduling parameters survive:

Each method requires a Property with the matching name; attendeeProperty() appends, while the other methods replace that property name. Check optional source properties for null before copying them.

Alarms

Recurring events

Recurrence rules are modelled by the immutable Recurrence value object and built fluently (each modifier returns a new instance):

Attach one to an event, with optional exception (EXDATE) and extra (RDATE) dates:

PERIOD-valued RDATEs carry an occurrence-specific end or duration. Add them with addRecurrencePeriod(Period ...$periods) and inspect them with recurrenceDatePeriods(). EXDATE removes their slots, detached overrides take normal precedence, and a sparse range/single override retains a PERIOD slot's duration unless an effective range or single explicitly replaces it. addRecurrenceDate() and addExceptionDate() split adjacent DATE, floating, UTC, or differently zoned inputs into separate parameter-compatible properties without reordering them; a single content line cannot safely mix those forms. For a zoned explicit PERIOD, the end must remain later than the start after authoritative embedded-VTIMEZONE resolution, not merely in lexical wall time.

Unknown/IANA/experimental RRULE parts are retained in Recurrence::$unknownParts and re-emitted after the canonically ordered known parts, preserving their relative input order and duplicates. Lenient parsing preserves them; strict parsing rejects them. Because an unknown part may change the occurrence set, the default expander throws UnsupportedRecurrenceException instead of silently ignoring it. Invalid or duplicate known RRULE parts similarly remain a RawValue in lenient parser mode and fail strict mode. Programmatic construction validates RFC numeric ranges and contextual restrictions and throws InvalidValueException when they are violated. A programmatic RecurrencePart also rejects standard-part names, unescaped structural semicolons, dangling escapes, and control bytes; escaped semicolons are preserved across repeated round trips.

Expand the concrete occurrence starts in a window (RRULE + DATE/DATE-TIME/PERIOD RDATE − EXDATE, DST-aware for resolvable TZID values — wall-clock time is preserved across ordinary transitions):

Expansion wraps rlanvin/php-rrule behind a RecurrenceExpander interface — pass your own implementation to occurrencesBetween() to swap the engine. Event::occurrencesBetween() intentionally remains start-only. Use calendar-level expansion when a PERIOD's effective end/duration is needed.

UTC and resolvable-zoned DATE-TIME windows are ordinary instant bounds. DATE and floating DATE-TIME values have no instant or viewer timezone, so the default expander represents their fields in a neutral UTC-backed wall-clock coordinate. Query those series with UTC bounds carrying the desired calendar fields (for example, 09:00 UTC as the neutral representation of floating 09:00); returned DateTimeImmutable values are wall-clock containers, not UTC instants.

Modified & cancelled instances (RECURRENCE-ID)

A recurring series can have individual instances overridden by a second VEVENT with the same UID plus a RECURRENCE-ID. Expand at the calendar level to resolve those — Calendar::occurrencesBetween() returns rich Occurrence objects (the effective event per instance), applying modifications and dropping cancellations:

(Event::occurrencesBetween() expands a single event and returns bare DateTimeImmutable starts; Calendar::occurrencesBetween() is the override-aware version across the whole calendar.)

Build a range override with ->recurrenceId($originalSlot, Range::ThisAndFuture). RANGE is a parameter of that RECURRENCE-ID, not an independent event property, and is retained by toBuilder() and iTIP replies.

Calendar windows are inclusive and apply to the effective start, so moved-in overrides are included and moved-out overrides are excluded. RANGE=THISANDFUTURE propagates the same fixed wall-coordinate start delta (not a reusable month/year interval), duration/end and changed properties. Under the default expander's deterministic sparse-range merge policy, a later range replaces values it states; earlier non-temporal and duration changes remain effective when the later range does not replace them. Each later range starts a new timing segment: omitting DTSTART resets the start delta to zero rather than inheriting the prior move. A later non-cancelled range resumes a cancelled tail.

An explicit single-instance override wins for its slot and overlays only the properties it supplies. Omitted values inherit from the active range, or from the materialized master slot when no range is active; explicit DTSTART, DTEND, or DURATION wins, and supplied child components replace the inherited child set. This sparse-merge policy means omission does not clear inherited state. An active single override can restore its one slot inside a cancelled range. The effective event is materialized coherently without recurrence-set properties, while Occurrence::$recurrenceId continues to identify the original slot. Every explicit source override retains its complete RECURRENCE-ID, including IANA/X parameters (and RANGE=THISANDFUTURE on a range onset). Only synthetic later range events use generated recurrence IDs without RANGE or slot-specific parameters, so re-exporting one cannot accidentally reapply the range directive. A sparse orphan has no master state to inherit; it receives a coherent DTSTART copied from its recurrence ID when absent.

Duplicate revisions are selected independently of document order: higher SEQUENCE (missing is treated as 0), then later DTSTAMP, then later LAST-MODIFIED; a present timestamp sorts after a missing one. Remaining ties use lexicographic canonical ICS content, with the greater value preferred. This is a deterministic import selection policy based on RFC revision metadata, not application/provider conflict resolution. When duplicate identities must be compared, ambiguous/non-RFC SEQUENCE, DTSTAMP, or LAST-MODIFIED metadata is rejected rather than used to choose a winner.

Time zones

Zoned date-times reference a TZID. For portability, a calendar can carry its own VTIMEZONE definitions so clients don't need to know the zone. Calendar-level expansion uses an embedded definition as authoritative for its TZID, including non-IANA ids such as Outlook's Eastern Standard Time. withTimeZones() generates definitions from PHP's tz database for every referenced IANA zone:

Parsed VTIMEZONE blocks are first-class TimeZone components with typed Observance children:

You can also generate one directly: (new TimeZoneGenerator())->forIana('Europe/Paris'). Canonical identifiers and IANA backward-compatibility links such as US/Eastern are accepted. The default generator inspects 1970–2100. Known rule eras are bounded with UTC UNTIL, irregular transitions use exact RDATEs, and only a stable suffix verified for at least five consecutive years through the horizon for every side of a complete transition cycle remains unbounded. Supply explicit constructor bounds when another coverage horizon is required. TimeZoneResolver::fromCalendar() is the public calendar-scoped resolver used by default expansion.

Scheduling (iTIP)

Build common RFC 5546 scheduling messages — invitations, replies, and cancellations — with the appropriate METHOD via ITip. The source event must still contain the properties required by that transaction; validate the result when consuming untrusted or dynamically assembled data:

Validate a message against its method's constraints:

Within its documented transaction subset, validation also rejects duplicate or multi-valued METHOD and required singleton properties. Methods with attendee cardinality rules reject an ATTENDEE property that carries multiple calendar addresses.

ITip::reply() copies the complete source UID, DTSTART, RECURRENCE-ID (including RANGE), SEQUENCE, ORGANIZER, and matching ATTENDEE properties when present. It intentionally generates a fresh DTSTAMP, replaces the attendee's PARTSTAT, and removes RSVP because that request parameter is forbidden on a VEVENT REPLY; all other standard, IANA, and experimental parameters are retained. It rejects malformed/untyped or duplicate singleton UID, DTSTART, RECURRENCE-ID, SEQUENCE, or ORGANIZER metadata (and a missing UID) instead of producing a lossy reply. A newly created CANCEL also receives a fresh DTSTAMP. ITip::publish([]) is invalid and throws SchedulingException.

For the covered VEVENT transactions, validation requires one UTC DATE-TIME DTSTAMP without TZID, validates any SEQUENCE as one non-negative INTEGER, rejects the VTODO-only attendee states COMPLETED and IN-PROCESS, and rejects RSVP on REPLY. CANCEL requires SEQUENCE; its optional STATUS, when present, must be the singleton CANCELLED. REQUEST and CANCEL builders reject duplicate, multi-valued, untyped, or negative source SEQUENCE, and CANCEL refuses to overflow the RFC INTEGER range when incrementing it.

In the Laravel package, attaching an iTIP calendar advertises the method in the MIME type (text/calendar; method=REQUEST), so mail clients treat it as an invitation.

Custom & unknown properties

Add arbitrary properties with ->property() (it appends, so it can repeat):

When parsing, unsupported property values are retained as RawValue where the parser can recover (and unknown components become a GenericComponent), then re-emitted semantically. Canonical serialization and malformed-input limits still apply:

Strict vs lenient

Both the parser and serializer have a strict mode. Lenient is the default, because real-world .ics files frequently bend the RFC.

Mode Parser Serializer
Lenient (default) Recovers from violations; unparseable values become RawValue; duplicate parameters remain ambiguous Skips required-property checks but still enforces content-line-safe value encoding
Strict Throws on malformed structure and typed-value violations Enforces the package's selected required-property set

Error handling

Every exception implements Erenav\ICalendar\Exception\ICalendarException, so you can catch the whole family at once.

Gotchas & current limitations

Architecture

A layered, immutable object model. The canonical state of every component is its ordered property bag, which enables semantic preservation of supported unknown data.

Patterns in use: Composite (component tree), Builder (fluent construction), Strategy (Serializer interface), Factory (value-type construction), and a pipeline parser. See docs/PHASE-1-SPEC.md for the full design and decision record.

Testing

Use composer test (or vendor/bin/phpunit) when only the test suite is needed.

The suite is split into tests/Unit (per-class) and tests/Integration (serializer + round-trip). Round-trip stability is asserted as a fixed point: serialize(parse(x)) equals serialize(parse(serialize(parse(x)))).

Roadmap

Phase Scope Status
1 Core model, parse/serialize, Level-1 semantic preservation (documented RFC 5545 + 7986 subset) ✅ done
2 Recurrence + time zones — occurrencesBetween(), RECURRENCE-ID overrides, VTIMEZONE generation/typed components ✅ done
3 iTIP scheduling (RFC 5546) — METHOD, message builders, validation ✅ done
4 erenav/laravel-icalendar — service provider, facade, Eloquent mapping, feeds, Artisan, notifications ✅ released separately
5 jCal/xCal serializers and byte-fidelity round-trip someday

License

MIT


All versions of icalendar with dependencies

PHP Build Version
Package Version
Requires php Version >=8.3
rlanvin/php-rrule Version ^2.6
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package erenav/icalendar contains the following files

Loading the files please wait ...