<?php
/**
* CPAINT WSDL generation library
*
* This file is part of the CPAINT AJAX library.

* released under the terms of the LGPL
* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
*
* @package    CPAINT
* @author     Dominique Stender <dstender@st-webdevelopment.de>
* @copyright  Copyright (c) 2005-2006 Dominique Stender - http://sf.net/projects/cpaint
* @version    $id: cpaint2.wsdl.php 313 2006-09-30 08:22:39Z saloon12yrd $
*/




  /**
  * CPAINT WSDL generation library
  *
  * @package    CPAINT
  * @access     public
  * @author     Dominique Stender <dstender@st-webdevelopment.de>
  * @copyright  Copyright (c) 2005-2006 Dominique Stender - http://sf.net/projects/cpaint
  * @version    1.0.0
  */
  class cpaint_wsdl {

    /**
    * Classes version
    *
    * @access public
    * @var    string $version
    */
    public $version = '1.0.0';

    /**
    * array of API function names and their I/O parameters
    *
    * @access protected
    * @var    array $functions
    */
    public $functions;

    /**
    * array of complex type schemata
    *
    * @access protected
    * @var    array $types
    */
    public $types;

    /**
    * base name of the script providing the CPAINT api, with out extension
    *
    * @access protected
    * @var    string $basename
    */
    public $basename;

    /**
    * full URL to the CPAINT service
    *
    * @access protected
    * @var    string $api_url
    */
    public $api_url;

    /**
    * targetNamespace
    *
    * @access protected
    * @var    string $tns
    */
    public $tns;

    /**
    * URI of this service
    *
    * @access protected
    * @var    string $uri
    */
    public $uri;

    /**
    * constructor
    *
    * @access public
    * @return void
    */
    public function cpaint_wsdl() {
      // register the destructor
      register_shutdown_function(array(&$this, '__destruct'));

      $this->__construct();
    }

    /**
    * PHP5 constructor
    *
    * @access public
    * @return void
    */
    public function __construct() {
      $this->functions  = array();
      $this->types      = array();
      $this->basename   = preg_replace('/\.[a-z0-9]+$/', '', basename($_SERVER['SCRIPT_NAME']));
      $this->tns        = 'http://' . $_SERVER['SERVER_NAME'] . '/soap/' . $this->basename;
      $this->uri        = $_SERVER['SERVER_NAME'] . '/CPAINT/' . $this->basename . '/';
      $this->api_url    = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'];
    }

    /**
    * destructor
    *
    * @access private
    * @return void
    */
    public function __destruct() {
    }

    /**
    * generates the WSDL for a CPAINT API
    *
    * @access public
    * @param  array   $functions      array of functions and their I/O parameter schemata
    * @param  array   $types          array of complex type definitions
    * @return string
    */
    public function generate($functions, $types) {
      $return_value     = '<?xml version="1.0" encoding="UTF-8" ?>';
      $this->functions  = (array) $functions;
      $this->types      = (array) $types;

      // open base node
      $return_value .= '<definitions'
                      . ' xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"'
                      . ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"'
                      . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
                      . ' xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"'
                      . ' xmlns:si="http://soapinterop.org/xsd"'
                      . ' xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"'
                      . ' xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"'
                      . ' xmlns="http://schemas.xmlsoap.org/wsdl/"'
                      . ' xmlns:tns="' . $this->tns . '"'
                      . ' targetNamespace="' . $this->tns . '">';

      // iterate over all complex types
      $return_value .= '<types>'
                    . '<xsd:schema targetNamespace="' . $this->tns . '">'
                      . '<xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>'
                      . '<xsd:import namespace="http://schemas.xmlsoap.org/wsdl/"/>';

      foreach ($this->types as $type_definition) {
        $return_value .= $this->complex_type($type_definition);
      } // end: foreach

      $return_value .= '</xsd:schema>'
                    . '</types>';

      // iterate over all registered functions
      foreach ($this->functions as $function_name => $function_definition) {
        // build the request
        $return_value .= $this->request($function_name);

        // build the response
        $return_value .= $this->response($function_name);
      } // end: foreach

      // add port and binding information
      $return_value .= $this->port_information()
                    .  $this->binding_information()
                    .  $this->service_information();

      // close base node
      $return_value .= '</definitions>';

      return $return_value;
    }

    /**
    * generates a WSDL representation for all complex types
    *
    * @access protected
    * @param  array       $type_definition      schema of the complex type
    * @return string
    */
    public function complex_type($type_definition) {
      $return_value     = '';
      $type_definition  = (array) $type_definition;

      switch ($type_definition['type']) {
        case 'complex':
          $return_value .= '<xsd:complexType name="' . $type_definition['name'] . '">'
                        . '<xsd:all>';
          // recurse through the structure
          if (is_array($type_definition['struct'])) {

            foreach ($type_definition['struct'] as $structure) {
              $return_value .= $this->data_element($structure['name'], $structure['type']);
            } // end: foreach
          } // end: if
          $return_value .=  '</xsd:all>'
                          . '</xsd:complexType>';
          break;

        case 'list':
          $return_value .= '<xsd:complexType name="' . $type_definition['name'] . '">'
                          . '<xsd:complexContent>'
                            . '<xsd:restriction base="SOAP-ENC:Array">'
                              . '<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="' . $this->data_type($type_definition['base_type']) . '[]"/>'
                            . '</xsd:restriction>'
                          . '</xsd:complexContent>'
                        . '</xsd:complexType>';
          break;

        case 'restriction':
          $return_value .= '<xsd:simpleType name="' . $type_definition['name'] . '">'
                            . '<xsd:restriction base="' . $this->data_type($type_definition['base_type']) . '">';
          // iterate over all allowed values
          if (is_array($type_definition['values'])) {

            foreach ($type_definition['values'] as $value) {
              $return_value .= '<xsd:enumeration value="' . $value . '" />';
            } // end: foreach
          } // end: if

          $return_value .=    '</xsd:restriction>'
                          . '</xsd:simpleType>';
          break;

        default:
          // unknown type
      } // end: switch

      return $return_value;
    }

    /**
    * handles a single element of a complex data type
    *
    * @access protected
    * @param  string      $element_name       name of the element
    * @param  mixed       $element_type       data type of the element
    * @return string
    */
    public function data_element($element_name, $element_type) {
      $return_value = '';
      $element_name = (string) $element_name;
      $element_type = $this->data_type($element_type);

      if ($element_type != '') {
        $return_value = '<xsd:element name="' . $element_name . '" type="' . $element_type . '" />';
      } // end: if

      return $return_value;
    }

    /**
    * creates a XML datatype from a given type
    *
    * @access protected
    * @param  string    $type     native type
    * @return string
    */
    public function data_type($type) {
      $return_value = '';
      $type         = (string) $type;

      switch (strtolower($type)) {
        // supported scalar types
        case 'ENTITIES':
        case 'ENTITY':
        case 'ID':
        case 'IDREF':
        case 'IDREFS':
        case 'NCName':
        case 'NMTOKEN':
        case 'NMTOKENS':
        case 'Name':
        case 'anySimpleType':
        case 'anyType':
        case 'base64':
        case 'base64Binary':
        case 'boolean':
        case 'byte':
        case 'date':
        case 'dateTime':
        case 'decimal':
        case 'double':
        case 'duration':
        case 'float':
        case 'gDay':
        case 'gMonth':
        case 'gMonthDay':
        case 'gYear':
        case 'gYearMonth':
        case 'hexBinary':
        case 'i4':
        case 'int':
        case 'integer':
        case 'language':
        case 'long':
        case 'negativeInteger':
        case 'nonNegativeInteger':
        case 'nonPositiveInteger':
        case 'normalizedString':
        case 'positiveInteger':
        case 'short':
        case 'string':
        case 'time':
        case 'timeInstant':
        case 'token':
        case 'unsignedByte':
        case 'unsignedInt':
        case 'unsignedLong':
        case 'unsignedShort':
        case 'ur-type':
          $return_value = 'xsd:' . $type;
          break;

        default:

          foreach ($this->types as $id => $type_definition) {

            if ($type_definition['name'] == $type) {
              $return_value = 'tns:' . $type;
              break;
            } // end: if
          } // end: foreach
      } // end: switch

      return $return_value;
    }

    /**
    * generates a XML representation of a request / response argument
    *
    * @access private
    * @param  string    $name     name of the argument
    * @param  string    $type     data type of that argument
    * @return string
    */
    public function part($name, $type) {
      $return_value = false;
      $name         = (string) $name;
      $type         = $this->data_type($type);

      if ($type != '') {
        $return_value = '<part name="' . $name . '" type="' . $type . '" />';
      } // end: if

      return $return_value;
    }

    /**
    * generates a WSDL representation for a request
    *
    * @access protected
    * @param  string  $request_name     name of the request
    * @return string
    */
    public function request($request_name) {
      $return_value = '';
      $request_name = (string) $request_name;

      $return_value .= '<message name="' . $request_name . 'Request">'
                      . $this->part('head', 'cpaintRequestHead');

      // iterate over all request arguments
      foreach ($this->functions[$request_name]['input'] as $structure) {
        $return_value .= $this->part($structure['name'], $structure['type']);
      } // end: foreach

      $return_value .=  ''
                    . '</message>';

      return $return_value;
    }

    /**
    * generates a WSDL representation for a response
    *
    * @access protected
    * @param  string  $response_name     name of the response
    * @return string
    */
    public function response($response_name) {
      $return_value   = '';
      $response_name  = (string) $response_name;

      $return_value .= '<message name="' . $response_name . 'Response">'
                      . $this->part('head', 'cpaintResponseHead');

      // iterate over all response argument
      foreach ($this->functions[$response_name]['output'] as $structure) {
        $return_value .= $this->part($structure['name'], $structure['type']);
      } // end: foreach

      $return_value .= '</message>';

      return $return_value;
    }

    /**
    * generates the port information
    *
    * @access protected
    * @return string
    */
    public function port_information() {
      $return_value = '<portType name="' . $this->basename . 'PortType">';

      // describe all API operations
      foreach ($this->functions as $function_name => $function_definition) {
        $return_value .= '<operation name="' . $function_name . '">'
                        . '<documentation>'
                          . $function_definition['comment']
                        . '</documentation>'
                        . '<input message="tns:' . $function_name . 'Request" />'
                        . '<output message="tns:' . $function_name . 'Response" />'
                      . '</operation>';
      } // end: foreach

      $return_value .= '</portType>';

      return $return_value;
    }

    /**
    * generates the binding information
    *
    * @access protected
    * @return string
    */
    public function binding_information() {
      $return_value = '<binding name="' . $this->basename . 'Binding" type="tns:' . $this->basename . 'PortType">'
                      . '<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />';

      // describe all API operations
      foreach ($this->functions as $function_name => $function_definition) {
        $return_value .= '<operation name="' . $function_name . '">'
                        . '<soap:operation soapAction="uri:' . $this->uri . $function_name . '" style="rpc" />'
                        . '<input>'
                          . '<soap:body use="encoded" namespace="uri:' . $this->uri . '" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />'
                        . '</input>'
                        . '<output>'
                          . '<soap:body use="encoded" namespace="uri:' . $this->uri . '" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />'
                        . '</output>'
                      . '</operation>';
      } // end: foreach

      $return_value .= '</binding>';

      return $return_value;
    }

    /**
    * generates the service information
    *
    * @access protected
    * @return string
    */
    public function service_information() {
      $return_value = '<service name="' . $this->basename . '">'
                      . '<port name="' . $this->basename . 'Port" binding="tns:' . $this->basename . 'Binding">'
                        . '<soap:address location="' . $this->api_url . '"/>'
                      . '</port>'
                    . '</service>';

      return $return_value;
    }

  }

?>