aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/xml/lib/Serializer/functions.php
blob: 8d0330558cd2653cf6e38e19a4e36189f88a1783 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<?php

declare(strict_types=1);

namespace Sabre\Xml\Serializer;

use InvalidArgumentException;
use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable;

/**
 * This file provides a number of 'serializer' helper functions.
 *
 * These helper functions can be used to easily xml-encode common PHP
 * data structures, or can be placed in the $classMap.
 */

/**
 * The 'enum' serializer writes simple list of elements.
 *
 * For example, calling:
 *
 * enum($writer, [
 *   "{http://sabredav.org/ns}elem1",
 *   "{http://sabredav.org/ns}elem2",
 *   "{http://sabredav.org/ns}elem3",
 *   "{http://sabredav.org/ns}elem4",
 *   "{http://sabredav.org/ns}elem5",
 * ]);
 *
 * Will generate something like this (if the correct namespace is declared):
 *
 * <s:elem1 />
 * <s:elem2 />
 * <s:elem3 />
 * <s:elem4>content</s:elem4>
 * <s:elem5 attr="val" />
 *
 * @param string[] $values
 */
function enum(Writer $writer, array $values)
{
    foreach ($values as $value) {
        $writer->writeElement($value);
    }
}

/**
 * The valueObject serializer turns a simple PHP object into a classname.
 *
 * Every public property will be encoded as an xml element with the same
 * name, in the XML namespace as specified.
 *
 * Values that are set to null or an empty array are not serialized. To
 * serialize empty properties, you must specify them as an empty string.
 *
 * @param object $valueObject
 */
function valueObject(Writer $writer, $valueObject, string $namespace)
{
    foreach (get_object_vars($valueObject) as $key => $val) {
        if (is_array($val)) {
            // If $val is an array, it has a special meaning. We need to
            // generate one child element for each item in $val
            foreach ($val as $child) {
                $writer->writeElement('{'.$namespace.'}'.$key, $child);
            }
        } elseif (null !== $val) {
            $writer->writeElement('{'.$namespace.'}'.$key, $val);
        }
    }
}

/**
 * This serializer helps you serialize xml structures that look like
 * this:.
 *
 * <collection>
 *    <item>...</item>
 *    <item>...</item>
 *    <item>...</item>
 * </collection>
 *
 * In that previous example, this serializer just serializes the item element,
 * and this could be called like this:
 *
 * repeatingElements($writer, $items, '{}item');
 */
function repeatingElements(Writer $writer, array $items, string $childElementName)
{
    foreach ($items as $item) {
        $writer->writeElement($childElementName, $item);
    }
}

/**
 * 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 string|int|float|bool|array|object $value
 */
function standardSerializer(Writer $writer, $value)
{
    if (is_scalar($value)) {
        // String, integer, float, boolean
        $writer->text((string) $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_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));
    } elseif (!is_null($value)) {
        throw new InvalidArgumentException('The writer cannot serialize values of type: '.gettype($value));
    }
}