PageRenderTime 62ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/etc/inc/XML/Util.php

https://bitbucket.org/cougar/nas4free.hg
PHP | 913 lines | 328 code | 51 blank | 534 comment | 73 complexity | 1ef941beda978f6b82fe4213ac969abe MD5 | raw file
Possible License(s): Unlicense, MPL-2.0, GPL-2.0, GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
  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.37 2008/05/22 07:47:11 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.0
  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. static function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
  163. $encoding = 'ISO-8859-1')
  164. {
  165. switch ($replaceEntities) {
  166. case XML_UTIL_ENTITIES_XML:
  167. return strtr($string, array(
  168. '&' => '&amp;',
  169. '>' => '&gt;',
  170. '<' => '&lt;',
  171. '"' => '&quot;',
  172. '\'' => '&apos;' ));
  173. break;
  174. case XML_UTIL_ENTITIES_XML_REQUIRED:
  175. return strtr($string, array(
  176. '&' => '&amp;',
  177. '<' => '&lt;',
  178. '"' => '&quot;' ));
  179. break;
  180. case XML_UTIL_ENTITIES_HTML:
  181. return htmlentities($string, ENT_COMPAT, $encoding);
  182. break;
  183. }
  184. return $string;
  185. }
  186. /**
  187. * reverse XML entities
  188. *
  189. * With the optional second parameter, you may select, which
  190. * entities should be reversed.
  191. *
  192. * <code>
  193. * require_once 'XML/Util.php';
  194. *
  195. * // reverse XML entites:
  196. * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
  197. * </code>
  198. *
  199. * With the optional third parameter, you may pass the character encoding
  200. * <code>
  201. * require_once 'XML/Util.php';
  202. *
  203. * // reverse XML entites in UTF-8:
  204. * $string = XML_Util::reverseEntities(
  205. * 'This string contains &lt; &amp; &gt; as well as'
  206. * . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
  207. * XML_UTIL_ENTITIES_HTML,
  208. * 'UTF-8'
  209. * );
  210. * </code>
  211. *
  212. * @param string $string string where XML special chars
  213. * should be replaced
  214. * @param int $replaceEntities setting for entities in attribute values
  215. * (one of XML_UTIL_ENTITIES_XML,
  216. * XML_UTIL_ENTITIES_XML_REQUIRED,
  217. * XML_UTIL_ENTITIES_HTML)
  218. * @param string $encoding encoding value (if any)...
  219. * must be a valid encoding as determined
  220. * by the html_entity_decode() function
  221. *
  222. * @return string string with replaced chars
  223. * @access public
  224. * @static
  225. * @see replaceEntities()
  226. */
  227. static function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
  228. $encoding = 'ISO-8859-1')
  229. {
  230. switch ($replaceEntities) {
  231. case XML_UTIL_ENTITIES_XML:
  232. return strtr($string, array(
  233. '&amp;' => '&',
  234. '&gt;' => '>',
  235. '&lt;' => '<',
  236. '&quot;' => '"',
  237. '&apos;' => '\'' ));
  238. break;
  239. case XML_UTIL_ENTITIES_XML_REQUIRED:
  240. return strtr($string, array(
  241. '&amp;' => '&',
  242. '&lt;' => '<',
  243. '&quot;' => '"' ));
  244. break;
  245. case XML_UTIL_ENTITIES_HTML:
  246. return html_entity_decode($string, ENT_COMPAT, $encoding);
  247. break;
  248. }
  249. return $string;
  250. }
  251. /**
  252. * build an xml declaration
  253. *
  254. * <code>
  255. * require_once 'XML/Util.php';
  256. *
  257. * // get an XML declaration:
  258. * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
  259. * </code>
  260. *
  261. * @param string $version xml version
  262. * @param string $encoding character encoding
  263. * @param bool $standalone document is standalone (or not)
  264. *
  265. * @return string xml declaration
  266. * @access public
  267. * @static
  268. * @uses attributesToString() to serialize the attributes of the XML declaration
  269. */
  270. static function getXMLDeclaration($version = '1.0', $encoding = null,
  271. $standalone = null)
  272. {
  273. $attributes = array(
  274. 'version' => $version,
  275. );
  276. // add encoding
  277. if ($encoding !== null) {
  278. $attributes['encoding'] = $encoding;
  279. }
  280. // add standalone, if specified
  281. if ($standalone !== null) {
  282. $attributes['standalone'] = $standalone ? 'yes' : 'no';
  283. }
  284. return sprintf('<?xml%s?>',
  285. XML_Util::attributesToString($attributes, false));
  286. }
  287. /**
  288. * build a document type declaration
  289. *
  290. * <code>
  291. * require_once 'XML/Util.php';
  292. *
  293. * // get a doctype declaration:
  294. * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
  295. * </code>
  296. *
  297. * @param string $root name of the root tag
  298. * @param string $uri uri of the doctype definition
  299. * (or array with uri and public id)
  300. * @param string $internalDtd internal dtd entries
  301. *
  302. * @return string doctype declaration
  303. * @access public
  304. * @static
  305. * @since 0.2
  306. */
  307. static function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
  308. {
  309. if (is_array($uri)) {
  310. $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
  311. } elseif (!empty($uri)) {
  312. $ref = sprintf(' SYSTEM "%s"', $uri);
  313. } else {
  314. $ref = '';
  315. }
  316. if (empty($internalDtd)) {
  317. return sprintf('<!DOCTYPE %s%s>', $root, $ref);
  318. } else {
  319. return sprintf('<!DOCTYPE %s%s [\n%s\n]>', $root, $ref, $internalDtd);
  320. }
  321. }
  322. /**
  323. * create string representation of an attribute list
  324. *
  325. * <code>
  326. * require_once 'XML/Util.php';
  327. *
  328. * // build an attribute string
  329. * $att = array(
  330. * 'foo' => 'bar',
  331. * 'argh' => 'tomato'
  332. * );
  333. *
  334. * $attList = XML_Util::attributesToString($att);
  335. * </code>
  336. *
  337. * @param array $attributes attribute array
  338. * @param bool|array $sort sort attribute list alphabetically,
  339. * may also be an assoc array containing
  340. * the keys 'sort', 'multiline', 'indent',
  341. * 'linebreak' and 'entities'
  342. * @param bool $multiline use linebreaks, if more than
  343. * one attribute is given
  344. * @param string $indent string used for indentation of
  345. * multiline attributes
  346. * @param string $linebreak string used for linebreaks of
  347. * multiline attributes
  348. * @param int $entities setting for entities in attribute values
  349. * (one of XML_UTIL_ENTITIES_NONE,
  350. * XML_UTIL_ENTITIES_XML,
  351. * XML_UTIL_ENTITIES_XML_REQUIRED,
  352. * XML_UTIL_ENTITIES_HTML)
  353. *
  354. * @return string string representation of the attributes
  355. * @access public
  356. * @static
  357. * @uses replaceEntities() to replace XML entities in attribute values
  358. * @todo allow sort also to be an options array
  359. */
  360. static function attributesToString($attributes, $sort = true, $multiline = false,
  361. $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
  362. {
  363. /*
  364. * second parameter may be an array
  365. */
  366. if (is_array($sort)) {
  367. if (isset($sort['multiline'])) {
  368. $multiline = $sort['multiline'];
  369. }
  370. if (isset($sort['indent'])) {
  371. $indent = $sort['indent'];
  372. }
  373. if (isset($sort['linebreak'])) {
  374. $multiline = $sort['linebreak'];
  375. }
  376. if (isset($sort['entities'])) {
  377. $entities = $sort['entities'];
  378. }
  379. if (isset($sort['sort'])) {
  380. $sort = $sort['sort'];
  381. } else {
  382. $sort = true;
  383. }
  384. }
  385. $string = '';
  386. if (is_array($attributes) && !empty($attributes)) {
  387. if ($sort) {
  388. ksort($attributes);
  389. }
  390. if ( !$multiline || count($attributes) == 1) {
  391. foreach ($attributes as $key => $value) {
  392. if ($entities != XML_UTIL_ENTITIES_NONE) {
  393. if ($entities === XML_UTIL_CDATA_SECTION) {
  394. $entities = XML_UTIL_ENTITIES_XML;
  395. }
  396. $value = XML_Util::replaceEntities($value, $entities);
  397. }
  398. $string .= ' ' . $key . '="' . $value . '"';
  399. }
  400. } else {
  401. $first = true;
  402. foreach ($attributes as $key => $value) {
  403. if ($entities != XML_UTIL_ENTITIES_NONE) {
  404. $value = XML_Util::replaceEntities($value, $entities);
  405. }
  406. if ($first) {
  407. $string .= ' ' . $key . '="' . $value . '"';
  408. $first = false;
  409. } else {
  410. $string .= $linebreak . $indent . $key . '="' . $value . '"';
  411. }
  412. }
  413. }
  414. }
  415. return $string;
  416. }
  417. /**
  418. * Collapses empty tags.
  419. *
  420. * @param string $xml XML
  421. * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
  422. * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
  423. *
  424. * @return string XML
  425. * @access public
  426. * @static
  427. * @todo PEAR CS - unable to avoid "space after open parens" error
  428. * in the IF branch
  429. */
  430. static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
  431. {
  432. if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
  433. return preg_replace(
  434. '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
  435. . 'param)([^>]*)><\/\\1>/s',
  436. '<\\1\\2 />',
  437. $xml);
  438. } else {
  439. return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml);
  440. }
  441. }
  442. /**
  443. * create a tag
  444. *
  445. * This method will call XML_Util::createTagFromArray(), which
  446. * is more flexible.
  447. *
  448. * <code>
  449. * require_once 'XML/Util.php';
  450. *
  451. * // create an XML tag:
  452. * $tag = XML_Util::createTag('myNs:myTag',
  453. * array('foo' => 'bar'),
  454. * 'This is inside the tag',
  455. * 'http://www.w3c.org/myNs#');
  456. * </code>
  457. *
  458. * @param string $qname qualified tagname (including namespace)
  459. * @param array $attributes array containg attributes
  460. * @param mixed $content the content
  461. * @param string $namespaceUri URI of the namespace
  462. * @param int $replaceEntities whether to replace XML special chars in
  463. * content, embedd it in a CData section
  464. * or none of both
  465. * @param bool $multiline whether to create a multiline tag where
  466. * each attribute gets written to a single line
  467. * @param string $indent string used to indent attributes
  468. * (_auto indents attributes so they start
  469. * at the same column)
  470. * @param string $linebreak string used for linebreaks
  471. * @param bool $sortAttributes Whether to sort the attributes or not
  472. *
  473. * @return string XML tag
  474. * @access public
  475. * @static
  476. * @see createTagFromArray()
  477. * @uses createTagFromArray() to create the tag
  478. */
  479. static function createTag($qname, $attributes = array(), $content = null,
  480. $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  481. $multiline = false, $indent = '_auto', $linebreak = "\n",
  482. $sortAttributes = true)
  483. {
  484. $tag = array(
  485. 'qname' => $qname,
  486. 'attributes' => $attributes
  487. );
  488. // add tag content
  489. if ($content !== null) {
  490. $tag['content'] = $content;
  491. }
  492. // add namespace Uri
  493. if ($namespaceUri !== null) {
  494. $tag['namespaceUri'] = $namespaceUri;
  495. }
  496. return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline,
  497. $indent, $linebreak, $sortAttributes);
  498. }
  499. /**
  500. * create a tag from an array
  501. * this method awaits an array in the following format
  502. * <pre>
  503. * array(
  504. * // qualified name of the tag
  505. * 'qname' => $qname
  506. *
  507. * // namespace prefix (optional, if qname is specified or no namespace)
  508. * 'namespace' => $namespace
  509. *
  510. * // local part of the tagname (optional, if qname is specified)
  511. * 'localpart' => $localpart,
  512. *
  513. * // array containing all attributes (optional)
  514. * 'attributes' => array(),
  515. *
  516. * // tag content (optional)
  517. * 'content' => $content,
  518. *
  519. * // namespaceUri for the given namespace (optional)
  520. * 'namespaceUri' => $namespaceUri
  521. * )
  522. * </pre>
  523. *
  524. * <code>
  525. * require_once 'XML/Util.php';
  526. *
  527. * $tag = array(
  528. * 'qname' => 'foo:bar',
  529. * 'namespaceUri' => 'http://foo.com',
  530. * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  531. * 'content' => 'I\'m inside the tag',
  532. * );
  533. * // creating a tag with qualified name and namespaceUri
  534. * $string = XML_Util::createTagFromArray($tag);
  535. * </code>
  536. *
  537. * @param array $tag tag definition
  538. * @param int $replaceEntities whether to replace XML special chars in
  539. * content, embedd it in a CData section
  540. * or none of both
  541. * @param bool $multiline whether to create a multiline tag where each
  542. * attribute gets written to a single line
  543. * @param string $indent string used to indent attributes
  544. * (_auto indents attributes so they start
  545. * at the same column)
  546. * @param string $linebreak string used for linebreaks
  547. * @param bool $sortAttributes Whether to sort the attributes or not
  548. *
  549. * @return string XML tag
  550. * @access public
  551. * @static
  552. * @see createTag()
  553. * @uses attributesToString() to serialize the attributes of the tag
  554. * @uses splitQualifiedName() to get local part and namespace of a qualified name
  555. * @uses createCDataSection()
  556. * @uses raiseError()
  557. */
  558. static function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  559. $multiline = false, $indent = '_auto', $linebreak = "\n",
  560. $sortAttributes = true)
  561. {
  562. if (isset($tag['content']) && !is_scalar($tag['content'])) {
  563. return XML_Util::raiseError('Supplied non-scalar value as tag content',
  564. XML_UTIL_ERROR_NON_SCALAR_CONTENT);
  565. }
  566. if (!isset($tag['qname']) && !isset($tag['localPart'])) {
  567. return XML_Util::raiseError('You must either supply a qualified name '
  568. . '(qname) or local tag name (localPart).',
  569. XML_UTIL_ERROR_NO_TAG_NAME);
  570. }
  571. // if no attributes hav been set, use empty attributes
  572. if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
  573. $tag['attributes'] = array();
  574. }
  575. if (isset($tag['namespaces'])) {
  576. foreach ($tag['namespaces'] as $ns => $uri) {
  577. $tag['attributes']['xmlns:' . $ns] = $uri;
  578. }
  579. }
  580. if (!isset($tag['qname'])) {
  581. // qualified name is not given
  582. // check for namespace
  583. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  584. $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
  585. } else {
  586. $tag['qname'] = $tag['localPart'];
  587. }
  588. } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
  589. // namespace URI is set, but no namespace
  590. $parts = XML_Util::splitQualifiedName($tag['qname']);
  591. $tag['localPart'] = $parts['localPart'];
  592. if (isset($parts['namespace'])) {
  593. $tag['namespace'] = $parts['namespace'];
  594. }
  595. }
  596. if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
  597. // is a namespace given
  598. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  599. $tag['attributes']['xmlns:' . $tag['namespace']] =
  600. $tag['namespaceUri'];
  601. } else {
  602. // define this Uri as the default namespace
  603. $tag['attributes']['xmlns'] = $tag['namespaceUri'];
  604. }
  605. }
  606. // check for multiline attributes
  607. if ($multiline === true) {
  608. if ($indent === '_auto') {
  609. $indent = str_repeat(' ', (strlen($tag['qname'])+2));
  610. }
  611. }
  612. // create attribute list
  613. $attList = XML_Util::attributesToString($tag['attributes'],
  614. $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities);
  615. if (!isset($tag['content']) || (string)$tag['content'] == '') {
  616. $tag = sprintf('<%s%s />', $tag['qname'], $attList);
  617. } else {
  618. switch ($replaceEntities) {
  619. case XML_UTIL_ENTITIES_NONE:
  620. break;
  621. case XML_UTIL_CDATA_SECTION:
  622. $tag['content'] = XML_Util::createCDataSection($tag['content']);
  623. break;
  624. default:
  625. $tag['content'] = XML_Util::replaceEntities($tag['content'],
  626. $replaceEntities);
  627. break;
  628. }
  629. $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
  630. $tag['qname']);
  631. }
  632. return $tag;
  633. }
  634. /**
  635. * create a start element
  636. *
  637. * <code>
  638. * require_once 'XML/Util.php';
  639. *
  640. * // create an XML start element:
  641. * $tag = XML_Util::createStartElement('myNs:myTag',
  642. * array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
  643. * </code>
  644. *
  645. * @param string $qname qualified tagname (including namespace)
  646. * @param array $attributes array containg attributes
  647. * @param string $namespaceUri URI of the namespace
  648. * @param bool $multiline whether to create a multiline tag where each
  649. * attribute gets written to a single line
  650. * @param string $indent string used to indent attributes (_auto indents
  651. * attributes so they start at the same column)
  652. * @param string $linebreak string used for linebreaks
  653. * @param bool $sortAttributes Whether to sort the attributes or not
  654. *
  655. * @return string XML start element
  656. * @access public
  657. * @static
  658. * @see createEndElement(), createTag()
  659. */
  660. static function createStartElement($qname, $attributes = array(), $namespaceUri = null,
  661. $multiline = false, $indent = '_auto', $linebreak = "\n",
  662. $sortAttributes = true)
  663. {
  664. // if no attributes hav been set, use empty attributes
  665. if (!isset($attributes) || !is_array($attributes)) {
  666. $attributes = array();
  667. }
  668. if ($namespaceUri != null) {
  669. $parts = XML_Util::splitQualifiedName($qname);
  670. }
  671. // check for multiline attributes
  672. if ($multiline === true) {
  673. if ($indent === '_auto') {
  674. $indent = str_repeat(' ', (strlen($qname)+2));
  675. }
  676. }
  677. if ($namespaceUri != null) {
  678. // is a namespace given
  679. if (isset($parts['namespace']) && !empty($parts['namespace'])) {
  680. $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
  681. } else {
  682. // define this Uri as the default namespace
  683. $attributes['xmlns'] = $namespaceUri;
  684. }
  685. }
  686. // create attribute list
  687. $attList = XML_Util::attributesToString($attributes, $sortAttributes,
  688. $multiline, $indent, $linebreak);
  689. $element = sprintf('<%s%s>', $qname, $attList);
  690. return $element;
  691. }
  692. /**
  693. * create an end element
  694. *
  695. * <code>
  696. * require_once 'XML/Util.php';
  697. *
  698. * // create an XML start element:
  699. * $tag = XML_Util::createEndElement('myNs:myTag');
  700. * </code>
  701. *
  702. * @param string $qname qualified tagname (including namespace)
  703. *
  704. * @return string XML end element
  705. * @access public
  706. * @static
  707. * @see createStartElement(), createTag()
  708. */
  709. static function createEndElement($qname)
  710. {
  711. $element = sprintf('</%s>', $qname);
  712. return $element;
  713. }
  714. /**
  715. * create an XML comment
  716. *
  717. * <code>
  718. * require_once 'XML/Util.php';
  719. *
  720. * // create an XML start element:
  721. * $tag = XML_Util::createComment('I am a comment');
  722. * </code>
  723. *
  724. * @param string $content content of the comment
  725. *
  726. * @return string XML comment
  727. * @access public
  728. * @static
  729. */
  730. static function createComment($content)
  731. {
  732. $comment = sprintf('<!-- %s -->', $content);
  733. return $comment;
  734. }
  735. /**
  736. * create a CData section
  737. *
  738. * <code>
  739. * require_once 'XML/Util.php';
  740. *
  741. * // create a CData section
  742. * $tag = XML_Util::createCDataSection('I am content.');
  743. * </code>
  744. *
  745. * @param string $data data of the CData section
  746. *
  747. * @return string CData section with content
  748. * @access public
  749. * @static
  750. */
  751. static function createCDataSection($data)
  752. {
  753. return sprintf('<![CDATA[%s]]>',
  754. preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data)));
  755. }
  756. /**
  757. * split qualified name and return namespace and local part
  758. *
  759. * <code>
  760. * require_once 'XML/Util.php';
  761. *
  762. * // split qualified tag
  763. * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
  764. * </code>
  765. * the returned array will contain two elements:
  766. * <pre>
  767. * array(
  768. * 'namespace' => 'xslt',
  769. * 'localPart' => 'stylesheet'
  770. * );
  771. * </pre>
  772. *
  773. * @param string $qname qualified tag name
  774. * @param string $defaultNs default namespace (optional)
  775. *
  776. * @return array array containing namespace and local part
  777. * @access public
  778. * @static
  779. */
  780. static function splitQualifiedName($qname, $defaultNs = null)
  781. {
  782. if (strstr($qname, ':')) {
  783. $tmp = explode(':', $qname);
  784. return array(
  785. 'namespace' => $tmp[0],
  786. 'localPart' => $tmp[1]
  787. );
  788. }
  789. return array(
  790. 'namespace' => $defaultNs,
  791. 'localPart' => $qname
  792. );
  793. }
  794. /**
  795. * check, whether string is valid XML name
  796. *
  797. * <p>XML names are used for tagname, attribute names and various
  798. * other, lesser known entities.</p>
  799. * <p>An XML name may only consist of alphanumeric characters,
  800. * dashes, undescores and periods, and has to start with a letter
  801. * or an underscore.</p>
  802. *
  803. * <code>
  804. * require_once 'XML/Util.php';
  805. *
  806. * // verify tag name
  807. * $result = XML_Util::isValidName('invalidTag?');
  808. * if (is_a($result, 'PEAR_Error')) {
  809. * print 'Invalid XML name: ' . $result->getMessage();
  810. * }
  811. * </code>
  812. *
  813. * @param string $string string that should be checked
  814. *
  815. * @return mixed true, if string is a valid XML name, PEAR error otherwise
  816. * @access public
  817. * @static
  818. * @todo support for other charsets
  819. * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
  820. */
  821. static function isValidName($string)
  822. {
  823. // check for invalid chars
  824. if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
  825. return XML_Util::raiseError('XML names may only start with letter '
  826. . 'or underscore', XML_UTIL_ERROR_INVALID_START);
  827. }
  828. // check for invalid chars
  829. if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/',
  830. $string)
  831. ) {
  832. return XML_Util::raiseError('XML names may only contain alphanumeric '
  833. . 'chars, period, hyphen, colon and underscores',
  834. XML_UTIL_ERROR_INVALID_CHARS);
  835. }
  836. // XML name is valid
  837. return true;
  838. }
  839. /**
  840. * replacement for XML_Util::raiseError
  841. *
  842. * Avoids the necessity to always require
  843. * PEAR.php
  844. *
  845. * @param string $msg error message
  846. * @param int $code error code
  847. *
  848. * @return PEAR_Error
  849. * @access public
  850. * @static
  851. * @todo PEAR CS - should this use include_once instead?
  852. */
  853. static function raiseError($msg, $code)
  854. {
  855. require_once 'PEAR.php';
  856. //return PEAR::raiseError($msg, $code);
  857. $pear = new PEAR();
  858. return $pear->raiseError($msg, $code);
  859. }
  860. }
  861. ?>