00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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 ( '&' => '&', '<' => '<', '>' => '>', "'" => ''', '"' => '"' );
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;
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
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
00090
00091 if (!empty($this->xml)) {
00092
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
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
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
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
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
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
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
00220
00221 $pos = $this->position++;
00222
00223
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
00232
00233
00234
00235 $this->message[$pos]['depth'] = $this->depth++;
00236
00237
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
00246 $this->message[$pos]['parent'] = $this->parent;
00247
00248
00249 $this->depth_array[$this->depth] = $pos;
00250
00251 $this->parent = $pos;
00252 $qname = new QName($name);
00253
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
00265 } elseif ($this->status == 'body') {
00266
00267
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
00286 $this->message[$pos]['status'] = $this->status;
00287
00288
00289 $this->message[$pos]['name'] = htmlspecialchars($qname->name);
00290
00291
00292 $this->message[$pos]['attrs'] = $attrs;
00293
00294
00295 foreach ($attrs as $key => $value) {
00296
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
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
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
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
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
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
00392 $pos = $this->depth_array[$this->depth];
00393
00394 $this->depth--;
00395 $qname = new QName($name);
00396
00397
00398
00399 if ($this->message[$pos]['type'] == '') {
00400 if (isset($this->message[$pos]['children'])) {
00401
00402
00403
00404
00405
00406
00407
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
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
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
00495
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 ?>