Overview

Packages

  • awl
    • AuthPlugin
    • AwlDatabase
    • Browser
    • classEditor
    • DataEntry
    • DataUpdate
    • EMail
    • iCalendar
    • MenuSet
    • PgQuery
    • Session
    • Translation
    • User
    • Utilities
    • Validation
    • vCalendar
    • vComponent
    • XMLDocument
    • XMLElement
  • None
  • PHP

Classes

  • XMLDocument
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3: * Handling of namespacing for XML documents
  4: *
  5: * @package awl
  6: * @subpackage XMLDocument
  7: * @author Andrew McMillan <andrew@morphoss.com>
  8: * @copyright Morphoss Ltd - http://www.morphoss.com/
  9: * @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
 10: *
 11: */
 12: 
 13: require_once("XMLElement.php");
 14: 
 15: /**
 16: * A class for XML Documents which will contain namespaced XML elements
 17: *
 18: * @package   awl
 19: */
 20: class XMLDocument {
 21: 
 22:   /**#@+
 23:   * @access private
 24:   */
 25:   /**
 26:   * holds the namespaces which this document has been configured for.
 27:   * @var namespaces
 28:   */
 29:   private $namespaces;
 30: 
 31:   /**
 32:   * holds the prefixes which are shorthand for the namespaces.
 33:   * @var prefixes
 34:   */
 35:   private $prefixes;
 36: 
 37:   /**
 38:   * Holds the root document for the tree
 39:   * @var root
 40:   */
 41:   private $root;
 42: 
 43:   /**
 44:   * Simple XMLDocument constructor
 45:   *
 46:   * @param array $namespaces An array of 'namespace' => 'prefix' pairs, where the prefix is used as a short form for the namespace.
 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:   * Add a new namespace to the document, optionally specifying it's short prefix
 62:   *
 63:   * @param string $namespace The full namespace name to be added
 64:   * @param string $prefix An optional short form for the namespace.
 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:         //  Try and build a prefix based on the first alphabetic character of the last element of the namespace
 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:    * Return the default namespace for this document
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:   * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
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:   * Return a tag with namespace stripped and replaced with a short form, and the ns added to the document.
137:   *
138:   * @param string $in_tag The tag we want a namespace prefix on.
139:   * @param string $namespace The namespace we want it in (which will be parsed from $in_tag if not present
140:   * @param string $prefix The prefix we would like to use.  Leave it out and one will be assigned.
141:   *
142:   * @return string The tag with a namespace prefix consistent with previous tags in this namespace.
143:   */
144:   function Tag( $in_tag, $namespace=null, $prefix=null ) {
145: 
146:     if ( $namespace == null ) {
147:       // Attempt to split out from namespace:tag
148:       if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
149:         $namespace = $matches[1];
150:         $tag = $matches[2];
151:       }
152:       else {
153:         // There is nothing we can do here
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:   * Special helper for namespaced tags.
176:   *
177:   * @param object $element The tag we are adding a new namespaced element to
178:   * @param string $tag The tag name, possibly prefixed with the namespace
179:   * @param mixed  $content The content of the tag
180:   * @param array  $attributes An array of key/value pairs of attributes.
181:   * @param string $namespace The namespace for the tag
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:   * Special helper for tags in the DAV: namespace.
206:   *
207:   * @param object $element The tag we are adding a new namespaced element to
208:   * @param string $tag The tag name
209:   * @param mixed  $content The content of the tag
210:   * @param array  $attributes An array of key/value pairs of attributes.
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:   * Special helper for tags in the urn:ietf:params:xml:ns:caldav namespace.
219:   *
220:   * @param object $element The tag we are adding a new namespaced element to
221:   * @param string $tag The tag name
222:   * @param mixed  $content The content of the tag
223:   * @param array  $attributes An array of key/value pairs of attributes.
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:   * Special helper for tags in the urn:ietf:params:xml:ns:carddav namespace.
233:   *
234:   * @param object $element The tag we are adding a new namespaced element to
235:   * @param string $tag The tag name
236:   * @param mixed  $content The content of the tag
237:   * @param array  $attributes An array of key/value pairs of attributes.
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:   * Special helper for tags in the http://calendarserver.org/ns/ namespace.
247:   *
248:   * @param object $element The tag we are adding a new namespaced element to
249:   * @param string $tag The tag name
250:   * @param mixed  $content The content of the tag
251:   * @param array  $attributes An array of key/value pairs of attributes.
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:   * @param string $in_tag The tag name of the new element, possibly namespaced
261:   * @param mixed $content Either a string of content, or an array of sub-elements
262:   * @param array $attributes An array of attribute name/value pairs
263:   * @param array $xmlns An XML namespace specifier
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:   * Render the document tree into (nicely formatted) XML
280:   *
281:   * @param mixed $root A root XMLElement or a tagname to create one with the remaining parameters.
282:   * @param mixed $content Either a string of content, or an array of sub-elements
283:   * @param array $attributes An array of attribute name/value pairs
284:   * @param array $xmlns An XML namespace specifier
285:   *
286:   * @return A rendered namespaced XML document.
287:   */
288:   function Render( $root, $content=false, $attributes=false, $xmlns=null ) {
289:     if ( is_object($root) ) {
290:       /** They handed us a pre-existing object.  We'll just use it... */
291:       $this->root = $root;
292:     }
293:     else {
294:       /** We got a tag name, so we need to create the root element */
295:       $this->root = $this->NewXMLElement( $root, $content, $attributes, $xmlns );
296:     }
297: 
298:     /**
299:     * Add our namespace attributes here.
300:     */
301:     foreach( $this->namespaces AS $n => $p ) {
302:       $this->root->SetAttribute( 'xmlns'.($p == '' ? '' : ':') . $p, $n);
303:     }
304: 
305:     /** And render... */
306:     return $this->root->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
307:   }
308: 
309:   /**
310:   * Return a DAV::href XML element, or an array of them
311:   * @param mixed $url The URL (or array of URLs) to be wrapped in DAV::href tags
312:   *
313:   * @return XMLElement The newly created XMLElement object.
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: 
AWL API documentation generated by ApiGen 2.8.0