1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: App::import('Core', 'Set');
27: 28: 29: 30: 31: 32: 33: 34: 35:
36: class XmlNode extends Object {
37: 38: 39: 40: 41: 42:
43: var $name = null;
44: 45: 46: 47: 48: 49:
50: var $namespace = null;
51: 52: 53: 54: 55: 56:
57: var $namespaces = array();
58: 59: 60: 61: 62: 63:
64: var $value;
65: 66: 67: 68: 69: 70:
71: var $attributes = array();
72: 73: 74: 75: 76: 77:
78: var $children = array();
79: 80: 81: 82: 83: 84:
85: var $__parent = null;
86: 87: 88: 89: 90: 91: 92: 93:
94: function __construct($name = null, $value = null, $namespace = null) {
95: if (strpos($name, ':') !== false) {
96: list($prefix, $name) = explode(':', $name);
97: if (!$namespace) {
98: $namespace = $prefix;
99: }
100: }
101: $this->name = $name;
102: if ($namespace) {
103: $this->namespace = $namespace;
104: }
105:
106: if (is_array($value) || is_object($value)) {
107: $this->normalize($value);
108: } elseif (!empty($value) || $value === 0 || $value === '0') {
109: $this->createTextNode($value);
110: }
111: }
112: 113: 114: 115: 116: 117: 118:
119: function addNamespace($prefix, $url) {
120: if ($ns = Xml::addGlobalNs($prefix, $url)) {
121: $this->namespaces = array_merge($this->namespaces, $ns);
122: return true;
123: }
124: return false;
125: }
126: 127: 128: 129: 130: 131: 132:
133: function removeNamespace($prefix) {
134: if (Xml::removeGlobalNs($prefix)) {
135: return true;
136: }
137: return false;
138: }
139: 140: 141: 142: 143: 144: 145: 146:
147: function &createNode($name = null, $value = null, $namespace = false) {
148: $node =& new XmlNode($name, $value, $namespace);
149: $node->setParent($this);
150: return $node;
151: }
152: 153: 154: 155: 156: 157: 158: 159: 160:
161: function &createElement($name = null, $value = null, $attributes = array(), $namespace = false) {
162: $element =& new XmlElement($name, $value, $attributes, $namespace);
163: $element->setParent($this);
164: return $element;
165: }
166: 167: 168: 169: 170: 171:
172: function &createTextNode($value = null) {
173: $node = new XmlTextNode($value);
174: $node->setParent($this);
175: return $node;
176: }
177: 178: 179: 180: 181: 182: 183:
184: function normalize($object, $keyName = null, $options = array()) {
185: if (is_a($object, 'XmlNode')) {
186: return $object;
187: }
188: $name = null;
189: $options += array('format' => 'attributes');
190:
191: if ($keyName !== null && !is_numeric($keyName)) {
192: $name = $keyName;
193: } elseif (!empty($object->_name_)) {
194: $name = $object->_name_;
195: } elseif (isset($object->name)) {
196: $name = $object->name;
197: } elseif ($options['format'] == 'attributes') {
198: $name = get_class($object);
199: }
200:
201: $tagOpts = $this->__tagOptions($name);
202:
203: if ($tagOpts === false) {
204: return;
205: }
206:
207: if (isset($tagOpts['name'])) {
208: $name = $tagOpts['name'];
209: } elseif ($name != strtolower($name)) {
210: $name = Inflector::slug(Inflector::underscore($name));
211: }
212:
213: if (!empty($name)) {
214: $node =& $this->createElement($name);
215: } else {
216: $node =& $this;
217: }
218:
219: $namespace = array();
220: $attributes = array();
221: $children = array();
222: $chldObjs = array();
223:
224: if (is_object($object)) {
225: $chldObjs = get_object_vars($object);
226: } elseif (is_array($object)) {
227: $chldObjs = $object;
228: } elseif (!empty($object) || $object === 0 || $object === '0') {
229: $node->createTextNode($object);
230: }
231: $attr = array();
232:
233: if (isset($tagOpts['attributes'])) {
234: $attr = $tagOpts['attributes'];
235: }
236: if (isset($tagOpts['value']) && isset($chldObjs[$tagOpts['value']])) {
237: $node->createTextNode($chldObjs[$tagOpts['value']]);
238: unset($chldObjs[$tagOpts['value']]);
239: }
240:
241: $n = $name;
242: if (isset($chldObjs['_name_'])) {
243: $n = null;
244: unset($chldObjs['_name_']);
245: }
246: $c = 0;
247:
248: foreach ($chldObjs as $key => $val) {
249: if (in_array($key, $attr) && !is_object($val) && !is_array($val)) {
250: $attributes[$key] = $val;
251: } else {
252: if (!isset($tagOpts['children']) || $tagOpts['children'] === array() || (is_array($tagOpts['children']) && in_array($key, $tagOpts['children']))) {
253: if (!is_numeric($key)) {
254: $n = $key;
255: }
256: if (is_array($val)) {
257: foreach ($val as $n2 => $obj2) {
258: if (is_numeric($n2)) {
259: $n2 = $n;
260: }
261: $node->normalize($obj2, $n2, $options);
262: }
263: } else {
264: if (is_object($val)) {
265:
266: $node->normalize($val, $n, $options);
267: } elseif ($options['format'] == 'tags' && $this->__tagOptions($key) !== false) {
268: $tmp =& $node->createElement($key);
269: if (!empty($val) || $val === 0 || $val === '0') {
270: $tmp->createTextNode($val);
271: }
272: } elseif ($options['format'] == 'attributes') {
273: $node->addAttribute($key, $val);
274: }
275: }
276: }
277: }
278: $c++;
279: }
280: if (!empty($name)) {
281: return $node;
282: }
283: return $children;
284: }
285: 286: 287: 288: 289: 290: 291: 292:
293: function __tagOptions($name, $option = null) {
294: if (isset($this->__tags[$name])) {
295: $tagOpts = $this->__tags[$name];
296: } elseif (isset($this->__tags[strtolower($name)])) {
297: $tagOpts = $this->__tags[strtolower($name)];
298: } else {
299: return null;
300: }
301: if ($tagOpts === false) {
302: return false;
303: }
304: if (empty($option)) {
305: return $tagOpts;
306: }
307: if (isset($tagOpts[$option])) {
308: return $tagOpts[$option];
309: }
310: return null;
311: }
312: 313: 314: 315: 316:
317: function name() {
318: if (!empty($this->namespace)) {
319: $_this =& XmlManager::getInstance();
320: if (!isset($_this->options['verifyNs']) || !$_this->options['verifyNs'] || in_array($this->namespace, array_keys($_this->namespaces))) {
321: return $this->namespace . ':' . $this->name;
322: }
323: }
324: return $this->name;
325: }
326: 327: 328: 329: 330:
331: function setParent(&$parent) {
332: if (strtolower(get_class($this)) == 'xml') {
333: return;
334: }
335: if (isset($this->__parent) && is_object($this->__parent)) {
336: if ($this->__parent->compare($parent)) {
337: return;
338: }
339: foreach ($this->__parent->children as $i => $child) {
340: if ($this->compare($child)) {
341: array_splice($this->__parent->children, $i, 1);
342: break;
343: }
344: }
345: }
346: if ($parent == null) {
347: unset($this->__parent);
348: } else {
349: $parent->children[] =& $this;
350: $this->__parent =& $parent;
351: }
352: }
353: 354: 355: 356: 357: 358:
359: function cloneNode() {
360: return clone($this);
361: }
362: 363: 364: 365: 366: 367: 368:
369: function compare($node) {
370: $keys = array(get_object_vars($this), get_object_vars($node));
371: return ($keys[0] === $keys[1]);
372: }
373: 374: 375: 376: 377: 378: 379: 380:
381: function &append(&$child, $options = array()) {
382: if (empty($child)) {
383: $return = false;
384: return $return;
385: }
386:
387: if (is_object($child)) {
388: if ($this->compare($child)) {
389: trigger_error('Cannot append a node to itself.');
390: $return = false;
391: return $return;
392: }
393: } else if (is_array($child)) {
394: $child = Set::map($child);
395: if (is_array($child)) {
396: if (!is_a(current($child), 'XmlNode')) {
397: foreach ($child as $i => $childNode) {
398: $child[$i] = $this->normalize($childNode, null, $options);
399: }
400: } else {
401: foreach ($child as $childNode) {
402: $this->append($childNode, $options);
403: }
404: }
405: return $child;
406: }
407: } else {
408: $attributes = array();
409: if (func_num_args() >= 2) {
410: $attributes = func_get_arg(1);
411: }
412: $child =& $this->createNode($child, null, $attributes);
413: }
414:
415: $child = $this->normalize($child, null, $options);
416:
417: if (empty($child->namespace) && !empty($this->namespace)) {
418: $child->namespace = $this->namespace;
419: }
420:
421: if (is_a($child, 'XmlNode')) {
422: $child->setParent($this);
423: }
424:
425: return $child;
426: }
427: 428: 429: 430: 431: 432:
433: function &first() {
434: if (isset($this->children[0])) {
435: return $this->children[0];
436: } else {
437: $return = null;
438: return $return;
439: }
440: }
441: 442: 443: 444: 445: 446:
447: function &last() {
448: if (count($this->children) > 0) {
449: return $this->children[count($this->children) - 1];
450: } else {
451: $return = null;
452: return $return;
453: }
454: }
455: 456: 457: 458: 459: 460: 461:
462: function &child($id) {
463: $null = null;
464:
465: if (is_int($id)) {
466: if (isset($this->children[$id])) {
467: return $this->children[$id];
468: } else {
469: return null;
470: }
471: } elseif (is_string($id)) {
472: for ($i = 0; $i < count($this->children); $i++) {
473: if ($this->children[$i]->name == $id) {
474: return $this->children[$i];
475: }
476: }
477: }
478: return $null;
479: }
480: 481: 482: 483: 484: 485: 486:
487: function children($name) {
488: $nodes = array();
489: $count = count($this->children);
490: for ($i = 0; $i < $count; $i++) {
491: if ($this->children[$i]->name == $name) {
492: $nodes[] =& $this->children[$i];
493: }
494: }
495: return $nodes;
496: }
497: 498: 499: 500: 501: 502:
503: function &nextSibling() {
504: $null = null;
505: $count = count($this->__parent->children);
506: for ($i = 0; $i < $count; $i++) {
507: if ($this->__parent->children[$i] == $this) {
508: if ($i >= $count - 1 || !isset($this->__parent->children[$i + 1])) {
509: return $null;
510: }
511: return $this->__parent->children[$i + 1];
512: }
513: }
514: return $null;
515: }
516: 517: 518: 519: 520: 521:
522: function &previousSibling() {
523: $null = null;
524: $count = count($this->__parent->children);
525: for ($i = 0; $i < $count; $i++) {
526: if ($this->__parent->children[$i] == $this) {
527: if ($i == 0 || !isset($this->__parent->children[$i - 1])) {
528: return $null;
529: }
530: return $this->__parent->children[$i - 1];
531: }
532: }
533: return $null;
534: }
535: 536: 537: 538: 539: 540:
541: function &parent() {
542: return $this->__parent;
543: }
544: 545: 546: 547: 548: 549:
550: function &document() {
551: $document =& $this;
552: while (true) {
553: if (get_class($document) == 'Xml' || $document == null) {
554: break;
555: }
556: $document =& $document->parent();
557: }
558: return $document;
559: }
560: 561: 562: 563: 564: 565:
566: function hasChildren() {
567: if (is_array($this->children) && !empty($this->children)) {
568: return true;
569: }
570: return false;
571: }
572: 573: 574: 575: 576: 577:
578: function toString($options = array(), $depth = 0) {
579: if (is_int($options)) {
580: $depth = $options;
581: $options = array();
582: }
583: $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false, 'showEmpty' => true, 'leaveOpen' => false);
584: $options = array_merge($defaults, Xml::options(), $options);
585: $tag = !(strpos($this->name, '#') === 0);
586: $d = '';
587:
588: if ($tag) {
589: if ($options['whitespace']) {
590: $d .= str_repeat("\t", $depth);
591: }
592:
593: $d .= '<' . $this->name();
594: if (!empty($this->namespaces) > 0) {
595: foreach ($this->namespaces as $key => $val) {
596: $val = str_replace('"', '\"', $val);
597: $d .= ' xmlns:' . $key . '="' . $val . '"';
598: }
599: }
600:
601: $parent =& $this->parent();
602: if ($parent->name === '#document' && !empty($parent->namespaces)) {
603: foreach ($parent->namespaces as $key => $val) {
604: $val = str_replace('"', '\"', $val);
605: $d .= ' xmlns:' . $key . '="' . $val . '"';
606: }
607: }
608:
609: if (is_array($this->attributes) && !empty($this->attributes)) {
610: foreach ($this->attributes as $key => $val) {
611: if (is_bool($val) && $val === false) {
612: $val = 0;
613: }
614: $d .= ' ' . $key . '="' . htmlspecialchars($val, ENT_QUOTES, Configure::read('App.encoding')) . '"';
615: }
616: }
617: }
618:
619: if (!$this->hasChildren() && empty($this->value) && $this->value !== 0 && $tag) {
620: if (!$options['leaveOpen']) {
621: $d .= ' />';
622: }
623: if ($options['whitespace']) {
624: $d .= "\n";
625: }
626: } elseif ($tag || $this->hasChildren()) {
627: if ($tag) {
628: $d .= '>';
629: }
630: if ($this->hasChildren()) {
631: if ($options['whitespace']) {
632: $d .= "\n";
633: }
634: $count = count($this->children);
635: $cDepth = $depth + 1;
636: for ($i = 0; $i < $count; $i++) {
637: $d .= $this->children[$i]->toString($options, $cDepth);
638: }
639: if ($tag) {
640: if ($options['whitespace'] && $tag) {
641: $d .= str_repeat("\t", $depth);
642: }
643: if (!$options['leaveOpen']) {
644: $d .= '</' . $this->name() . '>';
645: }
646: if ($options['whitespace']) {
647: $d .= "\n";
648: }
649: }
650: }
651: }
652: return $d;
653: }
654: 655: 656: 657: 658: 659: 660:
661: function toArray($camelize = true) {
662: $out = $this->attributes;
663:
664: foreach ($this->children as $child) {
665: $key = $camelize ? Inflector::camelize($child->name) : $child->name;
666:
667: $leaf = false;
668: if (is_a($child, 'XmlTextNode')) {
669: $out['value'] = $child->value;
670: continue;
671: } elseif (isset($child->children[0]) && is_a($child->children[0], 'XmlTextNode')) {
672: $value = $child->children[0]->value;
673: if ($child->attributes) {
674: $value = array_merge(array('value' => $value), $child->attributes);
675: }
676: if (count($child->children) == 1) {
677: $leaf = true;
678: }
679: } elseif (count($child->children) === 0 && $child->value == '') {
680: $value = $child->attributes;
681: if (empty($value)) {
682: $leaf = true;
683: }
684: } else {
685: $value = $child->toArray($camelize);
686: }
687:
688: if (isset($out[$key])) {
689: if(!isset($out[$key][0]) || !is_array($out[$key]) || !is_int(key($out[$key]))) {
690: $out[$key] = array($out[$key]);
691: }
692: $out[$key][] = $value;
693: } elseif (isset($out[$child->name])) {
694: $t = $out[$child->name];
695: unset($out[$child->name]);
696: $out[$key] = array($t);
697: $out[$key][] = $value;
698: } elseif ($leaf) {
699: $out[$child->name] = $value;
700: } else {
701: $out[$key] = $value;
702: }
703: }
704: return $out;
705: }
706: 707: 708: 709: 710: 711:
712: function __toString() {
713: return $this->toString();
714: }
715: 716: 717: 718: 719: 720: 721:
722: function _killParent($recursive = true) {
723: unset($this->__parent, $this->_log);
724: if ($recursive && $this->hasChildren()) {
725: for ($i = 0; $i < count($this->children); $i++) {
726: $this->children[$i]->_killParent(true);
727: }
728: }
729: }
730: }
731:
732: 733: 734: 735: 736: 737: 738: 739: 740:
741: class Xml extends XmlNode {
742:
743: 744: 745: 746: 747: 748:
749: var $__parser;
750: 751: 752: 753: 754: 755:
756: var $__file;
757: 758: 759: 760: 761: 762:
763: var $__rawData = null;
764:
765: 766: 767: 768: 769: 770:
771: var $__header = null;
772:
773: 774: 775: 776: 777: 778: 779:
780: var $__tags = array();
781:
782: 783: 784: 785: 786: 787:
788: var $version = '1.0';
789:
790: 791: 792: 793: 794: 795:
796: var $encoding = 'UTF-8';
797:
798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816:
817: function __construct($input = null, $options = array()) {
818: $defaults = array(
819: 'root' => '#document', 'tags' => array(), 'namespaces' => array(),
820: 'version' => '1.0', 'encoding' => 'UTF-8', 'format' => 'attributes'
821: );
822: $options = array_merge($defaults, Xml::options(), $options);
823:
824: foreach (array('version', 'encoding', 'namespaces') as $key) {
825: $this->{$key} = $options[$key];
826: }
827: $this->__tags = $options['tags'];
828: parent::__construct('#document');
829:
830: if ($options['root'] !== '#document') {
831: $Root =& $this->createNode($options['root']);
832: } else {
833: $Root =& $this;
834: }
835:
836: if (!empty($input)) {
837: if (is_string($input)) {
838: $Root->load($input);
839: } elseif (is_array($input) || is_object($input)) {
840: $Root->append($input, $options);
841: }
842: }
843: }
844: 845: 846: 847: 848: 849: 850:
851: function load($input) {
852: if (!is_string($input)) {
853: return false;
854: }
855: $this->__rawData = null;
856: $this->__header = null;
857:
858: if (strstr($input, "<")) {
859: $this->__rawData = $input;
860: } elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
861: App::import('Core', 'HttpSocket');
862: $socket = new HttpSocket();
863: $this->__rawData = $socket->get($input);
864: } elseif (file_exists($input)) {
865: $this->__rawData = file_get_contents($input);
866: } else {
867: trigger_error('XML cannot be read');
868: return false;
869: }
870: return $this->parse();
871: }
872: 873: 874: 875: 876: 877: 878: 879:
880: function parse() {
881: $this->__initParser();
882: $this->__rawData = trim($this->__rawData);
883: $this->__header = trim(str_replace(
884: array('<' . '?', '?' . '>'),
885: array('', ''),
886: substr($this->__rawData, 0, strpos($this->__rawData, '?' . '>'))
887: ));
888:
889: xml_parse_into_struct($this->__parser, $this->__rawData, $vals);
890: $xml =& $this;
891: $count = count($vals);
892:
893: for ($i = 0; $i < $count; $i++) {
894: $data = $vals[$i];
895: $data += array('tag' => null, 'value' => null, 'attributes' => array());
896: switch ($data['type']) {
897: case "open" :
898: $xml =& $xml->createElement($data['tag'], $data['value'], $data['attributes']);
899: break;
900: case "close" :
901: $xml =& $xml->parent();
902: break;
903: case "complete" :
904: $xml->createElement($data['tag'], $data['value'], $data['attributes']);
905: break;
906: case 'cdata':
907: $xml->createTextNode($data['value']);
908: break;
909: }
910: }
911: xml_parser_free($this->__parser);
912: $this->__parser = null;
913: return true;
914: }
915: 916: 917: 918: 919: 920:
921: function __initParser() {
922: if (empty($this->__parser)) {
923: $this->__parser = xml_parser_create();
924: xml_set_object($this->__parser, $this);
925: xml_parser_set_option($this->__parser, XML_OPTION_CASE_FOLDING, 0);
926: xml_parser_set_option($this->__parser, XML_OPTION_SKIP_WHITE, 1);
927: }
928: }
929: 930: 931: 932: 933: 934: 935: 936: 937: 938:
939: function compose($options = array()) {
940: return $this->toString($options);
941: }
942: 943: 944: 945: 946: 947: 948: 949:
950: function error($msg, $code = 0, $line = 0) {
951: if (Configure::read('debug')) {
952: echo $msg . " " . $code . " " . $line;
953: }
954: }
955: 956: 957: 958: 959: 960: 961:
962: function getError($code) {
963: $r = @xml_error_string($code);
964: return $r;
965: }
966:
967:
968:
969: 970: 971: 972: 973: 974:
975: function &next() {
976: $return = null;
977: return $return;
978: }
979: 980: 981: 982: 983: 984:
985: function &previous() {
986: $return = null;
987: return $return;
988: }
989: 990: 991: 992: 993: 994:
995: function &parent() {
996: $return = null;
997: return $return;
998: }
999: 1000: 1001: 1002: 1003: 1004: 1005:
1006: function addNamespace($prefix, $url) {
1007: if ($count = count($this->children)) {
1008: for ($i = 0; $i < $count; $i++) {
1009: $this->children[$i]->addNamespace($prefix, $url);
1010: }
1011: return true;
1012: }
1013: return parent::addNamespace($prefix, $url);
1014: }
1015: 1016: 1017: 1018: 1019: 1020:
1021: function removeNamespace($prefix) {
1022: if ($count = count($this->children)) {
1023: for ($i = 0; $i < $count; $i++) {
1024: $this->children[$i]->removeNamespace($prefix);
1025: }
1026: return true;
1027: }
1028: return parent::removeNamespace($prefix);
1029: }
1030: 1031: 1032: 1033: 1034: 1035:
1036: function toString($options = array()) {
1037: if (is_bool($options)) {
1038: $options = array('header' => $options);
1039: }
1040:
1041: $defaults = array('header' => false, 'encoding' => $this->encoding);
1042: $options = array_merge($defaults, Xml::options(), $options);
1043: $data = parent::toString($options, 0);
1044:
1045: if ($options['header']) {
1046: if (!empty($this->__header)) {
1047: return $this->header($this->__header) . "\n" . $data;
1048: }
1049: return $this->header() . "\n" . $data;
1050: }
1051:
1052: return $data;
1053: }
1054: 1055: 1056: 1057: 1058: 1059:
1060: function header($attrib = array()) {
1061: $header = 'xml';
1062: if (is_string($attrib)) {
1063: $header = $attrib;
1064: } else {
1065:
1066: $attrib = array_merge(array('version' => $this->version, 'encoding' => $this->encoding), $attrib);
1067: foreach ($attrib as $key=>$val) {
1068: $header .= ' ' . $key . '="' . $val . '"';
1069: }
1070: }
1071: return '<' . '?' . $header . ' ?' . '>';
1072: }
1073:
1074: 1075: 1076: 1077: 1078:
1079: function __destruct() {
1080: $this->_killParent(true);
1081: }
1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091:
1092: function addGlobalNs($name, $url = null) {
1093: $_this =& XmlManager::getInstance();
1094: if ($ns = Xml::resolveNamespace($name, $url)) {
1095: $_this->namespaces = array_merge($_this->namespaces, $ns);
1096: return $ns;
1097: }
1098: return false;
1099: }
1100: 1101: 1102: 1103: 1104: 1105: 1106:
1107: function resolveNamespace($name, $url) {
1108: $_this =& XmlManager::getInstance();
1109: if ($url == null && isset($_this->defaultNamespaceMap[$name])) {
1110: $url = $_this->defaultNamespaceMap[$name];
1111: } elseif ($url == null) {
1112: return false;
1113: }
1114:
1115: if (!strpos($url, '://') && isset($_this->defaultNamespaceMap[$name])) {
1116: $_url = $_this->defaultNamespaceMap[$name];
1117: $name = $url;
1118: $url = $_url;
1119: }
1120: return array($name => $url);
1121: }
1122: 1123: 1124: 1125: 1126: 1127:
1128: function addGlobalNamespace($name, $url = null) {
1129: return Xml::addGlobalNs($name, $url);
1130: }
1131: 1132: 1133: 1134: 1135: 1136: 1137:
1138: function removeGlobalNs($name) {
1139: $_this =& XmlManager::getInstance();
1140: if (isset($_this->namespaces[$name])) {
1141: unset($_this->namespaces[$name]);
1142: unset($this->namespaces[$name]);
1143: return true;
1144: } elseif (in_array($name, $_this->namespaces)) {
1145: $keys = array_keys($_this->namespaces);
1146: $count = count($keys);
1147: for ($i = 0; $i < $count; $i++) {
1148: if ($_this->namespaces[$keys[$i]] == $name) {
1149: unset($_this->namespaces[$keys[$i]]);
1150: unset($this->namespaces[$keys[$i]]);
1151: return true;
1152: }
1153: }
1154: }
1155: return false;
1156: }
1157: 1158: 1159: 1160: 1161: 1162:
1163: function removeGlobalNamespace($name) {
1164: return Xml::removeGlobalNs($name);
1165: }
1166: 1167: 1168: 1169: 1170: 1171: 1172: 1173:
1174: function options($options = array()) {
1175: $_this =& XmlManager::getInstance();
1176: $_this->options = array_merge($_this->options, $options);
1177: return $_this->options;
1178: }
1179: }
1180: 1181: 1182: 1183:
1184: class XmlElement extends XmlNode {
1185: 1186: 1187: 1188: 1189: 1190: 1191: 1192: 1193:
1194: function __construct($name = null, $value = null, $attributes = array(), $namespace = false) {
1195: parent::__construct($name, $value, $namespace);
1196: $this->addAttribute($attributes);
1197: }
1198: 1199: 1200: 1201: 1202:
1203: function attributes() {
1204: return $this->attributes;
1205: }
1206: 1207: 1208: 1209: 1210: 1211: 1212:
1213: function addAttribute($name, $val = null) {
1214: if (is_object($name)) {
1215: $name = get_object_vars($name);
1216: }
1217: if (is_array($name)) {
1218: foreach ($name as $key => $val) {
1219: $this->addAttribute($key, $val);
1220: }
1221: return true;
1222: }
1223: if (is_numeric($name)) {
1224: $name = $val;
1225: $val = null;
1226: }
1227: if (!empty($name)) {
1228: if (strpos($name, 'xmlns') === 0) {
1229: if ($name == 'xmlns') {
1230: $this->namespace = $val;
1231: } else {
1232: list($pre, $prefix) = explode(':', $name);
1233: $this->addNamespace($prefix, $val);
1234: return true;
1235: }
1236: }
1237: $this->attributes[$name] = $val;
1238: return true;
1239: }
1240: return false;
1241: }
1242: 1243: 1244: 1245: 1246: 1247:
1248: function removeAttribute($attr) {
1249: if (array_key_exists($attr, $this->attributes)) {
1250: unset($this->attributes[$attr]);
1251: return true;
1252: }
1253: return false;
1254: }
1255: }
1256:
1257: 1258: 1259: 1260: 1261: 1262: 1263: 1264: 1265:
1266: class XmlTextNode extends XmlNode {
1267: 1268: 1269: 1270: 1271:
1272: var $name = '#text';
1273: 1274: 1275: 1276: 1277:
1278: var $value = null;
1279: 1280: 1281: 1282: 1283: 1284:
1285: function __construct($value = null) {
1286: $this->value = $value;
1287: }
1288: 1289: 1290: 1291: 1292:
1293: function hasChildren() {
1294: return false;
1295: }
1296: 1297: 1298: 1299: 1300: 1301:
1302: function append() {
1303: return false;
1304: }
1305: 1306: 1307: 1308: 1309: 1310:
1311: function toString($options = array(), $depth = 0) {
1312: if (is_int($options)) {
1313: $depth = $options;
1314: $options = array();
1315: }
1316:
1317: $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false);
1318: $options = array_merge($defaults, Xml::options(), $options);
1319: $val = $this->value;
1320:
1321: if ($options['convertEntities'] && function_exists('mb_convert_encoding')) {
1322: $val = mb_convert_encoding($val,'UTF-8', 'HTML-ENTITIES');
1323: }
1324:
1325: if ($options['cdata'] === true && !is_numeric($val)) {
1326: $val = '<![CDATA[' . $val . ']]>';
1327: }
1328:
1329: if ($options['whitespace']) {
1330: return str_repeat("\t", $depth) . $val . "\n";
1331: }
1332: return $val;
1333: }
1334: }
1335: 1336: 1337: 1338: 1339: 1340:
1341: class XmlManager {
1342:
1343: 1344: 1345: 1346: 1347: 1348:
1349: var $namespaces = array();
1350: 1351: 1352: 1353: 1354: 1355:
1356: var $options = array();
1357: 1358: 1359: 1360: 1361: 1362:
1363: var $defaultNamespaceMap = array(
1364: 'dc' => 'http://purl.org/dc/elements/1.1/',
1365: 'dct' => 'http://purl.org/dc/terms/',
1366: 'g' => 'http://base.google.com/ns/1.0',
1367: 'rc' => 'http://purl.org/rss/1.0/modules/content/',
1368: 'wf' => 'http://wellformedweb.org/CommentAPI/',
1369: 'fb' => 'http://rssnamespace.org/feedburner/ext/1.0',
1370: 'lj' => 'http://www.livejournal.org/rss/lj/1.0/',
1371: 'itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd',
1372: 'xhtml' => 'http://www.w3.org/1999/xhtml',
1373: 'atom' => 'http://www.w3.org/2005/Atom'
1374: );
1375: 1376: 1377: 1378: 1379: 1380:
1381: function &getInstance() {
1382: static $instance = array();
1383:
1384: if (!$instance) {
1385: $instance[0] =& new XmlManager();
1386: }
1387: return $instance[0];
1388: }
1389: }
1390: ?>
1391: