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: