/** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { $reader = new Reader(); // Wrapping the xml in a container, so root-less values can still be // parsed. $xml = <<<XML <?xml version="1.0"?> <xml-fragment xmlns="http://sabre.io/ns">{$this->getXml()}</xml-fragment> XML; $reader->xml($xml); $elementNamespace = null; while ($reader->read()) { if ($reader->depth < 1) { // Skipping the root node. continue; } switch ($reader->nodeType) { case Reader::ELEMENT: $writer->startElement($reader->getClark()); $empty = $reader->isEmptyElement; while ($reader->moveToNextAttribute()) { switch ($reader->namespaceURI) { case '': $writer->writeAttribute($reader->localName, $reader->value); break; case 'http://www.w3.org/2000/xmlns/': // Skip namespace declarations break; default: $writer->writeAttribute($reader->getClark(), $reader->value); break; } } if ($empty) { $writer->endElement(); } break; case Reader::CDATA: case Reader::TEXT: $writer->text($reader->value); break; case Reader::END_ELEMENT: $writer->endElement(); break; } } }
/** * This function is the 'default' serializer that is able to serialize most * things, and delegates to other serializers if needed. * * The standardSerializer supports a wide-array of values. * * $value may be a string or integer, it will just write out the string as text. * $value may be an instance of XmlSerializable or Element, in which case it * calls it's xmlSerialize() method. * $value may be a PHP callback/function/closure, in case we call the callback * and give it the Writer as an argument. * $value may be a an object, and if it's in the classMap we automatically call * the correct serializer for it. * $value may be null, in which case we do nothing. * * If $value is an array, the array must look like this: * * [ * [ * 'name' => '{namespaceUri}element-name', * 'value' => '...', * 'attributes' => [ 'attName' => 'attValue' ] * ] * [, * 'name' => '{namespaceUri}element-name2', * 'value' => '...', * ] * ] * * This would result in xml like: * * <element-name xmlns="namespaceUri" attName="attValue"> * ... * </element-name> * <element-name2> * ... * </element-name2> * * The value property may be any value standardSerializer supports, so you can * nest data-structures this way. Both value and attributes are optional. * * Alternatively, you can also specify the array using this syntax: * * [ * [ * '{namespaceUri}element-name' => '...', * '{namespaceUri}element-name2' => '...', * ] * ] * * This is excellent for simple key->value structures, and here you can also * specify anything for the value. * * You can even mix the two array syntaxes. * * @param Writer $writer * @param string|int|float|bool|array|object * @return void */ function standardSerializer(Writer $writer, $value) { if (is_scalar($value)) { // String, integer, float, boolean $writer->text($value); } elseif ($value instanceof XmlSerializable) { // XmlSerializable classes or Element classes. $value->xmlSerialize($writer); } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { // It's an object which class appears in the classmap. $writer->classMap[get_class($value)]($writer, $value); } elseif (is_callable($value)) { // A callback $value($writer); } elseif (is_null($value)) { // nothing! } elseif (is_array($value)) { foreach ($value as $name => $item) { if (is_int($name)) { // This item has a numeric index. We expect to be an array with a name and a value. if (!is_array($item) || !array_key_exists('name', $item)) { throw new InvalidArgumentException('When passing an array to ->write with numeric indices, every item must be an array containing at least the "name" key'); } $attributes = isset($item['attributes']) ? $item['attributes'] : []; $name = $item['name']; $item = isset($item['value']) ? $item['value'] : []; } elseif (is_array($item) && array_key_exists('value', $item)) { // This item has a text index. We expect to be an array with a value and optional attributes. $attributes = isset($item['attributes']) ? $item['attributes'] : []; $item = $item['value']; } else { // If it's an array with text-indices, we expect every item's // key to be an xml element name in clark notation. // No attributes can be passed. $attributes = []; } $writer->startElement($name); $writer->writeAttributes($attributes); $writer->write($item); $writer->endElement(); } } elseif (is_object($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)); } }
/** * This function is the 'default' serializer that is able to serialize most * things, and delegates to other serializers if needed. * * The standardSerializer supports a wide-array of values. * * $value may be a string or integer, it will just write out the string as text. * $value may be an instance of XmlSerializable or Element, in which case it * calls it's xmlSerialize() method. * $value may be a PHP callback/function/closure, in case we call the callback * and give it the Writer as an argument. * $value may be a an object, and if it's in the classMap we automatically call * the correct serializer for it. * $value may be null, in which case we do nothing. * * If $value is an array, the array must look like this: * * [ * [ * 'name' => '{namespaceUri}element-name', * 'value' => '...', * 'attributes' => [ 'attName' => 'attValue' ] * ] * [, * 'name' => '{namespaceUri}element-name2', * 'value' => '...', * ] * ] * * This would result in xml like: * * <element-name xmlns="namespaceUri" attName="attValue"> * ... * </element-name> * <element-name2> * ... * </element-name2> * * The value property may be any value standardSerializer supports, so you can * nest data-structures this way. Both value and attributes are optional. * * Alternatively, you can also specify the array using this syntax: * * [ * [ * '{namespaceUri}element-name' => '...', * '{namespaceUri}element-name2' => '...', * ] * ] * * This is excellent for simple key->value structures, and here you can also * specify anything for the value. * * You can even mix the two array syntaxes. * * @param Writer $writer * @param string|int|float|bool|array|object * @return void */ function standardSerializer(Writer $writer, $value) { if (is_scalar($value)) { // String, integer, float, boolean $writer->text($value); } elseif ($value instanceof XmlSerializable) { // XmlSerializable classes or Element classes. $value->xmlSerialize($writer); } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { // It's an object which class appears in the classmap. $writer->classMap[get_class($value)]($writer, $value); } elseif (is_callable($value)) { // A callback $value($writer); } elseif (is_null($value)) { // nothing! } elseif (is_array($value) && array_key_exists('name', $value)) { // if the array had a 'name' element, we assume that this array // describes a 'name' and optionally 'attributes' and 'value'. $name = $value['name']; $attributes = isset($value['attributes']) ? $value['attributes'] : []; $value = isset($value['value']) ? $value['value'] : null; $writer->startElement($name); $writer->writeAttributes($attributes); $writer->write($value); $writer->endElement(); } elseif (is_array($value)) { foreach ($value as $name => $item) { if (is_int($name)) { // This item has a numeric index. We just loop through the // array and throw it back in the writer. standardSerializer($writer, $item); } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { // The key is used for a name, but $item has 'attributes' and // possibly 'value' $writer->startElement($name); $writer->writeAttributes($item['attributes']); if (isset($item['value'])) { $writer->write($item['value']); } $writer->endElement(); } elseif (is_string($name)) { // This was a plain key-value array. $writer->startElement($name); $writer->write($item); $writer->endElement(); } else { throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: ' . gettype($name)); } } } elseif (is_object($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)); } }
/** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { $writer->text(\Sabre\Uri\resolve($writer->contextUri, $this->value)); }