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