1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: require_once("XMLElement.php");
14:
15: 16: 17: 18: 19:
20: class XMLDocument {
21:
22: 23: 24:
25: 26: 27: 28:
29: private $namespaces;
30:
31: 32: 33: 34:
35: private $prefixes;
36:
37: 38: 39: 40:
41: private $root;
42:
43: 44: 45: 46: 47:
48: function __construct( $namespaces = null ) {
49: $this->namespaces = array();
50: $this->prefixes = array();
51: if ( $namespaces != null ) {
52: foreach( $namespaces AS $ns => $prefix ) {
53: $this->namespaces[$ns] = $prefix;
54: $this->prefixes[$prefix] = $prefix;
55: }
56: }
57: $this->next_prefix = 0;
58: }
59:
60: 61: 62: 63: 64: 65:
66: function AddNamespace( $namespace, $prefix = null ) {
67: if ( !isset($this->namespaces[$namespace]) ) {
68: if ( isset($prefix) && ($prefix == "" || isset($this->prefixes[$prefix])) ) $prefix = null;
69: if ( $prefix == null ) {
70:
71: if ( preg_match('/^(.*):([^:]+)$/', $namespace, $matches) ) {
72: $alpha = preg_replace( '/[^a-z]/i', '', $matches[2] );
73: $prefix = strtoupper(substr($alpha,0,1));
74: }
75: else {
76: $prefix = 'X';
77: }
78: $i = "";
79: if ( isset($this->prefixes[$prefix]) ) {
80: for ( $i=1; $i<10 && isset($this->prefixes["$prefix$i"]); $i++ ) {
81: }
82: }
83: if ( isset($this->prefixes["$prefix$i"]) ) {
84: dbg_error_log("ERROR", "Cannot find a free prefix for this namespace");
85: exit;
86: }
87: $prefix = "$prefix$i";
88: dbg_error_log("XMLDocument", "auto-assigning prefix of '%s' for ns of '%s'", $prefix, $namespace );
89: }
90: else if ( $prefix == "" || isset($this->prefixes[$prefix]) ) {
91: dbg_error_log("ERROR", "Cannot assign the same prefix to two different namespaces");
92: exit;
93: }
94:
95: $this->prefixes[$prefix] = $prefix;
96: $this->namespaces[$namespace] = $prefix;
97: }
98: else {
99: if ( isset($this->namespaces[$namespace]) && $this->namespaces[$namespace] != $prefix ) {
100: dbg_error_log("ERROR", "Cannot use the same namespace with two different prefixes");
101: exit;
102: }
103: $this->prefixes[$prefix] = $prefix;
104: $this->namespaces[$namespace] = $prefix;
105: }
106: }
107:
108: 109: 110:
111: function DefaultNamespace() {
112: foreach( $this->namespaces AS $k => $v ) {
113: if ( $v == '' ) {
114: return $k;
115: }
116: }
117: return '';
118: }
119:
120: 121: 122: 123:
124: function GetXmlNsArray() {
125:
126: $ns = array();
127: foreach( $this->namespaces AS $n => $p ) {
128: if ( $p == "" ) $ns["xmlns"] = $n; else $ns["xmlns:$p"] = $n;
129: }
130:
131: return $ns;
132: }
133:
134:
135: 136: 137: 138: 139: 140: 141: 142: 143:
144: function Tag( $in_tag, $namespace=null, $prefix=null ) {
145:
146: if ( $namespace == null ) {
147:
148: if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
149: $namespace = $matches[1];
150: $tag = $matches[2];
151: }
152: else {
153:
154: return $in_tag;
155: }
156: }
157: else {
158: $tag = $in_tag;
159: }
160:
161: if ( !isset($this->namespaces[$namespace]) ) {
162: $this->AddNamespace( $namespace, $prefix );
163: }
164: $prefix = $this->namespaces[$namespace];
165:
166: return $prefix . ($prefix == "" ? "" : ":") . $tag;
167: }
168:
169: static public $ns_dav = 'DAV:';
170: static public $ns_caldav = 'urn:ietf:params:xml:ns:caldav';
171: static public $ns_carddav = 'urn:ietf:params:xml:ns:carddav';
172: static public $ns_calendarserver = 'http://calendarserver.org/ns/';
173:
174: 175: 176: 177: 178: 179: 180: 181: 182: 183:
184: function NSElement( &$element, $in_tag, $content=false, $attributes=false, $namespace=null ) {
185: if ( $namespace == null && preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
186: $namespace = $matches[1];
187: if ( preg_match('{^[A-Z][A-Z0-9]*$}', $namespace ) ) {
188: throw new Exception("Dodgy looking namespace from '".$in_tag."'!");
189: }
190: $tag = $matches[2];
191: }
192: else {
193: $tag = $in_tag;
194: if ( isset($namespace) ) {
195: $tag = str_replace($namespace.':', '', $tag);
196: }
197: }
198:
199: if ( isset($namespace) && !isset($this->namespaces[$namespace]) ) $this->AddNamespace( $namespace );
200: return $element->NewElement( $tag, $content, $attributes, $namespace );
201: }
202:
203:
204: 205: 206: 207: 208: 209: 210: 211:
212: function DAVElement( &$element, $tag, $content=false, $attributes=false ) {
213: if ( !isset($this->namespaces[self::$ns_dav]) ) $this->AddNamespace( self::$ns_dav, '' );
214: return $this->NSElement( $element, $tag, $content, $attributes, self::$ns_dav );
215: }
216:
217: 218: 219: 220: 221: 222: 223: 224:
225: function CalDAVElement( &$element, $tag, $content=false, $attributes=false ) {
226: if ( !isset($this->namespaces[self::$ns_caldav]) ) $this->AddNamespace( self::$ns_caldav, 'C' );
227: return $this->NSElement( $element, $tag, $content, $attributes, self::$ns_caldav );
228: }
229:
230:
231: 232: 233: 234: 235: 236: 237: 238:
239: function CardDAVElement( &$element, $tag, $content=false, $attributes=false ) {
240: if ( !isset($this->namespaces[self::$ns_carddav]) ) $this->AddNamespace( self::$ns_carddav, 'VC' );
241: return $this->NSElement( $element, $tag, $content, $attributes, self::$ns_carddav );
242: }
243:
244:
245: 246: 247: 248: 249: 250: 251: 252:
253: function CalendarserverElement( &$element, $tag, $content=false, $attributes=false ) {
254: if ( !isset($this->namespaces[self::$ns_calendarserver]) ) $this->AddNamespace( self::$ns_calendarserver, 'A' );
255: return $this->NSElement( $element, $tag, $content, $attributes, self::$ns_calendarserver );
256: }
257:
258:
259: 260: 261: 262: 263: 264:
265: function NewXMLElement( $in_tag, $content=false, $attributes=false, $xmlns=null ) {
266: if ( $xmlns == null && preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
267: $xmlns = $matches[1];
268: $tagname = $matches[2];
269: }
270: else {
271: $tagname = $in_tag;
272: }
273:
274: if ( isset($xmlns) && !isset($this->namespaces[$xmlns]) ) $this->AddNamespace( $xmlns );
275: return new XMLElement($tagname, $content, $attributes, $xmlns );
276: }
277:
278: 279: 280: 281: 282: 283: 284: 285: 286: 287:
288: function Render( $root, $content=false, $attributes=false, $xmlns=null ) {
289: if ( is_object($root) ) {
290:
291: $this->root = $root;
292: }
293: else {
294:
295: $this->root = $this->NewXMLElement( $root, $content, $attributes, $xmlns );
296: }
297:
298: 299: 300:
301: foreach( $this->namespaces AS $n => $p ) {
302: $this->root->SetAttribute( 'xmlns'.($p == '' ? '' : ':') . $p, $n);
303: }
304:
305:
306: return $this->root->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
307: }
308:
309: 310: 311: 312: 313: 314:
315: function href($url) {
316: if ( is_array($url) ) {
317: $set = array();
318: foreach( $url AS $href ) {
319: $set[] = $this->href( $href );
320: }
321: return $set;
322: }
323: if ( preg_match('[@+ ]',$url) ) {
324: trace_bug('URL "%s" was not encoded before call to XMLDocument::href()', $url );
325: $url = str_replace( '%2F', '/', rawurlencode($url));
326: }
327: return $this->NewXMLElement('href', $url, false, 'DAV:');
328: }
329:
330: }
331:
332:
333: