PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/script/lib/XML/Util.php

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