Deserializer
6 months ago
Element
6 months ago
Serializer
6 months ago
ContextStackTrait.php
6 months ago
Element.php
6 months ago
LibXMLException.php
6 months ago
ParseException.php
6 months ago
Reader.php
6 months ago
Service.php
6 months ago
Version.php
6 months ago
Writer.php
6 months ago
XmlDeserializable.php
6 months ago
XmlSerializable.php
6 months ago
Writer.php
262 lines
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace AmeliaVendor\Sabre\Xml; |
| 6 | |
| 7 | use XMLWriter; |
| 8 | |
| 9 | /** |
| 10 | * The XML Writer class. |
| 11 | * |
| 12 | * This class works exactly as PHP's built-in XMLWriter, with a few additions. |
| 13 | * |
| 14 | * Namespaces can be registered beforehand, globally. When the first element is |
| 15 | * written, namespaces will automatically be declared. |
| 16 | * |
| 17 | * The writeAttribute, startElement and writeElement can now take a |
| 18 | * clark-notation element name (example: {http://www.w3.org/2005/Atom}link). |
| 19 | * |
| 20 | * If, when writing the namespace is a known one a prefix will automatically be |
| 21 | * selected, otherwise a random prefix will be generated. |
| 22 | * |
| 23 | * Instead of standard string values, the writer can take Element classes (as |
| 24 | * defined by this library) to delegate the serialization. |
| 25 | * |
| 26 | * The write() method can take array structures to quickly write out simple xml |
| 27 | * trees. |
| 28 | * |
| 29 | * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). |
| 30 | * @author Evert Pot (http://evertpot.com/) |
| 31 | * @license http://sabre.io/license/ Modified BSD License |
| 32 | */ |
| 33 | class Writer extends \XMLWriter |
| 34 | { |
| 35 | use ContextStackTrait; |
| 36 | |
| 37 | /** |
| 38 | * Any namespace that the writer is asked to write, will be added here. |
| 39 | * |
| 40 | * Any of these elements will get a new namespace definition *every single |
| 41 | * time* they are used, but this array allows the writer to make sure that |
| 42 | * the prefixes are consistent anyway. |
| 43 | * |
| 44 | * @var array<string, string> |
| 45 | */ |
| 46 | protected array $adhocNamespaces = []; |
| 47 | |
| 48 | /** |
| 49 | * When the first element is written, this flag is set to true. |
| 50 | * |
| 51 | * This ensures that the namespaces in the namespaces map are only written |
| 52 | * once. |
| 53 | */ |
| 54 | protected bool $namespacesWritten = false; |
| 55 | |
| 56 | /** |
| 57 | * Writes a value to the output stream. |
| 58 | * |
| 59 | * The following values are supported: |
| 60 | * 1. Scalar values will be written as-is, as text. |
| 61 | * 2. Null values will be skipped (resulting in a short xml tag). |
| 62 | * 3. If a value is an instance of an Element class, writing will be |
| 63 | * delegated to the object. |
| 64 | * 4. If a value is an array, two formats are supported. |
| 65 | * |
| 66 | * Array format 1: |
| 67 | * [ |
| 68 | * "{namespace}name1" => "..", |
| 69 | * "{namespace}name2" => "..", |
| 70 | * ] |
| 71 | * |
| 72 | * One element will be created for each key in this array. The values of |
| 73 | * this array support any format this method supports (this method is |
| 74 | * called recursively). |
| 75 | * |
| 76 | * Array format 2: |
| 77 | * |
| 78 | * [ |
| 79 | * [ |
| 80 | * "name" => "{namespace}name1" |
| 81 | * "value" => "..", |
| 82 | * "attributes" => [ |
| 83 | * "attr" => "attribute value", |
| 84 | * ] |
| 85 | * ], |
| 86 | * [ |
| 87 | * "name" => "{namespace}name1" |
| 88 | * "value" => "..", |
| 89 | * "attributes" => [ |
| 90 | * "attr" => "attribute value", |
| 91 | * ] |
| 92 | * ] |
| 93 | * ] |
| 94 | * |
| 95 | * @param mixed $value PHP value to be written |
| 96 | */ |
| 97 | public function write($value): void |
| 98 | { |
| 99 | Serializer\standardSerializer($this, $value); |
| 100 | } |
| 101 | |
| 102 | /** |
| 103 | * Opens a new element. |
| 104 | * |
| 105 | * You can either just use a local element name, or you can use clark- |
| 106 | * notation to start a new element. |
| 107 | * |
| 108 | * Example: |
| 109 | * |
| 110 | * $writer->startElement('{http://www.w3.org/2005/Atom}entry'); |
| 111 | * |
| 112 | * Would result in something like: |
| 113 | * |
| 114 | * <entry xmlns="http://w3.org/2005/Atom"> |
| 115 | * |
| 116 | * Note: this function doesn't have the string typehint, because PHP's |
| 117 | * XMLWriter::startElement doesn't either. |
| 118 | * From PHP 8.0 the typehint exists, so it can be added here after PHP 7.4 is dropped. |
| 119 | * |
| 120 | * @param string $name |
| 121 | */ |
| 122 | public function startElement($name): bool |
| 123 | { |
| 124 | if ('{' === $name[0]) { |
| 125 | list($namespace, $localName) = |
| 126 | Service::parseClarkNotation($name); |
| 127 | |
| 128 | if (array_key_exists($namespace, $this->namespaceMap)) { |
| 129 | $result = $this->startElementNS( |
| 130 | '' === $this->namespaceMap[$namespace] ? null : $this->namespaceMap[$namespace], |
| 131 | $localName, |
| 132 | null |
| 133 | ); |
| 134 | } else { |
| 135 | // An empty namespace means it's the global namespace. This is |
| 136 | // allowed, but it mustn't get a prefix. |
| 137 | if ('' === $namespace) { |
| 138 | $result = $this->startElement($localName); |
| 139 | $this->writeAttribute('xmlns', ''); |
| 140 | } else { |
| 141 | if (!isset($this->adhocNamespaces[$namespace])) { |
| 142 | $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); |
| 143 | } |
| 144 | $result = $this->startElementNS($this->adhocNamespaces[$namespace], $localName, $namespace); |
| 145 | } |
| 146 | } |
| 147 | } else { |
| 148 | $result = parent::startElement($name); |
| 149 | } |
| 150 | |
| 151 | if (!$this->namespacesWritten) { |
| 152 | foreach ($this->namespaceMap as $namespace => $prefix) { |
| 153 | $this->writeAttribute($prefix ? 'xmlns:'.$prefix : 'xmlns', $namespace); |
| 154 | } |
| 155 | $this->namespacesWritten = true; |
| 156 | } |
| 157 | |
| 158 | return $result; |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Write a full element tag and it's contents. |
| 163 | * |
| 164 | * This method automatically closes the element as well. |
| 165 | * |
| 166 | * The element name may be specified in clark-notation. |
| 167 | * |
| 168 | * Examples: |
| 169 | * |
| 170 | * $writer->writeElement('{http://www.w3.org/2005/Atom}author',null); |
| 171 | * becomes: |
| 172 | * <author xmlns="http://www.w3.org/2005" /> |
| 173 | * |
| 174 | * $writer->writeElement('{http://www.w3.org/2005/Atom}author', [ |
| 175 | * '{http://www.w3.org/2005/Atom}name' => 'Evert Pot', |
| 176 | * ]); |
| 177 | * becomes: |
| 178 | * <author xmlns="http://www.w3.org/2005" /><name>Evert Pot</name></author> |
| 179 | * |
| 180 | * Note: this function doesn't have the string typehint, because PHP's |
| 181 | * XMLWriter::startElement doesn't either. |
| 182 | * From PHP 8.0 the typehint exists, so it can be added here after PHP 7.4 is dropped. |
| 183 | * |
| 184 | * @param string $name |
| 185 | * @param array<int|string, mixed>|string|object|null $content |
| 186 | */ |
| 187 | public function writeElement($name, $content = null): bool |
| 188 | { |
| 189 | $this->startElement($name); |
| 190 | if (!is_null($content)) { |
| 191 | $this->write($content); |
| 192 | } |
| 193 | $this->endElement(); |
| 194 | |
| 195 | return true; |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Writes a list of attributes. |
| 200 | * |
| 201 | * Attributes are specified as a key->value array. |
| 202 | * |
| 203 | * The key is an attribute name. If the key is a 'localName', the current |
| 204 | * xml namespace is assumed. If it's a 'clark notation key', this namespace |
| 205 | * will be used instead. |
| 206 | * |
| 207 | * @param array<string, string> $attributes |
| 208 | */ |
| 209 | public function writeAttributes(array $attributes): void |
| 210 | { |
| 211 | foreach ($attributes as $name => $value) { |
| 212 | $this->writeAttribute($name, $value); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | /** |
| 217 | * Writes a new attribute. |
| 218 | * |
| 219 | * The name may be specified in clark-notation. |
| 220 | * |
| 221 | * Returns true when successful. |
| 222 | * |
| 223 | * Note: this function doesn't have typehints, because for some reason |
| 224 | * PHP's XMLWriter::writeAttribute doesn't either. |
| 225 | * From PHP 8.0 the typehint exists, so it can be added here after PHP 7.4 is dropped. |
| 226 | * |
| 227 | * @param string $name |
| 228 | * @param string $value |
| 229 | */ |
| 230 | public function writeAttribute($name, $value): bool |
| 231 | { |
| 232 | if ('{' !== $name[0]) { |
| 233 | return parent::writeAttribute($name, $value); |
| 234 | } |
| 235 | |
| 236 | list( |
| 237 | $namespace, |
| 238 | $localName, |
| 239 | ) = Service::parseClarkNotation($name); |
| 240 | |
| 241 | if (array_key_exists($namespace, $this->namespaceMap)) { |
| 242 | // It's an attribute with a namespace we know |
| 243 | return $this->writeAttribute( |
| 244 | $this->namespaceMap[$namespace].':'.$localName, |
| 245 | $value |
| 246 | ); |
| 247 | } |
| 248 | |
| 249 | // We don't know the namespace, we must add it in-line |
| 250 | if (!isset($this->adhocNamespaces[$namespace])) { |
| 251 | $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); |
| 252 | } |
| 253 | |
| 254 | return $this->writeAttributeNS( |
| 255 | $this->adhocNamespaces[$namespace], |
| 256 | $localName, |
| 257 | $namespace, |
| 258 | $value |
| 259 | ); |
| 260 | } |
| 261 | } |
| 262 |