functions.php 6.7 KB

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