PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/digidoc/include/_PEAR/XML/Util.php

https://bitbucket.org/elver/liituja
PHP | 645 lines | 245 code | 39 blank | 361 comment | 67 complexity | 440e920953002815abec681410b744b3 MD5 | raw file
  1. <?PHP
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2002 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stephan Schmidt <schst@php-tools.net> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Util.php,v 1.1 2007/04/05 12:11:45 gen Exp $
  20. /**
  21. * error code for invalid chars in XML name
  22. */
  23. define("XML_UTIL_ERROR_INVALID_CHARS", 51);
  24. /**
  25. * error code for invalid chars in XML name
  26. */
  27. define("XML_UTIL_ERROR_INVALID_START", 52);
  28. /**
  29. * error code for non-scalar tag content
  30. */
  31. define("XML_UTIL_ERROR_NON_SCALAR_CONTENT", 60);
  32. /**
  33. * replace XML entities
  34. */
  35. define("XML_UTIL_REPLACE_ENTITIES", 1);
  36. /**
  37. * embedd content in a CData Section
  38. */
  39. define("XML_UTIL_CDATA_SECTION", 2);
  40. /**
  41. * do not replace entitites
  42. */
  43. define("XML_UTIL_ENTITIES_NONE", 0);
  44. /**
  45. * replace all XML entitites
  46. * This setting will replace <, >, ", ' and &
  47. */
  48. define("XML_UTIL_ENTITIES_XML", 1);
  49. /**
  50. * replace only required XML entitites
  51. * This setting will replace <, " and &
  52. */
  53. define("XML_UTIL_ENTITIES_XML_REQUIRED", 2);
  54. /**
  55. * replace HTML entitites
  56. * @link http://www.php.net/htmlentities
  57. */
  58. define("XML_UTIL_ENTITIES_HTML", 3);
  59. /**
  60. * utility class for working with XML documents
  61. *
  62. * @category XML
  63. * @package XML_Util
  64. * @version 0.6.0
  65. * @author Stephan Schmidt <schst@php.net>
  66. */
  67. class XML_Util {
  68. /**
  69. * return API version
  70. *
  71. * @access public
  72. * @static
  73. * @return string $version API version
  74. */
  75. function apiVersion()
  76. {
  77. return "0.6";
  78. }
  79. /**
  80. * replace XML entities
  81. *
  82. * With the optional second parameter, you may select, which
  83. * entities should be replaced.
  84. *
  85. * <code>
  86. * require_once 'XML/Util.php';
  87. *
  88. * // replace XML entites:
  89. * $string = XML_Util::replaceEntities("This string contains < & >.");
  90. * </code>
  91. *
  92. * @access public
  93. * @static
  94. * @param string string where XML special chars should be replaced
  95. * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
  96. * @return string string with replaced chars
  97. */
  98. function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
  99. {
  100. switch ($replaceEntities) {
  101. case XML_UTIL_ENTITIES_XML:
  102. return strtr($string,array(
  103. '&' => '&amp;',
  104. '>' => '&gt;',
  105. '<' => '&lt;',
  106. '"' => '&quot;',
  107. '\'' => '&apos;' ));
  108. break;
  109. case XML_UTIL_ENTITIES_XML_REQUIRED:
  110. return strtr($string,array(
  111. '&' => '&amp;',
  112. '<' => '&lt;',
  113. '"' => '&quot;' ));
  114. break;
  115. case XML_UTIL_ENTITIES_HTML:
  116. return htmlspecialchars($string);
  117. break;
  118. }
  119. return $string;
  120. }
  121. /**
  122. * build an xml declaration
  123. *
  124. * <code>
  125. * require_once 'XML/Util.php';
  126. *
  127. * // get an XML declaration:
  128. * $xmlDecl = XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
  129. * </code>
  130. *
  131. * @access public
  132. * @static
  133. * @param string $version xml version
  134. * @param string $encoding character encoding
  135. * @param boolean $standAlone document is standalone (or not)
  136. * @return string $decl xml declaration
  137. * @uses XML_Util::attributesToString() to serialize the attributes of the XML declaration
  138. */
  139. function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
  140. {
  141. $attributes = array(
  142. "version" => $version,
  143. );
  144. // add encoding
  145. if ($encoding !== null) {
  146. $attributes["encoding"] = $encoding;
  147. }
  148. // add standalone, if specified
  149. if ($standalone !== null) {
  150. $attributes["standalone"] = $standalone ? "yes" : "no";
  151. }
  152. return sprintf("<?xml%s?>", XML_Util::attributesToString($attributes, false));
  153. }
  154. /**
  155. * build a document type declaration
  156. *
  157. * <code>
  158. * require_once 'XML/Util.php';
  159. *
  160. * // get a doctype declaration:
  161. * $xmlDecl = XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
  162. * </code>
  163. *
  164. * @access public
  165. * @static
  166. * @param string $root name of the root tag
  167. * @param string $uri uri of the doctype definition (or array with uri and public id)
  168. * @param string $internalDtd internal dtd entries
  169. * @return string $decl doctype declaration
  170. * @since 0.2
  171. */
  172. function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
  173. {
  174. if (is_array($uri)) {
  175. $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
  176. } elseif (!empty($uri)) {
  177. $ref = sprintf( ' SYSTEM "%s"', $uri );
  178. } else {
  179. $ref = "";
  180. }
  181. if (empty($internalDtd)) {
  182. return sprintf("<!DOCTYPE %s%s>", $root, $ref);
  183. } else {
  184. return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
  185. }
  186. }
  187. /**
  188. * create string representation of an attribute list
  189. *
  190. * <code>
  191. * require_once 'XML/Util.php';
  192. *
  193. * // build an attribute string
  194. * $att = array(
  195. * "foo" => "bar",
  196. * "argh" => "tomato"
  197. * );
  198. *
  199. * $attList = XML_Util::attributesToString($att);
  200. * </code>
  201. *
  202. * @access public
  203. * @static
  204. * @param array $attributes attribute array
  205. * @param boolean|array $sort sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
  206. * @param boolean $multiline use linebreaks, if more than one attribute is given
  207. * @param string $indent string used for indentation of multiline attributes
  208. * @param string $linebreak string used for linebreaks of multiline attributes
  209. * @param integer $entities setting for entities in attribute values (one of XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
  210. * @return string string representation of the attributes
  211. * @uses XML_Util::replaceEntities() to replace XML entities in attribute values
  212. * @todo allow sort also to be an options array
  213. */
  214. function attributesToString($attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
  215. {
  216. /**
  217. * second parameter may be an array
  218. */
  219. if (is_array($sort)) {
  220. if (isset($sort['multiline'])) {
  221. $multiline = $sort['multiline'];
  222. }
  223. if (isset($sort['indent'])) {
  224. $indent = $sort['indent'];
  225. }
  226. if (isset($sort['linebreak'])) {
  227. $multiline = $sort['linebreak'];
  228. }
  229. if (isset($sort['entities'])) {
  230. $entities = $sort['entities'];
  231. }
  232. if (isset($sort['sort'])) {
  233. $sort = $sort['sort'];
  234. } else {
  235. $sort = true;
  236. }
  237. }
  238. $string = '';
  239. if (is_array($attributes) && !empty($attributes)) {
  240. if ($sort) {
  241. ksort($attributes);
  242. }
  243. if( !$multiline || count($attributes) == 1) {
  244. foreach ($attributes as $key => $value) {
  245. if ($entities != XML_UTIL_ENTITIES_NONE) {
  246. $value = XML_Util::replaceEntities($value, $entities);
  247. }
  248. $string .= ' '.$key.'="'.$value.'"';
  249. }
  250. } else {
  251. $first = true;
  252. foreach ($attributes as $key => $value) {
  253. if ($entities != XML_UTIL_ENTITIES_NONE) {
  254. $value = XML_Util::replaceEntities($value, $entities);
  255. }
  256. if ($first) {
  257. $string .= " ".$key.'="'.$value.'"';
  258. $first = false;
  259. } else {
  260. $string .= $linebreak.$indent.$key.'="'.$value.'"';
  261. }
  262. }
  263. }
  264. }
  265. return $string;
  266. }
  267. /**
  268. * create a tag
  269. *
  270. * This method will call XML_Util::createTagFromArray(), which
  271. * is more flexible.
  272. *
  273. * <code>
  274. * require_once 'XML/Util.php';
  275. *
  276. * // create an XML tag:
  277. * $tag = XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
  278. * </code>
  279. *
  280. * @access public
  281. * @static
  282. * @param string $qname qualified tagname (including namespace)
  283. * @param array $attributes array containg attributes
  284. * @param mixed $content
  285. * @param string $namespaceUri URI of the namespace
  286. * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
  287. * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
  288. * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
  289. * @param string $linebreak string used for linebreaks
  290. * @return string $string XML tag
  291. * @see XML_Util::createTagFromArray()
  292. * @uses XML_Util::createTagFromArray() to create the tag
  293. */
  294. function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n")
  295. {
  296. $tag = array(
  297. "qname" => $qname,
  298. "attributes" => $attributes
  299. );
  300. // add tag content
  301. if ($content !== null) {
  302. $tag["content"] = $content;
  303. }
  304. // add namespace Uri
  305. if ($namespaceUri !== null) {
  306. $tag["namespaceUri"] = $namespaceUri;
  307. }
  308. return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak);
  309. }
  310. /**
  311. * create a tag from an array
  312. * this method awaits an array in the following format
  313. * <pre>
  314. * array(
  315. * "qname" => $qname // qualified name of the tag
  316. * "namespace" => $namespace // namespace prefix (optional, if qname is specified or no namespace)
  317. * "localpart" => $localpart, // local part of the tagname (optional, if qname is specified)
  318. * "attributes" => array(), // array containing all attributes (optional)
  319. * "content" => $content, // tag content (optional)
  320. * "namespaceUri" => $namespaceUri // namespaceUri for the given namespace (optional)
  321. * )
  322. * </pre>
  323. *
  324. * <code>
  325. * require_once 'XML/Util.php';
  326. *
  327. * $tag = array(
  328. * "qname" => "foo:bar",
  329. * "namespaceUri" => "http://foo.com",
  330. * "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  331. * "content" => "I'm inside the tag",
  332. * );
  333. * // creating a tag with qualified name and namespaceUri
  334. * $string = XML_Util::createTagFromArray($tag);
  335. * </code>
  336. *
  337. * @access public
  338. * @static
  339. * @param array $tag tag definition
  340. * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
  341. * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
  342. * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
  343. * @param string $linebreak string used for linebreaks
  344. * @return string $string XML tag
  345. * @see XML_Util::createTag()
  346. * @uses XML_Util::attributesToString() to serialize the attributes of the tag
  347. * @uses XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
  348. */
  349. function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n" )
  350. {
  351. if (isset($tag["content"]) && !is_scalar($tag["content"])) {
  352. return XML_Util::raiseError( "Supplied non-scalar value as tag content", XML_UTIL_ERROR_NON_SCALAR_CONTENT );
  353. }
  354. // if no attributes hav been set, use empty attributes
  355. if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
  356. $tag["attributes"] = array();
  357. }
  358. // qualified name is not given
  359. if (!isset($tag["qname"])) {
  360. // check for namespace
  361. if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
  362. $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
  363. } else {
  364. $tag["qname"] = $tag["localPart"];
  365. }
  366. // namespace URI is set, but no namespace
  367. } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
  368. $parts = XML_Util::splitQualifiedName($tag["qname"]);
  369. $tag["localPart"] = $parts["localPart"];
  370. if (isset($parts["namespace"])) {
  371. $tag["namespace"] = $parts["namespace"];
  372. }
  373. }
  374. if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
  375. // is a namespace given
  376. if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
  377. $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
  378. } else {
  379. // define this Uri as the default namespace
  380. $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
  381. }
  382. }
  383. // check for multiline attributes
  384. if ($multiline === true) {
  385. if ($indent === "_auto") {
  386. $indent = str_repeat(" ", (strlen($tag["qname"])+2));
  387. }
  388. }
  389. // create attribute list
  390. $attList = XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak );
  391. if (!isset($tag["content"]) || (string)$tag["content"] == '') {
  392. $tag = sprintf("<%s%s />", $tag["qname"], $attList);
  393. } else {
  394. if ($replaceEntities == XML_UTIL_REPLACE_ENTITIES) {
  395. $tag["content"] = XML_Util::replaceEntities($tag["content"]);
  396. } elseif ($replaceEntities == XML_UTIL_CDATA_SECTION) {
  397. $tag["content"] = XML_Util::createCDataSection($tag["content"]);
  398. }
  399. $tag = sprintf("<%s%s>%s</%s>", $tag["qname"], $attList, $tag["content"], $tag["qname"] );
  400. }
  401. return $tag;
  402. }
  403. /**
  404. * create a start element
  405. *
  406. * <code>
  407. * require_once 'XML/Util.php';
  408. *
  409. * // create an XML start element:
  410. * $tag = XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
  411. * </code>
  412. *
  413. * @access public
  414. * @static
  415. * @param string $qname qualified tagname (including namespace)
  416. * @param array $attributes array containg attributes
  417. * @param string $namespaceUri URI of the namespace
  418. * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
  419. * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
  420. * @param string $linebreak string used for linebreaks
  421. * @return string $string XML start element
  422. * @see XML_Util::createEndElement(), XML_Util::createTag()
  423. */
  424. function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
  425. {
  426. // if no attributes hav been set, use empty attributes
  427. if (!isset($attributes) || !is_array($attributes)) {
  428. $attributes = array();
  429. }
  430. if ($namespaceUri != null) {
  431. $parts = XML_Util::splitQualifiedName($qname);
  432. }
  433. // check for multiline attributes
  434. if ($multiline === true) {
  435. if ($indent === "_auto") {
  436. $indent = str_repeat(" ", (strlen($qname)+2));
  437. }
  438. }
  439. if ($namespaceUri != null) {
  440. // is a namespace given
  441. if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
  442. $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
  443. } else {
  444. // define this Uri as the default namespace
  445. $attributes["xmlns"] = $namespaceUri;
  446. }
  447. }
  448. // create attribute list
  449. $attList = XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
  450. $element = sprintf("<%s%s>", $qname, $attList);
  451. return $element;
  452. }
  453. /**
  454. * create an end element
  455. *
  456. * <code>
  457. * require_once 'XML/Util.php';
  458. *
  459. * // create an XML start element:
  460. * $tag = XML_Util::createEndElement("myNs:myTag");
  461. * </code>
  462. *
  463. * @access public
  464. * @static
  465. * @param string $qname qualified tagname (including namespace)
  466. * @return string $string XML end element
  467. * @see XML_Util::createStartElement(), XML_Util::createTag()
  468. */
  469. function createEndElement($qname)
  470. {
  471. $element = sprintf("</%s>", $qname);
  472. return $element;
  473. }
  474. /**
  475. * create an XML comment
  476. *
  477. * <code>
  478. * require_once 'XML/Util.php';
  479. *
  480. * // create an XML start element:
  481. * $tag = XML_Util::createComment("I am a comment");
  482. * </code>
  483. *
  484. * @access public
  485. * @static
  486. * @param string $content content of the comment
  487. * @return string $comment XML comment
  488. */
  489. function createComment($content)
  490. {
  491. $comment = sprintf("<!-- %s -->", $content);
  492. return $comment;
  493. }
  494. /**
  495. * create a CData section
  496. *
  497. * <code>
  498. * require_once 'XML/Util.php';
  499. *
  500. * // create a CData section
  501. * $tag = XML_Util::createCDataSection("I am content.");
  502. * </code>
  503. *
  504. * @access public
  505. * @static
  506. * @param string $data data of the CData section
  507. * @return string $string CData section with content
  508. */
  509. function createCDataSection($data)
  510. {
  511. return sprintf("<![CDATA[%s]]>", $data);
  512. }
  513. /**
  514. * split qualified name and return namespace and local part
  515. *
  516. * <code>
  517. * require_once 'XML/Util.php';
  518. *
  519. * // split qualified tag
  520. * $parts = XML_Util::splitQualifiedName("xslt:stylesheet");
  521. * </code>
  522. * the returned array will contain two elements:
  523. * <pre>
  524. * array(
  525. * "namespace" => "xslt",
  526. * "localPart" => "stylesheet"
  527. * );
  528. * </pre>
  529. *
  530. * @access public
  531. * @static
  532. * @param string $qname qualified tag name
  533. * @param string $defaultNs default namespace (optional)
  534. * @return array $parts array containing namespace and local part
  535. */
  536. function splitQualifiedName($qname, $defaultNs = null)
  537. {
  538. if (strstr($qname, ':')) {
  539. $tmp = explode(":", $qname);
  540. return array(
  541. "namespace" => $tmp[0],
  542. "localPart" => $tmp[1]
  543. );
  544. }
  545. return array(
  546. "namespace" => $defaultNs,
  547. "localPart" => $qname
  548. );
  549. }
  550. /**
  551. * check, whether string is valid XML name
  552. *
  553. * <p>XML names are used for tagname, attribute names and various
  554. * other, lesser known entities.</p>
  555. * <p>An XML name may only consist of alphanumeric characters,
  556. * dashes, undescores and periods, and has to start with a letter
  557. * or an underscore.
  558. * </p>
  559. *
  560. * <code>
  561. * require_once 'XML/Util.php';
  562. *
  563. * // verify tag name
  564. * $result = XML_Util::isValidName("invalidTag?");
  565. * if (XML_Util::isError($result)) {
  566. * print "Invalid XML name: " . $result->getMessage();
  567. * }
  568. * </code>
  569. *
  570. * @access public
  571. * @static
  572. * @param string $string string that should be checked
  573. * @return mixed $valid true, if string is a valid XML name, PEAR error otherwise
  574. * @todo support for other charsets
  575. */
  576. function isValidName($string)
  577. {
  578. // check for invalid chars
  579. if (!preg_match("/^[[:alnum:]_\-.]$/", $string{0})) {
  580. return XML_Util::raiseError( "XML names may only start with letter or underscore", XML_UTIL_ERROR_INVALID_START );
  581. }
  582. // check for invalid chars
  583. if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/", $string)) {
  584. return XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", XML_UTIL_ERROR_INVALID_CHARS );
  585. }
  586. // XML name is valid
  587. return true;
  588. }
  589. /**
  590. * replacement for XML_Util::raiseError
  591. *
  592. * Avoids the necessity to always require
  593. * PEAR.php
  594. *
  595. * @access public
  596. * @param string error message
  597. * @param integer error code
  598. * @return object PEAR_Error
  599. */
  600. function raiseError($msg, $code)
  601. {
  602. require_once 'PEAR.php';
  603. return PEAR::raiseError($msg, $code);
  604. }
  605. }
  606. ?>