1<?php 2 /** 3 * SOAP parser 4 * @author ? 5 * @copyright Copyright (C) ? 6 * @copyright Portions Copyright (C) 2003,2004 Free Software Foundation, Inc. http://www.fsf.org/ 7 * @package phpgwapi 8 * @subpackage communication 9 * @version $Id: class.soap_parser.inc.php 14407 2004-02-10 13:51:20Z ceb $ 10 */ 11 12 /** 13 * SOAPx4 parser 14 * 15 * @package phpgwapi 16 * @subpackage communication 17 */ 18 class soap_parser 19 { 20 function soap_parser($xml='',$encoding='UTF-8') 21 { 22 global $soapTypes; 23 24 $this->soapTypes = $soapTypes; 25 $this->xml = $xml; 26 $this->xml_encoding = $encoding; 27 $this->root_struct = ""; 28 // options: envelope,header,body,method 29 // determines where in the message we are (envelope,header,body,method) 30 $this->status = ''; 31 $this->position = 0; 32 $this->pos_stat = 0; 33 $this->depth = 0; 34 $this->default_namespace = ''; 35 $this->namespaces = array(); 36 $this->message = array(); 37 $this->fault = false; 38 $this->fault_code = ''; 39 $this->fault_str = ''; 40 $this->fault_detail = ''; 41 $this->eval_str = ''; 42 $this->depth_array = array(); 43 $this->debug_flag = True; 44 $this->debug_str = ''; 45 $this->previous_element = ''; 46 47 $this->entities = array ( 48 '&' => '&', 49 '<' => '<', 50 '>' => '>', 51 "'" => ''', 52 '"' => '"' 53 ); 54 55 // Check whether content has been read. 56 if(!empty($xml)) 57 { 58 $this->debug('Entering soap_parser()'); 59 //$this->debug("DATA DUMP:\n\n$xml"); 60 // Create an XML parser. 61 $this->parser = xml_parser_create($this->xml_encoding); 62 // Set the options for parsing the XML data. 63 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); 64 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 65 // Set the object for the parser. 66 xml_set_object($this->parser, &$this); 67 // Set the element handlers for the parser. 68 xml_set_element_handler($this->parser, 'start_element','end_element'); 69 xml_set_character_data_handler($this->parser,'character_data'); 70 xml_set_default_handler($this->parser, 'default_handler'); 71 72 // Parse the XML file. 73 if(!xml_parse($this->parser,$xml,true)) 74 { 75 // Display an error message. 76 $this->debug(sprintf("XML error on line %d: %s", 77 xml_get_current_line_number($this->parser), 78 xml_error_string(xml_get_error_code($this->parser)))); 79 $this->fault = true; 80 } 81 else 82 { 83 // get final eval string 84 $this->eval_str = "\$response = ".trim($this->build_eval($this->root_struct)).";"; 85 } 86 xml_parser_free($this->parser); 87 } 88 else 89 { 90 $this->debug("xml was empty, didn't parse!"); 91 } 92 } 93 94 // loop through msg, building eval_str 95 function build_eval($pos) 96 { 97 $this->debug("inside build_eval() for $pos: ".$this->message[$pos]["name"]); 98 $eval_str = $this->message[$pos]['eval_str']; 99 // loop through children, building... 100 if($this->message[$pos]['children'] != '') 101 { 102 $this->debug('children string = '.$this->message[$pos]['children']); 103 $children = explode('|',$this->message[$pos]['children']); 104 $this->debug('it has '.count($children).' children'); 105 @reset($children); 106 while(list($c,$child_pos) = @each($children)) 107 /* foreach($children as $c => $child_pos) */ 108 { 109 //$this->debug("child pos $child_pos: ".$this->message[$child_pos]["name"]); 110 if($this->message[$child_pos]['eval_str'] != '') 111 { 112 $this->debug('entering build_eval() for '.$this->message[$child_pos]['name'].", array pos $c, pos: $child_pos"); 113 $eval_str .= $this->build_eval($child_pos).', '; 114 } 115 } 116 $eval_str = substr($eval_str,0,strlen($eval_str)-2); 117 } 118 // add current node's eval_str 119 $eval_str .= $this->message[$pos]['end_eval_str']; 120 return $eval_str; 121 } 122 123 // start-element handler 124 function start_element($parser, $name, $attrs) 125 { 126 // position in a total number of elements, starting from 0 127 // update class level pos 128 $pos = $this->position++; 129 // and set mine 130 $this->message[$pos]['pos'] = $pos; 131 132 // parent/child/depth determinations 133 134 // depth = how many levels removed from root? 135 // set mine as current global depth and increment global depth value 136 $this->message[$pos]['depth'] = $this->depth++; 137 138 // else add self as child to whoever the current parent is 139 if($pos != 0) 140 { 141 $this->message[$this->parent]['children'] .= "|$pos"; 142 } 143 // set my parent 144 $this->message[$pos]['parent'] = $this->parent; 145 // set self as current value for this depth 146 $this->depth_array[$this->depth] = $pos; 147 // set self as current parent 148 $this->parent = $pos; 149 150 // set status 151 if(ereg(":Envelope$",$name)) 152 { 153 $this->status = 'envelope'; 154 } 155 elseif(ereg(":Header$",$name)) 156 { 157 $this->status = 'header'; 158 } 159 elseif(ereg(":Body$",$name)) 160 { 161 $this->status = 'body'; 162 // set method 163 } 164 elseif($this->status == 'body') 165 { 166 $this->status = 'method'; 167 if(ereg(':',$name)) 168 { 169 $this->root_struct_name = substr(strrchr($name,':'),1); 170 } 171 else 172 { 173 $this->root_struct_name = $name; 174 } 175 $this->root_struct = $pos; 176 $this->message[$pos]['type'] = 'struct'; 177 } 178 // set my status 179 $this->message[$pos]['status'] = $this->status; 180 181 // set name 182 $this->message[$pos]['name'] = htmlspecialchars($name); 183 // set attrs 184 $this->message[$pos]['attrs'] = $attrs; 185 // get namespace 186 if(ereg(":",$name)) 187 { 188 $namespace = substr($name,0,strpos($name,':')); 189 $this->message[$pos]['namespace'] = $namespace; 190 $this->default_namespace = $namespace; 191 } 192 else 193 { 194 $this->message[$pos]['namespace'] = $this->default_namespace; 195 } 196 // loop through atts, logging ns and type declarations 197 @reset($attrs); 198 while (list($key,$value) = @each($attrs)) 199 /* foreach($attrs as $key => $value) */ 200 { 201 // if ns declarations, add to class level array of valid namespaces 202 if(ereg('xmlns:',$key)) 203 { 204 $namespaces[substr(strrchr($key,':'),1)] = $value; 205 if($name == $this->root_struct_name) 206 { 207 $this->methodNamespace = $value; 208 } 209 } 210 // if it's a type declaration, set type 211 elseif($key == 'xsi:type') 212 { 213 // then get attname and set $type 214 $type = substr(strrchr($value,':'),1); 215 } 216 } 217 218 // set type if available 219 if($type) 220 { 221 $this->message[$pos]['type'] = $type; 222 } 223 224 // debug 225 //$this->debug("parsed $name start, eval = '".$this->message[$pos]["eval_str"]."'"); 226 } 227 228 // end-element handler 229 function end_element($parser, $name) 230 { 231 // position of current element is equal to the last value left in depth_array for my depth 232 $pos = $this->depth_array[$this->depth]; 233 // bring depth down a notch 234 $this->depth--; 235 236 // get type if not set already 237 if($this->message[$pos]['type'] == '') 238 { 239// if($this->message[$pos]['cdata'] == '' && $this->message[$pos]['children'] != '') 240 if($this->message[$pos]['children'] != '') 241 { 242 $this->message[$pos]['type'] = 'SOAPStruct'; 243 } 244 else 245 { 246 $this->message[$pos]['type'] = 'string'; 247 } 248 } 249 250 // set eval str start if it has a valid type and is inside the method 251 if($pos >= $this->root_struct) 252 { 253 $this->message[$pos]['eval_str'] .= "\n CreateObject(\"phpgwapi.soapval\",\"".htmlspecialchars($name)."\", \"".$this->message[$pos]["type"]."\" "; 254 $this->message[$pos]['end_eval_str'] = ')'; 255 $this->message[$pos]['inval'] = 'true'; 256 /* 257 if($this->message[$pos]["name"] == $this->root_struct_name){ 258 $this->message[$pos]["end_eval_str"] .= " ,\"$this->methodNamespace\""; 259 } 260 */ 261 if($this->message[$pos]['children'] != '') 262 { 263 $this->message[$pos]['eval_str'] .= ', array( '; 264 $this->message[$pos]['end_eval_str'] .= ' )'; 265 } 266 } 267 268 // if i have no children and have cdata...then i must be a scalar value, so add my data to the eval_str 269 if($this->status == 'method' && $this->message[$pos]['children'] == '') 270 { 271 // add cdata w/ no quotes if only int/float/dbl 272 if($this->message[$pos]['type'] == 'string') 273 { 274 $this->message[$pos]['eval_str'] .= ", \"".$this->message[$pos]['cdata']."\""; 275 } 276 elseif($this->message[$pos]['type'] == 'int' || $this->message[$pos]['type'] == 'float' || $this->message[$pos]['type'] == 'double') 277 { 278 //$this->debug("adding cdata w/o quotes"); 279 $this->message[$pos]['eval_str'] .= ', '.trim($this->message[$pos]['cdata']); 280 } 281 elseif(is_string($this->message[$pos]['cdata'])) 282 { 283 //$this->debug("adding cdata w/ quotes"); 284 $this->message[$pos]['eval_str'] .= ", \"".$this->message[$pos]['cdata']."\""; 285 } 286 } 287 // if in the process of making a soap_val, close the parentheses and move on... 288 if($this->message[$pos]['inval'] == 'true') 289 { 290 $this->message[$pos]['inval'] == 'false'; 291 } 292 // if tag we are currently closing is the method wrapper 293 if($pos == $this->root_struct) 294 { 295 $this->status = 'body'; 296 } 297 elseif(ereg(':Body',$name)) 298 { 299 $this->status = 'header'; 300 } 301 elseif(ereg(':Header',$name)) 302 { 303 $this->status = 'envelope'; 304 } 305 // set parent back to my parent 306 $this->parent = $this->message[$pos]['parent']; 307 $this->debug("parsed $name end, type '".$this->message[$pos]['type']."'eval_str = '".trim($this->message[$pos]['eval_str'])."' and children = ".$this->message[$pos]['children']); 308 } 309 310 // element content handler 311 function character_data($parser, $data) 312 { 313 $pos = $this->depth_array[$this->depth]; 314 $this->message[$pos]['cdata'] .= $data; 315 //$this->debug("parsed ".$this->message[$pos]["name"]." cdata, eval = '$this->eval_str'"); 316 } 317 318 // default handler 319 function default_handler($parser, $data) 320 { 321 //$this->debug("DEFAULT HANDLER: $data"); 322 } 323 324 // function to get fault code 325 function fault() 326 { 327 if($this->fault) 328 { 329 return true; 330 } 331 else 332 { 333 return false; 334 } 335 } 336 337 // have this return a soap_val object 338 function get_response() 339 { 340 $this->debug("eval()ing eval_str: $this->eval_str"); 341 @eval("$this->eval_str"); 342 if($response) 343 { 344 $this->debug("successfully eval'd msg"); 345 return $response; 346 } 347 else 348 { 349 $this->debug('ERROR: did not successfully eval the msg'); 350 $this->fault = true; 351 return CreateObject('phpgwapi.soapval', 352 'Fault', 353 'struct', 354 array( 355 CreateObject('phpgwapi.soapval', 356 'faultcode', 357 'string', 358 'SOAP-ENV:Server' 359 ), 360 CreateObject('phpgwapi.soapval', 361 'faultstring', 362 'string', 363 "couldn't eval \"$this->eval_str\"" 364 ) 365 ) 366 ); 367 } 368 } 369 370 function debug($string) 371 { 372 if($this->debug_flag) 373 { 374 $this->debug_str .= "$string\n"; 375 } 376 } 377 378 function decode_entities($text) 379 { 380 @reset($this->entities); 381 while(list($entity,$encoded) = @each($this->entities)) 382 /* foreach($this->entities as $entity => $encoded) */ 383 { 384 $text = str_replace($encoded,$entity,$text); 385 } 386 return $text; 387 } 388 } 389?> 390