clubmaster / vobject
PHP 的 VObject 库允许您轻松解析和操作 iCalendar 和 vCard 对象
Requires
- php: >=5.3.1
This package is not auto-updated.
Last update: 2024-09-22 03:14:00 UTC
README
VObject 库允许您使用 PHP 轻松解析和操作 iCalendar 和 vCard 对象。VObject 库的目标是创建一个非常完整的库,具有易于使用的 API。
该项目是从 SabreDAV 分离出来的,该项目已经使用了数年。VObject 库有 100% 的单元测试覆盖率。
安装
VObject 需要 PHP 5.3,应使用 composer 进行安装。有关 composer 的一般说明可以在 [composer 网站](https://getcomposer.org.cn/doc/00-intro.md composer 网站上找到)。
之后,只需如下声明 vobject 依赖项
"require" : {
"sabre/vobject" : "dev-master"
}
然后,运行 composer.phar update
,您应该就可以正常使用了。但是,一旦第一个版本发布,您应该将 dev-master
切换到 2.0.*
。
使用
解析
在我们的示例中,我们将使用以下 vCard
BEGIN:VCARD
VERSION:3.0
PRODID:-//Sabre//Sabre VObject 2.0//EN
N:Planck;Max;;;
FN:Max Planck
EMAIL;TYPE=WORK:mplanck@example.org
item1.TEL;TYPE=CELL:(+49)3144435678
item1.X-ABLabel:Private cell
item2.TEL;TYPE=WORK:(+49)5554564744
item2.X-ABLabel:Work
END:VCARD
如果我们只想打印出 Max 的全名,我们可以直接使用属性访问
use Sabre\VObject; $card = VObject\Reader::read($data); echo $card->FN;
更改属性
创建属性与这非常相似。如果我们想添加他的生日,我们只需设置属性
$card->BDAY = '1858-04-23';
注意,在上面的示例中,我们实际上正在更新可能已经存在的任何 BDAY。如果我们想添加一个新的属性,而不是覆盖先前的属性,我们可以使用 add
方法。
$card->add('EMAIL','max@example.org');
参数
如果我们想同时指定这是 max 的家庭电子邮件地址,我们可以使用第三个参数
$card->add('EMAIL', 'max@example'org', array('type' => 'HOME'));
如果我们想读取所有电子邮件地址及其类型,这将看起来像这样
foreach($card->EMAIL as $email) {
echo $email['TYPE'], ' - ', $email;
}
组
在我们的示例中,您可以看到 TEL 属性是前缀的。这些都是 '组',允许您将多个相关属性组合在一起。组可以是任何用户定义的名称。
这个特定的示例是由 OS X 地址簿生成的,它使用 X-ABLabel
允许用户为属性指定自定义标签。OS X 地址簿使用组将标签与属性关联。
如果未指定组,VObject 库会简单地忽略它,因此这将起作用
foreach($card->TEL as $tel) { echo $tel, "\n"; }
但是,如果您想针对特定的组 + 属性,这也是可能的
echo $card->{'ITEM1.TEL'};
因此,如果您想显示所有电话号码及其自定义标签,将使用以下语法
foreach($card->TEL as $tel) { echo $card->{$tel->group . '.X-ABLABEL'}, ": "; echo $tel, "\n"; }
序列化/保存
如果您想生成更新的 VObject,只需调用 serialize() 方法即可。
echo $card->serialize();
组件
iCalendar 与 vCard 不同,始终有子组件。vCards 通常只是扁平列表,而 iCalendar 对象倾向于具有树状结构。以下段落将使用以下对象作为示例
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 2.0//EN
BEGIN:VEVENT
SUMMARY:Curiosity landing
DTSTART:20120806T051439Z
LOCATION:Mars
END:VEVENT
END:VCALENDAR
由于事件、任务和日志始终在子组件中,因此我们也是这样访问它们的。
use Sabre\VObject; $calendar = VObject\Reader::read($data); echo $calendar->VEVENT->SUMMARY;
使用工厂方法向日历添加组件
$event = VObject\Component::create('VEVENT'); $calendar->add($event); $event->SUMMARY = 'Curiosity launch'; $event->DTSTART = '20111126T150202Z'; $event->LOCATION = 'Cape Carnival';
顺便说一句,克隆也按预期工作,因为整个结构都会与其一起克隆。
$clonedEvent = clone $calendar->VEVENT[0]; $calendar->add($clonedEvent);
日期和时间处理
如果您曾经处理过iCalendar时区,您就知道这可能会很复杂。指定时区的方式有缺陷,这可能是我在某一天可能会写一篇文章的主题。VObject尽力确定正确的时区。已经测试和验证了许多标准格式,并为微软生成的时区信息和其他情况实现了特殊代码。
要获取真正的php DateTime
对象,您可以按如下方式请求:
$event = $calendar->VEVENT; $start = $event->DTSTART->getDateTime(); echo $start->format(\DateTime::W3C);
要使用DateTime对象设置属性,可以使用以下语法:
$dateTime = new \DateTime('2012-08-07 23:53:00', new DateTimeZone('Europe/Amsterdam')); $event->DTSTART->setDateTime($dateTime, VObject\Property\DateTime::DATE);
第二个参数指定了您设置的日期类型。以下有三种选项:
LOCAL
这是一个浮点时间,没有时区信息。这基本上是指定事件在当前的时区发生。这将被编码为DTSTART;VALUE=DATE-TIME:20120807235300
。UTC
这指定时间应以UTC时间编码。这被编码为DTSTART;VALUE=DATE-TIME:20120807205300Z
。注意额外的Z以及它“提前”两个小时。LOCALTZ
指定它应该以本地时区编码。例如DTSTART;VALUE=DATE-TIME;TZID=Europe/Amsterdam:20120807235300
。DATE
这是一个仅包含日期,不包含时间的日期。在这种情况下,这将编码为DTSTART;VALUE=DATE:20120807
。
一些重要的注意事项
- 当指定了
TZID
时,还应有一个与之匹配的包含所有时区信息的VTIMEZONE
对象。VObject目前不能自动嵌入此信息。然而,在实际中,其他客户端似乎在没有这些信息的情况下也能很好地工作。然而,为了完整性,这将会在将来添加。 - 如前所述,时区确定过程有时可能会失败。请报告您发现的任何问题,我会尽快添加解决方案!
重复规则
重复规则允许事件重复,例如对于每周会议或周年纪念。这是通过RRULE
属性完成的。RRULE
属性允许有很多不同的规则。VObject仅实现了在日历软件中实际出现的那些规则。
要了解更多关于RRULE
及其所有选项的信息,请查看RFC5545。VObject支持以下选项:
UNTIL
用于结束日期。INTERVAL
例如“每隔两天”。COUNT
在x项之后停止重复。FREQ=DAILY
用于每天重复,以及BYDAY
用于限制某些天数。FREQ=WEEKLY
用于每周重复,BYDAY
用于将此扩展到每周的多个工作日,以及WKST
用于指定周的开始日。FREQ=MONTHLY
用于每月重复,BYMONTHDAY
用于将此扩展到一个月中的某些天,BYDAY
用于将此扩展到一个月中的某些工作日,以及BYSETPOS
用于限制最后两个扩展。FREQ=YEARLY
用于每年重复,BYMONTH
用于将此扩展到一年中的某些月份,以及BYDAY
和BYWEEKDAY
用于进一步扩展BYMONTH
规则。
VObject支持用于排除的EXDATE
属性,但不支持RDATE
和EXRULE
属性。如果您对此感兴趣,请提交github问题,因为这会使它出现在我的雷达上。
这是一个相当复杂的话题,无法详细说明。然而,《RFC》有很多示例。
困难的部分不是编写RRULE,而是扩展它们。最复杂且难以阅读的代码隐藏在这个组件中。龙在这里。
因此,如果我们在每月第二个星期一开会,这将指定如下:
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:1102c450-e0d7-11e1-9b23-0800200c9a66
DTSTART:20120109T140000Z
RRULE:FREQ=MONTHLY;BYDAY=MO;BYSETPOS=2
END:VEVENT
END:VCALENDAR
请注意,通常不允许这样缩进对象,但这确实使阅读更加容易。这也是我第一次添加UID,这是所有VEVENT、VTODO和VJOURNAL对象必需的!
为了找出今年所有的会议,我们可以使用以下语法
use Sabre\VObject; $calendar = VObject\Reader::read($data); $calendar->expand(new DateTime('2012-01-01'), new DateTime('2012-12-31'));
展开方法的作用是查看其内部事件,并展开重复规则。我们的日历现在包含12个事件。第一个将移除RRULE,每个后续的VEVENT将具有正确的会议日期和一个RECURRENCE-ID
设置。
结果如下所示
BEGIN:VCALENDAR
VERSION:2.0
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
要显示日期列表,我们将这样做
foreach($calendar->VEVENT as $event) { echo $event->DTSTART->getDateTime()->format(\DateTime::ATOM); }
在重复事件中,也可以覆盖单个实例。VObject也会考虑这些。我们需要指定开始和结束日期的原因是,有些重复规则可以是“永不结束”。
您应该确保选择一个合理的日期范围。因为如果您选择一个50年的时间范围,对于每天重复的事件;这将导致超过18K个对象。
空闲忙碌报告生成
某些日历软件可以利用空闲忙碌报告来显示人们何时可用。
您可以使用FreeBusyGenerator
从日历自动生成这些报告。
以下是基于我们最近事件的一个示例
// 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'), $calendar ); // Grabbing the report $freebusy = $fbGenerator->result(); // The freebusy report is another VCALENDAR object, so we can serialize it as usual: echo $freebusy->serialize();
此脚本的输出将如下所示
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