Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * WSDL generator for the SOAP web service.
  19   *
  20   * @package    webservice_soap
  21   * @copyright  2016 Jun Pataleta
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace webservice_soap;
  25  
  26  /**
  27   * WSDL generator for the SOAP web service.
  28   *
  29   * @package    webservice_soap
  30   * @copyright  2016 Jun Pataleta
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  class wsdl {
  34      /** Namespace URI for the WSDL framework. */
  35      const NS_WSDL = 'http://schemas.xmlsoap.org/wsdl/';
  36  
  37      /** Encoding namespace URI as defined by SOAP 1.1 */
  38      const NS_SOAP_ENC = 'http://schemas.xmlsoap.org/soap/encoding/';
  39  
  40      /** Namespace URI for the WSDL SOAP binding. */
  41      const NS_SOAP = 'http://schemas.xmlsoap.org/wsdl/soap/';
  42  
  43      /** Schema namespace URI as defined by XSD. */
  44      const NS_XSD = 'http://www.w3.org/2001/XMLSchema';
  45  
  46      /** WSDL namespace for the WSDL HTTP GET and POST binding. */
  47      const NS_SOAP_TRANSPORT = 'http://schemas.xmlsoap.org/soap/http';
  48  
  49      /** BINDING - string constant attached to the service class name to identify binding nodes. */
  50      const BINDING = 'Binding';
  51  
  52      /** IN - string constant attached to the function name to identify input nodes. */
  53      const IN = 'In';
  54  
  55      /** OUT - string constant attached to the function name to identify output nodes. */
  56      const OUT = 'Out';
  57  
  58      /** PORT - string constant attached to the service class name to identify port nodes. */
  59      const PORT = 'Port';
  60  
  61      /** SERVICE string constant attached to the service class name to identify service nodes. */
  62      const SERVICE = 'Service';
  63  
  64      /** @var string The name of the service class. */
  65      private $serviceclass;
  66  
  67      /** @var string The WSDL namespace. */
  68      private $namespace;
  69  
  70      /** @var array The WSDL's message nodes. */
  71      private $messagenodes;
  72  
  73      /** @var \SimpleXMLElement The WSDL's binding node. */
  74      private $nodebinding;
  75  
  76      /** @var \SimpleXMLElement The WSDL's definitions node. */
  77      private $nodedefinitions;
  78  
  79      /** @var \SimpleXMLElement The WSDL's portType node. */
  80      private $nodeporttype;
  81  
  82      /** @var \SimpleXMLElement The WSDL's service node. */
  83      private $nodeservice;
  84  
  85      /** @var \SimpleXMLElement The WSDL's types node. */
  86      private $nodetypes;
  87  
  88      /**
  89       * webservice_soap_wsdl constructor.
  90       *
  91       * @param string $serviceclass The service class' name.
  92       * @param string $namespace The WSDL namespace.
  93       */
  94      public function __construct($serviceclass, $namespace) {
  95          $this->serviceclass = $serviceclass;
  96          $this->namespace = $namespace;
  97  
  98          // Initialise definitions node.
  99          $this->nodedefinitions = new \SimpleXMLElement('<definitions />');
 100          $this->nodedefinitions->addAttribute('xmlns', self::NS_WSDL);
 101          $this->nodedefinitions->addAttribute('x:xmlns:tns', $namespace);
 102          $this->nodedefinitions->addAttribute('x:xmlns:soap', self::NS_SOAP);
 103          $this->nodedefinitions->addAttribute('x:xmlns:xsd', self::NS_XSD);
 104          $this->nodedefinitions->addAttribute('x:xmlns:soap-enc', self::NS_SOAP_ENC);
 105          $this->nodedefinitions->addAttribute('x:xmlns:wsdl', self::NS_WSDL);
 106          $this->nodedefinitions->addAttribute('name', $serviceclass);
 107          $this->nodedefinitions->addAttribute('targetNamespace', $namespace);
 108  
 109          // Initialise types node.
 110          $this->nodetypes = $this->nodedefinitions->addChild('types');
 111          $typeschema = $this->nodetypes->addChild('x:xsd:schema');
 112          $typeschema->addAttribute('targetNamespace', $namespace);
 113  
 114          // Initialise the portType node.
 115          $this->nodeporttype = $this->nodedefinitions->addChild('portType');
 116          $this->nodeporttype->addAttribute('name', $serviceclass . self::PORT);
 117  
 118          // Initialise the binding node.
 119          $this->nodebinding = $this->nodedefinitions->addChild('binding');
 120          $this->nodebinding->addAttribute('name', $serviceclass . self::BINDING);
 121          $this->nodebinding->addAttribute('type', 'tns:' . $serviceclass . self::PORT);
 122          $soapbinding = $this->nodebinding->addChild('x:soap:binding');
 123          $soapbinding->addAttribute('style', 'rpc');
 124          $soapbinding->addAttribute('transport', self::NS_SOAP_TRANSPORT);
 125  
 126          // Initialise the service node.
 127          $this->nodeservice = $this->nodedefinitions->addChild('service');
 128          $this->nodeservice->addAttribute('name', $serviceclass . self::SERVICE);
 129          $serviceport = $this->nodeservice->addChild('port');
 130          $serviceport->addAttribute('name', $serviceclass . self::PORT);
 131          $serviceport->addAttribute('binding', 'tns:' . $serviceclass . self::BINDING);
 132          $soapaddress = $serviceport->addChild('x:soap:address');
 133          $soapaddress->addAttribute('location', $namespace);
 134  
 135          // Initialise message nodes.
 136          $this->messagenodes = array();
 137      }
 138  
 139      /**
 140       * Adds a complex type to the WSDL.
 141       *
 142       * @param string $classname The complex type's class name.
 143       * @param array $properties An associative array containing the properties of the complex type class.
 144       */
 145      public function add_complex_type($classname, $properties) {
 146          $typeschema = $this->nodetypes->children();
 147          // Append the complex type.
 148          $complextype = $typeschema->addChild('x:xsd:complexType');
 149          $complextype->addAttribute('name', $classname);
 150          $child = $complextype->addChild('x:xsd:all');
 151          foreach ($properties as $name => $options) {
 152              $param = $child->addChild('x:xsd:element');
 153              $param->addAttribute('name', $name);
 154              $param->addAttribute('type', $this->get_soap_type($options['type']));
 155              if (!empty($options['nillable'])) {
 156                  $param->addAttribute('nillable', 'true');
 157              }
 158          }
 159      }
 160  
 161      /**
 162       * Registers the external service method to the WSDL.
 163       *
 164       * @param string $functionname The name of the web service function to be registered.
 165       * @param array $inputparams Contains the function's input parameters with their associated types.
 166       * @param array $outputparams Contains the function's output parameters with their associated types.
 167       * @param string $documentation The function's description.
 168       */
 169      public function register($functionname, $inputparams = array(), $outputparams = array(), $documentation = '') {
 170          // Process portType operation nodes.
 171          $porttypeoperation = $this->nodeporttype->addChild('operation');
 172          $porttypeoperation->addAttribute('name', $functionname);
 173          // Documentation node.
 174          $porttypeoperation->addChild('documentation', $documentation);
 175  
 176          // Process binding operation nodes.
 177          $bindingoperation = $this->nodebinding->addChild('operation');
 178          $bindingoperation->addAttribute('name', $functionname);
 179          $soapoperation = $bindingoperation->addChild('x:soap:operation');
 180          $soapoperation->addAttribute('soapAction', $this->namespace . '#' . $functionname);
 181  
 182          // Input nodes.
 183          $this->process_params($functionname, $porttypeoperation, $bindingoperation, $inputparams);
 184  
 185          // Output nodes.
 186          $this->process_params($functionname, $porttypeoperation, $bindingoperation, $outputparams, true);
 187      }
 188  
 189      /**
 190       * Outputs the WSDL in XML format.
 191       *
 192       * @return mixed The string value of the WSDL in XML format. False, otherwise.
 193       */
 194      public function to_xml() {
 195          // Return WSDL in XML format.
 196          return $this->nodedefinitions->asXML();
 197      }
 198  
 199      /**
 200       * Utility method that returns the encoded SOAP type based on the given type string.
 201       *
 202       * @param string $type The input type string.
 203       * @return string The encoded type for the WSDL.
 204       */
 205      private function get_soap_type($type) {
 206          switch($type) {
 207              case 'int':
 208              case 'double':
 209              case 'string':
 210                  return 'xsd:' . $type;
 211              case 'array':
 212                  return 'soap-enc:Array';
 213              default:
 214                  return 'tns:' . $type;
 215          }
 216      }
 217  
 218      /**
 219       * Utility method that creates input/output nodes from input/output params.
 220       *
 221       * @param string $functionname The name of the function being registered.
 222       * @param \SimpleXMLElement $porttypeoperation The port type operation node.
 223       * @param \SimpleXMLElement $bindingoperation The binding operation node.
 224       * @param array $params The function's input/output parameters.
 225       * @param bool $isoutput Flag to indicate if the nodes to be generated are for input or for output.
 226       */
 227      private function process_params($functionname, \SimpleXMLElement $porttypeoperation, \SimpleXMLElement $bindingoperation,
 228                                      array $params = null, $isoutput = false) {
 229          // Do nothing if parameter array is empty.
 230          if (empty($params)) {
 231              return;
 232          }
 233  
 234          $postfix = self::IN;
 235          $childtype = 'input';
 236          if ($isoutput) {
 237              $postfix = self::OUT;
 238              $childtype = 'output';
 239          }
 240  
 241          // For portType operation node.
 242          $child = $porttypeoperation->addChild($childtype);
 243          $child->addAttribute('message', 'tns:' . $functionname . $postfix);
 244  
 245          // For binding operation node.
 246          $child = $bindingoperation->addChild($childtype);
 247          $soapbody = $child->addChild('x:soap:body');
 248          $soapbody->addAttribute('use', 'encoded');
 249          $soapbody->addAttribute('encodingStyle', self::NS_SOAP_ENC);
 250          $soapbody->addAttribute('namespace', $this->namespace);
 251  
 252          // Process message nodes.
 253          $messagein = $this->nodedefinitions->addChild('message');
 254          $messagein->addAttribute('name', $functionname . $postfix);
 255          foreach ($params as $name => $options) {
 256              $part = $messagein->addChild('part');
 257              $part->addAttribute('name', $name);
 258              $part->addAttribute('type', $this->get_soap_type($options['type']));
 259          }
 260      }
 261  }