• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

webservice/soap/lib/class.soap_parser.php

Go to the documentation of this file.
00001 <?php
00002 
00003 
00004 
00005 
00014 class soap_parser extends nusoap_base {
00015 
00016         var $xml = '';
00017         var $xml_encoding = '';
00018         var $method = '';
00019         var $root_struct = '';
00020         var $root_struct_name = '';
00021         var $root_struct_namespace = '';
00022         var $root_header = '';
00023     var $document = '';                 // incoming SOAP body (text)
00024         // determines where in the message we are (envelope,header,body,method)
00025         var $status = '';
00026         var $position = 0;
00027         var $depth = 0;
00028         var $default_namespace = '';
00029         var $namespaces = array();
00030         var $message = array();
00031     var $parent = '';
00032         var $fault = false;
00033         var $fault_code = '';
00034         var $fault_str = '';
00035         var $fault_detail = '';
00036         var $depth_array = array();
00037         var $debug_flag = true;
00038         var $soapresponse = NULL;
00039         var $responseHeaders = '';      // incoming SOAP headers (text)
00040         var $body_position = 0;
00041         // for multiref parsing:
00042         // array of id => pos
00043         var $ids = array();
00044         // array of id => hrefs => pos
00045         var $multirefs = array();
00046         // toggle for auto-decoding element content
00047         var $decode_utf8 = true;
00048 
00058         function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
00059                 $this->xml = $xml;
00060                 $this->xml_encoding = $encoding;
00061                 $this->method = $method;
00062                 $this->decode_utf8 = $decode_utf8;
00063 
00064                 // Check whether content has been read.
00065                 if(!empty($xml)){
00066                         $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
00067                         // Create an XML parser - why not xml_parser_create_ns?
00068                         $this->parser = xml_parser_create($this->xml_encoding);
00069                         // Set the options for parsing the XML data.
00070                         //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
00071                         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
00072                         xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
00073                         // Set the object for the parser.
00074                         xml_set_object($this->parser, $this);
00075                         // Set the element handlers for the parser.
00076                         xml_set_element_handler($this->parser, 'start_element','end_element');
00077                         xml_set_character_data_handler($this->parser,'character_data');
00078 
00079                         // Parse the XML file.
00080                         if(!xml_parse($this->parser,$xml,true)){
00081                             // Display an error message.
00082                             $err = sprintf('XML error parsing SOAP payload on line %d: %s',
00083                             xml_get_current_line_number($this->parser),
00084                             xml_error_string(xml_get_error_code($this->parser)));
00085                                 $this->debug($err);
00086                                 $this->debug("XML payload:\n" . $xml);
00087                                 $this->setError($err);
00088                         } else {
00089                                 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
00090                                 // get final value
00091                                 $this->soapresponse = $this->message[$this->root_struct]['result'];
00092                                 // get header value: no, because this is documented as XML string
00093 //                              if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
00094 //                                      $this->responseHeaders = $this->message[$this->root_header]['result'];
00095 //                              }
00096                                 // resolve hrefs/ids
00097                                 if(sizeof($this->multirefs) > 0){
00098                                         foreach($this->multirefs as $id => $hrefs){
00099                                                 $this->debug('resolving multirefs for id: '.$id);
00100                                                 $idVal = $this->buildVal($this->ids[$id]);
00101                                                 foreach($hrefs as $refPos => $ref){
00102                                                         $this->debug('resolving href at pos '.$refPos);
00103                                                         $this->multirefs[$id][$refPos] = $idVal;
00104                                                 }
00105                                         }
00106                                 }
00107                         }
00108                         xml_parser_free($this->parser);
00109                 } else {
00110                         $this->debug('xml was empty, didn\'t parse!');
00111                         $this->setError('xml was empty, didn\'t parse!');
00112                 }
00113         }
00114 
00123         function start_element($parser, $name, $attrs) {
00124                 // position in a total number of elements, starting from 0
00125                 // update class level pos
00126                 $pos = $this->position++;
00127                 // and set mine
00128                 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
00129                 // depth = how many levels removed from root?
00130                 // set mine as current global depth and increment global depth value
00131                 $this->message[$pos]['depth'] = $this->depth++;
00132 
00133                 // else add self as child to whoever the current parent is
00134                 if($pos != 0){
00135                         $this->message[$this->parent]['children'] .= '|'.$pos;
00136                 }
00137                 // set my parent
00138                 $this->message[$pos]['parent'] = $this->parent;
00139                 // set self as current parent
00140                 $this->parent = $pos;
00141                 // set self as current value for this depth
00142                 $this->depth_array[$this->depth] = $pos;
00143                 // get element prefix
00144                 if(strpos($name,':')){
00145                         // get ns prefix
00146                         $prefix = substr($name,0,strpos($name,':'));
00147                         // get unqualified name
00148                         $name = substr(strstr($name,':'),1);
00149                 }
00150                 // set status
00151                 if($name == 'Envelope'){
00152                         $this->status = 'envelope';
00153                 } elseif($name == 'Header'){
00154                         $this->root_header = $pos;
00155                         $this->status = 'header';
00156                 } elseif($name == 'Body'){
00157                         $this->status = 'body';
00158                         $this->body_position = $pos;
00159                 // set method
00160                 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
00161                         $this->status = 'method';
00162                         $this->root_struct_name = $name;
00163                         $this->root_struct = $pos;
00164                         $this->message[$pos]['type'] = 'struct';
00165                         $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
00166                 }
00167                 // set my status
00168                 $this->message[$pos]['status'] = $this->status;
00169                 // set name
00170                 $this->message[$pos]['name'] = htmlspecialchars($name);
00171                 // set attrs
00172                 $this->message[$pos]['attrs'] = $attrs;
00173 
00174                 // loop through atts, logging ns and type declarations
00175         $attstr = '';
00176                 foreach($attrs as $key => $value){
00177                 $key_prefix = $this->getPrefix($key);
00178                         $key_localpart = $this->getLocalPart($key);
00179                         // if ns declarations, add to class level array of valid namespaces
00180             if($key_prefix == 'xmlns'){
00181                                 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
00182                                         $this->XMLSchemaVersion = $value;
00183                                         $this->namespaces['xsd'] = $this->XMLSchemaVersion;
00184                                         $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
00185                                 }
00186                 $this->namespaces[$key_localpart] = $value;
00187                                 // set method namespace
00188                                 if($name == $this->root_struct_name){
00189                                         $this->methodNamespace = $value;
00190                                 }
00191                         // if it's a type declaration, set type
00192             } elseif($key_localpart == 'type'){
00193                 $value_prefix = $this->getPrefix($value);
00194                 $value_localpart = $this->getLocalPart($value);
00195                                 $this->message[$pos]['type'] = $value_localpart;
00196                                 $this->message[$pos]['typePrefix'] = $value_prefix;
00197                 if(isset($this->namespaces[$value_prefix])){
00198                         $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
00199                 } else if(isset($attrs['xmlns:'.$value_prefix])) {
00200                                         $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
00201                 }
00202                                 // should do something here with the namespace of specified type?
00203                         } elseif($key_localpart == 'arrayType'){
00204                                 $this->message[$pos]['type'] = 'array';
00205                                 /* do arrayType ereg here
00206                                 [1]    arrayTypeValue    ::=    atype asize
00207                                 [2]    atype    ::=    QName rank*
00208                                 [3]    rank    ::=    '[' (',')* ']'
00209                                 [4]    asize    ::=    '[' length~ ']'
00210                                 [5]    length    ::=    nextDimension* Digit+
00211                                 [6]    nextDimension    ::=    Digit+ ','
00212                                 */
00213                                 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
00214                                 if(ereg($expr,$value,$regs)){
00215                                         $this->message[$pos]['typePrefix'] = $regs[1];
00216                                         $this->message[$pos]['arrayTypePrefix'] = $regs[1];
00217                         if (isset($this->namespaces[$regs[1]])) {
00218                                 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
00219                         } else if (isset($attrs['xmlns:'.$regs[1]])) {
00220                                                 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
00221                         }
00222                                         $this->message[$pos]['arrayType'] = $regs[2];
00223                                         $this->message[$pos]['arraySize'] = $regs[3];
00224                                         $this->message[$pos]['arrayCols'] = $regs[4];
00225                                 }
00226                         }
00227                         // log id
00228                         if($key == 'id'){
00229                                 $this->ids[$value] = $pos;
00230                         }
00231                         // root
00232                         if($key_localpart == 'root' && $value == 1){
00233                                 $this->status = 'method';
00234                                 $this->root_struct_name = $name;
00235                                 $this->root_struct = $pos;
00236                                 $this->debug("found root struct $this->root_struct_name, pos $pos");
00237                         }
00238             // for doclit
00239             $attstr .= " $key=\"$value\"";
00240                 }
00241         // get namespace - must be done after namespace atts are processed
00242                 if(isset($prefix)){
00243                         $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
00244                         $this->default_namespace = $this->namespaces[$prefix];
00245                 } else {
00246                         $this->message[$pos]['namespace'] = $this->default_namespace;
00247                 }
00248         if($this->status == 'header'){
00249                 if ($this->root_header != $pos) {
00250                         $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
00251                 }
00252         } elseif($this->root_struct_name != ''){
00253                 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
00254         }
00255         }
00256 
00264         function end_element($parser, $name) {
00265                 // position of current element is equal to the last value left in depth_array for my depth
00266                 $pos = $this->depth_array[$this->depth--];
00267 
00268         // get element prefix
00269                 if(strpos($name,':')){
00270                         // get ns prefix
00271                         $prefix = substr($name,0,strpos($name,':'));
00272                         // get unqualified name
00273                         $name = substr(strstr($name,':'),1);
00274                 }
00275                 
00276                 // build to native type
00277                 if(isset($this->body_position) && $pos > $this->body_position){
00278                         // deal w/ multirefs
00279                         if(isset($this->message[$pos]['attrs']['href'])){
00280                                 // get id
00281                                 $id = substr($this->message[$pos]['attrs']['href'],1);
00282                                 // add placeholder to href array
00283                                 $this->multirefs[$id][$pos] = 'placeholder';
00284                                 // add set a reference to it as the result value
00285                                 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
00286             // build complex values
00287                         } elseif($this->message[$pos]['children'] != ''){
00288                         
00289                                 // if result has already been generated (struct/array
00290                                 if(!isset($this->message[$pos]['result'])){
00291                                         $this->message[$pos]['result'] = $this->buildVal($pos);
00292                                 }
00293                                 
00294                         // set value of simple type
00295                         } else {
00296                 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
00297                 if (isset($this->message[$pos]['type'])) {
00298                                         $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
00299                                 } else {
00300                                         $parent = $this->message[$pos]['parent'];
00301                                         if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
00302                                                 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
00303                                         } else {
00304                                                 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
00305                                         }
00306                                 }
00307 
00308                                 /* add value to parent's result, if parent is struct/array
00309                                 $parent = $this->message[$pos]['parent'];
00310                                 if($this->message[$parent]['type'] != 'map'){
00311                                         if(strtolower($this->message[$parent]['type']) == 'array'){
00312                                                 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
00313                                         } else {
00314                                                 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
00315                                         }
00316                                 }
00317                                 */
00318                         }
00319                 }
00320                 
00321         // for doclit
00322         if($this->status == 'header'){
00323                 if ($this->root_header != $pos) {
00324                         $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
00325                 }
00326         } elseif($pos >= $this->root_struct){
00327                 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
00328         }
00329                 // switch status
00330                 if($pos == $this->root_struct){
00331                         $this->status = 'body';
00332                         $this->root_struct_namespace = $this->message[$pos]['namespace'];
00333                 } elseif($name == 'Body'){
00334                         $this->status = 'envelope';
00335                  } elseif($name == 'Header'){
00336                         $this->status = 'envelope';
00337                 } elseif($name == 'Envelope'){
00338                         //
00339                 }
00340                 // set parent back to my parent
00341                 $this->parent = $this->message[$pos]['parent'];
00342         }
00343 
00351         function character_data($parser, $data){
00352                 $pos = $this->depth_array[$this->depth];
00353                 if ($this->xml_encoding=='UTF-8'){
00354                         // TODO: add an option to disable this for folks who want
00355                         // raw UTF-8 that, e.g., might not map to iso-8859-1
00356                         // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
00357                         if($this->decode_utf8){
00358                                 $data = utf8_decode($data);
00359                         }
00360                 }
00361         $this->message[$pos]['cdata'] .= $data;
00362         // for doclit
00363         if($this->status == 'header'){
00364                 $this->responseHeaders .= $data;
00365         } else {
00366                 $this->document .= $data;
00367         }
00368         }
00369 
00376         function get_response(){
00377                 return $this->soapresponse;
00378         }
00379 
00386         function getHeaders(){
00387             return $this->responseHeaders;
00388         }
00389 
00396         function decode_entities($text){
00397                 foreach($this->entities as $entity => $encoded){
00398                         $text = str_replace($encoded,$entity,$text);
00399                 }
00400                 return $text;
00401         }
00402 
00411         function decodeSimple($value, $type, $typens) {
00412                 // TODO: use the namespace!
00413                 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
00414                         return (string) $value;
00415                 }
00416                 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
00417                         return (int) $value;
00418                 }
00419                 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
00420                         return (double) $value;
00421                 }
00422                 if ($type == 'boolean') {
00423                         if (strtolower($value) == 'false' || strtolower($value) == 'f') {
00424                                 return false;
00425                         }
00426                         return (boolean) $value;
00427                 }
00428                 if ($type == 'base64' || $type == 'base64Binary') {
00429                         return base64_decode($value);
00430                 }
00431                 // obscure numeric types
00432                 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
00433                         || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
00434                         || $type == 'unsignedInt'
00435                         || $type == 'unsignedShort' || $type == 'unsignedByte') {
00436                         return (int) $value;
00437                 }
00438                 // everything else
00439                 return (string) $value;
00440         }
00441 
00448         function buildVal($pos){
00449                 if(!isset($this->message[$pos]['type'])){
00450                         $this->message[$pos]['type'] = '';
00451                 }
00452                 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
00453                 // if there are children...
00454                 if($this->message[$pos]['children'] != ''){
00455                         $children = explode('|',$this->message[$pos]['children']);
00456                         array_shift($children); // knock off empty
00457                         // md array
00458                         if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
00459                 $r=0; // rowcount
00460                 $c=0; // colcount
00461                 foreach($children as $child_pos){
00462                                         $this->debug("got an MD array element: $r, $c");
00463                                         $params[$r][] = $this->message[$child_pos]['result'];
00464                                     $c++;
00465                                     if($c == $this->message[$pos]['arrayCols']){
00466                                         $c = 0;
00467                                                 $r++;
00468                                     }
00469                 }
00470             // array
00471                         } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
00472                 $this->debug('adding array '.$this->message[$pos]['name']);
00473                 foreach($children as $child_pos){
00474                         $params[] = &$this->message[$child_pos]['result'];
00475                 }
00476             // apache Map type: java hashtable
00477             } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
00478                 foreach($children as $child_pos){
00479                         $kv = explode("|",$this->message[$child_pos]['children']);
00480                         $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
00481                 }
00482             // generic compound type
00483             //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
00484                     } else {
00485                         // Apache Vector type: treat as an array
00486                                 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
00487                                         $notstruct = 1;
00488                                 } else {
00489                         // is array or struct?
00490                         foreach($children as $child_pos){
00491                                 if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
00492                                         $notstruct = 1;
00493                                         break;
00494                                 }
00495                                 $keys[$this->message[$child_pos]['name']] = 1;
00496                         }
00497                     }
00498                 //
00499                 foreach($children as $child_pos){
00500                         if(isset($notstruct)){
00501                                 $params[] = &$this->message[$child_pos]['result'];
00502                         } else {
00503                                 if (isset($params[$this->message[$child_pos]['name']])) {
00504                                         // de-serialize repeated element name into an array
00505                                         if (!is_array($params[$this->message[$child_pos]['name']])) {
00506                                                 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
00507                                         }
00508                                         $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
00509                                 } else {
00510                                                 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
00511                                             }
00512                         }
00513                 }
00514                         }
00515                         return is_array($params) ? $params : array();
00516                 } else {
00517                 $this->debug('no children');
00518             if(strpos($this->message[$pos]['cdata'],'&')){
00519                         return  strtr($this->message[$pos]['cdata'],array_flip($this->entities));
00520             } else {
00521                 return $this->message[$pos]['cdata'];
00522             }
00523                 }
00524         }
00525 }
00526 
00527 
00528 
00529 
00530 ?>

Generated on Fri Dec 13 2013 13:52:17 for ILIAS Release_3_7_x_branch .rev 46817 by  doxygen 1.7.1