Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
soap_server.inc
1 <?php
18 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
19 
34 class Soap_Server extends Page
35 {
36 
37  // Standard Naming for MESSAGE: [function_name]SoapIn(Out)
38 
39  const SOAP_XML_SCHEMA_VERSION = 'http://www.w3.org/2001/XMLSchema';
40  const SOAP_XML_SCHEMA_INSTANCE = 'http://www.w3.org/2001/XMLSchema-instance';
41  const SOAP_SCHEMA_ENCODING = 'http://schemas.xmlsoap.org/soap/encoding/';
42  const SOAP_ENVELOP = 'http://schemas.xmlsoap.org/soap/envelope/';
43  const SCHEMA_SOAP_HTTP = 'http://schemas.xmlsoap.org/soap/http';
44  const SCHEMA_SOAP = 'http://schemas.xmlsoap.org/wsdl/soap/';
45  const SCHEMA_WSDL = 'http://schemas.xmlsoap.org/wsdl/';
46  const XML_CONTENT_TYPE = 'Content-Type: text/xml; charset=utf-8';
47 
48  private $function_list = Array();
49  private $complex_types = Array();
50  private $complex_elements = Array();
51  private $simple_types = Array ('float', 'int', 'string', 'boolean');
52  private $simple_restricted_types = Array();
53 
54  private $message_node;
55  private $portType_node;
56  private $binding_node;
57  private $schema_node;
58 
59  private $wsdl;
60  private $root;
61 
62  private $server_location;
63  private $server_url;
64 
65 
72  function __construct($assetid=0)
73  {
74  $this->_ser_attrs = TRUE;
75  parent::__construct($assetid);
76 
77  }//end constructor
78 
79 
87  public function _getAllowedLinks()
88  {
89  return Array(
90  SQ_LINK_TYPE_2 => Array('soap_api' => Array('card' => 'M', 'exclusive' => FALSE)),
91  SQ_LINK_TYPE_1 => Array('soap_api' => Array('card' => 'M', 'exclusive' => FALSE)),
92  );
93 
94  }//end _getAllowedLinks()
95 
96 
103  function printFrontend()
104  {
105  // Check the REQUEST METHOD
106  if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
107  // Set raw post data if it has not been set
108  if (!isset($HTTP_RAW_POST_DATA)) {
109  $HTTP_RAW_POST_DATA = file_get_contents("php://input");
110  }//end if
111  // If we dont have read access, try to authenticate via HTTP.
112 
113  $logged_in = $this->authenticateHttpUser();
114  if (!$this->readAccess()) {
115  header('WWW-Authenticate: Basic realm="Squiz Matrix"');
116  header('HTTP/1.0 401 Unauthorized');
117  return;
118 
119  }//end if
120 
121  // Lets service our gods.
122  $this->service($HTTP_RAW_POST_DATA);
123  } else {
124  $logged_in = $this->authenticateHttpUser();
125  if (!$this->readAccess()) {
126  // Just return nothing so the SoapClient will be like, wth?
127  header('WWW-Authenticate: Basic realm="Squiz Matrix"');
128  header('HTTP/1.0 401 Unauthorized');
129  return;
130  }//end if
131 
132  // If the query string has WSDL on it, somebody want the WSDL
133  if (isset($_SERVER['QUERY_STRING']) && strcasecmp($_SERVER['QUERY_STRING'], 'wsdl') == 0) {
134  $this->getWSDL();
135  } else {
136  // Or we just print the frontend interface to get functions/services details.
137  parent::printFrontend();
138  }//end else
139  }//end else
140 
141 
142  }//end if
143 
144 
151  private function authenticateHttpUser()
152  {
153  $logged_in = FALSE;
154  if (!(array_get_index($_SERVER, 'PHP_AUTH_USER') && array_get_index($_SERVER, 'PHP_AUTH_PW'))) {
155  return $logged_in;
156  }//end if
157 
158  if (!is_null($_SERVER['PHP_AUTH_USER'])) {
159  $auth_folder = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('authentication_folder');
160  $auth_systems = $auth_folder->getAuthSystems();
161 
162  $user = NULL;
163  foreach ($auth_systems as $systemid) {
164  $system = $GLOBALS['SQ_SYSTEM']->am->getAsset($systemid);
165  if (is_null($system)) continue;
166  $user = $system->authenticateUser($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
167  if (!is_null($user)) {
168  $GLOBALS['SQ_SYSTEM']->loginUser($user);
169  $logged_in = TRUE;
170  break;
171  }//end if
172  }//end foreach
173  }//end if
174 
175  unset($_SERVER['PHP_AUTH_USER']);
176  unset($_SERVER['PHP_AUTH_PW']);
177 
178  return $logged_in;
179 
180  }//end authenticateHttpUser()
181 
182 
189  function service($HTTP_RAW_POST_DATA)
190  {
191  ini_set("soap.wsdl_cache_enabled", "0");
192  require_once dirname(__FILE__).'/soap_server_error_handler.inc';
193 
194  // Set error handler
195  $old_error_handler = set_error_handler('soap_server_error_handler');
196 
197  $server = new SoapServer($this->data_path.'/content_file.wsdl');
198 
199  // traverse SOAP request
200  try {
201  $dom = new DOMDocument();
202  $dom->loadXML($HTTP_RAW_POST_DATA);
203  } catch (Exception $e) {
204  throw new Exception('Unable to parse SOAP request "'.$HTTP_RAW_POST_DATA.'": '.$e->getMessage());
205  }//end try catch
206 
207  // pointer to the children of root
208  $children = $dom->documentElement->childNodes;
209  $i = 0;
210  $function_names = Array();
211  while ($children->item($i)) {
212  // libxml element numeric node type
213  if (($children->item($i)->nodeType === 1) && (strpos($children->item($i)->tagName, 'Body'))) {
214  foreach ($children->item($i)->childNodes as $child) {
215  if ($child->nodeType === 1) {
216  $function_names[] = preg_replace('/(.*:)/', '', $child->tagName);
217  }//end if
218  }//end foreach
219  break;
220  }//end if
221  $i++;
222  }//end while
223 
224  $operations = $this->getAvailableOperations();
225  for ($i=0; $i < count($function_names); $i++) {
226  if (isset($operations[$function_names[$i]])) {
227  $class_name = $operations[$function_names[$i]]['class_name'];
228  $GLOBALS['SQ_SYSTEM']->am->includeAsset($class_name);
229  $server->setClass($class_name);
230  $server->handle();
231  }//end if
232  }//end foreach
233 
234  // Restore error handler
235  restore_error_handler();
236  exit(0);
237 
238  }//end service()
239 
240 
247  function printBody()
248  {
249  $available_operations = $this->getAvailableOperations(TRUE);
250  $list_api = $this->attr('list_api');
251  if (!isset($_GET['desc'])) {
252  echo '<ul style="list-style: circle;">';
253  foreach ($available_operations as $func_name => $class_info) {
254  ?>
255  <li style="padding-bottom:10px;">
256  <a href="<?php echo $_SERVER['PHP_SELF'].'?desc='.$func_name.'&group='.str_replace('soap_api_', '', $class_info['class_name']) ?>" style="color:#336699;font-weight:bold;font-size:13px;font-family:Tahoma;">
257  <?php echo ucwords($func_name) ?>
258  </a>
259  </li>
260  <br />
261  <?php
262  }//end if
263  echo '</ul>';
264  } else {
265  $func_name = $_GET['desc'];
266  if (isset($_GET['group'])) {
267  $group = $_GET['group'];
268  }//end if
269 
270  if (!empty($func_name) && !empty($group)) {
271 
272  // PAINT DETAILS IF We only print this if the method exists/available.
273  if (isset($available_operations[$func_name]) && $available_operations[$func_name]['class_name'] == 'soap_api_'.$group) {
274  $GLOBALS['SQ_SYSTEM']->am->includeAsset('soap_api_'.$group);
275  $class = new ReflectionClass(str_replace(' ', '_', ucwords(str_replace('_', ' ', 'soap_api_'.$group))));
276  $method = $class->getMethod($func_name);
277  ?>
278  <script language="Javascript" type="text/javascript">
279  function revealEntry(baseloc,entryname) {
280  imagename = "expand"+entryname;
281  if (document.getElementById(entryname).style.display == "none") {
282  document.getElementById(entryname).style.display = "";
283  document[imagename].src = baseloc+"/web/images/icons/internal_message/del.png";
284  }
285  else {
286  document.getElementById(entryname).style.display = "none";
287  document[imagename].src = baseloc+"/web/images/icons/internal_message/add.png";
288  }
289 
290  }
291  </script>
292 
293  <span style="font-size:16px;font-family:tahoma;font-weight:bold;">
294  <?php echo (($method->isPublic()) ? 'Public ' : '') ?>
295  </span>
296  <span style="color:#006699;font-size:16px;font-family:tahoma;font-weight:bold;">
297  <?php echo ucwords($func_name) ?>
298  </span>
299  <br />
300  <?php
301  $comment = $this->cleanComment($method->getDocComment());
302  $func_info = $this->getFuncDocInfo($comment);
303 
304  // EVAL FUNCTION HERE, we'd better make sure that we dont get PHP code injection.
305  // There is already a check on the PAINT DETAILS IF. But just in case.
306  $matches = Array();
307  preg_match('|(\w*)|', $group, $matches);
308  eval('$complexType = Soap_Api_'.ucwords($matches[1]).'::getComplexElements();');
309 
310  ?>
311  <span style="color:#000000;font-size:12px;font-family:tahoma;font-weight:normal;">
312  Description: <?php echo $func_info['func_desc'] ?>
313  </span>
314  <br /><br />
315 
316  <?php
317  // Only display this if the function has a REQUEST format declared
318  if (isset($complexType[$func_name])) {
319  foreach ($complexType[$func_name] as $key => $args_info) {
320  if ($key == 'arg_name') {
321  echo $this->_printFuncInfo('Argument Name: ', NULL, 'margin-left:15px;').$this->_printFuncInfo(NULL, $args_info).'<br />';
322  } else {
323  echo $this->_printFuncInfo(NULL, $args_info['type'], 'margin-left:15px;').' '.$this->_printFuncInfo($key.': ').'<br />';
324  }//end else
325  }//end foreach
326  }//end if
327  echo $this->_printFuncInfo('Return type: ', NULL, 'margin-left:15px;').$this->_printFuncInfo(NULL, $func_info['return_type']).'<br /><br />';
328  ?>
329  <a href="javascript:revealEntry('<?php echo sq_web_path('lib'); ?>','requestenvelope')" style="text-decoration:none;"><img name="expandrequestenvelope" src="<?php echo sq_web_path('lib'); ?>/web/images/icons/internal_message/del.png" border="0" /><span style="border:0;font-size:13px;font-family:tahoma;color:#0099FF;font-weight:bold;"> View Request Envelope</span></a><br /><br />
330 
331  <div id="requestenvelope" style="display:;margin-top:10px;margin-left:12px;margin-bottom:10px;">
332  <?php
333  if (isset($complexType[$func_name])) {
334  ?>
335  <span style="font-size:12px;font-family:tahoma;">
336  The <?php echo $this->_placeholder('Placeholder') ?> text should be replaced with the correct data
337  </span>
338  <div style="width:90%;margin-top:10px;padding:20px;padding-top:10px;padding-bottom:10px;background:#EEEEEE;color:#000000;font-size:12px;font-family:tahoma;font-weight:normal;">
339  <?php echo $this->_printRequestEnvelope($complexType[$func_name], $func_name); ?>
340  </div>
341  <?php
342  }//end if
343  ?></div>
344 
345  <a href="javascript:revealEntry('<?php echo sq_web_path('lib'); ?>','returnenvelope')" style="text-decoration:none;"><img name="expandreturnenvelope" src="<?php echo sq_web_path('lib'); ?>/web/images/icons/internal_message/del.png" border="0" /><span style="border:0;font-size:13px;font-family:tahoma;color:#0099FF;font-weight:bold"> View Response Envelope</span></a><br />
346 
347  <div id="returnenvelope" style="display:;margin-top:10px;margin-left:12px;margin-bottom:10px;">
348  <?php
349  if (isset($complexType[$func_name.'Response'])) {
350  ?>
351  <span style="font-size:12px;font-family:tahoma;">
352  The <?php echo $this->_placeholder('Placeholder') ?> text should be replaced with the correct data
353  </span>
354  <div style="width:90%;margin-top:10px;padding:20px;padding-top:10px;padding-bottom:10px;background:#EEEEEE;color:#000000;font-size:12px;font-family:tahoma;font-weight:normal;">
355  <?php echo $this->_printResponseEnvelope($complexType[$func_name.'Response'], $func_name); ?>
356  </div>
357  <?php
358  }//end if
359 
360  ?>
361  </div>
362  <?php
363 
364  } else {
365  echo $this->_printFuncInfo('Service does not exists');
366  }//end else
367 
368  }//end if
369 
370  }//end else
371 
372  }//end printBody()
373 
374 
381  private function getAvailableOperations($saveToObject=FALSE)
382  {
383  // Get all the APIs linked underneath
384  $available_operations = Array();
385 
386  $children = $GLOBALS['SQ_SYSTEM']->am->getChildren($this->id, 'soap_api', FALSE);
387  $list_api = $this->attr('list_api');
388  foreach ($children as $api_id => $type_info) {
389  if (!isset($list_api[$api_id])) continue;
390  $child = $GLOBALS['SQ_SYSTEM']->am->getAsset($api_id);
391  $type_code = $type_info[0]['type_code'];
392  $functions = $child->attr('function_list');
393 
394  foreach ($functions as $key => $value) {
395  $functions[$key] = Array (
396  'class_name' => $type_code,
397  );
398  }//end foreach
399 
400  $available_operations = array_merge($available_operations, $functions);
401  if ($saveToObject) {
402  $this->complex_elements = array_merge($this->complex_elements, $child->getComplexElements($functions));
403  $this->complex_types = array_merge($this->complex_types, $child->getComplexTypes($functions));
404  $this->simple_restricted_types = array_merge($this->simple_restricted_types, $child->getSimpleRestrictedTypes($functions));
405  $this->function_list = $available_operations;
406  }//end if
407 
408  }//end foreach
409  return $available_operations;
410 
411  }//end isFunctionEnable()
412 
413 
424  private function _printFuncInfo($title=NULL, $content=NULL, $extra=NULL)
425  {
426  $str = NULL;
427  if (!is_null($title)) {
428  $str = '<span style="font-size:12px;font-family:tahoma;font-weight:bold;'.$extra.'">'.$title.'</span>';
429  } else if (!is_null($content)) {
430  $ele_notes = '';
431  if (isset($this->simple_restricted_types[$content])) {
432  $restricted_values = $this->simple_restricted_types[$content]['enumeration'];
433  sort($restricted_values);
434  $ele_notes = implode(', ', $restricted_values);
435  }//end if
436  $str = '<span style="color:#006699;font-size:12px;font-family:tahoma;font-weight:bold;'.$extra.'" title="'.$ele_notes.'">'.$content.'</span>';
437  }//end if
438 
439  return $str;
440  }//end _printFuncInfoTitle()
441 
442 
452  private function _printRequestEnvelope($args_info, $func_name)
453  {
454  ob_start();
455  ?>
456  <span>
457  POST <?php echo $this->_placeholder($this->getUrl()) ?> HTTP/1.1<br />
458  Host: <?php echo $this->_placeholder(sq_root_url($this->getUrl(),FALSE)) ?><br />
459  Content-Type: <?php echo self::XML_CONTENT_TYPE ?><br />
460  Content-Length: <?php echo $this->_placeholder('length') ?><br />
461  </span>
462 
463  <br />
464 
465  <span>
466  &lt;?xml version="1.0" encoding="utf-8" ?&gt;<br />
467  &lt;soap:Envelope xmlns:xsi="<?php echo self::SOAP_XML_SCHEMA_INSTANCE ?>"
468  xmlns:ns1="<?php echo $this->getUrl() ?>"
469  xmlns:xsd="<?php echo self::SOAP_XML_SCHEMA_VERSION ?>"
470  xmlns:soap="<?php echo self::SOAP_ENVELOP ?>"&gt;<br />
471  </span>
472  <div style="padding-left:10px">
473  &lt;soap:Body&gt;<br />
474  <div style="padding-left:10px">
475  &lt;ns1:<?php echo $func_name ?>&gt;<br />
476  <?php
477  $this->printRequestResponseRecursive($args_info);
478  ?>
479  &lt;/ns1:<?php echo $func_name ?>&gt;<br />
480  </div>
481  &lt;/soap:Body&gt;<br />
482  </div>
483  &lt;/soap:Envelope&gt;<br />
484  <br />
485  <?php
486  $content = ob_get_contents();
487  ob_end_clean();
488 
489  return $content;
490 
491  }//end _printRequestEnvelope()
492 
493 
500  private function printRequestResponseRecursive($args_info)
501  {
502  foreach ($args_info as $key => $arg_info) {
503  $io_type = (is_array($arg_info)) ? $arg_info['type'] : '';
504  // We have a recursive complex type here
505  if ($io_type !== '' && isset($this->complex_elements[$io_type])) $io_type = '';
506  $ele_notes = '';
507  if (isset($this->simple_restricted_types[$io_type])) {
508  $restricted_values = $this->simple_restricted_types[$io_type]['enumeration'];
509  sort($restricted_values);
510 
511  $ele_notes = implode(', ', $restricted_values);
512  }//end if
513  ?><div style="padding-left:10px">
514  &lt;<?php echo $key ?>&gt;<?php if (!empty($io_type)) echo $this->_placeholder($io_type, $ele_notes);
515 
516  // If we have a complex type inside
517  if (!is_array($arg_info)) {
518  if (isset($this->complex_elements[$arg_info])) {
519  $this->printRequestResponseRecursive($this->complex_elements[$arg_info]);
520  } else if (isset($this->simple_restricted_types[$arg_info])) {
521  echo $this->_placeholder($arg_info);
522  }//end if
523  } else if (is_array($arg_info)) {
524  // We are not going in here, because we don't want to print out any restricted type details.
525  // It should be isset($this->simple_restricted_types[$arg_info['type']]) check instead
526  //if (in_array($arg_info['type'], $this->simple_restricted_types)) {
527  // $this->printRequestResponseRecursive($this->simple_restricted_types[$arg_info['type']]);
528  //} else
529  if (isset($this->complex_elements[$arg_info['type']])) {
530  $this->printRequestResponseRecursive($this->complex_elements[$arg_info['type']]);
531  } else {
532 
533  }//end else
534 
535  }//end else
536  ?>&lt;/<?php echo $key ?>&gt;
537  </div>
538  <?php
539  }//end foreach
540 
541  }//end printRequestResponseRecursive()
542 
543 
553  private function _printResponseEnvelope($args_info, $func_name)
554  {
555  ob_start();
556  ?>
557  <span>HTTP/1.0 200 OK<br />
558  Date: <?php echo $this->_placeholder(date("D, j M Y h:i:s e")) ?><br />
559  Server: <?php echo $this->_placeholder(sq_root_url($this->getUrl(),FALSE)) ?><br />
560  Content-Type: <?php echo self::XML_CONTENT_TYPE ?><br />
561  Content-Length: <?php echo $this->_placeholder('length') ?><br />
562  </span>
563 
564  <br />
565 
566  <span>
567  &lt;?xml version="1.0" encoding="utf-8" ?&gt;<br />
568  &lt;soap:Envelope xmlns:xsi="<?php echo self::SOAP_XML_SCHEMA_INSTANCE ?>" <br />
569  xmlns:ns1="<?php echo $this->getUrl() ?>"
570  xmlns:xsd="<?php echo self::SOAP_XML_SCHEMA_VERSION ?>" xmlns:soap="<?php echo self::SOAP_ENVELOP ?>"&gt;<br />
571  </span>
572 
573  <div style="padding-left:10px">
574  &lt;soap:Body&gt;<br />
575  <div style="padding-left:10px">
576  &lt;ns1:<?php echo $func_name.'Response'; ?>&gt;<br />
577 
578  <?php $this->printRequestResponseRecursive($args_info); ?>
579 
580  &lt;/ns1:<?php echo $func_name.'Response'; ?>&gt;<br />
581  </div>
582  &lt;/soap:Body&gt;<br />
583  </div>
584  &lt;/soap:Envelope&gt;<br />
585  <br />
586  <?php
587 
588  $content = ob_get_contents();
589  ob_end_clean();
590 
591  return $content;
592 
593  }//end _printResponseEnvelope()
594 
595 
604  private function _placeholder($name='', $title='')
605  {
606  if (empty($name)) $name=' ';
607 
608  $placeholder_tag1 = '<span style="color:#003366;font-weight:bold;font-family:tahoma;font-size:12px;" title="'.$title.'">';
609  $placeholder_tag2 = '</span>';
610 
611  return $placeholder_tag1.$name.$placeholder_tag2;
612 
613  }//end _placeholder()
614 
615 
624  private function cleanComment($comment)
625  {
626  $comment = str_replace('/*', '' , $comment);
627  $comment = str_replace('*/', '' , $comment);
628  $comment = str_replace('*', '' , $comment);
629 
630  return $comment;
631 
632  }//end if
633 
634 
642  private function getWSDL()
643  {
644  header(self::XML_CONTENT_TYPE);
645  if (!file_exists($this->data_path.'/content_file.wsdl')) {
646  $this->generateWSDL();
647  }//end if
648  $wsdl_content = file_get_contents($this->data_path.'/content_file.wsdl');
649  if (!is_null($wsdl_content)) {
650  echo $wsdl_content;
651  }//end if
652 
653  }//end getWSDL()
654 
655 
700  private function getXML()
701  {
702  $wsdl = new DomDocument("1.0", 'utf-8');
703 
704  // Set all the header stuff
705  $root = $wsdl->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:definitions');
706  $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:s','http://www.w3.org/2001/XMLSchema');
707  $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:tns', $this->getUrl());
708  $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap',self::SCHEMA_SOAP);
709  $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:wsdl',self::SCHEMA_WSDL);
710  $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soapenc',self::SOAP_SCHEMA_ENCODING);
711  $root->setAttribute('targetNamespace', $this->getUrl());
712 
713  // Create the Types Node and set Name
714  $types_node = $wsdl->createElementNS(self::SCHEMA_WSDL, 'types');
715  $this->schema_node = $wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'schema');
716  $this->schema_node->setAttribute('targetNamespace', $this->getUrl());
717  // Lets set to to the instance variable and let it go.
718 
719  // Create the PortType Node and set Name
720  $this->portType_node = $wsdl->createElementNS(self::SCHEMA_WSDL, 'portType');
721  $this->portType_node->setAttribute('name', 'MatrixSOAPService');
722 
723  // Create the Binding Node and set Name
724  $binding_node = $wsdl->createElementNS(self::SCHEMA_WSDL, 'binding');
725  $binding_node->setAttribute('name', 'MatrixSOAPServiceBinding');
726  $binding_node->setAttribute('type', 'tns:MatrixSOAPService');
727 
728  // Create the Soap Binding Node under Binding Node and set attributes
729  $soap_binding_node = $wsdl->createElementNS(self::SCHEMA_SOAP, 'binding');
730  $soap_binding_node->setAttribute('style', 'document');
731  $soap_binding_node->setAttribute('transport', self::SCHEMA_SOAP_HTTP);
732 
733  // Append the Soap Binding Node to the Binding Node
734  $binding_node->appendChild($soap_binding_node);
735 
736  // Assign soap binding node to the class instance var so we can append stuff to it
737  $this->binding_node = $binding_node; // Lets set it to the class instance variable and let it go.
738 
739  // Make the root element and the DomDoc obj be accessible anywhere within the class
740  $this->root = $root;
741  $this->wsdl = $wsdl;
742 
743  // Append Stuff (different types of OPERATIONs for both portTypes and Binding node) to all the nodes
744  $this->getAPIsOperations();
745 
746  // Append the type to SCHEMA node
747  $this->appendSimpleRestrictedTypes();
748  $this->appendComplexTypes();
749  $this->appendComplexElements();
750 
751  // Append SCHEMA node to TYPES node and then append TYPES node to ROOT node
752  $types_node->appendChild($this->schema_node);
753  $this->root->appendChild($types_node);
754 
755  // Create the MESSAGE(s) Node and set Name
756  $io = Array ('input', 'output');
757  foreach ($this->function_list as $func_name => $func_info) {
758  foreach ($io as $element) {
759  // Do the type before doing the messages
760  $this->message_node = $wsdl->createElementNS(self::SCHEMA_WSDL, 'message');
761  $this->message_node->setAttribute('name', $func_name.'Soap'.ucwords($element)
762  );
763  $part_node = $wsdl->createElementNS(self::SCHEMA_WSDL, 'part');
764  $part_node->setAttribute('name', 'parameters');
765 
766  // May be we should never have a method that accept anytype of string, should all be complex type. IMPORTANT to rethink.
767  $simpleType = FALSE;
768  $message_io = $simpleType ? 's:'.$func_info['arg_type'] : 'tns:'.$func_name.(($element == 'output')?'Response':'');
769  $part_node->setAttribute('element', $message_io);
770 
771  $this->message_node->appendChild($part_node);
772  $this->root->appendChild($this->message_node);
773  }//end foreach
774  }//end foreach
775 
776  // Append all the major nodes to root node
777  $this->root->appendChild($this->portType_node);
778  $this->root->appendChild($binding_node);
779  $this->appendService();
780 
781  // Append the root node to the DOM document
782  $wsdl->appendChild($root);
783 
784  // Finally produce XML
785  return $this->wsdl->saveXML();
786 
787  }//end getXML()
788 
789 
798  public function getAPIsOperations()
799  {
800  // Get all the APIs linked underneath
801 
802  $this->getAvailableOperations(TRUE);
803 
804  // Loop through the list of function names to get the docComment for each of them,
805  // and append a node to the XML tree right away
806  foreach ($this->function_list as $func_name => $func_info) {
807  // Get the Reflection class to get the info about each function
808  $class = new ReflectionClass(str_replace(' ', '_', ucwords(str_replace('_', ' ', $func_info['class_name']))));
809 
810  if (method_exists($func_info['class_name'], $func_name)) {
811  $input_output = Array ('input', 'output');
812 
813  // Adding OPERATION NODE to BINDING NODE
814  $operation_node = $this->wsdl->createElementNS(self::SCHEMA_WSDL, 'operation');
815  $operation_node->setAttribute('name', $func_name);
816  $sub_operation_node = $this->wsdl->createElementNS(self::SCHEMA_SOAP, 'operation');
817  $sub_operation_node->setAttribute('soapAction', '');
818 
819  // Get a copy of the Node here since we don't need the io_node here.
820  $operation_node_2 = clone $operation_node;
821 
822  $operation_node->appendChild($sub_operation_node);
823  // Adding IO node to each of the operations inside SOAP BINDING NODE.
824  foreach ($input_output as $element) {
825  $io_node = $this->wsdl->createElementNS(self::SCHEMA_WSDL, $element);
826  $body_node = $this->wsdl->createElementNS(self::SCHEMA_SOAP, 'body');
827  $body_node->setAttribute('use', 'literal');
828  $io_node->appendChild($body_node);
829  $operation_node->appendChild($io_node);
830  }//end foreach
831 
832  $this->binding_node->appendChild($operation_node);
833 
834  // Adding OPERATION NODE to PORTTYPE NODE using $operation_node_2
835  foreach ($input_output as $element) {
836  $io_message_node = $this->wsdl->createElementNS(self::SCHEMA_WSDL, $element);
837  $io_message_node->setAttribute('message', 'tns:'.$func_name.'Soap'.ucwords($element));
838  $operation_node_2->appendChild($io_message_node);
839  }//end foreach
840  $this->portType_node->appendChild($operation_node_2);
841 
842  // Get the PHPDoc comment
843  $method = $class->getMethod($func_name);
844  $doc_comment = $method->getDocComment();
845 
846  // Get the array of information about the function including arg_name, arg_type, return_type, etc...
847  $func_args_info = $this->getFuncDocInfo($doc_comment);
848 
849  // Add this information to the global function_list array.
850  $this->function_list[$func_name] = array_merge($func_info, $func_args_info);
851 
852  }//end if
853 
854  }//end foreach
855 
856  }//end getAPIsOperations()
857 
858 
867  private function getFuncDocInfo($doc_comment)
868  {
869  $matches = Array();
870 
871  preg_match_all('|@param\s+(\w+)\s+(\&)?\$(\w+)|', $doc_comment, $matches);
872 
873  $func_args_info = Array (
874  'arg_type' => isset($matches[1][0]) ? $matches[1][0] : '',
875  'arg_ref' => isset($matches[2][0]) ? $matches[2][0] : '',
876  'arg_name' => isset($matches[3][0]) ? $matches[3][0] : '',
877  );
878 
879  $matches_0 = Array();
880  preg_match_all('|Description:\s+((\w+\s)*)|', $doc_comment, $matches_0);
881  $func_args_info['func_desc'] = isset($matches_0[1][0]) ? trim($matches_0[1][0]) : '';
882 
883  $matches_2 = Array();
884  preg_match('|@return\s+(\w+)|', $doc_comment, $matches_2);
885  $func_args_info['return_type'] = $matches_2[1];
886 
887  return $func_args_info;
888 
889  }//end getFuncDocInfo()
890 
891 
899  private function appendComplexElements()
900  {
901  // Loop through the complex types came from all the APIs.
902  foreach ($this->complex_elements as $func_io => $sub_args_info) {
903 
904  // Hiding unknown complex elements
905  $func_name_safe = str_replace('Response', '', $func_io);
906  if (!array_key_exists($func_name_safe, $this->function_list)) continue;
907 
908  // Specify the REQUEST - RESPONSE for each function, point them to the definition of the complex type.
909  $element_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'element');
910  $element_node->setAttribute('name', $func_io);
911 
912  $complex_main_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'complexType');
913  // If we are declaring complex types which are needed for the input, we need to set the complex type name as we are not inside <element>
914 
915  $sequence_main_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'sequence');
916 
917  // It is raw type, DAMN
918  foreach ($sub_args_info as $sub_arg_name => $info) {
919 
920  $subelement_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'element');
921  $subelement_node->setAttribute('name', $sub_arg_name);
922  if (strtolower($info['type']) == 'any') {
923  $sub_complex_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'complexType');
924  $sub_sequence_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'sequence');
925  if ($info['mixed']) {
926  $sub_complex_node->setAttribute('mixed', $info['mixed'] ? "true" : "false");
927  }//end if
928  $any_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'any');
929  $sub_sequence_node->appendChild($any_node);
930  $sub_complex_node->appendChild($sub_sequence_node);
931  $subelement_node->appendChild($sub_complex_node);
932  } else {
933  $subelement_node->setAttribute('type', (!in_array($info['type'], $this->simple_types) ? 'tns:' : 's:' ).$info['type']);
934  }
935  $subelement_node->setAttribute('minOccurs', (!is_array($info)) ? '1': $info['min']);
936  $subelement_node->setAttribute('maxOccurs', (!is_array($info)) ? '1': $info['max']);
937  if ($info['nillable']) {
938  $subelement_node->setAttribute('nillable', strtolower((string) $info['nillable']));
939  }//end if
940 
941  $sequence_main_node->appendChild($subelement_node);
942  }//end foreach
943 
944  $complex_main_node->appendChild($sequence_main_node);
945  $element_node->appendChild($complex_main_node);
946  // end REQUEST - RESPONSE specification
947 
948  // This is the REQUEST - RESPONSE specification
949  // If we are declaring a complex type, we dont want to be inside the element node.
950  $this->schema_node->appendChild($element_node);
951 
952  }//end foreach
953 
954  }//end appendComplexElements()
955 
956 
964  private function appendComplexTypes()
965  {
966  // Loop through the complex types came from all the APIs.
967  foreach ($this->complex_types as $func_io => $sub_args_info) {
968  // Definition of the complex type (whether it is a string, int or float)
969  $complex_type_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'complexType');
970  $complex_type_node->setAttribute('name', $func_io);
971  if (is_array($sub_args_info)) {
972  $sequence_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'sequence');
973  foreach ($sub_args_info as $sub_arg_name => $info) {
974  if ($sub_arg_name == 'arg_name') continue;
975  $subelement_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'element');
976  $subelement_node->setAttribute('name', $sub_arg_name);
977  // We only add type attribute for most complex type attribute, if the complex type does not have a type
978  // very likely that it has a mixed attribute and return 'any'
979  if (!empty($info['type'])) {
980  $subelement_node->setAttribute('type', (!in_array($info['type'], $this->simple_types) ? 'tns:' : 's:' ).$info['type']);
981  }//end if
982  $subelement_node->setAttribute('minOccurs', $info['min']);
983  $subelement_node->setAttribute('maxOccurs', $info['max']);
984  if ($info['nillable']) {
985  $subelement_node->setAttribute('nillable', strtolower((string) $info['nillable']));
986  }//end if
987  if ($info['mixed']) {
988  $subelement_node->setAttribute('mixed', strtolower((string) $info['mixed']));
989  }//end if
990 
991  $sequence_node->appendChild($subelement_node);
992  }//end foreach
993  $complex_type_node->appendChild($sequence_node);
994  // End complex type definition
995  } else {
996  $complex_type_node->setAttribute('type', 'tns:'.$sub_args_info);
997  }//end else
998 
999  // This is the complex type definition
1000  $this->schema_node->appendChild($complex_type_node);
1001  }//end foreach
1002 
1003  }//end appendComplexTypes()
1004 
1005 
1013  private function appendSimpleRestrictedTypes()
1014  {
1015  // Loop through the complex types came from all the APIs.
1016  foreach ($this->simple_restricted_types as $simple_restricted_type_name => $sub_args_info) {
1017  // Definition of the simple type (whether it is a string, int or float)
1018  $simple_restricted_type_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'simpleType');
1019  $simple_restricted_type_node->setAttribute('name', $simple_restricted_type_name);
1020  if (is_array($sub_args_info)) {
1021  $restriction_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'restriction');
1022  $restriction_base = in_array($sub_args_info['restriction_base'], $this->simple_types) ? 's:'.$sub_args_info['restriction_base'] : $sub_args_info['restriction_base'];
1023  $restriction_node->setAttribute('base', $restriction_base);
1024  if (isset($sub_args_info['enumeration']) && is_array($sub_args_info['enumeration'])) {
1025  foreach ($sub_args_info['enumeration'] as $enum_val) {
1026  $enum_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'enumeration');
1027  $enum_node->setAttribute('value', $enum_val);
1028  $restriction_node->appendChild($enum_node);
1029  }//end foreach
1030  } else if (isset($sub_args_info['pattern'])) {
1031  $pattern_node = $this->wsdl->createElementNS(self::SOAP_XML_SCHEMA_VERSION, 'pattern');
1032  $pattern_node->setAttribute('value', $sub_args_info['pattern']);
1033  $restriction_node->appendChild($pattern_node);
1034  }//end else
1035  $simple_restricted_type_node->appendChild($restriction_node);
1036  // End complex type definition
1037  } else {
1038  $simple_restricted_type_node->setAttribute('type', 'tns:'.$sub_args_info);
1039  }//end else
1040 
1041  // This is the complex type definition
1042  $this->schema_node->appendChild($simple_restricted_type_node);
1043 
1044  }//end foreach
1045 
1046  }//end appendSimpleRestrictedTypes()
1047 
1048 
1055  private function appendService()
1056  {
1057  // Create a new service node
1058  $service_node = $this->wsdl->createElementNS(self::SCHEMA_WSDL, 'service');
1059  $service_node->setAttribute('name', 'MatrixWebServices');
1060 
1061  $port_node = $this->wsdl->createElementNS(self::SCHEMA_WSDL, 'port');
1062  $port_node->setAttribute('name', 'MatrixWebServicesPort');
1063  $port_node->setAttribute('binding', 'tns:MatrixSOAPServiceBinding');
1064 
1065  $address_node = $this->wsdl->createElementNS(self::SCHEMA_SOAP, 'address');
1066  $address_node->setAttribute('location', $this->getUrl().'?wsdl');
1067 
1068  $port_node->appendChild($address_node);
1069  $service_node->appendChild($port_node);
1070 
1071  // Append SERVICE node to root
1072  $this->root->appendChild($service_node);
1073 
1074  }//end appendComplexTypes()
1075 
1076 
1080  public function generateWSDL()
1081  {
1082  require_once SQ_FUDGE_PATH.'/general/file_system.inc';
1083  $wsdl_content = $this->getXML();
1084  create_directory($this->data_path);
1085  return string_to_file($wsdl_content, $this->data_path.'/content_file.wsdl');
1086 
1087  }//end generateWSDL()
1088 
1089 }//end class
1090 ?>