diff --git a/lib/Document.php b/lib/Document.php index 8319b49b..af47d261 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -188,7 +188,15 @@ public function createProperty(string $name, $value = null, ?array $parameters = $valueType = $parameters['VALUE'] ?? null; } - if ($valueType) { + // The VALUE parameter must have a single value, but some producers repeat + // it (e.g. VALUE=text;VALUE=TEXT) or list several types in it (e.g. + // VALUE=uri,text). The parser then passes all its values as an array: use + // the first one. + if (\is_array($valueType)) { + $valueType = reset($valueType); + } + + if (\is_string($valueType) && '' !== $valueType) { // The valueType argument comes first to figure out the correct // class. $class = $this->getClassNameForPropertyValue($valueType); diff --git a/tests/VObject/DocumentTest.php b/tests/VObject/DocumentTest.php index d03a8883..462c47c6 100644 --- a/tests/VObject/DocumentTest.php +++ b/tests/VObject/DocumentTest.php @@ -52,6 +52,16 @@ public function testCreate(): void self::assertInstanceOf(Property\Text::class, $prop); } + public function testCreatePropertyWithRepeatedValueParameter(): void + { + $vcard = new Component\VCard([], false); + + $prop = $vcard->createProperty('PRODID', 'foo', ['VALUE' => ['text', 'TEXT']]); + + self::assertInstanceOf(Property\Text::class, $prop); + self::assertEquals('foo', $prop->getValue()); + } + public function testGetClassNameForPropertyValue(): void { $vcal = new Component\VCalendar([], false); diff --git a/tests/VObject/ReaderTest.php b/tests/VObject/ReaderTest.php index 7b6c953f..82fd9b73 100644 --- a/tests/VObject/ReaderTest.php +++ b/tests/VObject/ReaderTest.php @@ -230,6 +230,33 @@ public function testReadPropertyNoName(): void self::assertEquals('PRODIGY', $result->parameters['TYPE']); } + public function testReadPropertyWithRepeatedValueParameter(): void + { + // ez-vcard writes the VALUE parameter twice. + $data = "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID;VALUE=text;VALUE=TEXT:ez-vcard 0.12.1\r\nEND:VCARD"; + $result = Reader::read($data); + + $result = $result->PRODID; + + self::assertInstanceOf(Property::class, $result); + self::assertEquals('ez-vcard 0.12.1', $result->getValue()); + self::assertEquals(['text', 'TEXT'], $result['VALUE']->getParts()); + } + + public function testReadPropertyWithListedValueParameter(): void + { + // ez-vcard lists several value types in a single VALUE parameter. + $data = "BEGIN:VCARD\r\nVERSION:3.0\r\nURL;VALUE=uri,text:https://jane.example\r\nEND:VCARD"; + $result = Reader::read($data); + + $result = $result->URL; + + self::assertInstanceOf(Property::class, $result); + self::assertEquals('https://jane.example', $result->getValue()); + self::assertEquals(['uri', 'text'], $result['VALUE']->getParts()); + self::assertEquals("URL;VALUE=uri,text:https://jane.example\r\n", $result->serialize()); + } + public function testReadPropertyParameterExtraColon(): void { $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring\r\nEND:VCALENDAR";