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

payment/bmf/lib/SOAP/class.ilBMFParser.php

Go to the documentation of this file.
00001 <?php
00002 //
00003 // +----------------------------------------------------------------------+
00004 // | PHP Version 4                                                        |
00005 // +----------------------------------------------------------------------+
00006 // | Copyright (c) 1997-2002 The PHP Group                                |
00007 // +----------------------------------------------------------------------+
00008 // | This source file is subject to version 2.02 of the PHP license,      |
00009 // | that is bundled with this package in the file LICENSE, and is        |
00010 // | available at through the world-wide-web at                           |
00011 // | http://www.php.net/license/2_02.txt.                                 |
00012 // | If you did not receive a copy of the PHP license and are unable to   |
00013 // | obtain it through the world-wide-web, please send a note to          |
00014 // | license@php.net so we can mail you a copy immediately.               |
00015 // +----------------------------------------------------------------------+
00016 // | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
00017 // | Authors: Dietrich Ayala <dietrich@ganx4.com> Original Author         |
00018 // +----------------------------------------------------------------------+
00019 //
00020 // $Id: class.ilBMFParser.php 5974 2004-11-24 17:03:42Z smeyer $
00021 //
00022 
00023 require_once dirname(__FILE__).'/class.ilBMFBase.php';
00024 require_once dirname(__FILE__).'/class.ilBMFValue.php';
00025 
00038 class ilBMFParser extends ilBMFBase
00039 {
00040     var $status = '';
00041     var $position = 0;
00042     var $pos_stat = 0;
00043     var $depth = 0;
00044     var $default_namespace = '';
00045     var $message = array();
00046     var $depth_array = array();
00047     var $previous_element = '';
00048     var $soapresponse = NULL;
00049     var $soapheaders = NULL;
00050     var $parent = 0;
00051     var $root_struct_name = array();
00052     var $header_struct_name = array();
00053     var $curent_root_struct_name = '';
00054     var $entities = array ( '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', "'" => '&apos;', '"' => '&quot;' );
00055     var $xml = '';
00056     var $xml_encoding = '';
00057     var $root_struct = array();
00058     var $header_struct = array();
00059     var $curent_root_struct = 0;
00060     var $references = array();
00061     var $need_references = array();
00062     var $XMLSchemaVersion;
00063     var $bodyDepth; // used to handle non-root elements before root body element
00064     
00071     function ilBMFParser($xml, $encoding = SOAP_DEFAULT_ENCODING, $attachments=NULL)
00072     {
00073         parent::ilBMFBase('Parser');
00074         $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
00075         
00076         $this->xml = $xml;
00077         $this->xml_encoding = $encoding;
00078         $this->attachments = $attachments;
00079         
00080         // check the xml tag for encoding
00081         if (preg_match('/<\?xml[^>]+encoding\s*?=\s*?(\'([^\']*)\'|"([^"]*)")[^>]*?[\?]>/',$xml,$m)) {
00082             $this->xml_encoding = strtoupper($m[2]?$m[2]:$m[3]);
00083         }
00084         
00085         # XXX need to do this better, remove newlines after > or before <
00086         $this->xml = preg_replace("/>[\r\n]+/", '>', $this->xml);
00087         $this->xml = preg_replace("/[\r\n]+</", '<', $this->xml);
00088         
00089         // determines where in the message we are (envelope,header,body,method)
00090         // Check whether content has been read.
00091         if (!empty($this->xml)) {
00092             // prepare the xml parser
00093             $parser = xml_parser_create($this->xml_encoding);
00094             xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
00095             xml_set_object($parser, $this);
00096             xml_set_element_handler($parser, 'startElement','endElement');
00097             xml_set_character_data_handler($parser,'characterData');
00098 
00099             // Parse the XML file.
00100             if (!xml_parse($parser,$this->xml,true)) {
00101                 $err = sprintf('XML error on line %d: %s',
00102                     xml_get_current_line_number($parser),
00103                     xml_error_string(xml_get_error_code($parser)));
00104                 $this->_raiseSoapFault($err,htmlspecialchars($this->xml));
00105             } else {
00106                 // build the response
00107                 if (count($this->root_struct))
00108                     $this->soapresponse = $this->buildResponse($this->root_struct[0]);
00109                 if (count($this->header_struct))
00110                     $this->soapheaders = $this->buildResponse($this->header_struct[0]);
00111             }
00112             xml_parser_free($parser);
00113         }
00114     }
00115     
00116    
00123     function domulti($d, &$ar, &$r, &$v, $ad=0)
00124     {
00125         if ($d) {
00126             $this->domulti($d-1, $ar, $r[$ar[$ad]], $v, $ad+1);
00127         } else {
00128             $r = $v;
00129         }
00130     }
00131     
00140     function buildResponse($pos)
00141     {
00142         $response = NULL;
00143 
00144         if (isset($this->message[$pos]['children'])) {
00145             $children = explode('|',$this->message[$pos]['children']);
00146 
00147             foreach ($children as $c => $child_pos) {
00148                 if ($this->message[$child_pos]['type'] != NULL) {
00149                     $response[] = $this->buildResponse($child_pos);
00150                 }
00151             }
00152             if (array_key_exists('arraySize',$this->message[$pos])) {
00153                 $ardepth = count($this->message[$pos]['arraySize']);
00154                 if ($ardepth > 1) {
00155                     $ar = array_pad(array(), $ardepth, 0);
00156                     if (array_key_exists('arrayOffset',$this->message[$pos])) {
00157                         for ($i = 0; $i < $ardepth; $i++) {
00158                             $ar[$i] += $this->message[$pos]['arrayOffset'][$i];
00159                         }
00160                     }
00161                     $elc = count($response);
00162                     for ($i = 0; $i < $elc; $i++) {
00163                         // recurse to build a multi-dim array
00164                         $this->domulti($ardepth, $ar, $newresp, $response[$i]);
00165                         
00166                         # increment our array pointers
00167                         $ad = $ardepth - 1;
00168                         $ar[$ad]++;
00169                         while ($ad > 0 && $ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) {
00170                             $ar[$ad] = 0;
00171                             $ad--;
00172                             $ar[$ad]++;
00173                         }
00174                     }
00175                     $response = $newresp;
00176                 } else if (isset($this->message[$pos]['arrayOffset']) &&
00177                            $this->message[$pos]['arrayOffset'][0] > 0) {
00178                     # check for padding
00179                     $pad = $this->message[$pos]['arrayOffset'][0]+count($response)*-1;
00180                     $response = array_pad($response,$pad,NULL);
00181                 }
00182             }
00183         }
00184         // build attributes
00185         $attrs = array();
00186         foreach ($this->message[$pos]['attrs'] as $atn => $atv) {
00187             if (!strstr($atn, 'xmlns') &&
00188                 !strpos($atn, ':')) $attrs[$atn]=$atv;
00189         }
00190         // add current node's value
00191         if ($response) {
00192             $nqn = new Qname($this->message[$pos]['name'],$this->message[$pos]['namespace']);
00193             $tqn = new Qname($this->message[$pos]['type'],$this->message[$pos]['type_namespace']);
00194             $response = new ilBMFValue($nqn->fqn(), $tqn->fqn(), $response, $attrs);
00195             if (isset($this->message[$pos]['arrayType'])) $response->arrayType = $this->message[$pos]['arrayType'];
00196         } else {
00197             $nqn = new Qname($this->message[$pos]['name'],$this->message[$pos]['namespace']);
00198             $tqn = new Qname($this->message[$pos]['type'],$this->message[$pos]['type_namespace']);
00199             $response = new ilBMFValue($nqn->fqn(), $tqn->fqn(), $this->message[$pos]['cdata'], $attrs);
00200         }
00201         // handle header attribute that we need
00202         if (array_key_exists('actor',$this->message[$pos])) {
00203             $response->actor = $this->message[$pos]['actor'];
00204         }
00205         if (array_key_exists('mustUnderstand',$this->message[$pos])) {
00206             $response->mustunderstand = $this->message[$pos]['mustUnderstand'];
00207         }
00208         return $response;
00209     }
00210     
00217     function startElement($parser, $name, $attrs)
00218     {
00219         // position in a total number of elements, starting from 0
00220         // update class level pos
00221         $pos = $this->position++;
00222 
00223         // and set mine
00224         $this->message[$pos] = array();
00225         $this->message[$pos]['type'] = '';
00226         $this->message[$pos]['type_namespace'] = '';
00227         $this->message[$pos]['cdata'] = '';
00228         $this->message[$pos]['pos'] = $pos;
00229         $this->message[$pos]['id'] = '';
00230         
00231         // parent/child/depth determinations
00232         
00233         // depth = how many levels removed from root?
00234         // set mine as current global depth and increment global depth value
00235         $this->message[$pos]['depth'] = $this->depth++;
00236         
00237         // else add self as child to whoever the current parent is
00238         if ($pos != 0) {
00239             if (isset($this->message[$this->parent]['children']))
00240                 $this->message[$this->parent]['children'] .= "|$pos";
00241             else
00242                 $this->message[$this->parent]['children'] = $pos;
00243         }
00244 
00245         // set my parent
00246         $this->message[$pos]['parent'] = $this->parent;
00247 
00248         // set self as current value for this depth
00249         $this->depth_array[$this->depth] = $pos;
00250         // set self as current parent
00251         $this->parent = $pos;
00252         $qname = new QName($name);
00253         // set status
00254         if (strcasecmp('envelope',$qname->name)==0) {
00255             $this->status = 'envelope';
00256         } elseif (strcasecmp('header',$qname->name)==0) {
00257             $this->status = 'header';
00258             $this->header_struct_name[] = $this->curent_root_struct_name = $qname->name;
00259             $this->header_struct[] = $this->curent_root_struct = $pos;
00260             $this->message[$pos]['type'] = 'Struct';
00261         } elseif (strcasecmp('body',$qname->name)==0) {
00262             $this->status = 'body';
00263             $this->bodyDepth = $this->depth;
00264         // set method
00265         } elseif ($this->status == 'body') {
00266             // is this element allowed to be a root?
00267             // XXX this needs to be optimized, we loop through attrs twice now
00268             $can_root = $this->depth == $this->bodyDepth + 1;
00269             if ($can_root) {
00270                 foreach ($attrs as $key => $value) {
00271                     if (stristr($key, ':root') && !$value) {
00272                         $can_root = FALSE;
00273                     }
00274                 }
00275             }
00276 
00277             if ($can_root) {
00278                 $this->status = 'method';
00279                 $this->root_struct_name[] = $this->curent_root_struct_name = $qname->name;
00280                 $this->root_struct[] = $this->curent_root_struct = $pos;
00281                 $this->message[$pos]['type'] = 'Struct';
00282             }
00283         }
00284 
00285         // set my status
00286         $this->message[$pos]['status'] = $this->status;
00287         
00288         // set name
00289         $this->message[$pos]['name'] = htmlspecialchars($qname->name);
00290 
00291         // set attrs
00292         $this->message[$pos]['attrs'] = $attrs;
00293 
00294         // loop through atts, logging ns and type declarations
00295         foreach ($attrs as $key => $value) {
00296             // if ns declarations, add to class level array of valid namespaces
00297             $kqn = new QName($key);
00298             if ($kqn->ns == 'xmlns') {
00299                 $prefix = $kqn->name;
00300 
00301                 if (in_array($value, $this->_XMLSchema)) {
00302                     $this->_setSchemaVersion($value);
00303                 }
00304                 
00305                 $this->_namespaces[$value] = $prefix;
00306 
00307                 // set method namespace
00308                 # XXX unused???
00309                 #if ($name == $this->curent_root_struct_name) {
00310                 #    $this->methodNamespace = $value;
00311                 #}
00312             } elseif ($key == 'xmlns') {
00313                 $qname->ns = $this->_getNamespacePrefix($value);
00314                 $qname->namespace = $value;
00315             } elseif ($kqn->name == 'actor') {
00316                 $this->message[$pos]['actor'] = $value;
00317             } elseif ($kqn->name == 'mustUnderstand') {
00318                 $this->message[$pos]['mustUnderstand'] = $value;
00319                 
00320             // if it's a type declaration, set type
00321             } elseif ($kqn->name == 'type') {
00322                 $vqn = new QName($value);
00323                 $this->message[$pos]['type'] = $vqn->name;
00324                 $this->message[$pos]['type_namespace'] = $this->_getNamespaceForPrefix($vqn->ns);
00325                 #print "set type for {$this->message[$pos]['name']} to {$this->message[$pos]['type']}\n";
00326                 // should do something here with the namespace of specified type?
00327                 
00328             } elseif ($kqn->name == 'arrayType') {
00329                 $vqn = new QName($value);
00330                 $this->message[$pos]['type'] = 'Array';
00331                 #$type = $vqn->name;
00332                 if (isset($vqn->arraySize))
00333                     $this->message[$pos]['arraySize'] = $vqn->arraySize;
00334                 #$sa = strpos($type,'[');
00335                 #if ($sa > 0) {
00336                 #    $this->message[$pos]['arraySize'] = split(',',substr($type,$sa+1, strlen($type)-$sa-2));
00337                 #    $type = substr($type, 0, $sa);
00338                 #}
00339                 $this->message[$pos]['arrayType'] = $vqn->name;
00340                 
00341             } elseif ($kqn->name == 'offset') {
00342                 $this->message[$pos]['arrayOffset'] = split(',',substr($value, 1, strlen($value)-2));
00343                 
00344             } elseif ($kqn->name == 'id') {
00345                 # save id to reference array
00346                 $this->references[$value] = $pos;
00347                 $this->message[$pos]['id'] = $value;
00348                 
00349             } elseif ($kqn->name == 'href') {
00350                 if ($value[0] == '#') {
00351                     $ref = substr($value, 1);
00352                     if (array_key_exists($ref,$this->references)) {
00353                         # cdata, type, inval
00354                         $ref_pos = $this->references[$ref];
00355                         $this->message[$pos]['children'] = &$this->message[$ref_pos]['children'];
00356                         $this->message[$pos]['cdata'] = &$this->message[$ref_pos]['cdata'];
00357                         $this->message[$pos]['type'] = &$this->message[$ref_pos]['type'];
00358                         $this->message[$pos]['arraySize'] = &$this->message[$ref_pos]['arraySize'];
00359                         $this->message[$pos]['arrayType'] = &$this->message[$ref_pos]['arrayType'];
00360                     } else {
00361                         # reverse reference, store in 'need reference'
00362                         if (!isset($this->need_references[$ref])) $this->need_references[$ref] = array();
00363                         $this->need_references[$ref][] = $pos;
00364                     }
00365                 } else if (isset($this->attachments[$value])) {
00366                     $this->message[$pos]['cdata'] = $this->attachments[$value];
00367                 }
00368             }
00369         }
00370         // see if namespace is defined in tag
00371         if (array_key_exists('xmlns:'.$qname->ns,$attrs)) {
00372             $namespace = $attrs['xmlns:'.$qname->ns];
00373         } else if ($qname->ns && !$qname->namespace) {
00374             $namespace = $this->_getNamespaceForPrefix($qname->ns);
00375         } else {
00376         // get namespace
00377             $namespace = $qname->namespace?$qname->namespace:$this->default_namespace;
00378         }
00379         $this->message[$pos]['namespace'] = $namespace;
00380         $this->default_namespace = $namespace;
00381     }
00382     
00389     function endElement($parser, $name)
00390     {
00391         // position of current element is equal to the last value left in depth_array for my depth
00392         $pos = $this->depth_array[$this->depth];
00393         // bring depth down a notch
00394         $this->depth--;
00395         $qname = new QName($name);
00396         
00397         // get type if not explicitly declared in an xsi:type attribute
00398         // XXX check on integrating wsdl validation here
00399         if ($this->message[$pos]['type'] == '') {
00400             if (isset($this->message[$pos]['children'])) {
00401                 /* this is slow, need to look at some faster method
00402                 $children = explode('|',$this->message[$pos]['children']);
00403                 if (count($children) > 2 &&
00404                     $this->message[$children[1]]['name'] == $this->message[$children[2]]['name']) {
00405                     $this->message[$pos]['type'] = 'Array';
00406                 } else { 
00407                     $this->message[$pos]['type'] = 'Struct';
00408                 }*/
00409                 $this->message[$pos]['type'] = 'Struct';
00410             } else {
00411                 $parent = $this->message[$pos]['parent'];
00412                 if ($this->message[$parent]['type'] == 'Array' &&
00413                   array_key_exists('arrayType', $this->message[$parent])) {
00414                     $this->message[$pos]['type'] = $this->message[$parent]['arrayType'];
00415                 } else {
00416                     $this->message[$pos]['type'] = 'string';
00417                 }
00418             }
00419         }
00420         
00421         // if tag we are currently closing is the method wrapper
00422         if ($pos == $this->curent_root_struct) {
00423             $this->status = 'body';
00424         } elseif ($qname->name == 'Body' || $qname->name == 'Header') {
00425             $this->status = 'envelope';
00426         }
00427 
00428         // set parent back to my parent
00429         $this->parent = $this->message[$pos]['parent'];
00430         
00431         # handle any reverse references now
00432         $idref = $this->message[$pos]['id'];
00433 
00434         if ($idref != '' && array_key_exists($idref,$this->need_references)) {
00435             foreach ($this->need_references[$idref] as $ref_pos) {
00436                 #XXX is this stuff there already?
00437                 $this->message[$ref_pos]['children'] = &$this->message[$pos]['children'];
00438                 $this->message[$ref_pos]['cdata'] = &$this->message[$pos]['cdata'];
00439                 $this->message[$ref_pos]['type'] = &$this->message[$pos]['type'];
00440                 $this->message[$ref_pos]['arraySize'] = &$this->message[$pos]['arraySize'];
00441                 $this->message[$ref_pos]['arrayType'] = &$this->message[$pos]['arrayType'];
00442             }
00443             # wipe out our waiting list
00444             # $this->need_references[$idref] = array();
00445         }
00446     }
00447     
00454     function characterData($parser, $data)
00455     {
00456         $pos = $this->depth_array[$this->depth];
00457         if (isset($this->message[$pos]['cdata']))
00458             $this->message[$pos]['cdata'] .= $data;
00459         else
00460             $this->message[$pos]['cdata'] = $data;
00461     }
00462     
00472     function getResponse()
00473     {
00474         if ($this->soapresponse) {
00475             return $this->soapresponse;
00476         }
00477         return $this->_raiseSoapFault("couldn't build response");
00478     }
00479 
00489     function getHeaders()
00490     {
00491         if ($this->soapheaders) {
00492             return $this->soapheaders;
00493         }
00494         // we don't fault if there are no headers
00495         // that can be handled by the app if necessary
00496         return NULL;
00497     }
00498     
00508     function decodeEntities($text)
00509     {
00510         $trans_tbl = array_flip($this->entities);
00511         return strtr($text, $trans_tbl);
00512     }
00513 }
00514 
00515 ?>

Generated on Fri Dec 13 2013 09:06:37 for ILIAS Release_3_4_x_branch .rev 46804 by  doxygen 1.7.1