diff --git a/lib/ContextStackTrait.php b/lib/ContextStackTrait.php index d1b756f..ee3a3ba 100644 --- a/lib/ContextStackTrait.php +++ b/lib/ContextStackTrait.php @@ -57,6 +57,26 @@ trait ContextStackTrait { */ public $namespaceMap = []; + /** + * This is a list of custom serializers for specific classes. + * + * The writer may use this if you attempt to serialize an object with a + * class that does not implement XmlSerializable. + * + * Instead it will look at this classmap to see if there is a custom + * serializer here. This is useful if you don't want your value objects + * to be responsible for serializing themselves. + * + * The keys in this classmap need to be fully qualified PHP class names, + * the values must be callbacks. The callbacks take two arguments. The + * writer class, and the value that must be written. + * + * function (Writer $writer, object $value) + * + * @var array + */ + public $classMap = []; + /** * Backups of previous contexts. * @@ -78,7 +98,8 @@ function pushContext() { $this->contextStack[] = [ $this->elementMap, $this->contextUri, - $this->namespaceMap + $this->namespaceMap, + $this->classMap ]; } @@ -93,7 +114,8 @@ function popContext() { list( $this->elementMap, $this->contextUri, - $this->namespaceMap + $this->namespaceMap, + $this->classMap ) = array_pop($this->contextStack); } diff --git a/lib/Service.php b/lib/Service.php index 2aa1c9a..8625a00 100644 --- a/lib/Service.php +++ b/lib/Service.php @@ -38,6 +38,26 @@ class Service { */ public $namespaceMap = []; + /** + * This is a list of custom serializers for specific classes. + * + * The writer may use this if you attempt to serialize an object with a + * class that does not implement XmlSerializable. + * + * Instead it will look at this classmap to see if there is a custom + * serializer here. This is useful if you don't want your value objects + * to be responsible for serializing themselves. + * + * The keys in this classmap need to be fully qualified PHP class names, + * the values must be callbacks. The callbacks take two arguments. The + * writer class, and the value that must be written. + * + * function (Writer $writer, object $value) + * + * @var array + */ + public $classMap = []; + /** * Returns a fresh XML Reader * @@ -60,6 +80,7 @@ function getWriter() { $w = new Writer(); $w->namespaceMap = $this->namespaceMap; + $w->classMap = $this->classMap; return $w; } diff --git a/lib/Writer.php b/lib/Writer.php index 4edba33..d417479 100644 --- a/lib/Writer.php +++ b/lib/Writer.php @@ -100,8 +100,14 @@ function write($value) { if (is_scalar($value)) { $this->text($value); - } elseif ($value instanceof XmlSerializable) { + } elseif (is_object($value) && $value instanceof XmlSerializable) { + $value->xmlSerialize($this); + + } elseif (is_object($value) && isset($this->classMap[get_class($value)])) { + $this->classMap[get_class($value)]($this, $value); + } elseif (is_callable($value)) { + $value($this); } elseif (is_null($value)) { // noop } elseif (is_array($value)) { @@ -142,7 +148,11 @@ function write($value) { } elseif (is_object($value)) { - throw new InvalidArgumentException('The writer cannot serialize objects of type: ' . get_class($value)); + throw new InvalidArgumentException('The writer cannot serialize objects of class: ' . get_class($value)); + + } else { + + throw new InvalidArgumentException('The writer cannot serialize values of type: ' . gettype($value)); } diff --git a/tests/Sabre/Xml/WriterTest.php b/tests/Sabre/Xml/WriterTest.php index c48da51..44f581f 100644 --- a/tests/Sabre/Xml/WriterTest.php +++ b/tests/Sabre/Xml/WriterTest.php @@ -323,6 +323,49 @@ function testStartElementSimple() { $this->assertEquals($output, $this->writer->outputMemory()); + } + + function testCallback() { + + $this->compare([ + '{http://sabredav.org/ns}root' => function(Writer $writer) { + $writer->text('deferred writer'); + }, + ], << +deferred writer + +HI + ); + + } + + function testClassMap() { + + $obj = (object)[ + 'key1' => 'value1', + 'key2' => 'value2', + ]; + + $this->writer->classMap['stdClass'] = function(Writer $writer, $value) { + + foreach (get_object_vars($value) as $key => $val) { + $writer->writeElement('{http://sabredav.org/ns}' . $key, $val); + } + + }; + + $this->compare([ + '{http://sabredav.org/ns}root' => $obj + ], << + + value1 + value2 + + +HI + ); } }