1<?php 2 3namespace Sabre\Xml\Serializer; 4 5use InvalidArgumentException; 6use Sabre\Xml\Writer; 7use Sabre\Xml\XmlSerializable; 8 9/** 10 * This file provides a number of 'serializer' helper functions. 11 * 12 * These helper functions can be used to easily xml-encode common PHP 13 * data structures, or can be placed in the $classMap. 14 */ 15 16/** 17 * The 'enum' serializer writes simple list of elements. 18 * 19 * For example, calling: 20 * 21 * enum($writer, [ 22 * "{http://sabredav.org/ns}elem1", 23 * "{http://sabredav.org/ns}elem2", 24 * "{http://sabredav.org/ns}elem3", 25 * "{http://sabredav.org/ns}elem4", 26 * "{http://sabredav.org/ns}elem5", 27 * ]); 28 * 29 * Will generate something like this (if the correct namespace is declared): 30 * 31 * <s:elem1 /> 32 * <s:elem2 /> 33 * <s:elem3 /> 34 * <s:elem4>content</s:elem4> 35 * <s:elem5 attr="val" /> 36 * 37 * @param Writer $writer 38 * @param string[] $values 39 * @return void 40 */ 41function enum(Writer $writer, array $values) { 42 43 foreach ($values as $value) { 44 $writer->writeElement($value); 45 } 46} 47 48/** 49 * The valueObject serializer turns a simple PHP object into a classname. 50 * 51 * Every public property will be encoded as an xml element with the same 52 * name, in the XML namespace as specified. 53 * 54 * Values that are set to null or an empty array are not serialized. To 55 * serialize empty properties, you must specify them as an empty string. 56 * 57 * @param Writer $writer 58 * @param object $valueObject 59 * @param string $namespace 60 */ 61function valueObject(Writer $writer, $valueObject, $namespace) { 62 foreach (get_object_vars($valueObject) as $key => $val) { 63 if (is_array($val)) { 64 // If $val is an array, it has a special meaning. We need to 65 // generate one child element for each item in $val 66 foreach ($val as $child) { 67 $writer->writeElement('{' . $namespace . '}' . $key, $child); 68 } 69 70 } elseif ($val !== null) { 71 $writer->writeElement('{' . $namespace . '}' . $key, $val); 72 } 73 } 74} 75 76 77/** 78 * This serializer helps you serialize xml structures that look like 79 * this: 80 * 81 * <collection> 82 * <item>...</item> 83 * <item>...</item> 84 * <item>...</item> 85 * </collection> 86 * 87 * In that previous example, this serializer just serializes the item element, 88 * and this could be called like this: 89 * 90 * repeatingElements($writer, $items, '{}item'); 91 * 92 * @param Writer $writer 93 * @param array $items A list of items sabre/xml can serialize. 94 * @param string $childElementName Element name in clark-notation 95 * @return void 96 */ 97function repeatingElements(Writer $writer, array $items, $childElementName) { 98 99 foreach ($items as $item) { 100 $writer->writeElement($childElementName, $item); 101 } 102 103} 104 105/** 106 * This function is the 'default' serializer that is able to serialize most 107 * things, and delegates to other serializers if needed. 108 * 109 * The standardSerializer supports a wide-array of values. 110 * 111 * $value may be a string or integer, it will just write out the string as text. 112 * $value may be an instance of XmlSerializable or Element, in which case it 113 * calls it's xmlSerialize() method. 114 * $value may be a PHP callback/function/closure, in case we call the callback 115 * and give it the Writer as an argument. 116 * $value may be a an object, and if it's in the classMap we automatically call 117 * the correct serializer for it. 118 * $value may be null, in which case we do nothing. 119 * 120 * If $value is an array, the array must look like this: 121 * 122 * [ 123 * [ 124 * 'name' => '{namespaceUri}element-name', 125 * 'value' => '...', 126 * 'attributes' => [ 'attName' => 'attValue' ] 127 * ] 128 * [, 129 * 'name' => '{namespaceUri}element-name2', 130 * 'value' => '...', 131 * ] 132 * ] 133 * 134 * This would result in xml like: 135 * 136 * <element-name xmlns="namespaceUri" attName="attValue"> 137 * ... 138 * </element-name> 139 * <element-name2> 140 * ... 141 * </element-name2> 142 * 143 * The value property may be any value standardSerializer supports, so you can 144 * nest data-structures this way. Both value and attributes are optional. 145 * 146 * Alternatively, you can also specify the array using this syntax: 147 * 148 * [ 149 * [ 150 * '{namespaceUri}element-name' => '...', 151 * '{namespaceUri}element-name2' => '...', 152 * ] 153 * ] 154 * 155 * This is excellent for simple key->value structures, and here you can also 156 * specify anything for the value. 157 * 158 * You can even mix the two array syntaxes. 159 * 160 * @param Writer $writer 161 * @param string|int|float|bool|array|object 162 * @return void 163 */ 164function standardSerializer(Writer $writer, $value) { 165 166 if (is_scalar($value)) { 167 168 // String, integer, float, boolean 169 $writer->text($value); 170 171 } elseif ($value instanceof XmlSerializable) { 172 173 // XmlSerializable classes or Element classes. 174 $value->xmlSerialize($writer); 175 176 } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { 177 178 // It's an object which class appears in the classmap. 179 $writer->classMap[get_class($value)]($writer, $value); 180 181 } elseif (is_callable($value)) { 182 183 // A callback 184 $value($writer); 185 186 } elseif (is_null($value)) { 187 188 // nothing! 189 190 } elseif (is_array($value) && array_key_exists('name', $value)) { 191 192 // if the array had a 'name' element, we assume that this array 193 // describes a 'name' and optionally 'attributes' and 'value'. 194 195 $name = $value['name']; 196 $attributes = isset($value['attributes']) ? $value['attributes'] : []; 197 $value = isset($value['value']) ? $value['value'] : null; 198 199 $writer->startElement($name); 200 $writer->writeAttributes($attributes); 201 $writer->write($value); 202 $writer->endElement(); 203 204 } elseif (is_array($value)) { 205 206 foreach ($value as $name => $item) { 207 208 if (is_int($name)) { 209 210 // This item has a numeric index. We just loop through the 211 // array and throw it back in the writer. 212 standardSerializer($writer, $item); 213 214 } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { 215 216 // The key is used for a name, but $item has 'attributes' and 217 // possibly 'value' 218 $writer->startElement($name); 219 $writer->writeAttributes($item['attributes']); 220 if (isset($item['value'])) { 221 $writer->write($item['value']); 222 } 223 $writer->endElement(); 224 225 } elseif (is_string($name)) { 226 227 // This was a plain key-value array. 228 $writer->startElement($name); 229 $writer->write($item); 230 $writer->endElement(); 231 232 } else { 233 234 throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: ' . gettype($name)); 235 236 } 237 } 238 239 } elseif (is_object($value)) { 240 241 throw new InvalidArgumentException('The writer cannot serialize objects of class: ' . get_class($value)); 242 243 } else { 244 245 throw new InvalidArgumentException('The writer cannot serialize values of type: ' . gettype($value)); 246 247 } 248 249} 250