| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- <?php
- namespace Sabre\VObject\Parser;
- use Sabre\VObject\Component;
- use Sabre\VObject\Component\VCalendar;
- use Sabre\VObject\Component\VCard;
- use Sabre\VObject\EofException;
- use Sabre\VObject\ParseException;
- use Sabre\Xml as SabreXml;
- /**
- * XML Parser.
- *
- * This parser parses both the xCal and xCard formats.
- *
- * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
- * @author Ivan Enderlin
- * @license http://sabre.io/license/ Modified BSD License
- */
- class XML extends Parser
- {
- const XCAL_NAMESPACE = 'urn:ietf:params:xml:ns:icalendar-2.0';
- const XCARD_NAMESPACE = 'urn:ietf:params:xml:ns:vcard-4.0';
- /**
- * The input data.
- *
- * @var array
- */
- protected $input;
- /**
- * A pointer/reference to the input.
- *
- * @var array
- */
- private $pointer;
- /**
- * Document, root component.
- *
- * @var \Sabre\VObject\Document
- */
- protected $root;
- /**
- * Creates the parser.
- *
- * Optionally, it's possible to parse the input stream here.
- *
- * @param mixed $input
- * @param int $options any parser options (OPTION constants)
- */
- public function __construct($input = null, $options = 0)
- {
- if (0 === $options) {
- $options = parent::OPTION_FORGIVING;
- }
- parent::__construct($input, $options);
- }
- /**
- * Parse xCal or xCard.
- *
- * @param resource|string $input
- * @param int $options
- *
- * @throws \Exception
- *
- * @return \Sabre\VObject\Document
- */
- public function parse($input = null, $options = 0)
- {
- if (!is_null($input)) {
- $this->setInput($input);
- }
- if (0 !== $options) {
- $this->options = $options;
- }
- if (is_null($this->input)) {
- throw new EofException('End of input stream, or no input supplied');
- }
- switch ($this->input['name']) {
- case '{'.self::XCAL_NAMESPACE.'}icalendar':
- $this->root = new VCalendar([], false);
- $this->pointer = &$this->input['value'][0];
- $this->parseVCalendarComponents($this->root);
- break;
- case '{'.self::XCARD_NAMESPACE.'}vcards':
- foreach ($this->input['value'] as &$vCard) {
- $this->root = new VCard(['version' => '4.0'], false);
- $this->pointer = &$vCard;
- $this->parseVCardComponents($this->root);
- // We just parse the first <vcard /> element.
- break;
- }
- break;
- default:
- throw new ParseException('Unsupported XML standard');
- }
- return $this->root;
- }
- /**
- * Parse a xCalendar component.
- */
- protected function parseVCalendarComponents(Component $parentComponent)
- {
- foreach ($this->pointer['value'] ?: [] as $children) {
- switch (static::getTagName($children['name'])) {
- case 'properties':
- $this->pointer = &$children['value'];
- $this->parseProperties($parentComponent);
- break;
- case 'components':
- $this->pointer = &$children;
- $this->parseComponent($parentComponent);
- break;
- }
- }
- }
- /**
- * Parse a xCard component.
- */
- protected function parseVCardComponents(Component $parentComponent)
- {
- $this->pointer = &$this->pointer['value'];
- $this->parseProperties($parentComponent);
- }
- /**
- * Parse xCalendar and xCard properties.
- *
- * @param string $propertyNamePrefix
- */
- protected function parseProperties(Component $parentComponent, $propertyNamePrefix = '')
- {
- foreach ($this->pointer ?: [] as $xmlProperty) {
- list($namespace, $tagName) = SabreXml\Service::parseClarkNotation($xmlProperty['name']);
- $propertyName = $tagName;
- $propertyValue = [];
- $propertyParameters = [];
- $propertyType = 'text';
- // A property which is not part of the standard.
- if (self::XCAL_NAMESPACE !== $namespace
- && self::XCARD_NAMESPACE !== $namespace) {
- $propertyName = 'xml';
- $value = '<'.$tagName.' xmlns="'.$namespace.'"';
- foreach ($xmlProperty['attributes'] as $attributeName => $attributeValue) {
- $value .= ' '.$attributeName.'="'.str_replace('"', '\"', $attributeValue).'"';
- }
- $value .= '>'.$xmlProperty['value'].'</'.$tagName.'>';
- $propertyValue = [$value];
- $this->createProperty(
- $parentComponent,
- $propertyName,
- $propertyParameters,
- $propertyType,
- $propertyValue
- );
- continue;
- }
- // xCard group.
- if ('group' === $propertyName) {
- if (!isset($xmlProperty['attributes']['name'])) {
- continue;
- }
- $this->pointer = &$xmlProperty['value'];
- $this->parseProperties(
- $parentComponent,
- strtoupper($xmlProperty['attributes']['name']).'.'
- );
- continue;
- }
- // Collect parameters.
- foreach ($xmlProperty['value'] as $i => $xmlPropertyChild) {
- if (!is_array($xmlPropertyChild)
- || 'parameters' !== static::getTagName($xmlPropertyChild['name'])) {
- continue;
- }
- $xmlParameters = $xmlPropertyChild['value'];
- foreach ($xmlParameters as $xmlParameter) {
- $propertyParameterValues = [];
- foreach ($xmlParameter['value'] as $xmlParameterValues) {
- $propertyParameterValues[] = $xmlParameterValues['value'];
- }
- $propertyParameters[static::getTagName($xmlParameter['name'])]
- = implode(',', $propertyParameterValues);
- }
- array_splice($xmlProperty['value'], $i, 1);
- }
- $propertyNameExtended = ($this->root instanceof VCalendar
- ? 'xcal'
- : 'xcard').':'.$propertyName;
- switch ($propertyNameExtended) {
- case 'xcal:geo':
- $propertyType = 'float';
- $propertyValue['latitude'] = 0;
- $propertyValue['longitude'] = 0;
- foreach ($xmlProperty['value'] as $xmlRequestChild) {
- $propertyValue[static::getTagName($xmlRequestChild['name'])]
- = $xmlRequestChild['value'];
- }
- break;
- case 'xcal:request-status':
- $propertyType = 'text';
- foreach ($xmlProperty['value'] as $xmlRequestChild) {
- $propertyValue[static::getTagName($xmlRequestChild['name'])]
- = $xmlRequestChild['value'];
- }
- break;
- case 'xcal:freebusy':
- $propertyType = 'freebusy';
- // We don't break because we only want to set
- // another property type.
- // no break
- case 'xcal:categories':
- case 'xcal:resources':
- case 'xcal:exdate':
- foreach ($xmlProperty['value'] as $specialChild) {
- $propertyValue[static::getTagName($specialChild['name'])]
- = $specialChild['value'];
- }
- break;
- case 'xcal:rdate':
- $propertyType = 'date-time';
- foreach ($xmlProperty['value'] as $specialChild) {
- $tagName = static::getTagName($specialChild['name']);
- if ('period' === $tagName) {
- $propertyParameters['value'] = 'PERIOD';
- $propertyValue[] = implode('/', $specialChild['value']);
- } else {
- $propertyValue[] = $specialChild['value'];
- }
- }
- break;
- default:
- $propertyType = static::getTagName($xmlProperty['value'][0]['name']);
- foreach ($xmlProperty['value'] as $value) {
- $propertyValue[] = $value['value'];
- }
- if ('date' === $propertyType) {
- $propertyParameters['value'] = 'DATE';
- }
- break;
- }
- $this->createProperty(
- $parentComponent,
- $propertyNamePrefix.$propertyName,
- $propertyParameters,
- $propertyType,
- $propertyValue
- );
- }
- }
- /**
- * Parse a component.
- */
- protected function parseComponent(Component $parentComponent)
- {
- $components = $this->pointer['value'] ?: [];
- foreach ($components as $component) {
- $componentName = static::getTagName($component['name']);
- $currentComponent = $this->root->createComponent(
- $componentName,
- null,
- false
- );
- $this->pointer = &$component;
- $this->parseVCalendarComponents($currentComponent);
- $parentComponent->add($currentComponent);
- }
- }
- /**
- * Create a property.
- *
- * @param string $name
- * @param array $parameters
- * @param string $type
- * @param mixed $value
- */
- protected function createProperty(Component $parentComponent, $name, $parameters, $type, $value)
- {
- $property = $this->root->createProperty(
- $name,
- null,
- $parameters,
- $type
- );
- $parentComponent->add($property);
- $property->setXmlValue($value);
- }
- /**
- * Sets the input data.
- *
- * @param resource|string $input
- */
- public function setInput($input)
- {
- if (is_resource($input)) {
- $input = stream_get_contents($input);
- }
- if (is_string($input)) {
- $reader = new SabreXml\Reader();
- $reader->elementMap['{'.self::XCAL_NAMESPACE.'}period']
- = XML\Element\KeyValue::class;
- $reader->elementMap['{'.self::XCAL_NAMESPACE.'}recur']
- = XML\Element\KeyValue::class;
- $reader->xml($input);
- $input = $reader->parse();
- }
- $this->input = $input;
- }
- /**
- * Get tag name from a Clark notation.
- *
- * @param string $clarkedTagName
- *
- * @return string
- */
- protected static function getTagName($clarkedTagName)
- {
- list(, $tagName) = SabreXml\Service::parseClarkNotation($clarkedTagName);
- return $tagName;
- }
- }
|