VObject 3 documentation
=======================

The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545)
and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP.
The goal of the VObject library is to create a very complete library, with an easy to use API.

This project is a spin-off from [SabreDAV](http://code.google.com/p/sabredav/), where it has
been used for several years. The VObject library has 100% unittest coverage.

VObject 3 is the new and improved version of the library.

Notable new stuff:

* New and improved parser.
* Better support for vCard 2.1.
* Support for RFC6868.
* Serializing to jCard/jCal.
* Lots of tiny API improvements that combined make everything much easier.

Installation
------------

VObject requires PHP 5.3, and should be installed using composer.
The general composer instructions can be found on the [composer website](http://getcomposer.org/doc/00-intro.md composer website).

After that, just declare the VObject dependency as follows:

```
"require" : {
    "sabre/vobject" : "3.0.*"
}
```

Then, run `composer.phar update` and you should be good.

Usage
-----

A few notes about the examples:

1. The assumption for every example, is that the VObject source has been
   included.
2. It's also assumed that `use Sabre\VObject` has been called to import the
   VObject namespace.
3. Short-array syntax is used everywhere, which requires PHP 5.4. If you are
   still on PHP 5.3, replace `[...]` with `array(...)` where appropriate.

### Creating vCards.

To create a vCard, you can simply instantiate the vCard component, and pass
the properties you need:

```php
<?php

$vcard = new VObject\Component\VCard([
    'FN'  => 'Cowboy Henk',
    'TEL' => '+1 555 34567 455',
    'N'   => ['Henk', 'Cowboy', '', 'Dr.', 'MD'],
]);

echo $vcard->serialize();
```


This will output:

```
BEGIN:VCARD
VERSION:3.0
PRODID:-//Sabre//Sabre VObject 3.0.0-alpha5//EN
FN:Cowboy Henk
TEL:+1 555 34567 455
N:Henk;Cowboy;;Dr.;MD
END:VCARD
```


### Adding properties

Certain properties, such as `TEL`, `ADR` or `EMAIL` may appear more than once.
To add any additional properties, use the `add()` method on the vCard.

```php
<?php

$vcard->add('TEL', '+1 555 34567 456', ['type' => 'fax']);
```

The third argument of the add() method allows you to specify a list of
parameters.

### Manipulating properties

The vCard also allows object-access to manipulate properties:

```php
<?php

// Overwrites or sets a property:
$vcard->FN = 'Doctor McNinja';

// Removes a property
unset($vcard->FN);

// Checks for existence of a property:

isset($vcard->FN);
```

### Working with parameters

To get access to a parameter, you can simply use array-access:

```php
$type = $vcard->TEL['TYPE']:
echo (string)$type;
```

Parameters can also appear multiple times. To get to their values, just loop
through them:

```php
if ($param = $vcard->TEL['TYPE']) {
    foreach($param as $value) {
      echo $value, "\n";
    }
}
```

To change parameters for properties, you can use array-access syntax:

```php
<?php

$vcard->TEL['TYPE'] = ['WORK','FAX']:
```

Or when you're working with singular parameters:

```php
$vcard->TEL['PREF'] = 1;
```

It is also possible add a list of parameters while creating the property.

```php
$vcard->add(
    'EMAIL',
    'foo@example.org',
    [
        'type' => ['home', 'work'],
        'pref' => 1,
    ]
);
```

### Parsing vCard or iCalendar

To parse a vCard or iCalendar object, simply call:

```php
<?php

// $data must be either a string, or a stream.
$vcard = VObject\Reader::read($data);
```

When you're working with vCards generated by broken software (such as the
latest Microsoft Outlook), you can pass a 'forgiving' option that will do an
attempt to mend the broken data.

```php
<?php

$vcard = VObject\Reader::read($data, VObject\Reader::OPTION_FORGIVING);
```

### Reading property values

For properties that are stored as a string, you can simply call:

```php
<?php

echo (string)$vcard->FN;
```

For properties that contain more than 1 part, such as `ADR`, `N` or `ORG` you
can call `getParts()`.

```php
<?php

print_r(
    $vcard->ORG->getParts();
);
```

### Looping through properties.

Properties such as `ADR`, `EMAIL` and `TEL` may appear more than once in a
vCard. To loop through them, you can simply throw them in a `foreach()`
statement:

```php
<?php

foreach($vcard->TEL as $tel) {
    echo "Phone number: ", (string)$tel, "\n";
}

foreach($vcard->ADR as $adr) {
    print_r($adr->getParts());
}
```

### vCard property grouping

It's allowed in vCards to group multiple properties together with an arbitrary
string.

Apple clients use this feature to assign custom labels to things like phone
numbers and email addresses. Below is an example:

```
BEGIN:VCARD
VERSION:3.0
groupname.TEL:+1 555 12342567
groupname.X-ABLABEL:UK number
END:VCARD
```


In our example, you can see that the TEL properties are prefixed. These are 'groups' and
allow you to group multiple related properties together.

In most situations these group names are ignored, so when you execute the following
example, the `TEL` properties are still traversed.

```php
<?php

foreach($vcard->TEL as $tel) {
    echo (string)$tel, "\n";
}
```

But if you would like to target a specific group + property, this is possible too:

```php
<?php

echo (string)$vcard->{'groupname.TEL'};
```

To expand that example a little bit; if you'd like to traverse through all phone
numbers and display their custom labels, you'd do something like this:


```php
<?php

foreach($vcard->TEL as $tel) {

    echo (string)$vcard->{$tel->group . '.X-ABLABEL'}, ": ";
    echo (string)$tel, "\n";

}
```

### iCalendar

iCalendar works much the same way as vCards, but has a couple of features
that vCard does not.

First, in vCard there's only 1 component (everything between `BEGIN:VCARD`
and `END:VCARD`), but in iCalendar, there are nested components.

A simple illustration, lets create an iCalendar that contains an event.

```php
<?php

$vcalendar = new VObject\Component\VCalendar();

$vcalendar->add('VEVENT', [
    'SUMMARY' => 'Birthday party',
    'DTSTART' => new \DateTime('2013-04-07'),
    'RRULE' => 'FREQ=YEARLY',
]);

echo $vcalendar->serialize();
```

This will output the following:

```
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 3.0.0-alpha5//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:Birthday party
DTSTART;TZID=Europe/London;VALUE=DATE-TIME:20130407T000000
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR
```


The add() method will always return the instance of the property or
sub-component it's creating. This makes for easy further manipulation.

Here's another example that adds an attendee and an organizer:

```php
<?php

$vcalendar = new VObject\Component\VCalendar();

$vevent = $vcalendar->add('VEVENT', [
    'SUMMARY' => 'Meeting',
    'DTSTART' => new \DateTime('2013-04-07'),
]);

$vevent->add('ORGANIZER','mailto:organizer@example.org');
$vevent->add('ATTENDEE','mailto:attendee1@example.org');
$vevent->add('ATTENDEE','mailto:attendee2@example.org');
```

### Date and time handling

Parsing Dates and Times from iCalendar and vCard can be difficult.
Most of this is abstracted by the VObject library.

Given an event, in a calendar, you can get a real PHP `DateTime` object using
the following syntax:

```php
<?php

$start = $vcalendar->VEVENT->DTSTART->getDateTime();
echo $start->format(\DateTime::W3C);
```

To update the property with a new `DateTime` object, just use the following syntax:

```php
<?php

$dateTime = new \DateTime('2012-08-07 23:53:00', new \DateTimeZone('Europe/Amsterdam'));
$event->DTSTART = $dateTime;
```

### Expanding recurrence rules

Recurrence rules allow events to recur, for example for a weekly meeting, or an anniversary.
This is done with the `RRULE` property. The `RRULE` property allows for a LOT of different
rules. VObject only implements the ones that actually appear in calendar software.

To read more about `RRULE` and all the options, check out [RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5).
VObject supports the following options:

1. `UNTIL` for an end date.
2. `INTERVAL` for for example "every 2 days".
3. `COUNT` to stop recurring after x items.
4. `FREQ=DAILY` to recur every day, and `BYDAY` to limit it to certain days.
5. `FREQ=WEEKLY` to recur every week, `BYDAY` to expand this to multiple weekdays in every week and `WKST` to specify on which day the week starts.
6. `FREQ=MONTHLY` to recur every month, `BYMONTHDAY` to expand this to certain days in a month, `BYDAY` to expand it to certain weekdays occuring in a month, and `BYSETPOS` to limit the last two expansions.
7. `FREQ=YEARLY` to recur every year, `BYMONTH` to expand that to certain months in a year, and `BYDAY` and `BYWEEKDAY` to expand the `BYMONTH` rule even further.

VObject supports the `EXDATE` property for exclusions, but not yet the `RDATE` and `EXRULE`
properties. If you're interested in this, please file a github issue, as this will put it
on my radar.

This is a bit of a complex subject to go in excruciating detail. The
[RFC](https://tools.ietf.org/html/rfc5545#section-3.8.5) has a lot of examples though.

The hard part is not to write the RRULE, it is to expand them. The most complex and
hard-to-read code is hidden in this component. Dragons be here.

So, if we have a meeting every 2nd monday of the month, this would be specified as such:

```
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 2.0//EN
BEGIN:VEVENT
UID:1102c450-e0d7-11e1-9b23-0800200c9a66
DTSTART:20120109T140000Z
RRULE:FREQ=MONTHLY;BYDAY=MO;BYSETPOS=2
END:VEVENT
END:VCALENDAR
```

Note that I added in `UID` property. For simplicity I've kept it out of
previous examples, but do note that a `UID` property is required for all
`VEVENT`, `VTODO` and `VJOURNAL` objects!

To figure out all the meetings for this year, we can use the following syntax:

```php
<?php

$vcalendar = VObject\Reader::read($data);
$vcalendar->expand(new DateTime('2012-01-01'), new DateTime('2012-12-31'));
```

What the expand method does, is look at its inner events, and expand the recurring
rule. Our calendar now contains 12 events. The first will have its RRULE stripped,
and every subsequent VEVENT has the correct meeting date and a `RECURRENCE-ID` set.

This results in something like this:

```
BEGIN:VCALENDAR
  VERSION:2.0
  PRODID:-//Sabre//Sabre VObject 2.0//EN
  BEGIN:VEVENT
    UID:1102c450-e0d7-11e1-9b23-0800200c9a66
    DTSTART:20120109T140000Z
  END:VEVENT
  BEGIN:VEVENT
    UID:1102c450-e0d7-11e1-9b23-0800200c9a66
    RECURRENCE-ID:20120213T140000Z
    DTSTART:20120213T140000Z
  END:VEVENT
  BEGIN:VEVENT
    UID:1102c450-e0d7-11e1-9b23-0800200c9a66
    RECURRENCE-ID:20120312T140000Z
    DTSTART:20120312T140000Z
  END:VEVENT
  ..etc..
END:VCALENDAR
```

Note that I added some extra spaces for convenience..

To show the list of dates, we would do this as such:

```php
<?php

foreach($vcalendar->VEVENT as $vevent) {
    echo $vevent->DTSTART->getDateTime()->format(\DateTime::ATOM);
}
```

In a recurring event, single instances can also be overriden. VObject also takes these
into consideration. The reason we needed to specify a start and end-date, is because
some recurrence rules can be 'never ending'.

You should make sure you pick a sane date-range. Because if you pick a 50 year
time-range, for a daily recurring event; this would result in over 18K objects.

### Free-busy report generation

Some calendaring software can make use of FREEBUSY reports to show when people are
available.

You can automatically generate these reports from calendars using the `FreeBusyGenerator`.

Example based on our last event:

```php
<?php

// We're giving it the calendar object. It's also possible to specify multiple objects,
// by setting them as an array.
//
// We must also specify a start and end date, because recurring events are expanded.
$fbGenerator = new VObject\FreeBusyGenerator(
    new DateTime('2012-01-01'),
    new DateTime('2012-12-31'),
    $vcalendar
);

// Grabbing the report
$freebusy = $fbGenerator->result();

// The freebusy report is another VCALENDAR object, so we can serialize it as usual:
echo $freebusy->serialize();
```

The output of this script will look like this:

```
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 2.0//EN
CALSCALE:GREGORIAN
BEGIN:VFREEBUSY
DTSTART;VALUE=DATE-TIME:20111231T230000Z
DTEND;VALUE=DATE-TIME:20111231T230000Z
DTSTAMP;VALUE=DATE-TIME:20120808T131628Z
FREEBUSY;FBTYPE=BUSY:20120109T140000Z/20120109T140000Z
FREEBUSY;FBTYPE=BUSY:20120213T140000Z/20120213T140000Z
FREEBUSY;FBTYPE=BUSY:20120312T140000Z/20120312T140000Z
FREEBUSY;FBTYPE=BUSY:20120409T140000Z/20120409T140000Z
FREEBUSY;FBTYPE=BUSY:20120514T140000Z/20120514T140000Z
FREEBUSY;FBTYPE=BUSY:20120611T140000Z/20120611T140000Z
FREEBUSY;FBTYPE=BUSY:20120709T140000Z/20120709T140000Z
FREEBUSY;FBTYPE=BUSY:20120813T140000Z/20120813T140000Z
FREEBUSY;FBTYPE=BUSY:20120910T140000Z/20120910T140000Z
FREEBUSY;FBTYPE=BUSY:20121008T140000Z/20121008T140000Z
FREEBUSY;FBTYPE=BUSY:20121112T140000Z/20121112T140000Z
FREEBUSY;FBTYPE=BUSY:20121210T140000Z/20121210T140000Z
END:VFREEBUSY
END:VCALENDAR
```

### Converting to jCard

To create a json-version of your iCalendar or vCard, simply call
`jsonSerialize` on your component:

```php
<?php

echo json_encode($vcard->jsonSerialize());
```

The json formats are based on these draft RFCs:

* http://tools.ietf.org/html/draft-ietf-jcardcal-jcard-03
* http://tools.ietf.org/html/draft-kewisch-et-al-icalendar-in-json-02

Because these are still in draft, so is the jsonSerialize implementation. The
output format may therefore break between versions to comply with the latest
version of the spec.

### Parsing jCard and jCal.

To parse a jCard or jCal object, use the following snippet:

```php
<?php

$input = 'jcard.json';
$jCard = VObject\Reader::readJson(fopen('jcard.json', 'r'));

?>
```

You can pass either a json string, a readable stream, or an array if you
already called json_decode on the input.

This feature was added in sabre/vobject 3.1.

### Splitting export files

Generally when software makes backups of calendars or contacts, they will
put all the objects in a single file. In the case of vCards, this is often
a stream of VCARD objects, in the case of iCalendar, this tends to be a
single VCALENDAR objects, with many components.

Protocols such as Card- and CalDAV expect only 1 object per resource. The
vobject library provides 2 classes to split these backup files up into many.

To do this, use the splitter objects:

```php
<?php

// You can either pass a readable stream, or a string.
$h = fopen('backupfile.vcf', 'r');
$splitter = new VObject\Splitter\VCard($h);

while($vcard = $splitter->next()) {

    // $vCard is a single vCard object. You can just call serialize() on it
    // if you were looking for the string version.

}
```

Next to the VCard splitter, there's also an ICalendar splitter. The latter
creates a `VCALENDAR` object per `VEVENT`, `VTODO` or `VJOURNAL`, and ensures
that the `VTIMEZONE` information is kept intact, and any `VEVENT` objects that
belong together (because they are expections for an `RRULE` and thus have the
same `UID`) will be kept together, exactly like CalDAV expects.

### Converting between different vCard versions.

Since sabre/vobject 3.1, there's also a feature to convert between various
vCard versions. Currently it's possible to convert from vCard 2.1, 3.0 and
4.0 and to 3.0 and 4.0. It's not yet possible to convert to vCard 2.1.

To do this, simply call the convert() method on the vCard object.

```php
<?php

$input = <<<VCARD
BEGIN:VCARD
VERSION:2.1
FN;CHARSET=UTF-8:Foo
TEL;PREF;HOME:+1 555 555 555
END:VCARD
VCARD;

$vCard = VObject\Reader::read($input);
$vCard->convert(VObject\Document::VCARD40);

echo $vcard->serialize();

// This will output:
/*
BEGIN:VCARD
VERSION:4.0
FN:Foo
TEL;PREF=1;TYPE=HOME:+1 555 555 555
END:VCARD
*/
?>
```

Note that not everything can cleanly convert between versions, and it's
probable that there's a few properties that could be converted between
versions, but isn't yet. If you find something, open a feature request ticket
on Github.

Full API documentation
----------------------

Full API documentation can be found on github:

https://github.com/fruux/sabre-vobject/wiki/ApiIndex

Reading the source may also be helpful instead :)

CLI tool
--------

Since vObject 3.1, a new cli tool is shipped in the bin/ directory.

This tool has the following features:

* A `validate` command.
* A `repair` command to repair objects that are slightly broken.
* A `color` command, to show an iCalendar object or vCard on the console with
  ansi-colors, which may help debugging.
* A `convert` command, allowing you to convert between iCalendar 2.0, vCard 2.1,
  vCard 3.0, vCard 4.0, jCard and jCal.

Just run it using `bin/vobject`. Composer will automatically also put a
symlink in `vendor/bin` as well, or a directory of your choosing if you set
the `bin-dir` setting in your composer.json.

Support
-------

Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions.

