1 | <?php |
---|
2 | |
---|
3 | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
---|
4 | |
---|
5 | /** |
---|
6 | * XML_Util |
---|
7 | * |
---|
8 | * XML Utilities package |
---|
9 | * |
---|
10 | * PHP versions 4 and 5 |
---|
11 | * |
---|
12 | * LICENSE: |
---|
13 | * |
---|
14 | * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net> |
---|
15 | * All rights reserved. |
---|
16 | * |
---|
17 | * Redistribution and use in source and binary forms, with or without |
---|
18 | * modification, are permitted provided that the following conditions |
---|
19 | * are met: |
---|
20 | * |
---|
21 | * * Redistributions of source code must retain the above copyright |
---|
22 | * notice, this list of conditions and the following disclaimer. |
---|
23 | * * Redistributions in binary form must reproduce the above copyright |
---|
24 | * notice, this list of conditions and the following disclaimer in the |
---|
25 | * documentation and/or other materials provided with the distribution. |
---|
26 | * * The name of the author may not be used to endorse or promote products |
---|
27 | * derived from this software without specific prior written permission. |
---|
28 | * |
---|
29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
---|
30 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
---|
31 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
---|
32 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
---|
33 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
---|
34 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
---|
35 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
---|
36 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
---|
37 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
---|
38 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
---|
39 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
40 | * |
---|
41 | * @category XML |
---|
42 | * @package XML_Util |
---|
43 | * @author Stephan Schmidt <schst@php.net> |
---|
44 | * @copyright 2003-2008 Stephan Schmidt <schst@php.net> |
---|
45 | * @license http://opensource.org/licenses/bsd-license New BSD License |
---|
46 | * @version CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $ |
---|
47 | * @link http://pear.php.net/package/XML_Util |
---|
48 | */ |
---|
49 | |
---|
50 | /** |
---|
51 | * error code for invalid chars in XML name |
---|
52 | */ |
---|
53 | define('XML_UTIL_ERROR_INVALID_CHARS', 51); |
---|
54 | |
---|
55 | /** |
---|
56 | * error code for invalid chars in XML name |
---|
57 | */ |
---|
58 | define('XML_UTIL_ERROR_INVALID_START', 52); |
---|
59 | |
---|
60 | /** |
---|
61 | * error code for non-scalar tag content |
---|
62 | */ |
---|
63 | define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60); |
---|
64 | |
---|
65 | /** |
---|
66 | * error code for missing tag name |
---|
67 | */ |
---|
68 | define('XML_UTIL_ERROR_NO_TAG_NAME', 61); |
---|
69 | |
---|
70 | /** |
---|
71 | * replace XML entities |
---|
72 | */ |
---|
73 | define('XML_UTIL_REPLACE_ENTITIES', 1); |
---|
74 | |
---|
75 | /** |
---|
76 | * embedd content in a CData Section |
---|
77 | */ |
---|
78 | define('XML_UTIL_CDATA_SECTION', 5); |
---|
79 | |
---|
80 | /** |
---|
81 | * do not replace entitites |
---|
82 | */ |
---|
83 | define('XML_UTIL_ENTITIES_NONE', 0); |
---|
84 | |
---|
85 | /** |
---|
86 | * replace all XML entitites |
---|
87 | * This setting will replace <, >, ", ' and & |
---|
88 | */ |
---|
89 | define('XML_UTIL_ENTITIES_XML', 1); |
---|
90 | |
---|
91 | /** |
---|
92 | * replace only required XML entitites |
---|
93 | * This setting will replace <, " and & |
---|
94 | */ |
---|
95 | define('XML_UTIL_ENTITIES_XML_REQUIRED', 2); |
---|
96 | |
---|
97 | /** |
---|
98 | * replace HTML entitites |
---|
99 | * @link http://www.php.net/htmlentities |
---|
100 | */ |
---|
101 | define('XML_UTIL_ENTITIES_HTML', 3); |
---|
102 | |
---|
103 | /** |
---|
104 | * Collapse all empty tags. |
---|
105 | */ |
---|
106 | define('XML_UTIL_COLLAPSE_ALL', 1); |
---|
107 | |
---|
108 | /** |
---|
109 | * Collapse only empty XHTML tags that have no end tag. |
---|
110 | */ |
---|
111 | define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2); |
---|
112 | |
---|
113 | /** |
---|
114 | * utility class for working with XML documents |
---|
115 | * |
---|
116 | |
---|
117 | * @category XML |
---|
118 | * @package XML_Util |
---|
119 | * @author Stephan Schmidt <schst@php.net> |
---|
120 | * @copyright 2003-2008 Stephan Schmidt <schst@php.net> |
---|
121 | * @license http://opensource.org/licenses/bsd-license New BSD License |
---|
122 | * @version Release: 1.2.1 |
---|
123 | * @link http://pear.php.net/package/XML_Util |
---|
124 | */ |
---|
125 | class XML_Util |
---|
126 | { |
---|
127 | /** |
---|
128 | * return API version |
---|
129 | * |
---|
130 | * @return string $version API version |
---|
131 | * @access public |
---|
132 | * @static |
---|
133 | */ |
---|
134 | function apiVersion() |
---|
135 | { |
---|
136 | return '1.1'; |
---|
137 | } |
---|
138 | |
---|
139 | /** |
---|
140 | * replace XML entities |
---|
141 | * |
---|
142 | * With the optional second parameter, you may select, which |
---|
143 | * entities should be replaced. |
---|
144 | * |
---|
145 | * <code> |
---|
146 | * require_once 'XML/Util.php'; |
---|
147 | * |
---|
148 | * // replace XML entites: |
---|
149 | * $string = XML_Util::replaceEntities('This string contains < & >.'); |
---|
150 | * </code> |
---|
151 | * |
---|
152 | * With the optional third parameter, you may pass the character encoding |
---|
153 | * <code> |
---|
154 | * require_once 'XML/Util.php'; |
---|
155 | * |
---|
156 | * // replace XML entites in UTF-8: |
---|
157 | * $string = XML_Util::replaceEntities( |
---|
158 | * 'This string contains < & > as well as ä, ö, ß, à and ê', |
---|
159 | * XML_UTIL_ENTITIES_HTML, |
---|
160 | * 'UTF-8' |
---|
161 | * ); |
---|
162 | * </code> |
---|
163 | * |
---|
164 | * @param string $string string where XML special chars |
---|
165 | * should be replaced |
---|
166 | * @param int $replaceEntities setting for entities in attribute values |
---|
167 | * (one of XML_UTIL_ENTITIES_XML, |
---|
168 | * XML_UTIL_ENTITIES_XML_REQUIRED, |
---|
169 | * XML_UTIL_ENTITIES_HTML) |
---|
170 | * @param string $encoding encoding value (if any)... |
---|
171 | * must be a valid encoding as determined |
---|
172 | * by the htmlentities() function |
---|
173 | * |
---|
174 | * @return string string with replaced chars |
---|
175 | * @access public |
---|
176 | * @static |
---|
177 | * @see reverseEntities() |
---|
178 | */ |
---|
179 | function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, |
---|
180 | $encoding = 'ISO-8859-1') |
---|
181 | { |
---|
182 | switch ($replaceEntities) { |
---|
183 | case XML_UTIL_ENTITIES_XML: |
---|
184 | return strtr($string, array( |
---|
185 | '&' => '&', |
---|
186 | '>' => '>', |
---|
187 | '<' => '<', |
---|
188 | '"' => '"', |
---|
189 | '\'' => ''' )); |
---|
190 | break; |
---|
191 | case XML_UTIL_ENTITIES_XML_REQUIRED: |
---|
192 | return strtr($string, array( |
---|
193 | '&' => '&', |
---|
194 | '<' => '<', |
---|
195 | '"' => '"' )); |
---|
196 | break; |
---|
197 | case XML_UTIL_ENTITIES_HTML: |
---|
198 | return htmlentities($string, ENT_COMPAT, $encoding); |
---|
199 | break; |
---|
200 | } |
---|
201 | return $string; |
---|
202 | } |
---|
203 | |
---|
204 | /** |
---|
205 | * reverse XML entities |
---|
206 | * |
---|
207 | * With the optional second parameter, you may select, which |
---|
208 | * entities should be reversed. |
---|
209 | * |
---|
210 | * <code> |
---|
211 | * require_once 'XML/Util.php'; |
---|
212 | * |
---|
213 | * // reverse XML entites: |
---|
214 | * $string = XML_Util::reverseEntities('This string contains < & >.'); |
---|
215 | * </code> |
---|
216 | * |
---|
217 | * With the optional third parameter, you may pass the character encoding |
---|
218 | * <code> |
---|
219 | * require_once 'XML/Util.php'; |
---|
220 | * |
---|
221 | * // reverse XML entites in UTF-8: |
---|
222 | * $string = XML_Util::reverseEntities( |
---|
223 | * 'This string contains < & > as well as' |
---|
224 | * . ' ä, ö, ß, à and ê', |
---|
225 | * XML_UTIL_ENTITIES_HTML, |
---|
226 | * 'UTF-8' |
---|
227 | * ); |
---|
228 | * </code> |
---|
229 | * |
---|
230 | * @param string $string string where XML special chars |
---|
231 | * should be replaced |
---|
232 | * @param int $replaceEntities setting for entities in attribute values |
---|
233 | * (one of XML_UTIL_ENTITIES_XML, |
---|
234 | * XML_UTIL_ENTITIES_XML_REQUIRED, |
---|
235 | * XML_UTIL_ENTITIES_HTML) |
---|
236 | * @param string $encoding encoding value (if any)... |
---|
237 | * must be a valid encoding as determined |
---|
238 | * by the html_entity_decode() function |
---|
239 | * |
---|
240 | * @return string string with replaced chars |
---|
241 | * @access public |
---|
242 | * @static |
---|
243 | * @see replaceEntities() |
---|
244 | */ |
---|
245 | function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, |
---|
246 | $encoding = 'ISO-8859-1') |
---|
247 | { |
---|
248 | switch ($replaceEntities) { |
---|
249 | case XML_UTIL_ENTITIES_XML: |
---|
250 | return strtr($string, array( |
---|
251 | '&' => '&', |
---|
252 | '>' => '>', |
---|
253 | '<' => '<', |
---|
254 | '"' => '"', |
---|
255 | ''' => '\'' )); |
---|
256 | break; |
---|
257 | case XML_UTIL_ENTITIES_XML_REQUIRED: |
---|
258 | return strtr($string, array( |
---|
259 | '&' => '&', |
---|
260 | '<' => '<', |
---|
261 | '"' => '"' )); |
---|
262 | break; |
---|
263 | case XML_UTIL_ENTITIES_HTML: |
---|
264 | return html_entity_decode($string, ENT_COMPAT, $encoding); |
---|
265 | break; |
---|
266 | } |
---|
267 | return $string; |
---|
268 | } |
---|
269 | |
---|
270 | /** |
---|
271 | * build an xml declaration |
---|
272 | * |
---|
273 | * <code> |
---|
274 | * require_once 'XML/Util.php'; |
---|
275 | * |
---|
276 | * // get an XML declaration: |
---|
277 | * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true); |
---|
278 | * </code> |
---|
279 | * |
---|
280 | * @param string $version xml version |
---|
281 | * @param string $encoding character encoding |
---|
282 | * @param bool $standalone document is standalone (or not) |
---|
283 | * |
---|
284 | * @return string xml declaration |
---|
285 | * @access public |
---|
286 | * @static |
---|
287 | * @uses attributesToString() to serialize the attributes of the XML declaration |
---|
288 | */ |
---|
289 | function getXMLDeclaration($version = '1.0', $encoding = null, |
---|
290 | $standalone = null) |
---|
291 | { |
---|
292 | $attributes = array( |
---|
293 | 'version' => $version, |
---|
294 | ); |
---|
295 | // add encoding |
---|
296 | if ($encoding !== null) { |
---|
297 | $attributes['encoding'] = $encoding; |
---|
298 | } |
---|
299 | // add standalone, if specified |
---|
300 | if ($standalone !== null) { |
---|
301 | $attributes['standalone'] = $standalone ? 'yes' : 'no'; |
---|
302 | } |
---|
303 | |
---|
304 | return sprintf('<?xml%s?>', |
---|
305 | XML_Util::attributesToString($attributes, false)); |
---|
306 | } |
---|
307 | |
---|
308 | /** |
---|
309 | * build a document type declaration |
---|
310 | * |
---|
311 | * <code> |
---|
312 | * require_once 'XML/Util.php'; |
---|
313 | * |
---|
314 | * // get a doctype declaration: |
---|
315 | * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd'); |
---|
316 | * </code> |
---|
317 | * |
---|
318 | * @param string $root name of the root tag |
---|
319 | * @param string $uri uri of the doctype definition |
---|
320 | * (or array with uri and public id) |
---|
321 | * @param string $internalDtd internal dtd entries |
---|
322 | * |
---|
323 | * @return string doctype declaration |
---|
324 | * @access public |
---|
325 | * @static |
---|
326 | * @since 0.2 |
---|
327 | */ |
---|
328 | function getDocTypeDeclaration($root, $uri = null, $internalDtd = null) |
---|
329 | { |
---|
330 | if (is_array($uri)) { |
---|
331 | $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']); |
---|
332 | } elseif (!empty($uri)) { |
---|
333 | $ref = sprintf(' SYSTEM "%s"', $uri); |
---|
334 | } else { |
---|
335 | $ref = ''; |
---|
336 | } |
---|
337 | |
---|
338 | if (empty($internalDtd)) { |
---|
339 | return sprintf('<!DOCTYPE %s%s>', $root, $ref); |
---|
340 | } else { |
---|
341 | return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd); |
---|
342 | } |
---|
343 | } |
---|
344 | |
---|
345 | /** |
---|
346 | * create string representation of an attribute list |
---|
347 | * |
---|
348 | * <code> |
---|
349 | * require_once 'XML/Util.php'; |
---|
350 | * |
---|
351 | * // build an attribute string |
---|
352 | * $att = array( |
---|
353 | * 'foo' => 'bar', |
---|
354 | * 'argh' => 'tomato' |
---|
355 | * ); |
---|
356 | * |
---|
357 | * $attList = XML_Util::attributesToString($att); |
---|
358 | * </code> |
---|
359 | * |
---|
360 | * @param array $attributes attribute array |
---|
361 | * @param bool|array $sort sort attribute list alphabetically, |
---|
362 | * may also be an assoc array containing |
---|
363 | * the keys 'sort', 'multiline', 'indent', |
---|
364 | * 'linebreak' and 'entities' |
---|
365 | * @param bool $multiline use linebreaks, if more than |
---|
366 | * one attribute is given |
---|
367 | * @param string $indent string used for indentation of |
---|
368 | * multiline attributes |
---|
369 | * @param string $linebreak string used for linebreaks of |
---|
370 | * multiline attributes |
---|
371 | * @param int $entities setting for entities in attribute values |
---|
372 | * (one of XML_UTIL_ENTITIES_NONE, |
---|
373 | * XML_UTIL_ENTITIES_XML, |
---|
374 | * XML_UTIL_ENTITIES_XML_REQUIRED, |
---|
375 | * XML_UTIL_ENTITIES_HTML) |
---|
376 | * |
---|
377 | * @return string string representation of the attributes |
---|
378 | * @access public |
---|
379 | * @static |
---|
380 | * @uses replaceEntities() to replace XML entities in attribute values |
---|
381 | * @todo allow sort also to be an options array |
---|
382 | */ |
---|
383 | function attributesToString($attributes, $sort = true, $multiline = false, |
---|
384 | $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML) |
---|
385 | { |
---|
386 | /* |
---|
387 | * second parameter may be an array |
---|
388 | */ |
---|
389 | if (is_array($sort)) { |
---|
390 | if (isset($sort['multiline'])) { |
---|
391 | $multiline = $sort['multiline']; |
---|
392 | } |
---|
393 | if (isset($sort['indent'])) { |
---|
394 | $indent = $sort['indent']; |
---|
395 | } |
---|
396 | if (isset($sort['linebreak'])) { |
---|
397 | $multiline = $sort['linebreak']; |
---|
398 | } |
---|
399 | if (isset($sort['entities'])) { |
---|
400 | $entities = $sort['entities']; |
---|
401 | } |
---|
402 | if (isset($sort['sort'])) { |
---|
403 | $sort = $sort['sort']; |
---|
404 | } else { |
---|
405 | $sort = true; |
---|
406 | } |
---|
407 | } |
---|
408 | $string = ''; |
---|
409 | if (is_array($attributes) && !empty($attributes)) { |
---|
410 | if ($sort) { |
---|
411 | ksort($attributes); |
---|
412 | } |
---|
413 | if ( !$multiline || count($attributes) == 1) { |
---|
414 | foreach ($attributes as $key => $value) { |
---|
415 | if ($entities != XML_UTIL_ENTITIES_NONE) { |
---|
416 | if ($entities === XML_UTIL_CDATA_SECTION) { |
---|
417 | $entities = XML_UTIL_ENTITIES_XML; |
---|
418 | } |
---|
419 | $value = XML_Util::replaceEntities($value, $entities); |
---|
420 | } |
---|
421 | $string .= ' ' . $key . '="' . $value . '"'; |
---|
422 | } |
---|
423 | } else { |
---|
424 | $first = true; |
---|
425 | foreach ($attributes as $key => $value) { |
---|
426 | if ($entities != XML_UTIL_ENTITIES_NONE) { |
---|
427 | $value = XML_Util::replaceEntities($value, $entities); |
---|
428 | } |
---|
429 | if ($first) { |
---|
430 | $string .= ' ' . $key . '="' . $value . '"'; |
---|
431 | $first = false; |
---|
432 | } else { |
---|
433 | $string .= $linebreak . $indent . $key . '="' . $value . '"'; |
---|
434 | } |
---|
435 | } |
---|
436 | } |
---|
437 | } |
---|
438 | return $string; |
---|
439 | } |
---|
440 | |
---|
441 | /** |
---|
442 | * Collapses empty tags. |
---|
443 | * |
---|
444 | * @param string $xml XML |
---|
445 | * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) |
---|
446 | * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones. |
---|
447 | * |
---|
448 | * @return string XML |
---|
449 | * @access public |
---|
450 | * @static |
---|
451 | * @todo PEAR CS - unable to avoid "space after open parens" error |
---|
452 | * in the IF branch |
---|
453 | */ |
---|
454 | function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) |
---|
455 | { |
---|
456 | if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) { |
---|
457 | return preg_replace( |
---|
458 | '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|' |
---|
459 | . 'param)([^>]*)><\/\\1>/s', |
---|
460 | '<\\1\\2 />', |
---|
461 | $xml); |
---|
462 | } else { |
---|
463 | return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml); |
---|
464 | } |
---|
465 | } |
---|
466 | |
---|
467 | /** |
---|
468 | * create a tag |
---|
469 | * |
---|
470 | * This method will call XML_Util::createTagFromArray(), which |
---|
471 | * is more flexible. |
---|
472 | * |
---|
473 | * <code> |
---|
474 | * require_once 'XML/Util.php'; |
---|
475 | * |
---|
476 | * // create an XML tag: |
---|
477 | * $tag = XML_Util::createTag('myNs:myTag', |
---|
478 | * array('foo' => 'bar'), |
---|
479 | * 'This is inside the tag', |
---|
480 | * 'http://www.w3c.org/myNs#'); |
---|
481 | * </code> |
---|
482 | * |
---|
483 | * @param string $qname qualified tagname (including namespace) |
---|
484 | * @param array $attributes array containg attributes |
---|
485 | * @param mixed $content the content |
---|
486 | * @param string $namespaceUri URI of the namespace |
---|
487 | * @param int $replaceEntities whether to replace XML special chars in |
---|
488 | * content, embedd it in a CData section |
---|
489 | * or none of both |
---|
490 | * @param bool $multiline whether to create a multiline tag where |
---|
491 | * each attribute gets written to a single line |
---|
492 | * @param string $indent string used to indent attributes |
---|
493 | * (_auto indents attributes so they start |
---|
494 | * at the same column) |
---|
495 | * @param string $linebreak string used for linebreaks |
---|
496 | * @param bool $sortAttributes Whether to sort the attributes or not |
---|
497 | * |
---|
498 | * @return string XML tag |
---|
499 | * @access public |
---|
500 | * @static |
---|
501 | * @see createTagFromArray() |
---|
502 | * @uses createTagFromArray() to create the tag |
---|
503 | */ |
---|
504 | function createTag($qname, $attributes = array(), $content = null, |
---|
505 | $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, |
---|
506 | $multiline = false, $indent = '_auto', $linebreak = "\n", |
---|
507 | $sortAttributes = true) |
---|
508 | { |
---|
509 | $tag = array( |
---|
510 | 'qname' => $qname, |
---|
511 | 'attributes' => $attributes |
---|
512 | ); |
---|
513 | |
---|
514 | // add tag content |
---|
515 | if ($content !== null) { |
---|
516 | $tag['content'] = $content; |
---|
517 | } |
---|
518 | |
---|
519 | // add namespace Uri |
---|
520 | if ($namespaceUri !== null) { |
---|
521 | $tag['namespaceUri'] = $namespaceUri; |
---|
522 | } |
---|
523 | |
---|
524 | return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, |
---|
525 | $indent, $linebreak, $sortAttributes); |
---|
526 | } |
---|
527 | |
---|
528 | /** |
---|
529 | * create a tag from an array |
---|
530 | * this method awaits an array in the following format |
---|
531 | * <pre> |
---|
532 | * array( |
---|
533 | * // qualified name of the tag |
---|
534 | * 'qname' => $qname |
---|
535 | * |
---|
536 | * // namespace prefix (optional, if qname is specified or no namespace) |
---|
537 | * 'namespace' => $namespace |
---|
538 | * |
---|
539 | * // local part of the tagname (optional, if qname is specified) |
---|
540 | * 'localpart' => $localpart, |
---|
541 | * |
---|
542 | * // array containing all attributes (optional) |
---|
543 | * 'attributes' => array(), |
---|
544 | * |
---|
545 | * // tag content (optional) |
---|
546 | * 'content' => $content, |
---|
547 | * |
---|
548 | * // namespaceUri for the given namespace (optional) |
---|
549 | * 'namespaceUri' => $namespaceUri |
---|
550 | * ) |
---|
551 | * </pre> |
---|
552 | * |
---|
553 | * <code> |
---|
554 | * require_once 'XML/Util.php'; |
---|
555 | * |
---|
556 | * $tag = array( |
---|
557 | * 'qname' => 'foo:bar', |
---|
558 | * 'namespaceUri' => 'http://foo.com', |
---|
559 | * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), |
---|
560 | * 'content' => 'I\'m inside the tag', |
---|
561 | * ); |
---|
562 | * // creating a tag with qualified name and namespaceUri |
---|
563 | * $string = XML_Util::createTagFromArray($tag); |
---|
564 | * </code> |
---|
565 | * |
---|
566 | * @param array $tag tag definition |
---|
567 | * @param int $replaceEntities whether to replace XML special chars in |
---|
568 | * content, embedd it in a CData section |
---|
569 | * or none of both |
---|
570 | * @param bool $multiline whether to create a multiline tag where each |
---|
571 | * attribute gets written to a single line |
---|
572 | * @param string $indent string used to indent attributes |
---|
573 | * (_auto indents attributes so they start |
---|
574 | * at the same column) |
---|
575 | * @param string $linebreak string used for linebreaks |
---|
576 | * @param bool $sortAttributes Whether to sort the attributes or not |
---|
577 | * |
---|
578 | * @return string XML tag |
---|
579 | * @access public |
---|
580 | * @static |
---|
581 | * @see createTag() |
---|
582 | * @uses attributesToString() to serialize the attributes of the tag |
---|
583 | * @uses splitQualifiedName() to get local part and namespace of a qualified name |
---|
584 | * @uses createCDataSection() |
---|
585 | * @uses raiseError() |
---|
586 | */ |
---|
587 | function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, |
---|
588 | $multiline = false, $indent = '_auto', $linebreak = "\n", |
---|
589 | $sortAttributes = true) |
---|
590 | { |
---|
591 | if (isset($tag['content']) && !is_scalar($tag['content'])) { |
---|
592 | return XML_Util::raiseError('Supplied non-scalar value as tag content', |
---|
593 | XML_UTIL_ERROR_NON_SCALAR_CONTENT); |
---|
594 | } |
---|
595 | |
---|
596 | if (!isset($tag['qname']) && !isset($tag['localPart'])) { |
---|
597 | return XML_Util::raiseError('You must either supply a qualified name ' |
---|
598 | . '(qname) or local tag name (localPart).', |
---|
599 | XML_UTIL_ERROR_NO_TAG_NAME); |
---|
600 | } |
---|
601 | |
---|
602 | // if no attributes hav been set, use empty attributes |
---|
603 | if (!isset($tag['attributes']) || !is_array($tag['attributes'])) { |
---|
604 | $tag['attributes'] = array(); |
---|
605 | } |
---|
606 | |
---|
607 | if (isset($tag['namespaces'])) { |
---|
608 | foreach ($tag['namespaces'] as $ns => $uri) { |
---|
609 | $tag['attributes']['xmlns:' . $ns] = $uri; |
---|
610 | } |
---|
611 | } |
---|
612 | |
---|
613 | if (!isset($tag['qname'])) { |
---|
614 | // qualified name is not given |
---|
615 | |
---|
616 | // check for namespace |
---|
617 | if (isset($tag['namespace']) && !empty($tag['namespace'])) { |
---|
618 | $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart']; |
---|
619 | } else { |
---|
620 | $tag['qname'] = $tag['localPart']; |
---|
621 | } |
---|
622 | } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) { |
---|
623 | // namespace URI is set, but no namespace |
---|
624 | |
---|
625 | $parts = XML_Util::splitQualifiedName($tag['qname']); |
---|
626 | |
---|
627 | $tag['localPart'] = $parts['localPart']; |
---|
628 | if (isset($parts['namespace'])) { |
---|
629 | $tag['namespace'] = $parts['namespace']; |
---|
630 | } |
---|
631 | } |
---|
632 | |
---|
633 | if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) { |
---|
634 | // is a namespace given |
---|
635 | if (isset($tag['namespace']) && !empty($tag['namespace'])) { |
---|
636 | $tag['attributes']['xmlns:' . $tag['namespace']] = |
---|
637 | $tag['namespaceUri']; |
---|
638 | } else { |
---|
639 | // define this Uri as the default namespace |
---|
640 | $tag['attributes']['xmlns'] = $tag['namespaceUri']; |
---|
641 | } |
---|
642 | } |
---|
643 | |
---|
644 | // check for multiline attributes |
---|
645 | if ($multiline === true) { |
---|
646 | if ($indent === '_auto') { |
---|
647 | $indent = str_repeat(' ', (strlen($tag['qname'])+2)); |
---|
648 | } |
---|
649 | } |
---|
650 | |
---|
651 | // create attribute list |
---|
652 | $attList = XML_Util::attributesToString($tag['attributes'], |
---|
653 | $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities); |
---|
654 | if (!isset($tag['content']) || (string)$tag['content'] == '') { |
---|
655 | $tag = sprintf('<%s%s />', $tag['qname'], $attList); |
---|
656 | } else { |
---|
657 | switch ($replaceEntities) { |
---|
658 | case XML_UTIL_ENTITIES_NONE: |
---|
659 | break; |
---|
660 | case XML_UTIL_CDATA_SECTION: |
---|
661 | $tag['content'] = XML_Util::createCDataSection($tag['content']); |
---|
662 | break; |
---|
663 | default: |
---|
664 | $tag['content'] = XML_Util::replaceEntities($tag['content'], |
---|
665 | $replaceEntities); |
---|
666 | break; |
---|
667 | } |
---|
668 | $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'], |
---|
669 | $tag['qname']); |
---|
670 | } |
---|
671 | return $tag; |
---|
672 | } |
---|
673 | |
---|
674 | /** |
---|
675 | * create a start element |
---|
676 | * |
---|
677 | * <code> |
---|
678 | * require_once 'XML/Util.php'; |
---|
679 | * |
---|
680 | * // create an XML start element: |
---|
681 | * $tag = XML_Util::createStartElement('myNs:myTag', |
---|
682 | * array('foo' => 'bar') ,'http://www.w3c.org/myNs#'); |
---|
683 | * </code> |
---|
684 | * |
---|
685 | * @param string $qname qualified tagname (including namespace) |
---|
686 | * @param array $attributes array containg attributes |
---|
687 | * @param string $namespaceUri URI of the namespace |
---|
688 | * @param bool $multiline whether to create a multiline tag where each |
---|
689 | * attribute gets written to a single line |
---|
690 | * @param string $indent string used to indent attributes (_auto indents |
---|
691 | * attributes so they start at the same column) |
---|
692 | * @param string $linebreak string used for linebreaks |
---|
693 | * @param bool $sortAttributes Whether to sort the attributes or not |
---|
694 | * |
---|
695 | * @return string XML start element |
---|
696 | * @access public |
---|
697 | * @static |
---|
698 | * @see createEndElement(), createTag() |
---|
699 | */ |
---|
700 | function createStartElement($qname, $attributes = array(), $namespaceUri = null, |
---|
701 | $multiline = false, $indent = '_auto', $linebreak = "\n", |
---|
702 | $sortAttributes = true) |
---|
703 | { |
---|
704 | // if no attributes hav been set, use empty attributes |
---|
705 | if (!isset($attributes) || !is_array($attributes)) { |
---|
706 | $attributes = array(); |
---|
707 | } |
---|
708 | |
---|
709 | if ($namespaceUri != null) { |
---|
710 | $parts = XML_Util::splitQualifiedName($qname); |
---|
711 | } |
---|
712 | |
---|
713 | // check for multiline attributes |
---|
714 | if ($multiline === true) { |
---|
715 | if ($indent === '_auto') { |
---|
716 | $indent = str_repeat(' ', (strlen($qname)+2)); |
---|
717 | } |
---|
718 | } |
---|
719 | |
---|
720 | if ($namespaceUri != null) { |
---|
721 | // is a namespace given |
---|
722 | if (isset($parts['namespace']) && !empty($parts['namespace'])) { |
---|
723 | $attributes['xmlns:' . $parts['namespace']] = $namespaceUri; |
---|
724 | } else { |
---|
725 | // define this Uri as the default namespace |
---|
726 | $attributes['xmlns'] = $namespaceUri; |
---|
727 | } |
---|
728 | } |
---|
729 | |
---|
730 | // create attribute list |
---|
731 | $attList = XML_Util::attributesToString($attributes, $sortAttributes, |
---|
732 | $multiline, $indent, $linebreak); |
---|
733 | $element = sprintf('<%s%s>', $qname, $attList); |
---|
734 | return $element; |
---|
735 | } |
---|
736 | |
---|
737 | /** |
---|
738 | * create an end element |
---|
739 | * |
---|
740 | * <code> |
---|
741 | * require_once 'XML/Util.php'; |
---|
742 | * |
---|
743 | * // create an XML start element: |
---|
744 | * $tag = XML_Util::createEndElement('myNs:myTag'); |
---|
745 | * </code> |
---|
746 | * |
---|
747 | * @param string $qname qualified tagname (including namespace) |
---|
748 | * |
---|
749 | * @return string XML end element |
---|
750 | * @access public |
---|
751 | * @static |
---|
752 | * @see createStartElement(), createTag() |
---|
753 | */ |
---|
754 | function createEndElement($qname) |
---|
755 | { |
---|
756 | $element = sprintf('</%s>', $qname); |
---|
757 | return $element; |
---|
758 | } |
---|
759 | |
---|
760 | /** |
---|
761 | * create an XML comment |
---|
762 | * |
---|
763 | * <code> |
---|
764 | * require_once 'XML/Util.php'; |
---|
765 | * |
---|
766 | * // create an XML start element: |
---|
767 | * $tag = XML_Util::createComment('I am a comment'); |
---|
768 | * </code> |
---|
769 | * |
---|
770 | * @param string $content content of the comment |
---|
771 | * |
---|
772 | * @return string XML comment |
---|
773 | * @access public |
---|
774 | * @static |
---|
775 | */ |
---|
776 | function createComment($content) |
---|
777 | { |
---|
778 | $comment = sprintf('<!-- %s -->', $content); |
---|
779 | return $comment; |
---|
780 | } |
---|
781 | |
---|
782 | /** |
---|
783 | * create a CData section |
---|
784 | * |
---|
785 | * <code> |
---|
786 | * require_once 'XML/Util.php'; |
---|
787 | * |
---|
788 | * // create a CData section |
---|
789 | * $tag = XML_Util::createCDataSection('I am content.'); |
---|
790 | * </code> |
---|
791 | * |
---|
792 | * @param string $data data of the CData section |
---|
793 | * |
---|
794 | * @return string CData section with content |
---|
795 | * @access public |
---|
796 | * @static |
---|
797 | */ |
---|
798 | function createCDataSection($data) |
---|
799 | { |
---|
800 | return sprintf('<![CDATA[%s]]>', |
---|
801 | preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data))); |
---|
802 | |
---|
803 | } |
---|
804 | |
---|
805 | /** |
---|
806 | * split qualified name and return namespace and local part |
---|
807 | * |
---|
808 | * <code> |
---|
809 | * require_once 'XML/Util.php'; |
---|
810 | * |
---|
811 | * // split qualified tag |
---|
812 | * $parts = XML_Util::splitQualifiedName('xslt:stylesheet'); |
---|
813 | * </code> |
---|
814 | * the returned array will contain two elements: |
---|
815 | * <pre> |
---|
816 | * array( |
---|
817 | * 'namespace' => 'xslt', |
---|
818 | * 'localPart' => 'stylesheet' |
---|
819 | * ); |
---|
820 | * </pre> |
---|
821 | * |
---|
822 | * @param string $qname qualified tag name |
---|
823 | * @param string $defaultNs default namespace (optional) |
---|
824 | * |
---|
825 | * @return array array containing namespace and local part |
---|
826 | * @access public |
---|
827 | * @static |
---|
828 | */ |
---|
829 | function splitQualifiedName($qname, $defaultNs = null) |
---|
830 | { |
---|
831 | if (strstr($qname, ':')) { |
---|
832 | $tmp = explode(':', $qname); |
---|
833 | return array( |
---|
834 | 'namespace' => $tmp[0], |
---|
835 | 'localPart' => $tmp[1] |
---|
836 | ); |
---|
837 | } |
---|
838 | return array( |
---|
839 | 'namespace' => $defaultNs, |
---|
840 | 'localPart' => $qname |
---|
841 | ); |
---|
842 | } |
---|
843 | |
---|
844 | /** |
---|
845 | * check, whether string is valid XML name |
---|
846 | * |
---|
847 | * <p>XML names are used for tagname, attribute names and various |
---|
848 | * other, lesser known entities.</p> |
---|
849 | * <p>An XML name may only consist of alphanumeric characters, |
---|
850 | * dashes, undescores and periods, and has to start with a letter |
---|
851 | * or an underscore.</p> |
---|
852 | * |
---|
853 | * <code> |
---|
854 | * require_once 'XML/Util.php'; |
---|
855 | * |
---|
856 | * // verify tag name |
---|
857 | * $result = XML_Util::isValidName('invalidTag?'); |
---|
858 | * if (is_a($result, 'PEAR_Error')) { |
---|
859 | * print 'Invalid XML name: ' . $result->getMessage(); |
---|
860 | * } |
---|
861 | * </code> |
---|
862 | * |
---|
863 | * @param string $string string that should be checked |
---|
864 | * |
---|
865 | * @return mixed true, if string is a valid XML name, PEAR error otherwise |
---|
866 | * @access public |
---|
867 | * @static |
---|
868 | * @todo support for other charsets |
---|
869 | * @todo PEAR CS - unable to avoid 85-char limit on second preg_match |
---|
870 | */ |
---|
871 | function isValidName($string) |
---|
872 | { |
---|
873 | // check for invalid chars |
---|
874 | if (!preg_match('/^[[:alpha:]_]$/', $string{0})) { |
---|
875 | return XML_Util::raiseError('XML names may only start with letter ' |
---|
876 | . 'or underscore', XML_UTIL_ERROR_INVALID_START); |
---|
877 | } |
---|
878 | |
---|
879 | // check for invalid chars |
---|
880 | if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/', |
---|
881 | $string) |
---|
882 | ) { |
---|
883 | return XML_Util::raiseError('XML names may only contain alphanumeric ' |
---|
884 | . 'chars, period, hyphen, colon and underscores', |
---|
885 | XML_UTIL_ERROR_INVALID_CHARS); |
---|
886 | } |
---|
887 | // XML name is valid |
---|
888 | return true; |
---|
889 | } |
---|
890 | |
---|
891 | /** |
---|
892 | * replacement for XML_Util::raiseError |
---|
893 | * |
---|
894 | * Avoids the necessity to always require |
---|
895 | * PEAR.php |
---|
896 | * |
---|
897 | * @param string $msg error message |
---|
898 | * @param int $code error code |
---|
899 | * |
---|
900 | * @return PEAR_Error |
---|
901 | * @access public |
---|
902 | * @static |
---|
903 | * @todo PEAR CS - should this use include_once instead? |
---|
904 | */ |
---|
905 | function raiseError($msg, $code) |
---|
906 | { |
---|
907 | require_once 'PEAR.php'; |
---|
908 | return PEAR::raiseError($msg, $code); |
---|
909 | } |
---|
910 | } |
---|
911 | ?> |
---|