source: trunk/prototype/plugins/davicalCliente/XMLElement.php @ 7655

Revision 7655, 9.8 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/**
3* A class to assist with construction of XML documents
4*
5* @package   awl
6* @subpackage   XMLElement
7* @author    Andrew McMillan <andrew@mcmillan.net.nz>
8* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
9* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
10*/
11require_once ROOTPATH.'/plugins/davicalCliente/AWLUtilities.php';
12
13/**
14* A class for XML elements which may have attributes, or contain
15* other XML sub-elements
16*
17* @package   awl
18*/
19class XMLElement {
20  protected $tagname;
21  protected $xmlns;
22  protected $attributes;
23  protected $content;
24  protected $_parent;
25
26  /**
27  * Constructor - nothing fancy as yet.
28  *
29  * @param string $tagname The tag name of the new element
30  * @param mixed $content Either a string of content, or an array of sub-elements
31  * @param array $attributes An array of attribute name/value pairs
32  * @param string $xmlns An XML namespace specifier
33  */
34  function __construct( $tagname, $content=false, $attributes=false, $xmlns=null ) {
35    $this->tagname=$tagname;
36    if ( gettype($content) == "object" ) {
37      // Subtree to be parented here
38      $this->content = array(&$content);
39    }
40    else {
41      // Array or text
42      $this->content = $content;
43    }
44    $this->attributes = $attributes;
45    if ( isset($this->attributes['xmlns']) ) {  // Oversimplification to be removed
46      $this->xmlns = $this->attributes['xmlns'];
47    }
48    if ( isset($xmlns) ) {
49      $this->xmlns = $xmlns;
50    }
51  }
52
53
54  /**
55  * Count the number of elements
56  * @return int The number of elements
57  */
58  function CountElements( ) {
59    if ( $this->content === false ) return 0;
60    if ( is_array($this->content) ) return count($this->content);
61    if ( $this->content == '' ) return 0;
62    return 1;
63  }
64
65  /**
66  * Set an element attribute to a value
67  *
68  * @param string The attribute name
69  * @param string The attribute value
70  */
71  function SetAttribute($k,$v) {
72    if ( gettype($this->attributes) != "array" ) $this->attributes = array();
73    $this->attributes[$k] = $v;
74    if ( strtolower($k) == 'xmlns' ) {
75      $this->xmlns = $v;
76    }
77  }
78
79  /**
80  * Set the whole content to a value
81  *
82  * @param mixed The element content, which may be text, or an array of sub-elements
83  */
84  function SetContent($v) {
85    $this->content = $v;
86  }
87
88  /**
89  * Accessor for the tag name
90  *
91  * @return string The tag name of the element
92  */
93  function GetTag() {
94    return $this->tagname;
95  }
96
97  /**
98  * Accessor for the full-namespaced tag name
99  *
100  * @return string The tag name of the element, prefixed by the namespace
101  */
102  function GetNSTag() {
103    return $this->xmlns . ':' . $this->tagname;
104  }
105
106  /**
107  * Accessor for a single attribute
108  * @param string $attr The name of the attribute.
109  * @return string The value of that attribute of the element
110  */
111  function GetAttribute( $attr ) {
112    if ( $attr == 'xmlns' ) return $this->xmlns;
113    if ( isset($this->attributes[$attr]) ) return $this->attributes[$attr];
114    return null;
115  }
116
117  /**
118  * Accessor for the attributes
119  *
120  * @return array The attributes of this element
121  */
122  function GetAttributes() {
123    return $this->attributes;
124  }
125
126  /**
127  * Accessor for the content
128  *
129  * @return array The content of this element
130  */
131  function GetContent() {
132    return $this->content;
133  }
134
135  /**
136  * Return an array of elements matching the specified tag, or all elements if no tag is supplied.
137  * Unlike GetContent() this will always return an array.
138  *
139  * @return array The XMLElements within the tree which match this tag
140  */
141  function GetElements( $tag=null, $recursive=false ) {
142    $elements = array();
143    if ( gettype($this->content) == "array" ) {
144      foreach( $this->content AS $k => $v ) {
145        if ( !isset($tag) || (isset($v->tagname) && $v->tagname == $tag) ) {
146          $elements[] = $v;
147        }
148        if ( $recursive ) {
149          $elements = $elements + $v->GetElements($tag,true);
150        }
151      }
152    }
153    else if ( !isset($tag) || (isset($v->content->tagname) && $this->content->tagname == $tag) ) {
154      $elements[] = $this->content;
155    }
156    return $elements;
157  }
158
159
160  /**
161  * Return an array of elements matching the specified path
162  *
163  * @return array The XMLElements within the tree which match this tag
164  */
165  function GetPath( $path ) {
166    $elements = array();
167    // printf( "Querying within '%s' for path '%s'\n", $this->tagname, $path );
168    if ( !preg_match( '#(/)?([^/]+)(/?.*)$#', $path, $matches ) ) return $elements;
169    // printf( "Matches: %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3] );
170    if ( $matches[2] == '*' || strtolower($matches[2]) == strtolower($this->tagname) ) {
171      if ( $matches[3] == '' ) {
172        /**
173        * That is the full path
174        */
175        $elements[] = $this;
176      }
177      else if ( gettype($this->content) == "array" ) {
178        /**
179        * There is more to the path, so we recurse into that sub-part
180        */
181        foreach( $this->content AS $k => $v ) {
182          $elements = array_merge( $elements, $v->GetPath($matches[3]) );
183        }
184      }
185    }
186
187    if ( $matches[1] != '/' && gettype($this->content) == "array" ) {
188      /**
189      * If our input $path was not rooted, we recurse further
190      */
191      foreach( $this->content AS $k => $v ) {
192        $elements = array_merge( $elements, $v->GetPath($path) );
193      }
194    }
195    // printf( "Found %d within '%s' for path '%s'\n", count($elements), $this->tagname, $path );
196    return $elements;
197  }
198
199
200  /**
201  * Add a sub-element
202  *
203  * @param object An XMLElement to be appended to the array of sub-elements
204  */
205  function AddSubTag(&$v) {
206    if ( gettype($this->content) != "array" ) $this->content = array();
207    $this->content[] =& $v;
208    return count($this->content);
209  }
210
211  /**
212  * Add a new sub-element
213  *
214  * @param string The tag name of the new element
215  * @param mixed Either a string of content, or an array of sub-elements
216  * @param array An array of attribute name/value pairs
217  *
218  * @return objectref A reference to the new XMLElement
219  */
220  function &NewElement( $tagname, $content=false, $attributes=false, $xmlns=null ) {
221    if ( gettype($this->content) != "array" ) $this->content = array();
222    $element = new XMLElement($tagname,$content,$attributes,$xmlns);
223    $this->content[] =& $element;
224    return $element;
225  }
226
227
228  /**
229  * Render just the internal content
230  *
231  * @return string The content of this element, as a string without this element wrapping it.
232  */
233  function RenderContent($indent=0, $nslist=null ) {
234    $r = "";
235    if ( is_array($this->content) ) {
236      /**
237      * Render the sub-elements with a deeper indent level
238      */
239      $r .= "\n";
240      foreach( $this->content AS $k => $v ) {
241        if ( is_object($v) ) {
242          $r .= $v->Render($indent+1, "", $nslist);
243        }
244      }
245      $r .= substr("                        ",0,$indent);
246    }
247    else {
248      /**
249      * Render the content, with special characters escaped
250      *
251      */
252      $r .= htmlspecialchars($this->content, ENT_NOQUOTES );
253    }
254    return $r;
255  }
256
257
258  /**
259  * Render the document tree into (nicely formatted) XML
260  *
261  * @param int The indenting level for the pretty formatting of the element
262  */
263  function Render($indent=0, $xmldef="", $nslist=null) {
264    $r = ( $xmldef == "" ? "" : $xmldef."\n");
265
266    $attr = "";
267    $tagname = $this->tagname;
268    if ( gettype($this->attributes) == "array" ) {
269      /**
270      * Render the element attribute values
271      */
272      foreach( $this->attributes AS $k => $v ) {
273        if ( preg_match('#^xmlns(:?(.+))?$#', $k, $matches ) ) {
274          if ( !isset($nslist) ) $nslist = array();
275          $prefix = (isset($matches[2]) ? $matches[2] : '');
276          if ( isset($nslist[$v]) && $nslist[$v] == $prefix ) continue; // No need to include in list as it's in a wrapping element
277          $nslist[$v] = $prefix;
278          if ( !isset($this->xmlns) ) $this->xmlns = $v;
279        }
280        $attr .= sprintf( ' %s="%s"', $k, htmlspecialchars($v) );
281      }
282    }
283    if ( isset($this->xmlns) && isset($nslist[$this->xmlns]) && $nslist[$this->xmlns] != '' ) {
284      $tagname = $nslist[$this->xmlns] . ':' . $tagname;
285    }
286
287    $r .= substr("                        ",0,$indent) . '<' . $tagname . $attr;
288
289    if ( (is_array($this->content) && count($this->content) > 0) || (!is_array($this->content) && strlen($this->content) > 0) ) {
290      $r .= ">";
291      $r .= $this->RenderContent($indent,$nslist);
292      $r .= '</' . $tagname.">\n";
293    }
294    else {
295      $r .= "/>\n";
296    }
297    return $r;
298  }
299
300
301  function __tostring() {
302    return $this->Render();
303  }
304}
305
306
307/**
308* Rebuild an XML tree in our own style from the parsed XML tags using
309* a tail-recursive approach.
310*
311* @param array $xmltags An array of XML tags we get from using the PHP XML parser
312* @param intref &$start_from A pointer to our current integer offset into $xmltags
313* @return mixed Either a single XMLElement, or an array of XMLElement objects.
314*/
315function BuildXMLTree( $xmltags, &$start_from ) {
316  $content = array();
317
318  if ( !isset($start_from) ) $start_from = 0;
319
320  for( $i=0; $i < 50000 && isset($xmltags[$start_from]); ++$i) {
321    $tagdata = $xmltags[$start_from++];
322    if ( !isset($tagdata) || !isset($tagdata['tag']) || !isset($tagdata['type']) ) break;
323    if ( $tagdata['type'] == "close" ) break;
324    $attributes = ( isset($tagdata['attributes']) ? $tagdata['attributes'] : false );
325    if ( $tagdata['type'] == "open" ) {
326      $subtree = BuildXMLTree( $xmltags, $start_from );
327      $content[] = new XMLElement($tagdata['tag'], $subtree, $attributes );
328    }
329    else if ( $tagdata['type'] == "complete" ) {
330      $value = ( isset($tagdata['value']) ? $tagdata['value'] : false );
331      $content[] = new XMLElement($tagdata['tag'], $value, $attributes );
332    }
333  }
334
335  /**
336  * If there is only one element, return it directly, otherwise return the
337  * array of them
338  */
339  if ( count($content) == 1 ) {
340    return $content[0];
341  }
342  return $content;
343}
344
Note: See TracBrowser for help on using the repository browser.