Example #1
0
    /**
     * 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;
            }
        }
    }
Example #2
0
/**
 * 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));
    }
}
Example #3
0
/**
 * 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));
    }
}
Example #4
0
 /**
  * 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));
 }