ILIAS  Release_4_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilBMFClient.php
Go to the documentation of this file.
1 <?php
25 require_once dirname(__FILE__).'/class.ilBMFValue.php';
26 require_once dirname(__FILE__).'/class.ilBMFBase.php';
27 require_once dirname(__FILE__).'/class.ilBMFTransport.php';
28 require_once dirname(__FILE__).'/class.ilBMFWSDL.php';
29 require_once dirname(__FILE__).'/class.ilBMFFault.php';
30 require_once dirname(__FILE__).'/class.ilBMFParser.php';
31 
32 // Arnaud: the following code was taken from DataObject and adapted to suit
33 
34 // this will be horrifically slow!!!!
35 // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer
36 // these two are BC/FC handlers for call in PHP4/5
37 
38 if (!class_exists('ilBMFClient_Overload')) {
39  if (substr(phpversion(), 0, 1) == 5) {
40  class ilBMFClient_Overload extends ilBMFBase {
41  function __call($method, $args)
42  {
43  $return = null;
44  $this->_call($method, $args, $return);
45  return $return;
46  }
47  }
48  } else {
49  if (!function_exists('clone')) {
50  eval('function clone($t) { return $t; }');
51  }
52  eval('
53  class ilBMFClient_Overload extends ilBMFBase {
54  function __call($method, $args, &$return)
55  {
56  return $this->_call($method, $args, $return);
57  }
58  }');
59  }
60 }
61 
82 {
98  var $_endpoint = '';
99 
105  var $_portName = '';
106 
112  var $__endpointType = '';
113 
119  var $xml;
120 
126  var $wire;
127  var $__last_request = null;
128  var $__last_response = null;
129 
135  var $__options = array('trace'=>0);
136 
143 
149  var $headersOut = null;
150 
156  var $headersIn = null;
157 
163  var $__proxy_params = array();
164 
165  var $_soap_transport = null;
166 
178  function ilBMFClient($endpoint, $wsdl = false, $portName = false,
179  $proxy_params = array())
180  {
181  parent::ilBMFBase('Client');
182 
183  $this->_endpoint = $endpoint;
184  $this->_portName = $portName;
185  $this->__proxy_params = $proxy_params;
186 
187  // This hack should perhaps be removed as it might cause unexpected
188  // behaviour.
189  $wsdl = $wsdl
190  ? $wsdl
191  : strtolower(substr($endpoint, -4)) == 'wsdl';
192 
193  // make values
194  if ($wsdl) {
195  $this->__endpointType = 'wsdl';
196  // instantiate wsdl class
197  $this->_wsdl =& new ilBMFWSDL($this->_endpoint,
198  $this->__proxy_params);
199  if ($this->_wsdl->fault) {
200  $this->_raiseSoapFault($this->_wsdl->fault);
201  }
202  }
203  }
204 
205  function _reset()
206  {
207  $this->xml = null;
208  $this->wire = null;
209  $this->__last_request = null;
210  $this->__last_response = null;
211  $this->headersIn = null;
212  $this->headersOut = null;
213  }
214 
226  function setEncoding($encoding)
227  {
228  if (in_array($encoding, $this->_encodings)) {
229  $this->_encoding = $encoding;
230  return;
231  }
232  return $this->_raiseSoapFault('Invalid Encoding');
233  }
234 
245  function addHeader(&$soap_value)
246  {
247  // Add a new header to the message.
248  if (is_a($soap_value, 'ilBMFHeader')) {
249  $this->headersOut[] =& $soap_value;
250  } elseif (is_array($soap_value)) {
251  // name, value, namespace, mustunderstand, actor
252  $this->headersOut[] =& new ilBMFHeader($soap_value[0],
253  null,
254  $soap_value[1],
255  $soap_value[2],
256  $soap_value[3]);;
257  } else {
258  $this->_raiseSoapFault('Invalid parameter provided to addHeader(). Must be an array or a SOAP_Header.');
259  }
260  }
261 
290  function &call($method, &$params, $namespace = false, $soapAction = false)
291  {
292  $this->headersIn = null;
293  $this->__last_request = null;
294  $this->__last_response = null;
295  $this->wire = null;
296  $this->xml = null;
297 
298  $soap_data =& $this->__generate($method, $params, $namespace, $soapAction);
299  if (PEAR::isError($soap_data)) {
300  $fault =& $this->_raiseSoapFault($soap_data);
301  return $fault;
302  }
303 
304  // __generate() may have changed the endpoint if the WSDL has more
305  // than one service, so we need to see if we need to generate a new
306  // transport to hook to a different URI. Since the transport protocol
307  // can also change, we need to get an entirely new object. This could
308  // probably be optimized.
309  if (!$this->_soap_transport ||
310  $this->_endpoint != $this->_soap_transport->url) {
311  $this->_soap_transport =& ilBMFTransport::getTransport($this->_endpoint);
312  if (PEAR::isError($this->_soap_transport)) {
313  $fault =& $this->_soap_transport;
314  $this->_soap_transport = null;
315  $fault =& $this->_raiseSoapFault($fault);
316  return $fault;
317  }
318  }
319  $this->_soap_transport->encoding = $this->_encoding;
320 
321  // Send the message.
322  $transport_options = array_merge_recursive($this->__proxy_params,
323  $this->__options);
324  $this->xml = $this->_soap_transport->send($soap_data, $transport_options);
325 
326  // Save the wire information for debugging.
327  if ($this->__options['trace'] > 0) {
328  $this->__last_request =& $this->_soap_transport->outgoing_payload;
329  $this->__last_response =& $this->_soap_transport->incoming_payload;
330  $this->wire = $this->__get_wire();
331  }
332  if ($this->_soap_transport->fault) {
333  $fault =& $this->_raiseSoapFault($this->xml);
334  return $fault;
335  }
336 
337  $this->__attachments =& $this->_soap_transport->attachments;
338  $this->__result_encoding = $this->_soap_transport->result_encoding;
339 
340  if (isset($this->__options['result']) &&
341  $this->__options['result'] != 'parse') {
342  return $this->xml;
343  }
344 
345  $result = &$this->__parse($this->xml, $this->__result_encoding, $this->__attachments);
346 
347  return $result;
348  }
349 
368  function setOpt($category, $option, $value = null)
369  {
370  if (!is_null($value)) {
371  if (!isset($this->__options[$category])) {
372  $this->__options[$category] = array();
373  }
374  $this->__options[$category][$option] = $value;
375  } else {
376  $this->__options[$category] = $option;
377  }
378  }
379 
399  function _call($method, $params, &$return_value)
400  {
401  // Overloading lowercases the method name, we need to look into the
402  // wsdl and try to find the correct method name to get the correct
403  // case for the call.
404  if ($this->_wsdl) {
405  $this->_wsdl->matchMethod($method);
406  }
407 
408  $return_value =& $this->call($method, $params);
409 
410  return true;
411  }
412 
413  function &__getlastrequest()
414  {
415  $request =& $this->__last_request;
416  return $request;
417  }
418 
419  function &__getlastresponse()
420  {
421  $response =& $this->__last_response;
422  return $response;
423  }
424 
425  function __use($use)
426  {
427  $this->__options['use'] = $use;
428  }
429 
430  function __style($style)
431  {
432  $this->__options['style'] = $style;
433  }
434 
435  function __trace($level)
436  {
437  $this->__options['trace'] = $level;
438  }
439 
440  function &__generate($method, &$params, $namespace = false,
441  $soapAction = false)
442  {
443  $this->fault = null;
444  $this->__options['input']='parse';
445  $this->__options['result']='parse';
446  $this->__options['parameters'] = false;
447 
448  if ($params && gettype($params) != 'array') {
449  $params = array($params);
450  }
451 
452  if (gettype($namespace) == 'array') {
453  foreach ($namespace as $optname => $opt) {
454  $this->__options[strtolower($optname)] = $opt;
455  }
456  if (isset($this->__options['namespace'])) {
457  $namespace = $this->__options['namespace'];
458  } else {
459  $namespace = false;
460  }
461  } else {
462  // We'll place $soapAction into our array for usage in the
463  // transport.
464  $this->__options['soapaction'] = $soapAction;
465  $this->__options['namespace'] = $namespace;
466  }
467 
468  if ($this->__endpointType == 'wsdl') {
469  $this->_setSchemaVersion($this->_wsdl->xsd);
470 
471  // Get port name.
472  if (!$this->_portName) {
473  $this->_portName = $this->_wsdl->getPortName($method);
474  }
475  if (PEAR::isError($this->_portName)) {
476  $fault =& $this->_raiseSoapFault($this->_portName);
477  return $fault;
478  }
479 
480  // Get endpoint.
481  $this->_endpoint = $this->_wsdl->getEndpoint($this->_portName);
482  if (PEAR::isError($this->_endpoint)) {
483  $fault =& $this->_raiseSoapFault($this->_endpoint);
484  return $fault;
485  }
486 
487  // Get operation data.
488  $opData = $this->_wsdl->getOperationData($this->_portName, $method);
489 
490  if (PEAR::isError($opData)) {
491  $fault =& $this->_raiseSoapFault($opData);
492  return $fault;
493  }
494  $namespace = $opData['namespace'];
495  $this->__options['style'] = $opData['style'];
496  $this->__options['use'] = $opData['input']['use'];
497  $this->__options['soapaction'] = $opData['soapAction'];
498 
499  // Set input parameters.
500  if ($this->__options['input'] == 'parse') {
501  $this->__options['parameters'] = $opData['parameters'];
502  $nparams = array();
503  if (isset($opData['input']['parts']) &&
504  count($opData['input']['parts'])) {
505  $i = 0;
506  foreach ($opData['input']['parts'] as $name => $part) {
507  $xmlns = '';
508  $attrs = array();
509  // Is the name a complex type?
510  if (isset($part['element'])) {
511  $xmlns = $this->_wsdl->namespaces[$part['namespace']];
512  $part = $this->_wsdl->elements[$part['namespace']][$part['type']];
513  $name = $part['name'];
514  }
515  if (isset($params[$name]) ||
516  $this->_wsdl->getDataHandler($name, $part['namespace'])) {
517  $nparams[$name] =& $params[$name];
518  } else {
519  // We now force an associative array for
520  // parameters if using WSDL.
521  $fault =& $this->_raiseSoapFault("The named parameter $name is not in the call parameters.");
522  return $fault;
523  }
524  if (gettype($nparams[$name]) != 'object' ||
525  !is_a($nparams[$name], 'ilBMFValue')) {
526  // Type is likely a qname, split it apart, and get
527  // the type namespace from WSDL.
528  $qname =& new QName($part['type']);
529  if ($qname->ns) {
530  $type_namespace = $this->_wsdl->namespaces[$qname->ns];
531  } elseif (isset($part['namespace'])) {
532  $type_namespace = $this->_wsdl->namespaces[$part['namespace']];
533  } else {
534  $type_namespace = null;
535  }
536  $qname->namespace = $type_namespace;
537  $type = $qname->name;
538  $pqname = $name;
539  if ($xmlns) {
540  $pqname = '{' . $xmlns . '}' . $name;
541  }
542  $nparams[$name] =& new ilBMFValue($pqname,
543  $qname->fqn(),
544  $nparams[$name],
545  $attrs);
546  } else {
547  // WSDL fixups to the SOAP value.
548  }
549  }
550  }
551  $params =& $nparams;
552  unset($nparams);
553  }
554  } else {
555  $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
556  }
557 
558  // Serialize the message.
559  $this->_section5 = (!isset($this->__options['use']) ||
560  $this->__options['use'] != 'literal');
561 
562  if (!isset($this->__options['style']) ||
563  $this->__options['style'] == 'rpc') {
564  $this->__options['style'] = 'rpc';
565  $this->docparams = true;
566  $mqname =& new QName($method, $namespace);
567  $methodValue =& new ilBMFValue($mqname->fqn(), 'Struct', $params);
568  $soap_msg = $this->_makeEnvelope($methodValue,
569  $this->headersOut,
570  $this->_encoding,
571  $this->__options);
572  } else {
573  if (!$params) {
574  $mqname =& new QName($method, $namespace);
575  $mynull = null;
576  $params =& new ilBMFValue($mqname->fqn(), 'Struct', $mynull);
577  } elseif ($this->__options['input'] == 'parse') {
578  if (is_array($params)) {
579  $nparams = array();
580  $keys = array_keys($params);
581  foreach ($keys as $k) {
582  if (gettype($params[$k]) != 'object') {
583  $nparams[] =& new ilBMFValue($k,
584  false,
585  $params[$k]);
586  } else {
587  $nparams[] =& $params[$k];
588  }
589  }
590  $params =& $nparams;
591  }
592  if ($this->__options['parameters']) {
593  $mqname =& new QName($method, $namespace);
594  $params =& new ilBMFValue($mqname->fqn(),
595  'Struct',
596  $params);
597  }
598  }
599  $soap_msg = $this->_makeEnvelope($params,
600  $this->headersOut,
601  $this->_encoding,
602  $this->__options);
603  }
604  unset($this->headersOut);
605 
606  if (PEAR::isError($soap_msg)) {
607  $fault =& $this->_raiseSoapFault($soap_msg);
608  return $fault;
609  }
610 
611  // Handle MIME or DIME encoding.
612  // TODO: DIME encoding should move to the transport, do it here for
613  // now and for ease of getting it done.
614  if (count($this->__attachments)) {
615  if ((isset($this->__options['attachments']) &&
616  $this->__options['attachments'] == 'Mime') ||
617  isset($this->__options['Mime'])) {
618  $soap_msg =& $this->_makeMimeMessage($soap_msg,
619  $this->_encoding);
620  } else {
621  // default is dime
622  $soap_msg =& $this->_makeDIMEMessage($soap_msg,
623  $this->_encoding);
624  $this->__options['headers']['Content-Type'] = 'application/dime';
625  }
626  if (PEAR::isError($soap_msg)) {
627  $fault =& $this->_raiseSoapFault($soap_msg);
628  return $fault;
629  }
630  }
631 
632  // Instantiate client.
633  if (is_array($soap_msg)) {
634  $soap_data =& $soap_msg['body'];
635  if (count($soap_msg['headers'])) {
636  if (isset($this->__options['headers'])) {
637  $this->__options['headers'] = array_merge($this->__options['headers'], $soap_msg['headers']);
638  } else {
639  $this->__options['headers'] = $soap_msg['headers'];
640  }
641  }
642  } else {
643  $soap_data =& $soap_msg;
644  }
645 
646  return $soap_data;
647  }
648 
649  function &__parse(&$response, $encoding, &$attachments)
650  {
651  // Parse the response.
652  $response =& new ilBMFParser($response, $encoding, $attachments);
653  if ($response->fault) {
654  $fault =& $this->_raiseSoapFault($response->fault);
655  return $fault;
656  }
657 
658  // Return array of parameters.
659  $return =& $response->getResponse();
660  $headers =& $response->getHeaders();
661  if ($headers) {
662  $this->headersIn =& $this->__decodeResponse($headers, false);
663  }
664 
665  $decoded = &$this->__decodeResponse($return);
666  return $decoded;
667  }
668 
669  function &__decodeResponse(&$response, $shift = true)
670  {
671  if (!$response) {
672  $decoded = null;
673  return $decoded;
674  }
675 
676  // Check for valid response.
677  if (PEAR::isError($response)) {
678  $fault =& $this->_raiseSoapFault($response);
679  return $fault;
680  } elseif (!is_a($response, 'ilbmfvalue')) {
681  $fault =& $this->_raiseSoapFault("Didn't get ilBMFValue object back from client");
682  return $fault;
683  }
684 
685  // Decode to native php datatype.
686  $returnArray =& $this->_decode($response);
687 
688  // Fault?
689  if (PEAR::isError($returnArray)) {
690  $fault =& $this->_raiseSoapFault($returnArray);
691  return $fault;
692  }
693 
694  if (is_object($returnArray) &&
695  strcasecmp(get_class($returnArray), 'stdClass') == 0) {
696  $returnArray = get_object_vars($returnArray);
697  }
698  if (is_array($returnArray)) {
699  if (isset($returnArray['faultcode']) ||
700  isset($returnArray['SOAP-ENV:faultcode'])) {
701  $faultcode = $faultstring = $faultdetail = $faultactor = '';
702  foreach ($returnArray as $k => $v) {
703  if (stristr($k, 'faultcode')) $faultcode = $v;
704  if (stristr($k, 'faultstring')) $faultstring = $v;
705  if (stristr($k, 'detail')) $faultdetail = $v;
706  if (stristr($k, 'faultactor')) $faultactor = $v;
707  }
708  $fault =& $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
709  return $fault;
710  }
711  // Return array of return values.
712  if ($shift && count($returnArray) == 1) {
713  $decoded = array_shift($returnArray);
714  return $decoded;
715  }
716  return $returnArray;
717  }
718  return $returnArray;
719  }
720 
721  function __get_wire()
722  {
723  if ($this->__options['trace'] > 0 &&
724  ($this->__last_request || $this->__last_response)) {
725  return "OUTGOING:\n\n" .
726  $this->__last_request .
727  "\n\nINCOMING\n\n" .
728  preg_replace("/></",">\r\n<", $this->__last_response);
729  }
730 
731  return null;
732  }
733 
734 }