You are here

nusoap.orig.php in Salesforce Suite 5

Same filename and directory in other branches
  1. 5.2 includes/nusoap.orig.php

File

includes/nusoap.orig.php
View source
<?php

/*
NuSOAP - Web Services Toolkit for PHP
Copyright (c) 2002 NuSphere Corporation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
If you have any questions or comments, please email:
Dietrich Ayala
dietrich@ganx4.com
http://dietrich.ganx4.com/nusoap
NuSphere Corporation
http://www.nusphere.com
*/

/* load classes

// necessary classes
require_once('class.soapclient.php');
require_once('class.soap_val.php');
require_once('class.soap_parser.php');
require_once('class.soap_fault.php');

// transport classes
require_once('class.soap_transport_http.php');

// optional add-on classes
require_once('class.xmlschema.php');
require_once('class.wsdl.php');

// server class
require_once('class.soap_server.php');*/

// class variable emulation
// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;

/**
*
* nusoap_base
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class nusoap_base {

  /**
   * Identification for HTTP headers.
   *
   * @var string
   * @access private
   */
  var $title = 'NuSOAP';

  /**
   * Version for HTTP headers.
   *
   * @var string
   * @access private
   */
  var $version = '0.7.2';

  /**
   * CVS revision for HTTP headers.
   *
   * @var string
   * @access private
   */
  var $revision = '$Revision$';

  /**
   * Current error string (manipulated by getError/setError)
   *
   * @var string
   * @access private
   */
  var $error_str = '';

  /**
   * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
   *
   * @var string
   * @access private
   */
  var $debug_str = '';

  /**
   * toggles automatic encoding of special characters as entities
   * (should always be true, I think)
   *
   * @var boolean
   * @access private
   */
  var $charencoding = true;

  /**
   * the debug level for this instance
   *
   * @var	integer
   * @access private
   */
  var $debugLevel;

  /**
   * set schema version
   *
   * @var      string
   * @access   public
   */
  var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';

  /**
   * charset encoding for outgoing messages
   *
   * @var      string
   * @access   public
   */
  var $soap_defencoding = 'ISO-8859-1';

  //var $soap_defencoding = 'UTF-8';

  /**
   * namespaces in an array of prefix => uri
   *
   * this is "seeded" by a set of constants, but it may be altered by code
   *
   * @var      array
   * @access   public
   */
  var $namespaces = [
    'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
    'xsd' => 'http://www.w3.org/2001/XMLSchema',
    'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
    'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
  ];

  /**
   * namespaces used in the current context, e.g. during serialization
   *
   * @var      array
   * @access   private
   */
  var $usedNamespaces = [];

  /**
   * XML Schema types in an array of uri => (array of xml type => php type)
   * is this legacy yet?
   * no, this is used by the xmlschema class to verify type => namespace mappings.
   * @var      array
   * @access   public
   */
  var $typemap = [
    'http://www.w3.org/2001/XMLSchema' => [
      'string' => 'string',
      'boolean' => 'boolean',
      'float' => 'double',
      'double' => 'double',
      'decimal' => 'double',
      'duration' => '',
      'dateTime' => 'string',
      'time' => 'string',
      'date' => 'string',
      'gYearMonth' => '',
      'gYear' => '',
      'gMonthDay' => '',
      'gDay' => '',
      'gMonth' => '',
      'hexBinary' => 'string',
      'base64Binary' => 'string',
      // abstract "any" types
      'anyType' => 'string',
      'anySimpleType' => 'string',
      // derived datatypes
      'normalizedString' => 'string',
      'token' => 'string',
      'language' => '',
      'NMTOKEN' => '',
      'NMTOKENS' => '',
      'Name' => '',
      'NCName' => '',
      'ID' => '',
      'IDREF' => '',
      'IDREFS' => '',
      'ENTITY' => '',
      'ENTITIES' => '',
      'integer' => 'integer',
      'nonPositiveInteger' => 'integer',
      'negativeInteger' => 'integer',
      'long' => 'integer',
      'int' => 'integer',
      'short' => 'integer',
      'byte' => 'integer',
      'nonNegativeInteger' => 'integer',
      'unsignedLong' => '',
      'unsignedInt' => '',
      'unsignedShort' => '',
      'unsignedByte' => '',
      'positiveInteger' => '',
    ],
    'http://www.w3.org/2000/10/XMLSchema' => [
      'i4' => '',
      'int' => 'integer',
      'boolean' => 'boolean',
      'string' => 'string',
      'double' => 'double',
      'float' => 'double',
      'dateTime' => 'string',
      'timeInstant' => 'string',
      'base64Binary' => 'string',
      'base64' => 'string',
      'ur-type' => 'array',
    ],
    'http://www.w3.org/1999/XMLSchema' => [
      'i4' => '',
      'int' => 'integer',
      'boolean' => 'boolean',
      'string' => 'string',
      'double' => 'double',
      'float' => 'double',
      'dateTime' => 'string',
      'timeInstant' => 'string',
      'base64Binary' => 'string',
      'base64' => 'string',
      'ur-type' => 'array',
    ],
    'http://soapinterop.org/xsd' => [
      'SOAPStruct' => 'struct',
    ],
    'http://schemas.xmlsoap.org/soap/encoding/' => [
      'base64' => 'string',
      'array' => 'array',
      'Array' => 'array',
    ],
    'http://xml.apache.org/xml-soap' => [
      'Map',
    ],
  ];

  /**
   * XML entities to convert
   *
   * @var      array
   * @access   public
   * @deprecated
   * @see	expandEntities
   */
  var $xmlEntities = [
    'quot' => '"',
    'amp' => '&',
    'lt' => '<',
    'gt' => '>',
    'apos' => "'",
  ];

  /**
   * constructor
   *
   * @access	public
   */
  function nusoap_base() {
    $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
  }

  /**
   * gets the global debug level, which applies to future instances
   *
   * @return	integer	Debug level 0-9, where 0 turns off
   * @access	public
   */
  function getGlobalDebugLevel() {
    return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
  }

  /**
   * sets the global debug level, which applies to future instances
   *
   * @param	int	$level	Debug level 0-9, where 0 turns off
   * @access	public
   */
  function setGlobalDebugLevel($level) {
    $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
  }

  /**
   * gets the debug level for this instance
   *
   * @return	int	Debug level 0-9, where 0 turns off
   * @access	public
   */
  function getDebugLevel() {
    return $this->debugLevel;
  }

  /**
   * sets the debug level for this instance
   *
   * @param	int	$level	Debug level 0-9, where 0 turns off
   * @access	public
   */
  function setDebugLevel($level) {
    $this->debugLevel = $level;
  }

  /**
   * adds debug data to the instance debug string with formatting
   *
   * @param    string $string debug data
   * @access   private
   */
  function debug($string) {
    if ($this->debugLevel > 0) {
      $this
        ->appendDebug($this
        ->getmicrotime() . ' ' . get_class($this) . ": {$string}\n");
    }
  }

  /**
   * adds debug data to the instance debug string without formatting
   *
   * @param    string $string debug data
   * @access   public
   */
  function appendDebug($string) {
    if ($this->debugLevel > 0) {

      // it would be nice to use a memory stream here to use
      // memory more efficiently
      $this->debug_str .= $string;
    }
  }

  /**
   * clears the current debug data for this instance
   *
   * @access   public
   */
  function clearDebug() {

    // it would be nice to use a memory stream here to use
    // memory more efficiently
    $this->debug_str = '';
  }

  /**
   * gets the current debug data for this instance
   *
   * @return   debug data
   * @access   public
   */
  function &getDebug() {

    // it would be nice to use a memory stream here to use
    // memory more efficiently
    return $this->debug_str;
  }

  /**
   * gets the current debug data for this instance as an XML comment
   * this may change the contents of the debug data
   *
   * @return   debug data as an XML comment
   * @access   public
   */
  function &getDebugAsXMLComment() {

    // it would be nice to use a memory stream here to use
    // memory more efficiently
    while (strpos($this->debug_str, '--')) {
      $this->debug_str = str_replace('--', '- -', $this->debug_str);
    }
    return "<!--\n" . $this->debug_str . "\n-->";
  }

  /**
   * expands entities, e.g. changes '<' to '&lt;'.
   *
   * @param	string	$val	The string in which to expand entities.
   * @access	private
   */
  function expandEntities($val) {
    if ($this->charencoding) {
      $val = str_replace('&', '&amp;', $val);
      $val = str_replace("'", '&apos;', $val);
      $val = str_replace('"', '&quot;', $val);
      $val = str_replace('<', '&lt;', $val);
      $val = str_replace('>', '&gt;', $val);
    }
    return $val;
  }

  /**
   * returns error string if present
   *
   * @return   mixed error string or false
   * @access   public
   */
  function getError() {
    if ($this->error_str != '') {
      return $this->error_str;
    }
    return false;
  }

  /**
   * sets error string
   *
   * @return   boolean $string error string
   * @access   private
   */
  function setError($str) {
    $this->error_str = $str;
  }

  /**
   * detect if array is a simple array or a struct (associative array)
   *
   * @param	mixed	$val	The PHP array
   * @return	string	(arraySimple|arrayStruct)
   * @access	private
   */
  function isArraySimpleOrStruct($val) {
    $keyList = array_keys($val);
    foreach ($keyList as $keyListValue) {
      if (!is_int($keyListValue)) {
        return 'arrayStruct';
      }
    }
    return 'arraySimple';
  }

  /**
   * serializes PHP values in accordance w/ section 5. Type information is
   * not serialized if $use == 'literal'.
   *
   * @param	mixed	$val	The value to serialize
   * @param	string	$name	The name (local part) of the XML element
   * @param	string	$type	The XML schema type (local part) for the element
   * @param	string	$name_ns	The namespace for the name of the XML element
   * @param	string	$type_ns	The namespace for the type of the element
   * @param	array	$attributes	The attributes to serialize as name=>value pairs
   * @param	string	$use	The WSDL "use" (encoded|literal)
   * @return	string	The serialized element, possibly with child elements
   * @access	public
   */
  function serialize_val($val, $name = false, $type = false, $name_ns = false, $type_ns = false, $attributes = false, $use = 'encoded') {
    $this
      ->debug("in serialize_val: name={$name}, type={$type}, name_ns={$name_ns}, type_ns={$type_ns}, use={$use}");
    $this
      ->appendDebug('value=' . $this
      ->varDump($val));
    $this
      ->appendDebug('attributes=' . $this
      ->varDump($attributes));
    if (is_object($val) && get_class($val) == 'soapval') {
      return $val
        ->serialize($use);
    }

    // force valid name if necessary
    if (is_numeric($name)) {
      $name = '__numeric_' . $name;
    }
    elseif (!$name) {
      $name = 'noname';
    }

    // if name has ns, add ns prefix to name
    $xmlns = '';
    if ($name_ns) {
      $prefix = 'nu' . rand(1000, 9999);
      $name = $prefix . ':' . $name;
      $xmlns .= " xmlns:{$prefix}=\"{$name_ns}\"";
    }

    // if type is prefixed, create type prefix
    if ($type_ns != '' && $type_ns == $this->namespaces['xsd']) {

      // need to fix this. shouldn't default to xsd if no ns specified
      // w/o checking against typemap
      $type_prefix = 'xsd';
    }
    elseif ($type_ns) {
      $type_prefix = 'ns' . rand(1000, 9999);
      $xmlns .= " xmlns:{$type_prefix}=\"{$type_ns}\"";
    }

    // serialize attributes if present
    $atts = '';
    if ($attributes) {
      foreach ($attributes as $k => $v) {
        $atts .= " {$k}=\"" . $this
          ->expandEntities($v) . '"';
      }
    }

    // serialize null value
    if (is_null($val)) {
      if ($use == 'literal') {

        // TODO: depends on minOccurs
        return "<{$name}{$xmlns} {$atts}/>";
      }
      else {
        if (isset($type) && isset($type_prefix)) {
          $type_str = " xsi:type=\"{$type_prefix}:{$type}\"";
        }
        else {
          $type_str = '';
        }
        return "<{$name}{$xmlns}{$type_str} {$atts} xsi:nil=\"true\"/>";
      }
    }

    // serialize if an xsd built-in primitive type
    if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
      if (is_bool($val)) {
        if ($type == 'boolean') {
          $val = $val ? 'true' : 'false';
        }
        elseif (!$val) {
          $val = 0;
        }
      }
      else {
        if (is_string($val)) {
          $val = $this
            ->expandEntities($val);
        }
      }
      if ($use == 'literal') {
        return "<{$name}{$xmlns} {$atts}>{$val}</{$name}>";
      }
      else {
        return "<{$name}{$xmlns} {$atts} xsi:type=\"xsd:{$type}\">{$val}</{$name}>";
      }
    }

    // detect type and serialize
    $xml = '';
    switch (true) {
      case is_bool($val) || $type == 'boolean':
        if ($type == 'boolean') {
          $val = $val ? 'true' : 'false';
        }
        elseif (!$val) {
          $val = 0;
        }
        if ($use == 'literal') {
          $xml .= "<{$name}{$xmlns} {$atts}>{$val}</{$name}>";
        }
        else {
          $xml .= "<{$name}{$xmlns} xsi:type=\"xsd:boolean\"{$atts}>{$val}</{$name}>";
        }
        break;
      case is_int($val) || is_long($val) || $type == 'int':
        if ($use == 'literal') {
          $xml .= "<{$name}{$xmlns} {$atts}>{$val}</{$name}>";
        }
        else {
          $xml .= "<{$name}{$xmlns} xsi:type=\"xsd:int\"{$atts}>{$val}</{$name}>";
        }
        break;
      case is_float($val) || is_double($val) || $type == 'float':
        if ($use == 'literal') {
          $xml .= "<{$name}{$xmlns} {$atts}>{$val}</{$name}>";
        }
        else {
          $xml .= "<{$name}{$xmlns} xsi:type=\"xsd:float\"{$atts}>{$val}</{$name}>";
        }
        break;
      case is_string($val) || $type == 'string':
        $val = $this
          ->expandEntities($val);
        if ($use == 'literal') {
          $xml .= "<{$name}{$xmlns} {$atts}>{$val}</{$name}>";
        }
        else {
          $xml .= "<{$name}{$xmlns} xsi:type=\"xsd:string\"{$atts}>{$val}</{$name}>";
        }
        break;
      case is_object($val):
        if (!$name) {
          $name = get_class($val);
          $this
            ->debug("In serialize_val, used class name {$name} as element name");
        }
        else {
          $this
            ->debug("In serialize_val, do not override name {$name} for element name for class " . get_class($val));
        }
        foreach (get_object_vars($val) as $k => $v) {
          $pXml = isset($pXml) ? $pXml . $this
            ->serialize_val($v, $k, false, false, false, false, $use) : $this
            ->serialize_val($v, $k, false, false, false, false, $use);
        }
        $xml .= '<' . $name . '>' . $pXml . '</' . $name . '>';
        break;
        break;
      case is_array($val) || $type:

        // detect if struct or array
        $valueType = $this
          ->isArraySimpleOrStruct($val);
        if ($valueType == 'arraySimple' || ereg('^ArrayOf', $type)) {
          $i = 0;
          if (is_array($val) && count($val) > 0) {
            foreach ($val as $v) {
              if (is_object($v) && get_class($v) == 'soapval') {
                $tt_ns = $v->type_ns;
                $tt = $v->type;
              }
              elseif (is_array($v)) {
                $tt = $this
                  ->isArraySimpleOrStruct($v);
              }
              else {
                $tt = gettype($v);
              }
              $array_types[$tt] = 1;

              // TODO: for literal, the name should be $name
              $xml .= $this
                ->serialize_val($v, 'item', false, false, false, false, $use);
              ++$i;
            }
            if (count($array_types) > 1) {
              $array_typename = 'xsd:anyType';
            }
            elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
              if ($tt == 'integer') {
                $tt = 'int';
              }
              $array_typename = 'xsd:' . $tt;
            }
            elseif (isset($tt) && $tt == 'arraySimple') {
              $array_typename = 'SOAP-ENC:Array';
            }
            elseif (isset($tt) && $tt == 'arrayStruct') {
              $array_typename = 'unnamed_struct_use_soapval';
            }
            else {

              // if type is prefixed, create type prefix
              if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']) {
                $array_typename = 'xsd:' . $tt;
              }
              elseif ($tt_ns) {
                $tt_prefix = 'ns' . rand(1000, 9999);
                $array_typename = "{$tt_prefix}:{$tt}";
                $xmlns .= " xmlns:{$tt_prefix}=\"{$tt_ns}\"";
              }
              else {
                $array_typename = $tt;
              }
            }
            $array_type = $i;
            if ($use == 'literal') {
              $type_str = '';
            }
            else {
              if (isset($type) && isset($type_prefix)) {
                $type_str = " xsi:type=\"{$type_prefix}:{$type}\"";
              }
              else {
                $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"" . $array_typename . "[{$array_type}]\"";
              }
            }

            // empty array
          }
          else {
            if ($use == 'literal') {
              $type_str = '';
            }
            else {
              if (isset($type) && isset($type_prefix)) {
                $type_str = " xsi:type=\"{$type_prefix}:{$type}\"";
              }
              else {
                $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
              }
            }
          }

          // TODO: for array in literal, there is no wrapper here
          $xml = "<{$name}{$xmlns}{$type_str}{$atts}>" . $xml . "</{$name}>";
        }
        else {

          // got a struct
          if (isset($type) && isset($type_prefix)) {
            $type_str = " xsi:type=\"{$type_prefix}:{$type}\"";
          }
          else {
            $type_str = '';
          }
          if ($use == 'literal') {
            $xml .= "<{$name}{$xmlns} {$atts}>";
          }
          else {
            $xml .= "<{$name}{$xmlns}{$type_str}{$atts}>";
          }
          foreach ($val as $k => $v) {

            // Apache Map
            if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
              $xml .= '<item>';
              $xml .= $this
                ->serialize_val($k, 'key', false, false, false, false, $use);
              $xml .= $this
                ->serialize_val($v, 'value', false, false, false, false, $use);
              $xml .= '</item>';
            }
            else {
              $xml .= $this
                ->serialize_val($v, $k, false, false, false, false, $use);
            }
          }
          $xml .= "</{$name}>";
        }
        break;
      default:
        $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
        break;
    }
    return $xml;
  }

  /**
   * serializes a message
   *
   * @param string $body the XML of the SOAP body
   * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
   * @param array $namespaces optional the namespaces used in generating the body and headers
   * @param string $style optional (rpc|document)
   * @param string $use optional (encoded|literal)
   * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
   * @return string the message
   * @access public
   */
  function serializeEnvelope($body, $headers = false, $namespaces = [], $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/') {

    // TODO: add an option to automatically run utf8_encode on $body and $headers
    // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
    // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
    $this
      ->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style={$style} use={$use} encodingStyle={$encodingStyle}");
    $this
      ->debug("headers:");
    $this
      ->appendDebug($this
      ->varDump($headers));
    $this
      ->debug("namespaces:");
    $this
      ->appendDebug($this
      ->varDump($namespaces));

    // serialize namespaces
    $ns_string = '';
    foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
      $ns_string .= " xmlns:{$k}=\"{$v}\"";
    }
    if ($encodingStyle) {
      $ns_string = " SOAP-ENV:encodingStyle=\"{$encodingStyle}\"{$ns_string}";
    }

    // serialize headers
    if ($headers) {
      if (is_array($headers)) {
        $xml = '';
        foreach ($headers as $header) {
          $xml .= $this
            ->serialize_val($header, false, false, false, false, false, $use);
        }
        $headers = $xml;
        $this
          ->debug("In serializeEnvelope, serialzied array of headers to {$headers}");
      }
      $headers = "<SOAP-ENV:Header>" . $headers . "</SOAP-ENV:Header>";
    }

    // serialize envelope
    return '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . ">" . '<SOAP-ENV:Envelope' . $ns_string . ">" . $headers . "<SOAP-ENV:Body>" . $body . "</SOAP-ENV:Body>" . "</SOAP-ENV:Envelope>";
  }

  /**
   * formats a string to be inserted into an HTML stream
   *
   * @param string $str The string to format
   * @return string The formatted string
   * @access public
   * @deprecated
   */
  function formatDump($str) {
    $str = htmlspecialchars($str);
    return nl2br($str);
  }

  /**
   * contracts (changes namespace to prefix) a qualified name
   *
   * @param    string $qname qname
   * @return	string contracted qname
   * @access   private
   */
  function contractQname($qname) {

    // get element namespace

    //$this->xdebug("Contract $qname");
    if (strrpos($qname, ':')) {

      // get unqualified name
      $name = substr($qname, strrpos($qname, ':') + 1);

      // get ns
      $ns = substr($qname, 0, strrpos($qname, ':'));
      $p = $this
        ->getPrefixFromNamespace($ns);
      if ($p) {
        return $p . ':' . $name;
      }
      return $qname;
    }
    else {
      return $qname;
    }
  }

  /**
   * expands (changes prefix to namespace) a qualified name
   *
   * @param    string $string qname
   * @return	string expanded qname
   * @access   private
   */
  function expandQname($qname) {

    // get element prefix
    if (strpos($qname, ':') && !ereg('^http://', $qname)) {

      // get unqualified name
      $name = substr(strstr($qname, ':'), 1);

      // get ns prefix
      $prefix = substr($qname, 0, strpos($qname, ':'));
      if (isset($this->namespaces[$prefix])) {
        return $this->namespaces[$prefix] . ':' . $name;
      }
      else {
        return $qname;
      }
    }
    else {
      return $qname;
    }
  }

  /**
   * returns the local part of a prefixed string
   * returns the original string, if not prefixed
   *
   * @param string $str The prefixed string
   * @return string The local part
   * @access public
   */
  function getLocalPart($str) {
    if ($sstr = strrchr($str, ':')) {

      // get unqualified name
      return substr($sstr, 1);
    }
    else {
      return $str;
    }
  }

  /**
   * returns the prefix part of a prefixed string
   * returns false, if not prefixed
   *
   * @param string $str The prefixed string
   * @return mixed The prefix or false if there is no prefix
   * @access public
   */
  function getPrefix($str) {
    if ($pos = strrpos($str, ':')) {

      // get prefix
      return substr($str, 0, $pos);
    }
    return false;
  }

  /**
   * pass it a prefix, it returns a namespace
   *
   * @param string $prefix The prefix
   * @return mixed The namespace, false if no namespace has the specified prefix
   * @access public
   */
  function getNamespaceFromPrefix($prefix) {
    if (isset($this->namespaces[$prefix])) {
      return $this->namespaces[$prefix];
    }

    //$this->setError("No namespace registered for prefix '$prefix'");
    return false;
  }

  /**
   * returns the prefix for a given namespace (or prefix)
   * or false if no prefixes registered for the given namespace
   *
   * @param string $ns The namespace
   * @return mixed The prefix, false if the namespace has no prefixes
   * @access public
   */
  function getPrefixFromNamespace($ns) {
    foreach ($this->namespaces as $p => $n) {
      if ($ns == $n || $ns == $p) {
        $this->usedNamespaces[$p] = $n;
        return $p;
      }
    }
    return false;
  }

  /**
   * returns the time in ODBC canonical form with microseconds
   *
   * @return string The time in ODBC canonical form with microseconds
   * @access public
   */
  function getmicrotime() {
    if (function_exists('gettimeofday')) {
      $tod = gettimeofday();
      $sec = $tod['sec'];
      $usec = $tod['usec'];
    }
    else {
      $sec = time();
      $usec = 0;
    }
    return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
  }

  /**
   * Returns a string with the output of var_dump
   *
   * @param mixed $data The variable to var_dump
   * @return string The output of var_dump
   * @access public
   */
  function varDump($data) {
    ob_start();
    var_dump($data);
    $ret_val = ob_get_contents();
    ob_end_clean();
    return $ret_val;
  }

}

// XML Schema Datatype Helper Functions

//xsd:dateTime helpers

/**
* convert unix timestamp to ISO 8601 compliant date string
*
* @param    string $timestamp Unix time stamp
* @access   public
*/
function timestamp_to_iso8601($timestamp, $utc = true) {
  $datestr = date('Y-m-d\\TH:i:sO', $timestamp);
  if ($utc) {
    $eregStr = '([0-9]{4})-' . '([0-9]{2})-' . '([0-9]{2})' . 'T' . '([0-9]{2}):' . '([0-9]{2}):' . '([0-9]{2})(\\.[0-9]*)?' . '(Z|[+\\-][0-9]{2}:?[0-9]{2})?';

    // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
    if (ereg($eregStr, $datestr, $regs)) {
      return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
    }
    return false;
  }
  else {
    return $datestr;
  }
}

/**
* convert ISO 8601 compliant date string to unix timestamp
*
* @param    string $datestr ISO 8601 compliant date string
* @access   public
*/
function iso8601_to_timestamp($datestr) {
  $eregStr = '([0-9]{4})-' . '([0-9]{2})-' . '([0-9]{2})' . 'T' . '([0-9]{2}):' . '([0-9]{2}):' . '([0-9]{2})(\\.[0-9]+)?' . '(Z|[+\\-][0-9]{2}:?[0-9]{2})?';

  // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  if (ereg($eregStr, $datestr, $regs)) {

    // not utc
    if ($regs[8] != 'Z') {
      $op = substr($regs[8], 0, 1);
      $h = substr($regs[8], 1, 2);
      $m = substr($regs[8], strlen($regs[8]) - 2, 2);
      if ($op == '-') {
        $regs[4] = $regs[4] + $h;
        $regs[5] = $regs[5] + $m;
      }
      elseif ($op == '+') {
        $regs[4] = $regs[4] - $h;
        $regs[5] = $regs[5] - $m;
      }
    }
    return strtotime("{$regs[1]}-{$regs[2]}-{$regs[3]} {$regs[4]}:{$regs[5]}:{$regs[6]}Z");
  }
  else {
    return false;
  }
}

/**
* sleeps some number of microseconds
*
* @param    string $usec the number of microseconds to sleep
* @access   public
* @deprecated
*/
function usleepWindows($usec) {
  $start = gettimeofday();
  do {
    $stop = gettimeofday();
    $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + $stop['usec'] - $start['usec'];
  } while ($timePassed < $usec);
}

/**
* Contains information for a SOAP fault.
* Mainly used for returning faults from deployed functions
* in a server instance.
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access public
*/
class soap_fault extends nusoap_base {

  /**
   * The fault code (client|server)
   * @var string
   * @access private
   */
  var $faultcode;

  /**
   * The fault actor
   * @var string
   * @access private
   */
  var $faultactor;

  /**
   * The fault string, a description of the fault
   * @var string
   * @access private
   */
  var $faultstring;

  /**
   * The fault detail, typically a string or array of string
   * @var mixed
   * @access private
   */
  var $faultdetail;

  /**
   * constructor
   *
   * @param string $faultcode (client | server)
   * @param string $faultactor only used when msg routed between multiple actors
   * @param string $faultstring human readable error message
   * @param mixed $faultdetail detail, typically a string or array of string
   */
  function soap_fault($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '') {
    parent::nusoap_base();
    $this->faultcode = $faultcode;
    $this->faultactor = $faultactor;
    $this->faultstring = $faultstring;
    $this->faultdetail = $faultdetail;
  }

  /**
   * serialize a fault
   *
   * @return	string	The serialization of the fault instance.
   * @access   public
   */
  function serialize() {
    $ns_string = '';
    foreach ($this->namespaces as $k => $v) {
      $ns_string .= "\n  xmlns:{$k}=\"{$v}\"";
    }
    $return_msg = '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?>' . '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string . ">\n" . '<SOAP-ENV:Body>' . '<SOAP-ENV:Fault>' . $this
      ->serialize_val($this->faultcode, 'faultcode') . $this
      ->serialize_val($this->faultactor, 'faultactor') . $this
      ->serialize_val($this->faultstring, 'faultstring') . $this
      ->serialize_val($this->faultdetail, 'detail') . '</SOAP-ENV:Fault>' . '</SOAP-ENV:Body>' . '</SOAP-ENV:Envelope>';
    return $return_msg;
  }

}

/**
* parses an XML Schema, allows access to it's data, other utility methods
* no validation... yet.
* very experimental and limited. As is discussed on XML-DEV, I'm one of the people
* that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
* tutorials I refer to :)
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class XMLSchema extends nusoap_base {

  // files
  var $schema = '';
  var $xml = '';

  // namespaces
  var $enclosingNamespaces;

  // schema info
  var $schemaInfo = [];
  var $schemaTargetNamespace = '';

  // types, elements, attributes defined by the schema
  var $attributes = [];
  var $complexTypes = [];
  var $complexTypeStack = [];
  var $currentComplexType = null;
  var $elements = [];
  var $elementStack = [];
  var $currentElement = null;
  var $simpleTypes = [];
  var $simpleTypeStack = [];
  var $currentSimpleType = null;

  // imports
  var $imports = [];

  // parser vars
  var $parser;
  var $position = 0;
  var $depth = 0;
  var $depth_array = [];
  var $message = [];
  var $defaultNamespace = [];

  /**
   * constructor
   *
   * @param    string $schema schema document URI
   * @param    string $xml xml document URI
   * @param	string $namespaces namespaces defined in enclosing XML
   * @access   public
   */
  function XMLSchema($schema = '', $xml = '', $namespaces = []) {
    parent::nusoap_base();
    $this
      ->debug('xmlschema class instantiated, inside constructor');

    // files
    $this->schema = $schema;
    $this->xml = $xml;

    // namespaces
    $this->enclosingNamespaces = $namespaces;
    $this->namespaces = array_merge($this->namespaces, $namespaces);

    // parse schema file
    if ($schema != '') {
      $this
        ->debug('initial schema file: ' . $schema);
      $this
        ->parseFile($schema, 'schema');
    }

    // parse xml file
    if ($xml != '') {
      $this
        ->debug('initial xml file: ' . $xml);
      $this
        ->parseFile($xml, 'xml');
    }
  }

  /**
   * parse an XML file
   *
   * @param string $xml, path/URL to XML file
   * @param string $type, (schema | xml)
   * @return boolean
   * @access public
   */
  function parseFile($xml, $type) {

    // parse xml file
    if ($xml != "") {
      $xmlStr = @join("", @file($xml));
      if ($xmlStr == "") {
        $msg = 'Error reading XML from ' . $xml;
        $this
          ->setError($msg);
        $this
          ->debug($msg);
        return false;
      }
      else {
        $this
          ->debug("parsing {$xml}");
        $this
          ->parseString($xmlStr, $type);
        $this
          ->debug("done parsing {$xml}");
        return true;
      }
    }
    return false;
  }

  /**
   * parse an XML string
   *
   * @param    string $xml path or URL
   * @param string $type, (schema|xml)
   * @access   private
   */
  function parseString($xml, $type) {

    // parse xml string
    if ($xml != "") {

      // Create an XML parser.
      $this->parser = xml_parser_create();

      // Set the options for parsing the XML data.
      xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);

      // Set the object for the parser.
      xml_set_object($this->parser, $this);

      // Set the element handlers for the parser.
      if ($type == "schema") {
        xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
        xml_set_character_data_handler($this->parser, 'schemaCharacterData');
      }
      elseif ($type == "xml") {
        xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
        xml_set_character_data_handler($this->parser, 'xmlCharacterData');
      }

      // Parse the XML file.
      if (!xml_parse($this->parser, $xml, true)) {

        // Display an error message.
        $errstr = sprintf('XML error parsing XML schema on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
        $this
          ->debug($errstr);
        $this
          ->debug("XML payload:\n" . $xml);
        $this
          ->setError($errstr);
      }
      xml_parser_free($this->parser);
    }
    else {
      $this
        ->debug('no xml passed to parseString()!!');
      $this
        ->setError('no xml passed to parseString()!!');
    }
  }

  /**
   * start-element handler
   *
   * @param    string $parser XML parser object
   * @param    string $name element name
   * @param    string $attrs associative array of attributes
   * @access   private
   */
  function schemaStartElement($parser, $name, $attrs) {

    // position in the total number of elements, starting from 0
    $pos = $this->position++;
    $depth = $this->depth++;

    // set self as current value for this depth
    $this->depth_array[$depth] = $pos;
    $this->message[$pos] = array(
      'cdata' => '',
    );
    if ($depth > 0) {
      $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
    }
    else {
      $this->defaultNamespace[$pos] = false;
    }

    // get element prefix
    if ($prefix = $this
      ->getPrefix($name)) {

      // get unqualified name
      $name = $this
        ->getLocalPart($name);
    }
    else {
      $prefix = '';
    }

    // loop thru attributes, expanding, and registering namespace declarations
    if (count($attrs) > 0) {
      foreach ($attrs as $k => $v) {

        // if ns declarations, add to class level array of valid namespaces
        if (ereg("^xmlns", $k)) {

          //$this->xdebug("$k: $v");

          //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
          if ($ns_prefix = substr(strrchr($k, ':'), 1)) {

            //$this->xdebug("Add namespace[$ns_prefix] = $v");
            $this->namespaces[$ns_prefix] = $v;
          }
          else {
            $this->defaultNamespace[$pos] = $v;
            if (!$this
              ->getPrefixFromNamespace($v)) {
              $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
            }
          }
          if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
            $this->XMLSchemaVersion = $v;
            $this->namespaces['xsi'] = $v . '-instance';
          }
        }
      }
      foreach ($attrs as $k => $v) {

        // expand each attribute
        $k = strpos($k, ':') ? $this
          ->expandQname($k) : $k;
        $v = strpos($v, ':') ? $this
          ->expandQname($v) : $v;
        $eAttrs[$k] = $v;
      }
      $attrs = $eAttrs;
    }
    else {
      $attrs = array();
    }

    // find status, register data
    switch ($name) {
      case 'all':

      // (optional) compositor content for a complexType
      case 'choice':
      case 'group':
      case 'sequence':

        //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
        $this->complexTypes[$this->currentComplexType]['compositor'] = $name;

        //if($name == 'all' || $name == 'sequence'){

        //	$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';

        //}
        break;
      case 'attribute':

        // complexType attribute

        //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
        $this
          ->xdebug("parsing attribute:");
        $this
          ->appendDebug($this
          ->varDump($attrs));
        if (!isset($attrs['form'])) {
          $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
        }
        if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
          $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
          if (!strpos($v, ':')) {

            // no namespace in arrayType attribute value...
            if ($this->defaultNamespace[$pos]) {

              // ...so use the default
              $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
            }
          }
        }
        if (isset($attrs['name'])) {
          $this->attributes[$attrs['name']] = $attrs;
          $aname = $attrs['name'];
        }
        elseif (isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType') {
          if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
            $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
          }
          else {
            $aname = '';
          }
        }
        elseif (isset($attrs['ref'])) {
          $aname = $attrs['ref'];
          $this->attributes[$attrs['ref']] = $attrs;
        }
        if ($this->currentComplexType) {

          // This should *always* be
          $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
        }

        // arrayType attribute
        if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this
          ->getLocalPart($aname) == 'arrayType') {
          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
          $prefix = $this
            ->getPrefix($aname);
          if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
            $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
          }
          else {
            $v = '';
          }
          if (strpos($v, '[,]')) {
            $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
          }
          $v = substr($v, 0, strpos($v, '['));

          // clip the []
          if (!strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
            $v = $this->XMLSchemaVersion . ':' . $v;
          }
          $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
        }
        break;
      case 'complexContent':

        // (optional) content for a complexType
        break;
      case 'complexType':
        array_push($this->complexTypeStack, $this->currentComplexType);
        if (isset($attrs['name'])) {
          $this
            ->xdebug('processing named complexType ' . $attrs['name']);

          //$this->currentElement = false;
          $this->currentComplexType = $attrs['name'];
          $this->complexTypes[$this->currentComplexType] = $attrs;
          $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';

          // This is for constructs like
          //           <complexType name="ListOfString" base="soap:Array">
          //                <sequence>
          //                    <element name="string" type="xsd:string"
          //                        minOccurs="0" maxOccurs="unbounded" />
          //                </sequence>
          //            </complexType>
          if (isset($attrs['base']) && ereg(':Array$', $attrs['base'])) {
            $this
              ->xdebug('complexType is unusual array');
            $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
          }
          else {
            $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
          }
        }
        else {
          $this
            ->xdebug('processing unnamed complexType for element ' . $this->currentElement);
          $this->currentComplexType = $this->currentElement . '_ContainedType';

          //$this->currentElement = false;
          $this->complexTypes[$this->currentComplexType] = $attrs;
          $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';

          // This is for constructs like
          //           <complexType name="ListOfString" base="soap:Array">
          //                <sequence>
          //                    <element name="string" type="xsd:string"
          //                        minOccurs="0" maxOccurs="unbounded" />
          //                </sequence>
          //            </complexType>
          if (isset($attrs['base']) && ereg(':Array$', $attrs['base'])) {
            $this
              ->xdebug('complexType is unusual array');
            $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
          }
          else {
            $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
          }
        }
        break;
      case 'element':
        array_push($this->elementStack, $this->currentElement);

        // elements defined as part of a complex type should
        // not really be added to $this->elements, but for some
        // reason, they are
        if (!isset($attrs['form'])) {
          $attrs['form'] = $this->schemaInfo['elementFormDefault'];
        }
        if (isset($attrs['type'])) {
          $this
            ->xdebug("processing typed element " . $attrs['name'] . " of type " . $attrs['type']);
          if (!$this
            ->getPrefix($attrs['type'])) {
            if ($this->defaultNamespace[$pos]) {
              $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
              $this
                ->xdebug('used default namespace to make type ' . $attrs['type']);
            }
          }

          // This is for constructs like
          //           <complexType name="ListOfString" base="soap:Array">
          //                <sequence>
          //                    <element name="string" type="xsd:string"
          //                        minOccurs="0" maxOccurs="unbounded" />
          //                </sequence>
          //            </complexType>
          if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
            $this
              ->xdebug('arrayType for unusual array is ' . $attrs['type']);
            $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
          }
          $this->currentElement = $attrs['name'];
          $this->elements[$attrs['name']] = $attrs;
          $this->elements[$attrs['name']]['typeClass'] = 'element';
          $ename = $attrs['name'];
        }
        elseif (isset($attrs['ref'])) {
          $this
            ->xdebug("processing element as ref to " . $attrs['ref']);
          $this->currentElement = "ref to " . $attrs['ref'];
          $ename = $this
            ->getLocalPart($attrs['ref']);
        }
        else {
          $this
            ->xdebug("processing untyped element " . $attrs['name']);
          $this->currentElement = $attrs['name'];
          $this->elements[$attrs['name']] = $attrs;
          $this->elements[$attrs['name']]['typeClass'] = 'element';
          $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
          $this->elements[$attrs['name']]['type'] = $attrs['type'];
          $ename = $attrs['name'];
        }
        if (isset($ename) && $this->currentComplexType) {
          $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
        }
        break;
      case 'enumeration':

        //	restriction value list member
        $this
          ->xdebug('enumeration ' . $attrs['value']);
        if ($this->currentSimpleType) {
          $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
        }
        elseif ($this->currentComplexType) {
          $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
        }
        break;
      case 'extension':

        // simpleContent or complexContent type extension
        $this
          ->xdebug('extension ' . $attrs['base']);
        if ($this->currentComplexType) {
          $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
        }
        break;
      case 'import':
        if (isset($attrs['schemaLocation'])) {

          //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
          $this->imports[$attrs['namespace']][] = array(
            'location' => $attrs['schemaLocation'],
            'loaded' => false,
          );
        }
        else {

          //$this->xdebug('import namespace ' . $attrs['namespace']);
          $this->imports[$attrs['namespace']][] = array(
            'location' => '',
            'loaded' => true,
          );
          if (!$this
            ->getPrefixFromNamespace($attrs['namespace'])) {
            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
          }
        }
        break;
      case 'list':

        // simpleType value list
        break;
      case 'restriction':

        // simpleType, simpleContent or complexContent value restriction
        $this
          ->xdebug('restriction ' . $attrs['base']);
        if ($this->currentSimpleType) {
          $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
        }
        elseif ($this->currentComplexType) {
          $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
          if (strstr($attrs['base'], ':') == ':Array') {
            $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
          }
        }
        break;
      case 'schema':
        $this->schemaInfo = $attrs;
        $this->schemaInfo['schemaVersion'] = $this
          ->getNamespaceFromPrefix($prefix);
        if (isset($attrs['targetNamespace'])) {
          $this->schemaTargetNamespace = $attrs['targetNamespace'];
        }
        if (!isset($attrs['elementFormDefault'])) {
          $this->schemaInfo['elementFormDefault'] = 'unqualified';
        }
        if (!isset($attrs['attributeFormDefault'])) {
          $this->schemaInfo['attributeFormDefault'] = 'unqualified';
        }
        break;
      case 'simpleContent':

        // (optional) content for a complexType
        break;
      case 'simpleType':
        array_push($this->simpleTypeStack, $this->currentSimpleType);
        if (isset($attrs['name'])) {
          $this
            ->xdebug("processing simpleType for name " . $attrs['name']);
          $this->currentSimpleType = $attrs['name'];
          $this->simpleTypes[$attrs['name']] = $attrs;
          $this->simpleTypes[$attrs['name']]['typeClass'] = 'simpleType';
          $this->simpleTypes[$attrs['name']]['phpType'] = 'scalar';
        }
        else {
          $this
            ->xdebug('processing unnamed simpleType for element ' . $this->currentElement);
          $this->currentSimpleType = $this->currentElement . '_ContainedType';

          //$this->currentElement = false;
          $this->simpleTypes[$this->currentSimpleType] = $attrs;
          $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
        }
        break;
      case 'union':

        // simpleType type list
        break;
      default:
    }
  }

  /**
   * end-element handler
   *
   * @param    string $parser XML parser object
   * @param    string $name element name
   * @access   private
   */
  function schemaEndElement($parser, $name) {

    // bring depth down a notch
    $this->depth--;

    // position of current element is equal to the last value left in depth_array for my depth
    if (isset($this->depth_array[$this->depth])) {
      $pos = $this->depth_array[$this->depth];
    }

    // get element prefix
    if ($prefix = $this
      ->getPrefix($name)) {

      // get unqualified name
      $name = $this
        ->getLocalPart($name);
    }
    else {
      $prefix = '';
    }

    // move on...
    if ($name == 'complexType') {
      $this
        ->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
      $this->currentComplexType = array_pop($this->complexTypeStack);

      //$this->currentElement = false;
    }
    if ($name == 'element') {
      $this
        ->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
      $this->currentElement = array_pop($this->elementStack);
    }
    if ($name == 'simpleType') {
      $this
        ->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
      $this->currentSimpleType = array_pop($this->simpleTypeStack);
    }
  }

  /**
   * element content handler
   *
   * @param    string $parser XML parser object
   * @param    string $data element content
   * @access   private
   */
  function schemaCharacterData($parser, $data) {
    $pos = $this->depth_array[$this->depth - 1];
    $this->message[$pos]['cdata'] .= $data;
  }

  /**
   * serialize the schema
   *
   * @access   public
   */
  function serializeSchema() {
    $schemaPrefix = $this
      ->getPrefixFromNamespace($this->XMLSchemaVersion);
    $xml = '';

    // imports
    if (sizeof($this->imports) > 0) {
      foreach ($this->imports as $ns => $list) {
        foreach ($list as $ii) {
          if ($ii['location'] != '') {
            $xml .= " <{$schemaPrefix}:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
          }
          else {
            $xml .= " <{$schemaPrefix}:import namespace=\"" . $ns . "\" />\n";
          }
        }
      }
    }

    // complex types
    foreach ($this->complexTypes as $typeName => $attrs) {
      $contentStr = '';

      // serialize child elements
      if (isset($attrs['elements']) && count($attrs['elements']) > 0) {
        foreach ($attrs['elements'] as $element => $eParts) {
          if (isset($eParts['ref'])) {
            $contentStr .= "   <{$schemaPrefix}:element ref=\"{$element}\"/>\n";
          }
          else {
            $contentStr .= "   <{$schemaPrefix}:element name=\"{$element}\" type=\"" . $this
              ->contractQName($eParts['type']) . "\"";
            foreach ($eParts as $aName => $aValue) {

              // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
              if ($aName != 'name' && $aName != 'type') {
                $contentStr .= " {$aName}=\"{$aValue}\"";
              }
            }
            $contentStr .= "/>\n";
          }
        }

        // compositor wraps elements
        if (isset($attrs['compositor']) && $attrs['compositor'] != '') {
          $contentStr = "  <{$schemaPrefix}:{$attrs['compositor']}>\n" . $contentStr . "  </{$schemaPrefix}:{$attrs['compositor']}>\n";
        }
      }

      // attributes
      if (isset($attrs['attrs']) && count($attrs['attrs']) >= 1) {
        foreach ($attrs['attrs'] as $attr => $aParts) {
          $contentStr .= "    <{$schemaPrefix}:attribute";
          foreach ($aParts as $a => $v) {
            if ($a == 'ref' || $a == 'type') {
              $contentStr .= " {$a}=\"" . $this
                ->contractQName($v) . '"';
            }
            elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
              $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
              $contentStr .= ' wsdl:arrayType="' . $this
                ->contractQName($v) . '"';
            }
            else {
              $contentStr .= " {$a}=\"{$v}\"";
            }
          }
          $contentStr .= "/>\n";
        }
      }

      // if restriction
      if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != '') {
        $contentStr = "   <{$schemaPrefix}:restriction base=\"" . $this
          ->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . "   </{$schemaPrefix}:restriction>\n";

        // complex or simple content
        if (isset($attrs['elements']) && count($attrs['elements']) > 0 || isset($attrs['attrs']) && count($attrs['attrs']) > 0) {
          $contentStr = "  <{$schemaPrefix}:complexContent>\n" . $contentStr . "  </{$schemaPrefix}:complexContent>\n";
        }
      }

      // finalize complex type
      if ($contentStr != '') {
        $contentStr = " <{$schemaPrefix}:complexType name=\"{$typeName}\">\n" . $contentStr . " </{$schemaPrefix}:complexType>\n";
      }
      else {
        $contentStr = " <{$schemaPrefix}:complexType name=\"{$typeName}\"/>\n";
      }
      $xml .= $contentStr;
    }

    // simple types
    if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
      foreach ($this->simpleTypes as $typeName => $eParts) {
        $xml .= " <{$schemaPrefix}:simpleType name=\"{$typeName}\">\n  <{$schemaPrefix}:restriction base=\"" . $this
          ->contractQName($eParts['type']) . "\"/>\n";
        if (isset($eParts['enumeration'])) {
          foreach ($eParts['enumeration'] as $e) {
            $xml .= "  <{$schemaPrefix}:enumeration value=\"{$e}\"/>\n";
          }
        }
        $xml .= " </{$schemaPrefix}:simpleType>";
      }
    }

    // elements
    if (isset($this->elements) && count($this->elements) > 0) {
      foreach ($this->elements as $element => $eParts) {
        $xml .= " <{$schemaPrefix}:element name=\"{$element}\" type=\"" . $this
          ->contractQName($eParts['type']) . "\"/>\n";
      }
    }

    // attributes
    if (isset($this->attributes) && count($this->attributes) > 0) {
      foreach ($this->attributes as $attr => $aParts) {
        $xml .= " <{$schemaPrefix}:attribute name=\"{$attr}\" type=\"" . $this
          ->contractQName($aParts['type']) . "\"\n/>";
      }
    }

    // finish 'er up
    $el = "<{$schemaPrefix}:schema targetNamespace=\"{$this->schemaTargetNamespace}\"\n";
    foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
      $el .= " xmlns:{$nsp}=\"{$ns}\"\n";
    }
    $xml = $el . ">\n" . $xml . "</{$schemaPrefix}:schema>\n";
    return $xml;
  }

  /**
   * adds debug data to the clas level debug string
   *
   * @param    string $string debug data
   * @access   private
   */
  function xdebug($string) {
    $this
      ->debug('<' . $this->schemaTargetNamespace . '> ' . $string);
  }

  /**
   * get the PHP type of a user defined type in the schema
   * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
   * returns false if no type exists, or not w/ the given namespace
   * else returns a string that is either a native php type, or 'struct'
   *
   * @param string $type, name of defined type
   * @param string $ns, namespace of type
   * @return mixed
   * @access public
   * @deprecated
   */
  function getPHPType($type, $ns) {
    if (isset($this->typemap[$ns][$type])) {

      //print "found type '$type' and ns $ns in typemap<br>";
      return $this->typemap[$ns][$type];
    }
    elseif (isset($this->complexTypes[$type])) {

      //print "getting type '$type' and ns $ns from complexTypes array<br>";
      return $this->complexTypes[$type]['phpType'];
    }
    return false;
  }

  /**
   * returns an associative array of information about a given type
   * returns false if no type exists by the given name
   *
   *	For a complexType typeDef = array(
   *	'restrictionBase' => '',
   *	'phpType' => '',
   *	'compositor' => '(sequence|all)',
   *	'elements' => array(), // refs to elements array
   *	'attrs' => array() // refs to attributes array
   *	... and so on (see addComplexType)
   *	)
   *
   *   For simpleType or element, the array has different keys.
   *
   * @param string
   * @return mixed
   * @access public
   * @see addComplexType
   * @see addSimpleType
   * @see addElement
   */
  function getTypeDef($type) {

    //$this->debug("in getTypeDef for type $type");
    if (isset($this->complexTypes[$type])) {
      $this
        ->xdebug("in getTypeDef, found complexType {$type}");
      return $this->complexTypes[$type];
    }
    elseif (isset($this->simpleTypes[$type])) {
      $this
        ->xdebug("in getTypeDef, found simpleType {$type}");
      if (!isset($this->simpleTypes[$type]['phpType'])) {

        // get info for type to tack onto the simple type
        // TODO: can this ever really apply (i.e. what is a simpleType really?)
        $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
        $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
        $etype = $this
          ->getTypeDef($uqType);
        if ($etype) {
          $this
            ->xdebug("in getTypeDef, found type for simpleType {$type}:");
          $this
            ->xdebug($this
            ->varDump($etype));
          if (isset($etype['phpType'])) {
            $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
          }
          if (isset($etype['elements'])) {
            $this->simpleTypes[$type]['elements'] = $etype['elements'];
          }
        }
      }
      return $this->simpleTypes[$type];
    }
    elseif (isset($this->elements[$type])) {
      $this
        ->xdebug("in getTypeDef, found element {$type}");
      if (!isset($this->elements[$type]['phpType'])) {

        // get info for type to tack onto the element
        $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
        $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
        $etype = $this
          ->getTypeDef($uqType);
        if ($etype) {
          $this
            ->xdebug("in getTypeDef, found type for element {$type}:");
          $this
            ->xdebug($this
            ->varDump($etype));
          if (isset($etype['phpType'])) {
            $this->elements[$type]['phpType'] = $etype['phpType'];
          }
          if (isset($etype['elements'])) {
            $this->elements[$type]['elements'] = $etype['elements'];
          }
        }
        elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
          $this
            ->xdebug("in getTypeDef, element {$type} is an XSD type");
          $this->elements[$type]['phpType'] = 'scalar';
        }
      }
      return $this->elements[$type];
    }
    elseif (isset($this->attributes[$type])) {
      $this
        ->xdebug("in getTypeDef, found attribute {$type}");
      return $this->attributes[$type];
    }
    elseif (ereg('_ContainedType$', $type)) {
      $this
        ->xdebug("in getTypeDef, have an untyped element {$type}");
      $typeDef['typeClass'] = 'simpleType';
      $typeDef['phpType'] = 'scalar';
      $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
      return $typeDef;
    }
    $this
      ->xdebug("in getTypeDef, did not find {$type}");
    return false;
  }

  /**
   * returns a sample serialization of a given type, or false if no type by the given name
   *
   * @param string $type, name of type
   * @return mixed
   * @access public
   * @deprecated
   */
  function serializeTypeDef($type) {

    //print "in sTD() for type $type<br>";
    if ($typeDef = $this
      ->getTypeDef($type)) {
      $str .= '<' . $type;
      if (is_array($typeDef['attrs'])) {
        foreach ($attrs as $attName => $data) {
          $str .= " {$attName}=\"{type = " . $data['type'] . "}\"";
        }
      }
      $str .= " xmlns=\"" . $this->schema['targetNamespace'] . "\"";
      if (count($typeDef['elements']) > 0) {
        $str .= ">";
        foreach ($typeDef['elements'] as $element => $eData) {
          $str .= $this
            ->serializeTypeDef($element);
        }
        $str .= "</{$type}>";
      }
      elseif ($typeDef['typeClass'] == 'element') {
        $str .= "></{$type}>";
      }
      else {
        $str .= "/>";
      }
      return $str;
    }
    return false;
  }

  /**
   * returns HTML form elements that allow a user
   * to enter values for creating an instance of the given type.
   *
   * @param string $name, name for type instance
   * @param string $type, name of type
   * @return string
   * @access public
   * @deprecated
   */
  function typeToForm($name, $type) {

    // get typedef
    if ($typeDef = $this
      ->getTypeDef($type)) {

      // if struct
      if ($typeDef['phpType'] == 'struct') {
        $buffer .= '<table>';
        foreach ($typeDef['elements'] as $child => $childDef) {
          $buffer .= "\n\t\t\t\t\t<tr><td align='right'>{$childDef['name']} (type: " . $this
            ->getLocalPart($childDef['type']) . "):</td>\n\t\t\t\t\t<td><input type='text' name='parameters[" . $name . "][{$childDef['name']}]'></td></tr>";
        }
        $buffer .= '</table>';

        // if array
      }
      elseif ($typeDef['phpType'] == 'array') {
        $buffer .= '<table>';
        for ($i = 0; $i < 3; $i++) {
          $buffer .= "\n\t\t\t\t\t<tr><td align='right'>array item (type: {$typeDef['arrayType']}):</td>\n\t\t\t\t\t<td><input type='text' name='parameters[" . $name . "][]'></td></tr>";
        }
        $buffer .= '</table>';

        // if scalar
      }
      else {
        $buffer .= "<input type='text' name='parameters[{$name}]'>";
      }
    }
    else {
      $buffer .= "<input type='text' name='parameters[{$name}]'>";
    }
    return $buffer;
  }

  /**
   * adds a complex type to the schema
   *
   * example: array
   *
   * addType(
   * 	'ArrayOfstring',
   * 	'complexType',
   * 	'array',
   * 	'',
   * 	'SOAP-ENC:Array',
   * 	array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
   * 	'xsd:string'
   * );
   *
   * example: PHP associative array ( SOAP Struct )
   *
   * addType(
   * 	'SOAPStruct',
   * 	'complexType',
   * 	'struct',
   * 	'all',
   * 	array('myVar'=> array('name'=>'myVar','type'=>'string')
   * );
   *
   * @param name
   * @param typeClass (complexType|simpleType|attribute)
   * @param phpType: currently supported are array and struct (php assoc array)
   * @param compositor (all|sequence|choice)
   * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   * @param elements = array ( name = array(name=>'',type=>'') )
   * @param attrs = array(
   * 	array(
   *		'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
   *		"http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
   * 	)
   * )
   * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
   * @access public
   * @see getTypeDef
   */
  function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = [], $attrs = [], $arrayType = '') {
    $this->complexTypes[$name] = array(
      'name' => $name,
      'typeClass' => $typeClass,
      'phpType' => $phpType,
      'compositor' => $compositor,
      'restrictionBase' => $restrictionBase,
      'elements' => $elements,
      'attrs' => $attrs,
      'arrayType' => $arrayType,
    );
    $this
      ->xdebug("addComplexType {$name}:");
    $this
      ->appendDebug($this
      ->varDump($this->complexTypes[$name]));
  }

  /**
   * adds a simple type to the schema
   *
   * @param string $name
   * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   * @param string $typeClass (should always be simpleType)
   * @param string $phpType (should always be scalar)
   * @param array $enumeration array of values
   * @access public
   * @see xmlschema
   * @see getTypeDef
   */
  function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = []) {
    $this->simpleTypes[$name] = array(
      'name' => $name,
      'typeClass' => $typeClass,
      'phpType' => $phpType,
      'type' => $restrictionBase,
      'enumeration' => $enumeration,
    );
    $this
      ->xdebug("addSimpleType {$name}:");
    $this
      ->appendDebug($this
      ->varDump($this->simpleTypes[$name]));
  }

  /**
   * adds an element to the schema
   *
   * @param array $attrs attributes that must include name and type
   * @see xmlschema
   * @access public
   */
  function addElement($attrs) {
    if (!$this
      ->getPrefix($attrs['type'])) {
      $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
    }
    $this->elements[$attrs['name']] = $attrs;
    $this->elements[$attrs['name']]['typeClass'] = 'element';
    $this
      ->xdebug("addElement " . $attrs['name']);
    $this
      ->appendDebug($this
      ->varDump($this->elements[$attrs['name']]));
  }

}

/**
* For creating serializable abstractions of native PHP types.  This class
* allows element name/namespace, XSD type, and XML attributes to be
* associated with a value.  This is extremely useful when WSDL is not
* used, but is also useful when WSDL is used with polymorphic types, including
* xsd:anyType and user-defined types.
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class soapval extends nusoap_base {

  /**
   * The XML element name
   *
   * @var string
   * @access private
   */
  var $name;

  /**
   * The XML type name (string or false)
   *
   * @var mixed
   * @access private
   */
  var $type;

  /**
   * The PHP value
   *
   * @var mixed
   * @access private
   */
  var $value;

  /**
   * The XML element namespace (string or false)
   *
   * @var mixed
   * @access private
   */
  var $element_ns;

  /**
   * The XML type namespace (string or false)
   *
   * @var mixed
   * @access private
   */
  var $type_ns;

  /**
   * The XML element attributes (array or false)
   *
   * @var mixed
   * @access private
   */
  var $attributes;

  /**
   * constructor
   *
   * @param    string $name optional name
   * @param    mixed $type optional type name
   * @param	mixed $value optional value
   * @param	mixed $element_ns optional namespace of value
   * @param	mixed $type_ns optional namespace of type
   * @param	mixed $attributes associative array of attributes to add to element serialization
   * @access   public
   */
  function soapval($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false) {
    parent::nusoap_base();
    $this->name = $name;
    $this->type = $type;
    $this->value = $value;
    $this->element_ns = $element_ns;
    $this->type_ns = $type_ns;
    $this->attributes = $attributes;
  }

  /**
   * return serialized value
   *
   * @param	string $use The WSDL use value (encoded|literal)
   * @return	string XML data
   * @access   public
   */
  function serialize($use = 'encoded') {
    return $this
      ->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use);
  }

  /**
   * decodes a soapval object into a PHP native type
   *
   * @return	mixed
   * @access   public
   */
  function decode() {
    return $this->value;
  }

}

/**
* transport class for sending/receiving data via HTTP and HTTPS
* NOTE: PHP must be compiled with the CURL extension for HTTPS support
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access public
*/
class soap_transport_http extends nusoap_base {
  var $url = '';
  var $uri = '';
  var $digest_uri = '';
  var $scheme = '';
  var $host = '';
  var $port = '';
  var $path = '';
  var $request_method = 'POST';
  var $protocol_version = '1.0';
  var $encoding = '';
  var $outgoing_headers = [];
  var $incoming_headers = [];
  var $incoming_cookies = [];
  var $outgoing_payload = '';
  var $incoming_payload = '';
  var $useSOAPAction = true;
  var $persistentConnection = false;
  var $ch = false;

  // cURL handle
  var $username = '';
  var $password = '';
  var $authtype = '';
  var $digestRequest = [];
  var $certRequest = [];

  // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
  // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
  // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
  // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
  // passphrase: SSL key password/passphrase
  // verifypeer: default is 1
  // verifyhost: default is 1

  /**
   * constructor
   */
  function soap_transport_http($url) {
    parent::nusoap_base();
    $this
      ->setURL($url);
    ereg('\\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
    $this->outgoing_headers['User-Agent'] = $this->title . '/' . $this->version . ' (' . $rev[1] . ')';
    $this
      ->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']);
  }
  function setURL($url) {
    $this->url = $url;
    $u = parse_url($url);
    foreach ($u as $k => $v) {
      $this
        ->debug("{$k} = {$v}");
      $this->{$k} = $v;
    }

    // add any GET params to path
    if (isset($u['query']) && $u['query'] != '') {
      $this->path .= '?' . $u['query'];
    }

    // set default port
    if (!isset($u['port'])) {
      if ($u['scheme'] == 'https') {
        $this->port = 443;
      }
      else {
        $this->port = 80;
      }
    }
    $this->uri = $this->path;
    $this->digest_uri = $this->uri;

    // build headers
    if (!isset($u['port'])) {
      $this->outgoing_headers['Host'] = $this->host;
    }
    else {
      $this->outgoing_headers['Host'] = $this->host . ':' . $this->port;
    }
    $this
      ->debug('set Host: ' . $this->outgoing_headers['Host']);
    if (isset($u['user']) && $u['user'] != '') {
      $this
        ->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
    }
  }
  function connect($connection_timeout = 0, $response_timeout = 30) {

    // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
    // "regular" socket.
    // TODO: disabled for now because OpenSSL must be *compiled* in (not just
    //       loaded), and until PHP5 stream_get_wrappers is not available.
    //	  	if ($this->scheme == 'https') {
    //		  	if (version_compare(phpversion(), '4.3.0') >= 0) {
    //		  		if (extension_loaded('openssl')) {
    //		  			$this->scheme = 'ssl';
    //		  			$this->debug('Using SSL over OpenSSL');
    //		  		}
    //		  	}
    //		}
    $this
      ->debug("connect connection_timeout {$connection_timeout}, response_timeout {$response_timeout}, scheme {$this->scheme}, host {$this->host}, port {$this->port}");
    if ($this->scheme == 'http' || $this->scheme == 'ssl') {

      // use persistent connection
      if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
        if (!feof($this->fp)) {
          $this
            ->debug('Re-use persistent connection');
          return true;
        }
        fclose($this->fp);
        $this
          ->debug('Closed persistent connection at EOF');
      }

      // munge host if using OpenSSL
      if ($this->scheme == 'ssl') {
        $host = 'ssl://' . $this->host;
      }
      else {
        $host = $this->host;
      }
      $this
        ->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);

      // open socket
      if ($connection_timeout > 0) {
        $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
      }
      else {
        $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
      }

      // test pointer
      if (!$this->fp) {
        $msg = 'Couldn\'t open socket connection to server ' . $this->url;
        if ($this->errno) {
          $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
        }
        else {
          $msg .= ' prior to connect().  This is often a problem looking up the host name.';
        }
        $this
          ->debug($msg);
        $this
          ->setError($msg);
        return false;
      }

      // set response timeout
      $this
        ->debug('set response timeout to ' . $response_timeout);
      socket_set_timeout($this->fp, $response_timeout);
      $this
        ->debug('socket connected');
      return true;
    }
    else {
      if ($this->scheme == 'https') {
        if (!extension_loaded('curl')) {
          $this
            ->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
          return false;
        }
        $this
          ->debug('connect using https');

        // init CURL
        $this->ch = curl_init();

        // set url
        $hostURL = $this->port != '' ? "https://{$this->host}:{$this->port}" : "https://{$this->host}";

        // add path
        $hostURL .= $this->path;
        curl_setopt($this->ch, CURLOPT_URL, $hostURL);

        // follow location headers (re-directs)
        curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);

        // ask for headers in the response output
        curl_setopt($this->ch, CURLOPT_HEADER, 1);

        // ask for the response output as the return value
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);

        // encode
        // We manage this ourselves through headers and encoding
        //		if(function_exists('gzuncompress')){
        //			curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
        //		}
        // persistent connection
        if ($this->persistentConnection) {

          // The way we send data, we cannot use persistent connections, since
          // there will be some "junk" at the end of our request.

          //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
          $this->persistentConnection = false;
          $this->outgoing_headers['Connection'] = 'close';
          $this
            ->debug('set Connection: ' . $this->outgoing_headers['Connection']);
        }

        // set timeout
        if ($connection_timeout != 0) {
          curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
        }

        // TODO: cURL has added a connection timeout separate from the response timeout

        //if ($connection_timeout != 0) {

        //	curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);

        //}

        //if ($response_timeout != 0) {

        //	curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);

        //}

        // recent versions of cURL turn on peer/host checking by default,
        // while PHP binaries are not compiled with a default location for the
        // CA cert bundle, so disable peer/host checking.

        //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);

        // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
        if ($this->authtype == 'certificate') {
          if (isset($this->certRequest['cainfofile'])) {
            curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']);
          }
          if (isset($this->certRequest['verifypeer'])) {
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
          }
          else {
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
          }
          if (isset($this->certRequest['verifyhost'])) {
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
          }
          else {
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
          }
          if (isset($this->certRequest['sslcertfile'])) {
            curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
          }
          if (isset($this->certRequest['sslkeyfile'])) {
            curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
          }
          if (isset($this->certRequest['passphrase'])) {
            curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
          }
        }
        $this
          ->debug('cURL connection set up');
        return true;
      }
      else {
        $this
          ->setError('Unknown scheme ' . $this->scheme);
        $this
          ->debug('Unknown scheme ' . $this->scheme);
        return false;
      }
    }
  }

  /**
   * send the SOAP message via HTTP
   *
   * @param    string $data message data
   * @param    integer $timeout set connection timeout in seconds
   * @param	integer $response_timeout set response timeout in seconds
   * @param	array $cookies cookies to send
   * @return	string data
   * @access   public
   */
  function send($data, $timeout = 0, $response_timeout = 30, $cookies = NULL) {
    $this
      ->debug('entered send() with data of length: ' . strlen($data));
    $this->tryagain = true;
    $tries = 0;
    while ($this->tryagain) {
      $this->tryagain = false;
      if ($tries++ < 2) {

        // make connnection
        if (!$this
          ->connect($timeout, $response_timeout)) {
          return false;
        }

        // send request
        if (!$this
          ->sendRequest($data, $cookies)) {
          return false;
        }

        // get response
        $respdata = $this
          ->getResponse();
      }
      else {
        $this
          ->setError('Too many tries to get an OK response');
      }
    }
    $this
      ->debug('end of send()');
    return $respdata;
  }

  /**
   * send the SOAP message via HTTPS 1.0 using CURL
   *
   * @param    string $msg message data
   * @param    integer $timeout set connection timeout in seconds
   * @param	integer $response_timeout set response timeout in seconds
   * @param	array $cookies cookies to send
   * @return	string data
   * @access   public
   */
  function sendHTTPS($data, $timeout = 0, $response_timeout = 30, $cookies) {
    return $this
      ->send($data, $timeout, $response_timeout, $cookies);
  }

  /**
   * if authenticating, set user credentials here
   *
   * @param    string $username
   * @param    string $password
   * @param	string $authtype (basic, digest, certificate)
   * @param	array $digestRequest (keys must be nonce, nc, realm, qop)
   * @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
   * @access   public
   */
  function setCredentials($username, $password, $authtype = 'basic', $digestRequest = [], $certRequest = []) {
    $this
      ->debug("Set credentials for authtype {$authtype}");

    // cf. RFC 2617
    if ($authtype == 'basic') {
      $this->outgoing_headers['Authorization'] = 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password);
    }
    elseif ($authtype == 'digest') {
      if (isset($digestRequest['nonce'])) {
        $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;

        // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
        // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
        $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;

        // H(A1) = MD5(A1)
        $HA1 = md5($A1);

        // A2 = Method ":" digest-uri-value
        $A2 = 'POST:' . $this->digest_uri;

        // H(A2)
        $HA2 = md5($A2);

        // KD(secret, data) = H(concat(secret, ":", data))
        // if qop == auth:
        // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
        //                              ":" nc-value
        //                              ":" unq(cnonce-value)
        //                              ":" unq(qop-value)
        //                              ":" H(A2)
        //                            ) <">
        // if qop is missing,
        // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
        $unhashedDigest = '';
        $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
        $cnonce = $nonce;
        if ($digestRequest['qop'] != '') {
          $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
        }
        else {
          $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
        }
        $hashedDigest = md5($unhashedDigest);
        $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
      }
    }
    elseif ($authtype == 'certificate') {
      $this->certRequest = $certRequest;
    }
    $this->username = $username;
    $this->password = $password;
    $this->authtype = $authtype;
    $this->digestRequest = $digestRequest;
    if (isset($this->outgoing_headers['Authorization'])) {
      $this
        ->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
    }
    else {
      $this
        ->debug('Authorization header not set');
    }
  }

  /**
   * set the soapaction value
   *
   * @param    string $soapaction
   * @access   public
   */
  function setSOAPAction($soapaction) {
    $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
    $this
      ->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']);
  }

  /**
   * use http encoding
   *
   * @param    string $enc encoding style. supported values: gzip, deflate, or both
   * @access   public
   */
  function setEncoding($enc = 'gzip, deflate') {
    if (function_exists('gzdeflate')) {
      $this->protocol_version = '1.1';
      $this->outgoing_headers['Accept-Encoding'] = $enc;
      $this
        ->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']);
      if (!isset($this->outgoing_headers['Connection'])) {
        $this->outgoing_headers['Connection'] = 'close';
        $this->persistentConnection = false;
        $this
          ->debug('set Connection: ' . $this->outgoing_headers['Connection']);
      }
      set_magic_quotes_runtime(0);

      // deprecated
      $this->encoding = $enc;
    }
  }

  /**
   * set proxy info here
   *
   * @param    string $proxyhost
   * @param    string $proxyport
   * @param	string $proxyusername
   * @param	string $proxypassword
   * @access   public
   */
  function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
    $this->uri = $this->url;
    $this->host = $proxyhost;
    $this->port = $proxyport;
    if ($proxyusername != '' && $proxypassword != '') {
      $this->outgoing_headers['Proxy-Authorization'] = ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword);
      $this
        ->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']);
    }
  }

  /**
   * decode a string that is encoded w/ "chunked' transfer encoding
   * as defined in RFC2068 19.4.6
   *
   * @param    string $buffer
   * @param    string $lb
   * @returns	string
   * @access   public
   * @deprecated
   */
  function decodeChunked($buffer, $lb) {

    // length := 0
    $length = 0;
    $new = '';

    // read chunk-size, chunk-extension (if any) and CRLF
    // get the position of the linebreak
    $chunkend = strpos($buffer, $lb);
    if ($chunkend == FALSE) {
      $this
        ->debug('no linebreak found in decodeChunked');
      return $new;
    }
    $temp = substr($buffer, 0, $chunkend);
    $chunk_size = hexdec(trim($temp));
    $chunkstart = $chunkend + strlen($lb);

    // while (chunk-size > 0) {
    while ($chunk_size > 0) {
      $this
        ->debug("chunkstart: {$chunkstart} chunk_size: {$chunk_size}");
      $chunkend = strpos($buffer, $lb, $chunkstart + $chunk_size);

      // Just in case we got a broken connection
      if ($chunkend == FALSE) {
        $chunk = substr($buffer, $chunkstart);

        // append chunk-data to entity-body
        $new .= $chunk;
        $length += strlen($chunk);
        break;
      }

      // read chunk-data and CRLF
      $chunk = substr($buffer, $chunkstart, $chunkend - $chunkstart);

      // append chunk-data to entity-body
      $new .= $chunk;

      // length := length + chunk-size
      $length += strlen($chunk);

      // read chunk-size and CRLF
      $chunkstart = $chunkend + strlen($lb);
      $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
      if ($chunkend == FALSE) {
        break;

        //Just in case we got a broken connection
      }
      $temp = substr($buffer, $chunkstart, $chunkend - $chunkstart);
      $chunk_size = hexdec(trim($temp));
      $chunkstart = $chunkend;
    }
    return $new;
  }

  /*
   *	Writes payload, including HTTP headers, to $this->outgoing_payload.
   */
  function buildPayload($data, $cookie_str = '') {

    // add content-length header
    $this->outgoing_headers['Content-Length'] = strlen($data);
    $this
      ->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']);

    // start building outgoing payload:
    $req = "{$this->request_method} {$this->uri} HTTP/{$this->protocol_version}";
    $this
      ->debug("HTTP request: {$req}");
    $this->outgoing_payload = "{$req}\r\n";

    // loop thru headers, serializing
    foreach ($this->outgoing_headers as $k => $v) {
      $hdr = $k . ': ' . $v;
      $this
        ->debug("HTTP header: {$hdr}");
      $this->outgoing_payload .= "{$hdr}\r\n";
    }

    // add any cookies
    if ($cookie_str != '') {
      $hdr = 'Cookie: ' . $cookie_str;
      $this
        ->debug("HTTP header: {$hdr}");
      $this->outgoing_payload .= "{$hdr}\r\n";
    }

    // header/body separator
    $this->outgoing_payload .= "\r\n";

    // add data
    $this->outgoing_payload .= $data;
  }
  function sendRequest($data, $cookies = NULL) {

    // build cookie string
    $cookie_str = $this
      ->getCookiesForRequest($cookies, $this->scheme == 'ssl' || $this->scheme == 'https');

    // build payload
    $this
      ->buildPayload($data, $cookie_str);
    if ($this->scheme == 'http' || $this->scheme == 'ssl') {

      // send payload
      if (!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
        $this
          ->setError('couldn\'t write message data to socket');
        $this
          ->debug('couldn\'t write message data to socket');
        return false;
      }
      $this
        ->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
      return true;
    }
    else {
      if ($this->scheme == 'https') {

        // set payload
        // TODO: cURL does say this should only be the verb, and in fact it
        // turns out that the URI and HTTP version are appended to this, which
        // some servers refuse to work with

        //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
        foreach ($this->outgoing_headers as $k => $v) {
          $curl_headers[] = "{$k}: {$v}";
        }
        if ($cookie_str != '') {
          $curl_headers[] = 'Cookie: ' . $cookie_str;
        }
        curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
        if ($this->request_method == "POST") {
          curl_setopt($this->ch, CURLOPT_POST, 1);
          curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
        }
        else {
        }
        $this
          ->debug('set cURL payload');
        return true;
      }
    }
  }
  function getResponse() {
    $this->incoming_payload = '';
    if ($this->scheme == 'http' || $this->scheme == 'ssl') {

      // loop until headers have been retrieved
      $data = '';
      while (!isset($lb)) {

        // We might EOF during header read.
        if (feof($this->fp)) {
          $this->incoming_payload = $data;
          $this
            ->debug('found no headers before EOF after length ' . strlen($data));
          $this
            ->debug("received before EOF:\n" . $data);
          $this
            ->setError('server failed to send headers');
          return false;
        }
        $tmp = fgets($this->fp, 256);
        $tmplen = strlen($tmp);
        $this
          ->debug("read line of {$tmplen} bytes: " . trim($tmp));
        if ($tmplen == 0) {
          $this->incoming_payload = $data;
          $this
            ->debug('socket read of headers timed out after length ' . strlen($data));
          $this
            ->debug("read before timeout: " . $data);
          $this
            ->setError('socket read of headers timed out');
          return false;
        }
        $data .= $tmp;
        $pos = strpos($data, "\r\n\r\n");
        if ($pos > 1) {
          $lb = "\r\n";
        }
        else {
          $pos = strpos($data, "\n\n");
          if ($pos > 1) {
            $lb = "\n";
          }
        }

        // remove 100 header
        if (isset($lb) && ereg('^HTTP/1.1 100', $data)) {
          unset($lb);
          $data = '';
        }

        //
      }

      // store header data
      $this->incoming_payload .= $data;
      $this
        ->debug('found end of headers after length ' . strlen($data));

      // process headers
      $header_data = trim(substr($data, 0, $pos));
      $header_array = explode($lb, $header_data);
      $this->incoming_headers = array();
      $this->incoming_cookies = array();
      foreach ($header_array as $header_line) {
        $arr = explode(':', $header_line, 2);
        if (count($arr) > 1) {
          $header_name = strtolower(trim($arr[0]));
          $this->incoming_headers[$header_name] = trim($arr[1]);
          if ($header_name == 'set-cookie') {

            // TODO: allow multiple cookies from parseCookie
            $cookie = $this
              ->parseCookie(trim($arr[1]));
            if ($cookie) {
              $this->incoming_cookies[] = $cookie;
              $this
                ->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
            }
            else {
              $this
                ->debug('did not find cookie in ' . trim($arr[1]));
            }
          }
        }
        else {
          if (isset($header_name)) {

            // append continuation line to previous header
            $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
          }
        }
      }

      // loop until msg has been received
      if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
        $content_length = 2147483647;

        // ignore any content-length header
        $chunked = true;
        $this
          ->debug("want to read chunked content");
      }
      elseif (isset($this->incoming_headers['content-length'])) {
        $content_length = $this->incoming_headers['content-length'];
        $chunked = false;
        $this
          ->debug("want to read content of length {$content_length}");
      }
      else {
        $content_length = 2147483647;
        $chunked = false;
        $this
          ->debug("want to read content to EOF");
      }
      $data = '';
      do {
        if ($chunked) {
          $tmp = fgets($this->fp, 256);
          $tmplen = strlen($tmp);
          $this
            ->debug("read chunk line of {$tmplen} bytes");
          if ($tmplen == 0) {
            $this->incoming_payload = $data;
            $this
              ->debug('socket read of chunk length timed out after length ' . strlen($data));
            $this
              ->debug("read before timeout:\n" . $data);
            $this
              ->setError('socket read of chunk length timed out');
            return false;
          }
          $content_length = hexdec(trim($tmp));
          $this
            ->debug("chunk length {$content_length}");
        }
        $strlen = 0;
        while ($strlen < $content_length && !feof($this->fp)) {
          $readlen = min(8192, $content_length - $strlen);
          $tmp = fread($this->fp, $readlen);
          $tmplen = strlen($tmp);
          $this
            ->debug("read buffer of {$tmplen} bytes");
          if ($tmplen == 0 && !feof($this->fp)) {
            $this->incoming_payload = $data;
            $this
              ->debug('socket read of body timed out after length ' . strlen($data));
            $this
              ->debug("read before timeout:\n" . $data);
            $this
              ->setError('socket read of body timed out');
            return false;
          }
          $strlen += $tmplen;
          $data .= $tmp;
        }
        if ($chunked && $content_length > 0) {
          $tmp = fgets($this->fp, 256);
          $tmplen = strlen($tmp);
          $this
            ->debug("read chunk terminator of {$tmplen} bytes");
          if ($tmplen == 0) {
            $this->incoming_payload = $data;
            $this
              ->debug('socket read of chunk terminator timed out after length ' . strlen($data));
            $this
              ->debug("read before timeout:\n" . $data);
            $this
              ->setError('socket read of chunk terminator timed out');
            return false;
          }
        }
      } while ($chunked && $content_length > 0 && !feof($this->fp));
      if (feof($this->fp)) {
        $this
          ->debug('read to EOF');
      }
      $this
        ->debug('read body of length ' . strlen($data));
      $this->incoming_payload .= $data;
      $this
        ->debug('received a total of ' . strlen($this->incoming_payload) . ' bytes of data from server');

      // close filepointer
      if (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close' || !$this->persistentConnection || feof($this->fp)) {
        fclose($this->fp);
        $this->fp = false;
        $this
          ->debug('closed socket');
      }

      // connection was closed unexpectedly
      if ($this->incoming_payload == '') {
        $this
          ->setError('no response from server');
        return false;
      }

      // decode transfer-encoding
      //		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
      //			if(!$data = $this->decodeChunked($data, $lb)){
      //				$this->setError('Decoding of chunked data failed');
      //				return false;
      //			}

      //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";

      // set decoded payload
      //			$this->incoming_payload = $header_data.$lb.$lb.$data;
      //		}
    }
    else {
      if ($this->scheme == 'https') {

        // send and receive
        $this
          ->debug('send and receive with cURL');
        $this->incoming_payload = curl_exec($this->ch);
        $data = $this->incoming_payload;
        $cErr = curl_error($this->ch);
        if ($cErr != '') {
          $err = 'cURL ERROR: ' . curl_errno($this->ch) . ': ' . $cErr . '<br>';

          // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
          foreach (curl_getinfo($this->ch) as $k => $v) {
            $err .= "{$k}: {$v}<br>";
          }
          $this
            ->debug($err);
          $this
            ->setError($err);
          curl_close($this->ch);
          return false;
        }
        else {

          //echo '<pre>';

          //var_dump(curl_getinfo($this->ch));

          //echo '</pre>';
        }

        // close curl
        $this
          ->debug('No cURL error, closing cURL');
        curl_close($this->ch);

        // remove 100 header(s)
        while (ereg('^HTTP/1.1 100', $data)) {
          if ($pos = strpos($data, "\r\n\r\n")) {
            $data = ltrim(substr($data, $pos));
          }
          elseif ($pos = strpos($data, "\n\n")) {
            $data = ltrim(substr($data, $pos));
          }
        }

        // separate content from HTTP headers
        if ($pos = strpos($data, "\r\n\r\n")) {
          $lb = "\r\n";
        }
        elseif ($pos = strpos($data, "\n\n")) {
          $lb = "\n";
        }
        else {
          $this
            ->debug('no proper separation of headers and document');
          $this
            ->setError('no proper separation of headers and document');
          return false;
        }
        $header_data = trim(substr($data, 0, $pos));
        $header_array = explode($lb, $header_data);
        $data = ltrim(substr($data, $pos));
        $this
          ->debug('found proper separation of headers and document');
        $this
          ->debug('cleaned data, stringlen: ' . strlen($data));

        // clean headers
        foreach ($header_array as $header_line) {
          $arr = explode(':', $header_line, 2);
          if (count($arr) > 1) {
            $header_name = strtolower(trim($arr[0]));
            $this->incoming_headers[$header_name] = trim($arr[1]);
            if ($header_name == 'set-cookie') {

              // TODO: allow multiple cookies from parseCookie
              $cookie = $this
                ->parseCookie(trim($arr[1]));
              if ($cookie) {
                $this->incoming_cookies[] = $cookie;
                $this
                  ->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
              }
              else {
                $this
                  ->debug('did not find cookie in ' . trim($arr[1]));
              }
            }
          }
          else {
            if (isset($header_name)) {

              // append continuation line to previous header
              $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
            }
          }
        }
      }
    }
    $arr = explode(' ', $header_array[0], 3);
    $http_version = $arr[0];
    $http_status = intval($arr[1]);
    $http_reason = count($arr) > 2 ? $arr[2] : '';

    // see if we need to resend the request with http digest authentication
    if (isset($this->incoming_headers['location']) && $http_status == 301) {
      $this
        ->debug("Got 301 {$http_reason} with Location: " . $this->incoming_headers['location']);
      $this
        ->setURL($this->incoming_headers['location']);
      $this->tryagain = true;
      return false;
    }

    // see if we need to resend the request with http digest authentication
    if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
      $this
        ->debug("Got 401 {$http_reason} with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
      if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
        $this
          ->debug('Server wants digest authentication');

        // remove "Digest " from our elements
        $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);

        // parse elements into array
        $digestElements = explode(',', $digestString);
        foreach ($digestElements as $val) {
          $tempElement = explode('=', trim($val), 2);
          $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
        }

        // should have (at least) qop, realm, nonce
        if (isset($digestRequest['nonce'])) {
          $this
            ->setCredentials($this->username, $this->password, 'digest', $digestRequest);
          $this->tryagain = true;
          return false;
        }
      }
      $this
        ->debug('HTTP authentication failed');
      $this
        ->setError('HTTP authentication failed');
      return false;
    }
    if ($http_status >= 300 && $http_status <= 307 || $http_status >= 400 && $http_status <= 417 || $http_status >= 501 && $http_status <= 505) {
      $this
        ->setError("Unsupported HTTP response status {$http_status} {$http_reason} (soapclient->response has contents of the response)");
      return false;
    }

    // decode content-encoding
    if (isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') {
      if (strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') {

        // if decoding works, use it. else assume data wasn't gzencoded
        if (function_exists('gzinflate')) {

          //$timer->setMarker('starting decoding of gzip/deflated content');

          // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
          // this means there are no Zlib headers, although there should be
          $this
            ->debug('The gzinflate function exists');
          $datalen = strlen($data);
          if ($this->incoming_headers['content-encoding'] == 'deflate') {
            if ($degzdata = @gzinflate($data)) {
              $data = $degzdata;
              $this
                ->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
              if (strlen($data) < $datalen) {

                // test for the case that the payload has been compressed twice
                $this
                  ->debug('The inflated payload is smaller than the gzipped one; try again');
                if ($degzdata = @gzinflate($data)) {
                  $data = $degzdata;
                  $this
                    ->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
                }
              }
            }
            else {
              $this
                ->debug('Error using gzinflate to inflate the payload');
              $this
                ->setError('Error using gzinflate to inflate the payload');
            }
          }
          elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
            if ($degzdata = @gzinflate(substr($data, 10))) {

              // do our best
              $data = $degzdata;
              $this
                ->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
              if (strlen($data) < $datalen) {

                // test for the case that the payload has been compressed twice
                $this
                  ->debug('The un-gzipped payload is smaller than the gzipped one; try again');
                if ($degzdata = @gzinflate(substr($data, 10))) {
                  $data = $degzdata;
                  $this
                    ->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
                }
              }
            }
            else {
              $this
                ->debug('Error using gzinflate to un-gzip the payload');
              $this
                ->setError('Error using gzinflate to un-gzip the payload');
            }
          }

          //$timer->setMarker('finished decoding of gzip/deflated content');

          //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";

          // set decoded payload
          $this->incoming_payload = $header_data . $lb . $lb . $data;
        }
        else {
          $this
            ->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
          $this
            ->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
        }
      }
      else {
        $this
          ->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
        $this
          ->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
      }
    }
    else {
      $this
        ->debug('No Content-Encoding header');
    }
    if (strlen($data) == 0) {
      $this
        ->debug('no data after headers!');
      $this
        ->setError('no data present after HTTP headers');
      return false;
    }
    return $data;
  }
  function setContentType($type, $charset = false) {
    $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
    $this
      ->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']);
  }
  function usePersistentConnection() {
    if (isset($this->outgoing_headers['Accept-Encoding'])) {
      return false;
    }
    $this->protocol_version = '1.1';
    $this->persistentConnection = true;
    $this->outgoing_headers['Connection'] = 'Keep-Alive';
    $this
      ->debug('set Connection: ' . $this->outgoing_headers['Connection']);
    return true;
  }

  /**
   * parse an incoming Cookie into it's parts
   *
   * @param	string $cookie_str content of cookie
   * @return	array with data of that cookie
   * @access	private
   */

  /*
   * TODO: allow a Set-Cookie string to be parsed into multiple cookies
   */
  function parseCookie($cookie_str) {
    $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
    $data = split(';', $cookie_str);
    $value_str = $data[0];
    $cookie_param = 'domain=';
    $start = strpos($cookie_str, $cookie_param);
    if ($start > 0) {
      $domain = substr($cookie_str, $start + strlen($cookie_param));
      $domain = substr($domain, 0, strpos($domain, ';'));
    }
    else {
      $domain = '';
    }
    $cookie_param = 'expires=';
    $start = strpos($cookie_str, $cookie_param);
    if ($start > 0) {
      $expires = substr($cookie_str, $start + strlen($cookie_param));
      $expires = substr($expires, 0, strpos($expires, ';'));
    }
    else {
      $expires = '';
    }
    $cookie_param = 'path=';
    $start = strpos($cookie_str, $cookie_param);
    if ($start > 0) {
      $path = substr($cookie_str, $start + strlen($cookie_param));
      $path = substr($path, 0, strpos($path, ';'));
    }
    else {
      $path = '/';
    }
    $cookie_param = ';secure;';
    if (strpos($cookie_str, $cookie_param) !== FALSE) {
      $secure = true;
    }
    else {
      $secure = false;
    }
    $sep_pos = strpos($value_str, '=');
    if ($sep_pos) {
      $name = substr($value_str, 0, $sep_pos);
      $value = substr($value_str, $sep_pos + 1);
      $cookie = array(
        'name' => $name,
        'value' => $value,
        'domain' => $domain,
        'path' => $path,
        'expires' => $expires,
        'secure' => $secure,
      );
      return $cookie;
    }
    return false;
  }

  /**
   * sort out cookies for the current request
   *
   * @param	array $cookies array with all cookies
   * @param	boolean $secure is the send-content secure or not?
   * @return	string for Cookie-HTTP-Header
   * @access	private
   */
  function getCookiesForRequest($cookies, $secure = false) {
    $cookie_str = '';
    if (!is_null($cookies) && is_array($cookies)) {
      foreach ($cookies as $cookie) {
        if (!is_array($cookie)) {
          continue;
        }
        $this
          ->debug("check cookie for validity: " . $cookie['name'] . '=' . $cookie['value']);
        if (isset($cookie['expires']) && !empty($cookie['expires'])) {
          if (strtotime($cookie['expires']) <= time()) {
            $this
              ->debug('cookie has expired');
            continue;
          }
        }
        if (isset($cookie['domain']) && !empty($cookie['domain'])) {
          $domain = preg_quote($cookie['domain']);
          if (!preg_match("'.*{$domain}\$'i", $this->host)) {
            $this
              ->debug('cookie has different domain');
            continue;
          }
        }
        if (isset($cookie['path']) && !empty($cookie['path'])) {
          $path = preg_quote($cookie['path']);
          if (!preg_match("'^{$path}.*'i", $this->path)) {
            $this
              ->debug('cookie is for a different path');
            continue;
          }
        }
        if (!$secure && isset($cookie['secure']) && $cookie['secure']) {
          $this
            ->debug('cookie is secure, transport is not');
          continue;
        }
        $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
        $this
          ->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
      }
    }
    return $cookie_str;
  }

}

/**
*
* soap_server allows the user to create a SOAP server
* that is capable of receiving messages and returning responses
*
* NOTE: WSDL functionality is experimental
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class soap_server extends nusoap_base {

  /**
   * HTTP headers of request
   * @var array
   * @access private
   */
  var $headers = [];

  /**
   * HTTP request
   * @var string
   * @access private
   */
  var $request = '';

  /**
   * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
   * @var string
   * @access public
   */
  var $requestHeaders = '';

  /**
   * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
   * @var string
   * @access public
   */
  var $document = '';

  /**
   * SOAP payload for request (text)
   * @var string
   * @access public
   */
  var $requestSOAP = '';

  /**
   * requested method namespace URI
   * @var string
   * @access private
   */
  var $methodURI = '';

  /**
   * name of method requested
   * @var string
   * @access private
   */
  var $methodname = '';

  /**
   * method parameters from request
   * @var array
   * @access private
   */
  var $methodparams = [];

  /**
   * SOAP Action from request
   * @var string
   * @access private
   */
  var $SOAPAction = '';

  /**
   * character set encoding of incoming (request) messages
   * @var string
   * @access public
   */
  var $xml_encoding = '';

  /**
   * toggles whether the parser decodes element content w/ utf8_decode()
   * @var boolean
   * @access public
   */
  var $decode_utf8 = true;

  /**
   * HTTP headers of response
   * @var array
   * @access public
   */
  var $outgoing_headers = [];

  /**
   * HTTP response
   * @var string
   * @access private
   */
  var $response = '';

  /**
   * SOAP headers for response (text)
   * @var string
   * @access public
   */
  var $responseHeaders = '';

  /**
   * SOAP payload for response (text)
   * @var string
   * @access private
   */
  var $responseSOAP = '';

  /**
   * method return value to place in response
   * @var mixed
   * @access private
   */
  var $methodreturn = false;

  /**
   * whether $methodreturn is a string of literal XML
   * @var boolean
   * @access public
   */
  var $methodreturnisliteralxml = false;

  /**
   * SOAP fault for response (or false)
   * @var mixed
   * @access private
   */
  var $fault = false;

  /**
   * text indication of result (for debugging)
   * @var string
   * @access private
   */
  var $result = 'successful';

  /**
   * assoc array of operations => opData; operations are added by the register()
   * method or by parsing an external WSDL definition
   * @var array
   * @access private
   */
  var $operations = [];

  /**
   * wsdl instance (if one)
   * @var mixed
   * @access private
   */
  var $wsdl = false;

  /**
   * URL for WSDL (if one)
   * @var mixed
   * @access private
   */
  var $externalWSDLURL = false;

  /**
   * whether to append debug to response as XML comment
   * @var boolean
   * @access public
   */
  var $debug_flag = false;

  /**
   * constructor
   * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
   *
   * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
   * @access   public
   */
  function soap_server($wsdl = false) {
    parent::nusoap_base();

    // turn on debugging?
    global $debug;
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER)) {
      $this
        ->debug("_SERVER is defined:");
      $this
        ->appendDebug($this
        ->varDump($_SERVER));
    }
    elseif (isset($HTTP_SERVER_VARS)) {
      $this
        ->debug("HTTP_SERVER_VARS is defined:");
      $this
        ->appendDebug($this
        ->varDump($HTTP_SERVER_VARS));
    }
    else {
      $this
        ->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
    }
    if (isset($debug)) {
      $this
        ->debug("In soap_server, set debug_flag={$debug} based on global flag");
      $this->debug_flag = $debug;
    }
    elseif (isset($_SERVER['QUERY_STRING'])) {
      $qs = explode('&', $_SERVER['QUERY_STRING']);
      foreach ($qs as $v) {
        if (substr($v, 0, 6) == 'debug=') {
          $this
            ->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
          $this->debug_flag = substr($v, 6);
        }
      }
    }
    elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
      $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
      foreach ($qs as $v) {
        if (substr($v, 0, 6) == 'debug=') {
          $this
            ->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
          $this->debug_flag = substr($v, 6);
        }
      }
    }

    // wsdl
    if ($wsdl) {
      $this
        ->debug("In soap_server, WSDL is specified");
      if (is_object($wsdl) && get_class($wsdl) == 'wsdl') {
        $this->wsdl = $wsdl;
        $this->externalWSDLURL = $this->wsdl->wsdl;
        $this
          ->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
      }
      else {
        $this
          ->debug('Create wsdl from ' . $wsdl);
        $this->wsdl = new wsdl($wsdl);
        $this->externalWSDLURL = $wsdl;
      }
      $this
        ->appendDebug($this->wsdl
        ->getDebug());
      $this->wsdl
        ->clearDebug();
      if ($err = $this->wsdl
        ->getError()) {
        die('WSDL ERROR: ' . $err);
      }
    }
  }

  /**
   * processes request and returns response
   *
   * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
   * @access   public
   */
  function service($data) {
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER['QUERY_STRING'])) {
      $qs = $_SERVER['QUERY_STRING'];
    }
    elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
      $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
    }
    else {
      $qs = '';
    }
    $this
      ->debug("In service, query string={$qs}");
    if (ereg('wsdl', $qs)) {
      $this
        ->debug("In service, this is a request for WSDL");
      if ($this->externalWSDLURL) {
        if (strpos($this->externalWSDLURL, "://") !== false) {

          // assume URL
          header('Location: ' . $this->externalWSDLURL);
        }
        else {

          // assume file
          header("Content-Type: text/xml\r\n");
          $fp = fopen($this->externalWSDLURL, 'r');
          fpassthru($fp);
        }
      }
      elseif ($this->wsdl) {
        header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
        print $this->wsdl
          ->serialize($this->debug_flag);
        if ($this->debug_flag) {
          $this
            ->debug('wsdl:');
          $this
            ->appendDebug($this
            ->varDump($this->wsdl));
          print $this
            ->getDebugAsXMLComment();
        }
      }
      else {
        header("Content-Type: text/html; charset=ISO-8859-1\r\n");
        print "This service does not provide WSDL";
      }
    }
    elseif ($data == '' && $this->wsdl) {
      $this
        ->debug("In service, there is no data, so return Web description");
      print $this->wsdl
        ->webDescription();
    }
    else {
      $this
        ->debug("In service, invoke the request");
      $this
        ->parse_request($data);
      if (!$this->fault) {
        $this
          ->invoke_method();
      }
      if (!$this->fault) {
        $this
          ->serialize_return();
      }
      $this
        ->send_response();
    }
  }

  /**
   * parses HTTP request headers.
   *
   * The following fields are set by this function (when successful)
   *
   * headers
   * request
   * xml_encoding
   * SOAPAction
   *
   * @access   private
   */
  function parse_http_headers() {
    global $HTTP_SERVER_VARS;
    $this->request = '';
    $this->SOAPAction = '';
    if (function_exists('getallheaders')) {
      $this
        ->debug("In parse_http_headers, use getallheaders");
      $headers = getallheaders();
      foreach ($headers as $k => $v) {
        $k = strtolower($k);
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }

      // get SOAPAction header
      if (isset($this->headers['soapaction'])) {
        $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
      }

      // get the character encoding of the incoming request
      if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
        $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1));
        if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
          $this->xml_encoding = strtoupper($enc);
        }
        else {
          $this->xml_encoding = 'US-ASCII';
        }
      }
      else {

        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
        $this->xml_encoding = 'ISO-8859-1';
      }
    }
    elseif (isset($_SERVER) && is_array($_SERVER)) {
      $this
        ->debug("In parse_http_headers, use _SERVER");
      foreach ($_SERVER as $k => $v) {
        if (substr($k, 0, 5) == 'HTTP_') {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
          $k = strtolower(substr($k, 5));
        }
        else {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
          $k = strtolower($k);
        }
        if ($k == 'soapaction') {

          // get SOAPAction header
          $k = 'SOAPAction';
          $v = str_replace('"', '', $v);
          $v = str_replace('\\', '', $v);
          $this->SOAPAction = $v;
        }
        else {
          if ($k == 'content-type') {

            // get the character encoding of the incoming request
            if (strpos($v, '=')) {
              $enc = substr(strstr($v, '='), 1);
              $enc = str_replace('"', '', $enc);
              $enc = str_replace('\\', '', $enc);
              if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
                $this->xml_encoding = strtoupper($enc);
              }
              else {
                $this->xml_encoding = 'US-ASCII';
              }
            }
            else {

              // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
              $this->xml_encoding = 'ISO-8859-1';
            }
          }
        }
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }
    }
    elseif (is_array($HTTP_SERVER_VARS)) {
      $this
        ->debug("In parse_http_headers, use HTTP_SERVER_VARS");
      foreach ($HTTP_SERVER_VARS as $k => $v) {
        if (substr($k, 0, 5) == 'HTTP_') {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
          $k = strtolower(substr($k, 5));
        }
        else {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
          $k = strtolower($k);
        }
        if ($k == 'soapaction') {

          // get SOAPAction header
          $k = 'SOAPAction';
          $v = str_replace('"', '', $v);
          $v = str_replace('\\', '', $v);
          $this->SOAPAction = $v;
        }
        else {
          if ($k == 'content-type') {

            // get the character encoding of the incoming request
            if (strpos($v, '=')) {
              $enc = substr(strstr($v, '='), 1);
              $enc = str_replace('"', '', $enc);
              $enc = str_replace('\\', '', $enc);
              if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
                $this->xml_encoding = strtoupper($enc);
              }
              else {
                $this->xml_encoding = 'US-ASCII';
              }
            }
            else {

              // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
              $this->xml_encoding = 'ISO-8859-1';
            }
          }
        }
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }
    }
    else {
      $this
        ->debug("In parse_http_headers, HTTP headers not accessible");
      $this
        ->setError("HTTP headers not accessible");
    }
  }

  /**
   * parses a request
   *
   * The following fields are set by this function (when successful)
   *
   * headers
   * request
   * xml_encoding
   * SOAPAction
   * request
   * requestSOAP
   * methodURI
   * methodname
   * methodparams
   * requestHeaders
   * document
   *
   * This sets the fault field on error
   *
   * @param    string $data XML string
   * @access   private
   */
  function parse_request($data = '') {
    $this
      ->debug('entering parse_request()');
    $this
      ->parse_http_headers();
    $this
      ->debug('got character encoding: ' . $this->xml_encoding);

    // uncompress if necessary
    if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
      $this
        ->debug('got content encoding: ' . $this->headers['content-encoding']);
      if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {

        // if decoding works, use it. else assume data wasn't gzencoded
        if (function_exists('gzuncompress')) {
          if ($this->headers['content-encoding'] == 'deflate' && ($degzdata = @gzuncompress($data))) {
            $data = $degzdata;
          }
          elseif ($this->headers['content-encoding'] == 'gzip' && ($degzdata = gzinflate(substr($data, 10)))) {
            $data = $degzdata;
          }
          else {
            $this
              ->fault('Client', 'Errors occurred when trying to decode the data');
            return;
          }
        }
        else {
          $this
            ->fault('Client', 'This Server does not support compressed data');
          return;
        }
      }
    }
    $this->request .= "\r\n" . $data;
    $data = $this
      ->parseRequest($this->headers, $data);
    $this->requestSOAP = $data;
    $this
      ->debug('leaving parse_request');
  }

  /**
   * invokes a PHP function for the requested SOAP method
   *
   * The following fields are set by this function (when successful)
   *
   * methodreturn
   *
   * Note that the PHP function that is called may also set the following
   * fields to affect the response sent to the client
   *
   * responseHeaders
   * outgoing_headers
   *
   * This sets the fault field on error
   *
   * @access   private
   */
  function invoke_method() {
    $this
      ->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
    if ($this->wsdl) {
      if ($this->opData = $this->wsdl
        ->getOperationData($this->methodname)) {
        $this
          ->debug('in invoke_method, found WSDL operation=' . $this->methodname);
        $this
          ->appendDebug('opData=' . $this
          ->varDump($this->opData));
      }
      elseif ($this->opData = $this->wsdl
        ->getOperationDataForSoapAction($this->SOAPAction)) {

        // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
        $this
          ->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
        $this
          ->appendDebug('opData=' . $this
          ->varDump($this->opData));
        $this->methodname = $this->opData['name'];
      }
      else {
        $this
          ->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
        $this
          ->fault('Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
        return;
      }
    }
    else {
      $this
        ->debug('in invoke_method, no WSDL to validate method');
    }

    // if a . is present in $this->methodname, we see if there is a class in scope,
    // which could be referred to. We will also distinguish between two deliminators,
    // to allow methods to be called a the class or an instance
    $class = '';
    $method = '';
    if (strpos($this->methodname, '..') > 0) {
      $delim = '..';
    }
    else {
      if (strpos($this->methodname, '.') > 0) {
        $delim = '.';
      }
      else {
        $delim = '';
      }
    }
    if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 && class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {

      // get the class and method name
      $class = substr($this->methodname, 0, strpos($this->methodname, $delim));
      $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
      $this
        ->debug("in invoke_method, class={$class} method={$method} delim={$delim}");
    }

    // does method exist?
    if ($class == '') {
      if (!function_exists($this->methodname)) {
        $this
          ->debug("in invoke_method, function '{$this->methodname}' not found!");
        $this->result = 'fault: method not found';
        $this
          ->fault('Client', "method '{$this->methodname}' not defined in service");
        return;
      }
    }
    else {
      $method_to_compare = substr(phpversion(), 0, 2) == '4.' ? strtolower($method) : $method;
      if (!in_array($method_to_compare, get_class_methods($class))) {
        $this
          ->debug("in invoke_method, method '{$this->methodname}' not found in class '{$class}'!");
        $this->result = 'fault: method not found';
        $this
          ->fault('Client', "method '{$this->methodname}' not defined in service");
        return;
      }
    }

    // evaluate message, getting back parameters
    // verify that request parameters match the method's signature
    if (!$this
      ->verify_method($this->methodname, $this->methodparams)) {

      // debug
      $this
        ->debug('ERROR: request not verified against method signature');
      $this->result = 'fault: request failed validation against method signature';

      // return fault
      $this
        ->fault('Client', "Operation '{$this->methodname}' not defined in service.");
      return;
    }

    // if there are parameters to pass
    $this
      ->debug('in invoke_method, params:');
    $this
      ->appendDebug($this
      ->varDump($this->methodparams));
    $this
      ->debug("in invoke_method, calling '{$this->methodname}'");
    if (!function_exists('call_user_func_array')) {
      if ($class == '') {
        $this
          ->debug('in invoke_method, calling function using eval()');
        $funcCall = "\$this->methodreturn = {$this->methodname}(";
      }
      else {
        if ($delim == '..') {
          $this
            ->debug('in invoke_method, calling class method using eval()');
          $funcCall = "\$this->methodreturn = " . $class . "::" . $method . "(";
        }
        else {
          $this
            ->debug('in invoke_method, calling instance method using eval()');

          // generate unique instance name
          $instname = "\$inst_" . time();
          $funcCall = $instname . " = new " . $class . "(); ";
          $funcCall .= "\$this->methodreturn = " . $instname . "->" . $method . "(";
        }
      }
      if ($this->methodparams) {
        foreach ($this->methodparams as $param) {
          if (is_array($param)) {
            $this
              ->fault('Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
            return;
          }
          $funcCall .= "\"{$param}\",";
        }
        $funcCall = substr($funcCall, 0, -1);
      }
      $funcCall .= ');';
      $this
        ->debug('in invoke_method, function call: ' . $funcCall);
      @eval($funcCall);
    }
    else {
      if ($class == '') {
        $this
          ->debug('in invoke_method, calling function using call_user_func_array()');
        $call_arg = "{$this->methodname}";

        // straight assignment changes $this->methodname to lower case after call_user_func_array()
      }
      elseif ($delim == '..') {
        $this
          ->debug('in invoke_method, calling class method using call_user_func_array()');
        $call_arg = array(
          $class,
          $method,
        );
      }
      else {
        $this
          ->debug('in invoke_method, calling instance method using call_user_func_array()');
        $instance = new $class();
        $call_arg = array(
          &$instance,
          $method,
        );
      }
      $this->methodreturn = call_user_func_array($call_arg, $this->methodparams);
    }
    $this
      ->debug('in invoke_method, methodreturn:');
    $this
      ->appendDebug($this
      ->varDump($this->methodreturn));
    $this
      ->debug("in invoke_method, called method {$this->methodname}, received {$this->methodreturn} of type " . gettype($this->methodreturn));
  }

  /**
   * serializes the return value from a PHP function into a full SOAP Envelope
   *
   * The following fields are set by this function (when successful)
   *
   * responseSOAP
   *
   * This sets the fault field on error
   *
   * @access   private
   */
  function serialize_return() {
    $this
      ->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);

    // if fault
    if (isset($this->methodreturn) && get_class($this->methodreturn) == 'soap_fault') {
      $this
        ->debug('got a fault object from method');
      $this->fault = $this->methodreturn;
      return;
    }
    elseif ($this->methodreturnisliteralxml) {
      $return_val = $this->methodreturn;

      // returned value(s)
    }
    else {
      $this
        ->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
      $this
        ->debug('serializing return value');
      if ($this->wsdl) {

        // weak attempt at supporting multiple output params
        if (sizeof($this->opData['output']['parts']) > 1) {
          $opParams = $this->methodreturn;
        }
        else {

          // TODO: is this really necessary?
          $opParams = array(
            $this->methodreturn,
          );
        }
        $return_val = $this->wsdl
          ->serializeRPCParameters($this->methodname, 'output', $opParams);
        $this
          ->appendDebug($this->wsdl
          ->getDebug());
        $this->wsdl
          ->clearDebug();
        if ($errstr = $this->wsdl
          ->getError()) {
          $this
            ->debug('got wsdl error: ' . $errstr);
          $this
            ->fault('Server', 'unable to serialize result');
          return;
        }
      }
      else {
        if (isset($this->methodreturn)) {
          $return_val = $this
            ->serialize_val($this->methodreturn, 'return');
        }
        else {
          $return_val = '';
          $this
            ->debug('in absence of WSDL, assume void return for backward compatibility');
        }
      }
    }
    $this
      ->debug('return value:');
    $this
      ->appendDebug($this
      ->varDump($return_val));
    $this
      ->debug('serializing response');
    if ($this->wsdl) {
      $this
        ->debug('have WSDL for serialization: style is ' . $this->opData['style']);
      if ($this->opData['style'] == 'rpc') {
        $this
          ->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
        if ($this->opData['output']['use'] == 'literal') {
          $payload = '<' . $this->methodname . 'Response xmlns="' . $this->methodURI . '">' . $return_val . '</' . $this->methodname . "Response>";
        }
        else {
          $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
        }
      }
      else {
        $this
          ->debug('style is not rpc for serialization: assume document');
        $payload = $return_val;
      }
    }
    else {
      $this
        ->debug('do not have WSDL for serialization: assume rpc/encoded');
      $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
    }
    $this->result = 'successful';
    if ($this->wsdl) {

      //if($this->debug_flag){
      $this
        ->appendDebug($this->wsdl
        ->getDebug());

      //	}
      if (isset($opData['output']['encodingStyle'])) {
        $encodingStyle = $opData['output']['encodingStyle'];
      }
      else {
        $encodingStyle = '';
      }

      // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
      $this->responseSOAP = $this
        ->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $encodingStyle);
    }
    else {
      $this->responseSOAP = $this
        ->serializeEnvelope($payload, $this->responseHeaders);
    }
    $this
      ->debug("Leaving serialize_return");
  }

  /**
   * sends an HTTP response
   *
   * The following fields are set by this function (when successful)
   *
   * outgoing_headers
   * response
   *
   * @access   private
   */
  function send_response() {
    $this
      ->debug('Enter send_response');
    if ($this->fault) {
      $payload = $this->fault
        ->serialize();
      $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
      $this->outgoing_headers[] = "Status: 500 Internal Server Error";
    }
    else {
      $payload = $this->responseSOAP;

      // Some combinations of PHP+Web server allow the Status
      // to come through as a header.  Since OK is the default
      // just do nothing.
      // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
      // $this->outgoing_headers[] = "Status: 200 OK";
    }

    // add debug data if in debug mode
    if (isset($this->debug_flag) && $this->debug_flag) {
      $payload .= $this
        ->getDebugAsXMLComment();
    }
    $this->outgoing_headers[] = "Server: {$this->title} Server v{$this->version}";
    ereg('\\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
    $this->outgoing_headers[] = "X-SOAP-Server: {$this->title}/{$this->version} (" . $rev[1] . ")";

    // Let the Web server decide about this

    //$this->outgoing_headers[] = "Connection: Close\r\n";
    $payload = $this
      ->getHTTPBody($payload);
    $type = $this
      ->getHTTPContentType();
    $charset = $this
      ->getHTTPContentTypeCharset();
    $this->outgoing_headers[] = "Content-Type: {$type}" . ($charset ? '; charset=' . $charset : '');

    //begin code to compress payload - by John

    // NOTE: there is no way to know whether the Web server will also compress
    // this data.
    if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
      if (strstr($this->headers['accept-encoding'], 'gzip')) {
        if (function_exists('gzencode')) {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content being gzipped -->";
          }
          $this->outgoing_headers[] = "Content-Encoding: gzip";
          $payload = gzencode($payload);
        }
        else {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content will not be gzipped: no gzencode -->";
          }
        }
      }
      elseif (strstr($this->headers['accept-encoding'], 'deflate')) {

        // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
        // instead of gzcompress output,
        // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
        if (function_exists('gzdeflate')) {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content being deflated -->";
          }
          $this->outgoing_headers[] = "Content-Encoding: deflate";
          $payload = gzdeflate($payload);
        }
        else {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content will not be deflated: no gzcompress -->";
          }
        }
      }
    }

    //end code
    $this->outgoing_headers[] = "Content-Length: " . strlen($payload);
    reset($this->outgoing_headers);
    foreach ($this->outgoing_headers as $hdr) {
      header($hdr, false);
    }
    print $payload;
    $this->response = join("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
  }

  /**
   * takes the value that was created by parsing the request
   * and compares to the method's signature, if available.
   *
   * @param	string	$operation	The operation to be invoked
   * @param	array	$request	The array of parameter values
   * @return	boolean	Whether the operation was found
   * @access   private
   */
  function verify_method($operation, $request) {
    if (isset($this->wsdl) && is_object($this->wsdl)) {
      if ($this->wsdl
        ->getOperationData($operation)) {
        return true;
      }
    }
    elseif (isset($this->operations[$operation])) {
      return true;
    }
    return false;
  }

  /**
   * processes SOAP message received from client
   *
   * @param	array	$headers	The HTTP headers
   * @param	string	$data		unprocessed request data from client
   * @return	mixed	value of the message, decoded into a PHP type
   * @access   private
   */
  function parseRequest($headers, $data) {
    $this
      ->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
    if (!strstr($headers['content-type'], 'text/xml')) {
      $this
        ->setError('Request not of type text/xml');
      return false;
    }
    if (strpos($headers['content-type'], '=')) {
      $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
      $this
        ->debug('Got response encoding: ' . $enc);
      if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
        $this->xml_encoding = strtoupper($enc);
      }
      else {
        $this->xml_encoding = 'US-ASCII';
      }
    }
    else {

      // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
      $this->xml_encoding = 'ISO-8859-1';
    }
    $this
      ->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');

    // parse response, get soap parser obj
    $parser = new soap_parser($data, $this->xml_encoding, '', $this->decode_utf8);

    // parser debug
    $this
      ->debug("parser debug: \n" . $parser
      ->getDebug());

    // if fault occurred during message parsing
    if ($err = $parser
      ->getError()) {
      $this->result = 'fault: error in msg parsing: ' . $err;
      $this
        ->fault('Client', "error in msg parsing:\n" . $err);

      // else successfully parsed request into soapval object
    }
    else {

      // get/set methodname
      $this->methodURI = $parser->root_struct_namespace;
      $this->methodname = $parser->root_struct_name;
      $this
        ->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
      $this
        ->debug('calling parser->get_response()');
      $this->methodparams = $parser
        ->get_response();

      // get SOAP headers
      $this->requestHeaders = $parser
        ->getHeaders();

      // add document for doclit support
      $this->document = $parser->document;
    }
  }

  /**
   * gets the HTTP body for the current response.
   *
   * @param string $soapmsg The SOAP payload
   * @return string The HTTP body, which includes the SOAP payload
   * @access private
   */
  function getHTTPBody($soapmsg) {
    return $soapmsg;
  }

  /**
   * gets the HTTP content type for the current response.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type for the current response.
   * @access private
   */
  function getHTTPContentType() {
    return 'text/xml';
  }

  /**
   * gets the HTTP content type charset for the current response.
   * returns false for non-text content types.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type charset for the current response.
   * @access private
   */
  function getHTTPContentTypeCharset() {
    return $this->soap_defencoding;
  }

  /**
   * add a method to the dispatch map (this has been replaced by the register method)
   *
   * @param    string $methodname
   * @param    string $in array of input values
   * @param    string $out array of output values
   * @access   public
   * @deprecated
   */
  function add_to_map($methodname, $in, $out) {
    $this->operations[$methodname] = array(
      'name' => $methodname,
      'in' => $in,
      'out' => $out,
    );
  }

  /**
   * register a service function with the server
   *
   * @param    string $name the name of the PHP function, class.method or class..method
   * @param    array $in assoc array of input values: key = param name, value = param type
   * @param    array $out assoc array of output values: key = param name, value = param type
   * @param	mixed $namespace the element namespace for the method or false
   * @param	mixed $soapaction the soapaction for the method or false
   * @param	mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
   * @param	mixed $use optional (encoded|literal) or false
   * @param	string $documentation optional Description to include in WSDL
   * @param	string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
   * @access   public
   */
  function register($name, $in = [], $out = [], $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '') {
    global $HTTP_SERVER_VARS;
    if ($this->externalWSDLURL) {
      die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
    }
    if (!$name) {
      die('You must specify a name when you register an operation');
    }
    if (!is_array($in)) {
      die('You must provide an array for operation inputs');
    }
    if (!is_array($out)) {
      die('You must provide an array for operation outputs');
    }
    if (false == $namespace) {
    }
    if (false == $soapaction) {
      if (isset($_SERVER)) {
        $SERVER_NAME = $_SERVER['SERVER_NAME'];
        $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
      }
      elseif (isset($HTTP_SERVER_VARS)) {
        $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
        $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
      }
      else {
        $this
          ->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
      }
      $soapaction = "http://{$SERVER_NAME}{$SCRIPT_NAME}/{$name}";
    }
    if (false == $style) {
      $style = "rpc";
    }
    if (false == $use) {
      $use = "encoded";
    }
    if ($use == 'encoded' && ($encodingStyle = '')) {
      $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
    }
    $this->operations[$name] = array(
      'name' => $name,
      'in' => $in,
      'out' => $out,
      'namespace' => $namespace,
      'soapaction' => $soapaction,
      'style' => $style,
    );
    if ($this->wsdl) {
      $this->wsdl
        ->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
    }
    return true;
  }

  /**
   * Specify a fault to be returned to the client.
   * This also acts as a flag to the server that a fault has occured.
   *
   * @param	string $faultcode
   * @param	string $faultstring
   * @param	string $faultactor
   * @param	string $faultdetail
   * @access   public
   */
  function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '') {
    if ($faultdetail == '' && $this->debug_flag) {
      $faultdetail = $this
        ->getDebug();
    }
    $this->fault = new soap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
    $this->fault->soap_defencoding = $this->soap_defencoding;
  }

  /**
   * Sets up wsdl object.
   * Acts as a flag to enable internal WSDL generation
   *
   * @param string $serviceName, name of the service
   * @param mixed $namespace optional 'tns' service namespace or false
   * @param mixed $endpoint optional URL of service endpoint or false
   * @param string $style optional (rpc|document) WSDL style (also specified by operation)
   * @param string $transport optional SOAP transport
   * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
   */
  function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) {
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER)) {
      $SERVER_NAME = $_SERVER['SERVER_NAME'];
      $SERVER_PORT = $_SERVER['SERVER_PORT'];
      $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
      $HTTPS = $_SERVER['HTTPS'];
    }
    elseif (isset($HTTP_SERVER_VARS)) {
      $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
      $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
      $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
      $HTTPS = $HTTP_SERVER_VARS['HTTPS'];
    }
    else {
      $this
        ->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
    }
    if ($SERVER_PORT == 80) {
      $SERVER_PORT = '';
    }
    else {
      $SERVER_PORT = ':' . $SERVER_PORT;
    }
    if (false == $namespace) {
      $namespace = "http://{$SERVER_NAME}/soap/{$serviceName}";
    }
    if (false == $endpoint) {
      if ($HTTPS == '1' || $HTTPS == 'on') {
        $SCHEME = 'https';
      }
      else {
        $SCHEME = 'http';
      }
      $endpoint = "{$SCHEME}://{$SERVER_NAME}{$SERVER_PORT}{$SCRIPT_NAME}";
    }
    if (false == $schemaTargetNamespace) {
      $schemaTargetNamespace = $namespace;
    }
    $this->wsdl = new wsdl();
    $this->wsdl->serviceName = $serviceName;
    $this->wsdl->endpoint = $endpoint;
    $this->wsdl->namespaces['tns'] = $namespace;
    $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
    $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
    if ($schemaTargetNamespace != $namespace) {
      $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
    }
    $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
    $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
    $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array(
      'location' => '',
      'loaded' => true,
    );
    $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array(
      'location' => '',
      'loaded' => true,
    );
    $this->wsdl->bindings[$serviceName . 'Binding'] = array(
      'name' => $serviceName . 'Binding',
      'style' => $style,
      'transport' => $transport,
      'portType' => $serviceName . 'PortType',
    );
    $this->wsdl->ports[$serviceName . 'Port'] = array(
      'binding' => $serviceName . 'Binding',
      'location' => $endpoint,
      'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/',
    );
  }

}

/**
* parses a WSDL file, allows access to it's data, other utility methods
* 
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access public 
*/
class wsdl extends nusoap_base {

  // URL or filename of the root of this WSDL
  var $wsdl;

  // define internal arrays of bindings, ports, operations, messages, etc.
  var $schemas = [];
  var $currentSchema;
  var $message = [];
  var $complexTypes = [];
  var $messages = [];
  var $currentMessage;
  var $currentOperation;
  var $portTypes = [];
  var $currentPortType;
  var $bindings = [];
  var $currentBinding;
  var $ports = [];
  var $currentPort;
  var $opData = [];
  var $status = '';
  var $documentation = false;
  var $endpoint = '';

  // array of wsdl docs to import
  var $import = [];

  // parser vars
  var $parser;
  var $position = 0;
  var $depth = 0;
  var $depth_array = [];

  // for getting wsdl
  var $proxyhost = '';
  var $proxyport = '';
  var $proxyusername = '';
  var $proxypassword = '';
  var $timeout = 0;
  var $response_timeout = 30;

  /**
   * constructor
   *
   * @param string $wsdl WSDL document URL
   * @param string $proxyhost
   * @param string $proxyport
   * @param string $proxyusername
   * @param string $proxypassword
   * @param integer $timeout set the connection timeout
   * @param integer $response_timeout set the response timeout
   * @access public
   */
  function wsdl($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30) {
    parent::nusoap_base();
    $this->wsdl = $wsdl;
    $this->proxyhost = $proxyhost;
    $this->proxyport = $proxyport;
    $this->proxyusername = $proxyusername;
    $this->proxypassword = $proxypassword;
    $this->timeout = $timeout;
    $this->response_timeout = $response_timeout;

    // parse wsdl file
    if ($wsdl != "") {
      $this
        ->debug('initial wsdl URL: ' . $wsdl);
      $this
        ->parseWSDL($wsdl);
    }

    // imports
    // TODO: handle imports more properly, grabbing them in-line and nesting them
    $imported_urls = array();
    $imported = 1;
    while ($imported > 0) {
      $imported = 0;

      // Schema imports
      foreach ($this->schemas as $ns => $list) {
        foreach ($list as $xs) {
          $wsdlparts = parse_url($this->wsdl);

          // this is bogusly simple!
          foreach ($xs->imports as $ns2 => $list2) {
            for ($ii = 0; $ii < count($list2); $ii++) {
              if (!$list2[$ii]['loaded']) {
                $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
                $url = $list2[$ii]['location'];
                if ($url != '') {
                  $urlparts = parse_url($url);
                  if (!isset($urlparts['host'])) {
                    $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
                  }
                  if (!in_array($url, $imported_urls)) {
                    $this
                      ->parseWSDL($url);
                    $imported++;
                    $imported_urls[] = $url;
                  }
                }
                else {
                  $this
                    ->debug("Unexpected scenario: empty URL for unloaded import");
                }
              }
            }
          }
        }
      }

      // WSDL imports
      $wsdlparts = parse_url($this->wsdl);

      // this is bogusly simple!
      foreach ($this->import as $ns => $list) {
        for ($ii = 0; $ii < count($list); $ii++) {
          if (!$list[$ii]['loaded']) {
            $this->import[$ns][$ii]['loaded'] = true;
            $url = $list[$ii]['location'];
            if ($url != '') {
              $urlparts = parse_url($url);
              if (!isset($urlparts['host'])) {
                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
              }
              if (!in_array($url, $imported_urls)) {
                $this
                  ->parseWSDL($url);
                $imported++;
                $imported_urls[] = $url;
              }
            }
            else {
              $this
                ->debug("Unexpected scenario: empty URL for unloaded import");
            }
          }
        }
      }
    }

    // add new data to operation data
    foreach ($this->bindings as $binding => $bindingData) {
      if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
        foreach ($bindingData['operations'] as $operation => $data) {
          $this
            ->debug('post-parse data gathering for ' . $operation);
          $this->bindings[$binding]['operations'][$operation]['input'] = isset($this->bindings[$binding]['operations'][$operation]['input']) ? array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) : $this->portTypes[$bindingData['portType']][$operation]['input'];
          $this->bindings[$binding]['operations'][$operation]['output'] = isset($this->bindings[$binding]['operations'][$operation]['output']) ? array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) : $this->portTypes[$bindingData['portType']][$operation]['output'];
          if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
            $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
          }
          if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
            $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
          }
          if (isset($bindingData['style'])) {
            $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
          }
          $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
          $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
          $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
        }
      }
    }
  }

  /**
   * parses the wsdl document
   *
   * @param string $wsdl path or URL
   * @access private
   */
  function parseWSDL($wsdl = '') {
    if ($wsdl == '') {
      $this
        ->debug('no wsdl passed to parseWSDL()!!');
      $this
        ->setError('no wsdl passed to parseWSDL()!!');
      return false;
    }

    // parse $wsdl for url format
    $wsdl_props = parse_url($wsdl);
    if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
      $this
        ->debug('getting WSDL http(s) URL ' . $wsdl);

      // get wsdl
      $tr = new soap_transport_http($wsdl);
      $tr->request_method = 'GET';
      $tr->useSOAPAction = false;
      if ($this->proxyhost && $this->proxyport) {
        $tr
          ->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
      }
      $tr
        ->setEncoding('gzip, deflate');
      $wsdl_string = $tr
        ->send('', $this->timeout, $this->response_timeout);

      //$this->debug("WSDL request\n" . $tr->outgoing_payload);

      //$this->debug("WSDL response\n" . $tr->incoming_payload);
      $this
        ->appendDebug($tr
        ->getDebug());

      // catch errors
      if ($err = $tr
        ->getError()) {
        $errstr = 'HTTP ERROR: ' . $err;
        $this
          ->debug($errstr);
        $this
          ->setError($errstr);
        unset($tr);
        return false;
      }
      unset($tr);
      $this
        ->debug("got WSDL URL");
    }
    else {

      // $wsdl is not http(s), so treat it as a file URL or plain file path
      if (isset($wsdl_props['scheme']) && $wsdl_props['scheme'] == 'file' && isset($wsdl_props['path'])) {
        $path = isset($wsdl_props['host']) ? $wsdl_props['host'] . ':' . $wsdl_props['path'] : $wsdl_props['path'];
      }
      else {
        $path = $wsdl;
      }
      $this
        ->debug('getting WSDL file ' . $path);
      if ($fp = @fopen($path, 'r')) {
        $wsdl_string = '';
        while ($data = fread($fp, 32768)) {
          $wsdl_string .= $data;
        }
        fclose($fp);
      }
      else {
        $errstr = "Bad path to WSDL file {$path}";
        $this
          ->debug($errstr);
        $this
          ->setError($errstr);
        return false;
      }
    }
    $this
      ->debug('Parse WSDL');

    // end new code added
    // Create an XML parser.
    $this->parser = xml_parser_create();

    // Set the options for parsing the XML data.
    // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);

    // Set the object for the parser.
    xml_set_object($this->parser, $this);

    // Set the element handlers for the parser.
    xml_set_element_handler($this->parser, 'start_element', 'end_element');
    xml_set_character_data_handler($this->parser, 'character_data');

    // Parse the XML file.
    if (!xml_parse($this->parser, $wsdl_string, true)) {

      // Display an error message.
      $errstr = sprintf('XML error parsing WSDL from %s on line %d: %s', $wsdl, xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
      $this
        ->debug($errstr);
      $this
        ->debug("XML payload:\n" . $wsdl_string);
      $this
        ->setError($errstr);
      return false;
    }

    // free the parser
    xml_parser_free($this->parser);
    $this
      ->debug('Parsing WSDL done');

    // catch wsdl parse errors
    if ($this
      ->getError()) {
      return false;
    }
    return true;
  }

  /**
   * start-element handler
   *
   * @param string $parser XML parser object
   * @param string $name element name
   * @param string $attrs associative array of attributes
   * @access private
   */
  function start_element($parser, $name, $attrs) {
    if ($this->status == 'schema') {
      $this->currentSchema
        ->schemaStartElement($parser, $name, $attrs);
      $this
        ->appendDebug($this->currentSchema
        ->getDebug());
      $this->currentSchema
        ->clearDebug();
    }
    elseif (ereg('schema$', $name)) {
      $this
        ->debug('Parsing WSDL schema');

      // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
      $this->status = 'schema';
      $this->currentSchema = new xmlschema('', '', $this->namespaces);
      $this->currentSchema
        ->schemaStartElement($parser, $name, $attrs);
      $this
        ->appendDebug($this->currentSchema
        ->getDebug());
      $this->currentSchema
        ->clearDebug();
    }
    else {

      // position in the total number of elements, starting from 0
      $pos = $this->position++;
      $depth = $this->depth++;

      // set self as current value for this depth
      $this->depth_array[$depth] = $pos;
      $this->message[$pos] = array(
        'cdata' => '',
      );

      // process attributes
      if (count($attrs) > 0) {

        // register namespace declarations
        foreach ($attrs as $k => $v) {
          if (ereg("^xmlns", $k)) {
            if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
              $this->namespaces[$ns_prefix] = $v;
            }
            else {
              $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
            }
            if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
              $this->XMLSchemaVersion = $v;
              $this->namespaces['xsi'] = $v . '-instance';
            }
          }
        }

        // expand each attribute prefix to its namespace
        foreach ($attrs as $k => $v) {
          $k = strpos($k, ':') ? $this
            ->expandQname($k) : $k;
          if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
            $v = strpos($v, ':') ? $this
              ->expandQname($v) : $v;
          }
          $eAttrs[$k] = $v;
        }
        $attrs = $eAttrs;
      }
      else {
        $attrs = array();
      }

      // get element prefix, namespace and name
      if (ereg(':', $name)) {

        // get ns prefix
        $prefix = substr($name, 0, strpos($name, ':'));

        // get ns
        $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';

        // get unqualified name
        $name = substr(strstr($name, ':'), 1);
      }

      // process attributes, expanding any prefixes to namespaces
      // find status, register data
      switch ($this->status) {
        case 'message':
          if ($name == 'part') {
            if (isset($attrs['type'])) {
              $this
                ->debug("msg " . $this->currentMessage . ": found part {$attrs['name']}: " . implode(',', $attrs));
              $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
            }
            if (isset($attrs['element'])) {
              $this
                ->debug("msg " . $this->currentMessage . ": found part {$attrs['name']}: " . implode(',', $attrs));
              $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
            }
          }
          break;
        case 'portType':
          switch ($name) {
            case 'operation':
              $this->currentPortOperation = $attrs['name'];
              $this
                ->debug("portType {$this->currentPortType} operation: {$this->currentPortOperation}");
              if (isset($attrs['parameterOrder'])) {
                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
              }
              break;
            case 'documentation':
              $this->documentation = true;
              break;

            // merge input/output data
            default:
              $m = isset($attrs['message']) ? $this
                ->getLocalPart($attrs['message']) : '';
              $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
              break;
          }
          break;
        case 'binding':
          switch ($name) {
            case 'binding':

              // get ns prefix
              if (isset($attrs['style'])) {
                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
              }
              $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
              break;
            case 'header':
              $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
              break;
            case 'operation':
              if (isset($attrs['soapAction'])) {
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
              }
              if (isset($attrs['style'])) {
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
              }
              if (isset($attrs['name'])) {
                $this->currentOperation = $attrs['name'];
                $this
                  ->debug("current binding operation: {$this->currentOperation}");
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
              }
              break;
            case 'input':
              $this->opStatus = 'input';
              break;
            case 'output':
              $this->opStatus = 'output';
              break;
            case 'body':
              if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
              }
              else {
                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
              }
              break;
          }
          break;
        case 'service':
          switch ($name) {
            case 'port':
              $this->currentPort = $attrs['name'];
              $this
                ->debug('current port: ' . $this->currentPort);
              $this->ports[$this->currentPort]['binding'] = $this
                ->getLocalPart($attrs['binding']);
              break;
            case 'address':
              $this->ports[$this->currentPort]['location'] = $attrs['location'];
              $this->ports[$this->currentPort]['bindingType'] = $namespace;
              $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
              $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
              break;
          }
          break;
      }

      // set status
      switch ($name) {
        case 'import':
          if (isset($attrs['location'])) {
            $this->import[$attrs['namespace']][] = array(
              'location' => $attrs['location'],
              'loaded' => false,
            );
            $this
              ->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
          }
          else {
            $this->import[$attrs['namespace']][] = array(
              'location' => '',
              'loaded' => true,
            );
            if (!$this
              ->getPrefixFromNamespace($attrs['namespace'])) {
              $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
            }
            $this
              ->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
          }
          break;

        //wait for schema

        //case 'types':

        //	$this->status = 'schema';
        //	break;
        case 'message':
          $this->status = 'message';
          $this->messages[$attrs['name']] = array();
          $this->currentMessage = $attrs['name'];
          break;
        case 'portType':
          $this->status = 'portType';
          $this->portTypes[$attrs['name']] = array();
          $this->currentPortType = $attrs['name'];
          break;
        case "binding":
          if (isset($attrs['name'])) {

            // get binding name
            if (strpos($attrs['name'], ':')) {
              $this->currentBinding = $this
                ->getLocalPart($attrs['name']);
            }
            else {
              $this->currentBinding = $attrs['name'];
            }
            $this->status = 'binding';
            $this->bindings[$this->currentBinding]['portType'] = $this
              ->getLocalPart($attrs['type']);
            $this
              ->debug("current binding: {$this->currentBinding} of portType: " . $attrs['type']);
          }
          break;
        case 'service':
          $this->serviceName = $attrs['name'];
          $this->status = 'service';
          $this
            ->debug('current service: ' . $this->serviceName);
          break;
        case 'definitions':
          foreach ($attrs as $name => $value) {
            $this->wsdl_info[$name] = $value;
          }
          break;
      }
    }
  }

  /**
   * end-element handler
   *
   * @param string $parser XML parser object
   * @param string $name element name
   * @access private
   */
  function end_element($parser, $name) {

    // unset schema status
    if (ereg('schema$', $name)) {
      $this->status = "";
      $this
        ->appendDebug($this->currentSchema
        ->getDebug());
      $this->currentSchema
        ->clearDebug();
      $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
      $this
        ->debug('Parsing WSDL schema done');
    }
    if ($this->status == 'schema') {
      $this->currentSchema
        ->schemaEndElement($parser, $name);
    }
    else {

      // bring depth down a notch
      $this->depth--;
    }

    // end documentation
    if ($this->documentation) {

      //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.

      //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
      $this->documentation = false;
    }
  }

  /**
   * element content handler
   *
   * @param string $parser XML parser object
   * @param string $data element content
   * @access private
   */
  function character_data($parser, $data) {
    $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
    if (isset($this->message[$pos]['cdata'])) {
      $this->message[$pos]['cdata'] .= $data;
    }
    if ($this->documentation) {
      $this->documentation .= $data;
    }
  }
  function getBindingData($binding) {
    if (is_array($this->bindings[$binding])) {
      return $this->bindings[$binding];
    }
  }

  /**
   * returns an assoc array of operation names => operation data
   *
   * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
   * @return array
   * @access public
   */
  function getOperations($bindingType = 'soap') {
    $ops = array();
    if ($bindingType == 'soap') {
      $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    }

    // loop thru ports
    foreach ($this->ports as $port => $portData) {

      // binding type of port matches parameter
      if ($portData['bindingType'] == $bindingType) {

        //$this->debug("getOperations for port $port");

        //$this->debug("port data: " . $this->varDump($portData));

        //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));

        // merge bindings
        if (isset($this->bindings[$portData['binding']]['operations'])) {
          $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
        }
      }
    }
    return $ops;
  }

  /**
   * returns an associative array of data necessary for calling an operation
   *
   * @param string $operation , name of operation
   * @param string $bindingType , type of binding eg: soap
   * @return array
   * @access public
   */
  function getOperationData($operation, $bindingType = 'soap') {
    if ($bindingType == 'soap') {
      $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    }

    // loop thru ports
    foreach ($this->ports as $port => $portData) {

      // binding type of port matches parameter
      if ($portData['bindingType'] == $bindingType) {

        // get binding

        //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
        foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {

          // note that we could/should also check the namespace here
          if ($operation == $bOperation) {
            $opData = $this->bindings[$portData['binding']]['operations'][$operation];
            return $opData;
          }
        }
      }
    }
  }

  /**
   * returns an associative array of data necessary for calling an operation
   *
   * @param string $soapAction soapAction for operation
   * @param string $bindingType type of binding eg: soap
   * @return array
   * @access public
   */
  function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
    if ($bindingType == 'soap') {
      $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    }

    // loop thru ports
    foreach ($this->ports as $port => $portData) {

      // binding type of port matches parameter
      if ($portData['bindingType'] == $bindingType) {

        // loop through operations for the binding
        foreach ($this->bindings[$portData['binding']]['operations'] as $bOperation => $opData) {
          if ($opData['soapAction'] == $soapAction) {
            return $opData;
          }
        }
      }
    }
  }

  /**
   * returns an array of information about a given type
   * returns false if no type exists by the given name
   *
   *	 typeDef = array(
   *	 'elements' => array(), // refs to elements array
   *	'restrictionBase' => '',
   *	'phpType' => '',
   *	'order' => '(sequence|all)',
   *	'attrs' => array() // refs to attributes array
   *	)
   *
   * @param $type string the type
   * @param $ns string namespace (not prefix) of the type
   * @return mixed
   * @access public
   * @see xmlschema
   */
  function getTypeDef($type, $ns) {
    $this
      ->debug("in getTypeDef: type={$type}, ns={$ns}");
    if (!$ns && isset($this->namespaces['tns'])) {
      $ns = $this->namespaces['tns'];
      $this
        ->debug("in getTypeDef: type namespace forced to {$ns}");
    }
    if (isset($this->schemas[$ns])) {
      $this
        ->debug("in getTypeDef: have schema for namespace {$ns}");
      for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
        $xs =& $this->schemas[$ns][$i];
        $t = $xs
          ->getTypeDef($type);
        $this
          ->appendDebug($xs
          ->getDebug());
        $xs
          ->clearDebug();
        if ($t) {
          if (!isset($t['phpType'])) {

            // get info for type to tack onto the element
            $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
            $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
            $etype = $this
              ->getTypeDef($uqType, $ns);
            if ($etype) {
              $this
                ->debug("found type for [element] {$type}:");
              $this
                ->debug($this
                ->varDump($etype));
              if (isset($etype['phpType'])) {
                $t['phpType'] = $etype['phpType'];
              }
              if (isset($etype['elements'])) {
                $t['elements'] = $etype['elements'];
              }
              if (isset($etype['attrs'])) {
                $t['attrs'] = $etype['attrs'];
              }
            }
          }
          return $t;
        }
      }
    }
    else {
      $this
        ->debug("in getTypeDef: do not have schema for namespace {$ns}");
    }
    return false;
  }

  /**
   * prints html description of services
   *
   * @access private
   */
  function webDescription() {
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER)) {
      $PHP_SELF = $_SERVER['PHP_SELF'];
    }
    elseif (isset($HTTP_SERVER_VARS)) {
      $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
    }
    else {
      $this
        ->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
    }
    $b = '
		<html><head><title>NuSOAP: ' . $this->serviceName . '</title>
		<style type="text/css">
		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
		    ul      { margin-top: 10px; margin-left: 20px; }
		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
		    .content{
			margin-left: 0px; padding-bottom: 2em; }
		    .nav {
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
			margin-top: 10px; margin-left: 0px; color: #000000;
			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
		    .title {
			font-family: arial; font-size: 26px; color: #ffffff;
			background-color: #999999; width: 105%; margin-left: 0px;
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
		    .hidden {
			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
			font-family: arial; overflow: hidden; width: 600;
			padding: 20px; font-size: 10px; background-color: #999999;
			layer-background-color:#FFFFFF; }
		    a,a:active  { color: charcoal; font-weight: bold; }
		    a:visited   { color: #666666; font-weight: bold; }
		    a:hover     { color: cc3300; font-weight: bold; }
		</style>
		<script language="JavaScript" type="text/javascript">
		<!--
		// POP-UP CAPTIONS...
		function lib_bwcheck(){ //Browsercheck (needed)
		    this.ver=navigator.appVersion
		    this.agent=navigator.userAgent
		    this.dom=document.getElementById?1:0
		    this.opera5=this.agent.indexOf("Opera 5")>-1
		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
		    this.ie=this.ie4||this.ie5||this.ie6
		    this.mac=this.agent.indexOf("Mac")>-1
		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
		    this.ns4=(document.layers && !this.dom)?1:0;
		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
		    return this
		}
		var bw = new lib_bwcheck()
		//Makes crossbrowser object.
		function makeObj(obj){
		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
		    if(!this.evnt) return false
		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
		    this.writeIt=b_writeIt;
		    return this
		}
		// A unit of measure that will be added when setting the position of a layer.
		//var px = bw.ns4||window.opera?"":"px";
		function b_writeIt(text){
		    if (bw.ns4){this.wref.write(text);this.wref.close()}
		    else this.wref.innerHTML = text
		}
		//Shows the messages
		var oDesc;
		function popup(divid){
		    if(oDesc = new makeObj(divid)){
			oDesc.css.visibility = "visible"
		    }
		}
		function popout(){ // Hides message
		    if(oDesc) oDesc.css.visibility = "hidden"
		}
		//-->
		</script>
		</head>
		<body>
		<div class=content>
			<br><br>
			<div class=title>' . $this->serviceName . '</div>
			<div class=nav>
				<p>View the <a href="' . $PHP_SELF . '?wsdl">WSDL</a> for the service.
				Click on an operation name to view it&apos;s details.</p>
				<ul>';
    foreach ($this
      ->getOperations() as $op => $data) {
      $b .= "<li><a href='#' onclick=\"popout();popup('{$op}')\">{$op}</a></li>";

      // create hidden div
      $b .= "<div id='{$op}' class='hidden'>\n\t\t\t\t    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
      foreach ($data as $donnie => $marie) {

        // loop through opdata
        if ($donnie == 'input' || $donnie == 'output') {

          // show input/output data
          $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
          foreach ($marie as $captain => $tenille) {

            // loop through data
            if ($captain == 'parts') {

              // loop thru parts
              $b .= "&nbsp;&nbsp;{$captain}:<br>";

              //if(is_array($tenille)){
              foreach ($tenille as $joanie => $chachi) {
                $b .= "&nbsp;&nbsp;&nbsp;&nbsp;{$joanie}: {$chachi}<br>";
              }

              //}
            }
            else {
              $b .= "&nbsp;&nbsp;{$captain}: {$tenille}<br>";
            }
          }
        }
        else {
          $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> {$marie}<br>";
        }
      }
      $b .= '</div>';
    }
    $b .= '
				<ul>
			</div>
		</div></body></html>';
    return $b;
  }

  /**
   * serialize the parsed wsdl
   *
   * @param mixed $debug whether to put debug=1 in endpoint URL
   * @return string serialization of WSDL
   * @access public
   */
  function serialize($debug = 0) {
    $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
    $xml .= "\n<definitions";
    foreach ($this->namespaces as $k => $v) {
      $xml .= " xmlns:{$k}=\"{$v}\"";
    }

    // 10.9.02 - add poulter fix for wsdl and tns declarations
    if (isset($this->namespaces['wsdl'])) {
      $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
    }
    if (isset($this->namespaces['tns'])) {
      $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
    }
    $xml .= '>';

    // imports
    if (sizeof($this->import) > 0) {
      foreach ($this->import as $ns => $list) {
        foreach ($list as $ii) {
          if ($ii['location'] != '') {
            $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
          }
          else {
            $xml .= '<import namespace="' . $ns . '" />';
          }
        }
      }
    }

    // types
    if (count($this->schemas) >= 1) {
      $xml .= "\n<types>";
      foreach ($this->schemas as $ns => $list) {
        foreach ($list as $xs) {
          $xml .= $xs
            ->serializeSchema();
        }
      }
      $xml .= '</types>';
    }

    // messages
    if (count($this->messages) >= 1) {
      foreach ($this->messages as $msgName => $msgParts) {
        $xml .= "\n<message name=\"" . $msgName . '">';
        if (is_array($msgParts)) {
          foreach ($msgParts as $partName => $partType) {

            // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
            if (strpos($partType, ':')) {
              $typePrefix = $this
                ->getPrefixFromNamespace($this
                ->getPrefix($partType));
            }
            elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {

              // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
              $typePrefix = 'xsd';
            }
            else {
              foreach ($this->typemap as $ns => $types) {
                if (isset($types[$partType])) {
                  $typePrefix = $this
                    ->getPrefixFromNamespace($ns);
                }
              }
              if (!isset($typePrefix)) {
                die("{$partType} has no namespace!");
              }
            }
            $ns = $this
              ->getNamespaceFromPrefix($typePrefix);
            $typeDef = $this
              ->getTypeDef($this
              ->getLocalPart($partType), $ns);
            if ($typeDef['typeClass'] == 'element') {
              $elementortype = 'element';
            }
            else {
              $elementortype = 'type';
            }
            $xml .= '<part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $this
              ->getLocalPart($partType) . '" />';
          }
        }
        $xml .= '</message>';
      }
    }

    // bindings & porttypes
    if (count($this->bindings) >= 1) {
      $binding_xml = '';
      $portType_xml = '';
      foreach ($this->bindings as $bindingName => $attrs) {
        $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
        $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
        $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
        foreach ($attrs['operations'] as $opName => $opParts) {
          $binding_xml .= '<operation name="' . $opName . '">';
          $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '"/>';
          if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
            $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
          }
          else {
            $enc_style = '';
          }
          $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
          if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
            $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
          }
          else {
            $enc_style = '';
          }
          $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
          $binding_xml .= '</operation>';
          $portType_xml .= '<operation name="' . $opParts['name'] . '"';
          if (isset($opParts['parameterOrder'])) {
            $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
          }
          $portType_xml .= '>';
          if (isset($opParts['documentation']) && $opParts['documentation'] != '') {
            $portType_xml .= '<documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
          }
          $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
          $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
          $portType_xml .= '</operation>';
        }
        $portType_xml .= '</portType>';
        $binding_xml .= '</binding>';
      }
      $xml .= $portType_xml . $binding_xml;
    }

    // services
    $xml .= "\n<service name=\"" . $this->serviceName . '">';
    if (count($this->ports) >= 1) {
      foreach ($this->ports as $pName => $attrs) {
        $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
        $xml .= '<soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
        $xml .= '</port>';
      }
    }
    $xml .= '</service>';
    return $xml . "\n</definitions>";
  }

  /**
   * serialize PHP values according to a WSDL message definition
   *
   * TODO
   * - multi-ref serialization
   * - validate PHP values against type definitions, return errors if invalid
   *
   * @param string $operation operation name
   * @param string $direction (input|output)
   * @param mixed $parameters parameter value(s)
   * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
   * @access public
   */
  function serializeRPCParameters($operation, $direction, $parameters) {
    $this
      ->debug("in serializeRPCParameters: operation={$operation}, direction={$direction}, XMLSchemaVersion={$this->XMLSchemaVersion}");
    $this
      ->appendDebug('parameters=' . $this
      ->varDump($parameters));
    if ($direction != 'input' && $direction != 'output') {
      $this
        ->debug('The value of the \\$direction argument needs to be either "input" or "output"');
      $this
        ->setError('The value of the \\$direction argument needs to be either "input" or "output"');
      return false;
    }
    if (!($opData = $this
      ->getOperationData($operation))) {
      $this
        ->debug('Unable to retrieve WSDL data for operation: ' . $operation);
      $this
        ->setError('Unable to retrieve WSDL data for operation: ' . $operation);
      return false;
    }
    $this
      ->debug('opData:');
    $this
      ->appendDebug($this
      ->varDump($opData));

    // Get encoding style for output and set to current
    $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
    if ($direction == 'input' && isset($opData['output']['encodingStyle']) && $opData['output']['encodingStyle'] != $encodingStyle) {
      $encodingStyle = $opData['output']['encodingStyle'];
      $enc_style = $encodingStyle;
    }

    // set input params
    $xml = '';
    if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
      $use = $opData[$direction]['use'];
      $this
        ->debug('have ' . count($opData[$direction]['parts']) . ' part(s) to serialize');
      if (is_array($parameters)) {
        $parametersArrayType = $this
          ->isArraySimpleOrStruct($parameters);
        $this
          ->debug('have ' . count($parameters) . ' parameter(s) provided as ' . $parametersArrayType . ' to serialize');
        foreach ($opData[$direction]['parts'] as $name => $type) {
          $this
            ->debug('serializing part "' . $name . '" of type "' . $type . '"');

          // Track encoding style
          if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
            $encodingStyle = $opData[$direction]['encodingStyle'];
            $enc_style = $encodingStyle;
          }
          else {
            $enc_style = false;
          }

          // NOTE: add error handling here
          // if serializeType returns false, then catch global error and fault
          if ($parametersArrayType == 'arraySimple') {
            $p = array_shift($parameters);
            $this
              ->debug('calling serializeType w/indexed param');
            $xml .= $this
              ->serializeType($name, $type, $p, $use, $enc_style);
          }
          elseif (isset($parameters[$name])) {
            $this
              ->debug('calling serializeType w/named param');
            $xml .= $this
              ->serializeType($name, $type, $parameters[$name], $use, $enc_style);
          }
          else {

            // TODO: only send nillable
            $this
              ->debug('calling serializeType w/null param');
            $xml .= $this
              ->serializeType($name, $type, null, $use, $enc_style);
          }
        }
      }
      else {
        $this
          ->debug('no parameters passed.');
      }
    }
    $this
      ->debug("serializeRPCParameters returning: {$xml}");
    return $xml;
  }

  /**
   * serialize a PHP value according to a WSDL message definition
   *
   * TODO
   * - multi-ref serialization
   * - validate PHP values against type definitions, return errors if invalid
   *
   * @param string $ type name
   * @param mixed $ param value
   * @return mixed new param or false if initial value didn't validate
   * @access public
   * @deprecated
   */
  function serializeParameters($operation, $direction, $parameters) {
    $this
      ->debug("in serializeParameters: operation={$operation}, direction={$direction}, XMLSchemaVersion={$this->XMLSchemaVersion}");
    $this
      ->appendDebug('parameters=' . $this
      ->varDump($parameters));
    if ($direction != 'input' && $direction != 'output') {
      $this
        ->debug('The value of the \\$direction argument needs to be either "input" or "output"');
      $this
        ->setError('The value of the \\$direction argument needs to be either "input" or "output"');
      return false;
    }
    if (!($opData = $this
      ->getOperationData($operation))) {
      $this
        ->debug('Unable to retrieve WSDL data for operation: ' . $operation);
      $this
        ->setError('Unable to retrieve WSDL data for operation: ' . $operation);
      return false;
    }
    $this
      ->debug('opData:');
    $this
      ->appendDebug($this
      ->varDump($opData));

    // Get encoding style for output and set to current
    $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
    if ($direction == 'input' && isset($opData['output']['encodingStyle']) && $opData['output']['encodingStyle'] != $encodingStyle) {
      $encodingStyle = $opData['output']['encodingStyle'];
      $enc_style = $encodingStyle;
    }

    // set input params
    $xml = '';
    if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
      $use = $opData[$direction]['use'];
      $this
        ->debug("use={$use}");
      $this
        ->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
      if (is_array($parameters)) {
        $parametersArrayType = $this
          ->isArraySimpleOrStruct($parameters);
        $this
          ->debug('have ' . $parametersArrayType . ' parameters');
        foreach ($opData[$direction]['parts'] as $name => $type) {
          $this
            ->debug('serializing part "' . $name . '" of type "' . $type . '"');

          // Track encoding style
          if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
            $encodingStyle = $opData[$direction]['encodingStyle'];
            $enc_style = $encodingStyle;
          }
          else {
            $enc_style = false;
          }

          // NOTE: add error handling here
          // if serializeType returns false, then catch global error and fault
          if ($parametersArrayType == 'arraySimple') {
            $p = array_shift($parameters);
            $this
              ->debug('calling serializeType w/indexed param');
            $xml .= $this
              ->serializeType($name, $type, $p, $use, $enc_style);
          }
          elseif (isset($parameters[$name])) {
            $this
              ->debug('calling serializeType w/named param');
            $xml .= $this
              ->serializeType($name, $type, $parameters[$name], $use, $enc_style);
          }
          else {

            // TODO: only send nillable
            $this
              ->debug('calling serializeType w/null param');
            $xml .= $this
              ->serializeType($name, $type, null, $use, $enc_style);
          }
        }
      }
      else {
        $this
          ->debug('no parameters passed.');
      }
    }
    $this
      ->debug("serializeParameters returning: {$xml}");
    return $xml;
  }

  /**
   * serializes a PHP value according a given type definition
   *
   * @param string $name name of value (part or element)
   * @param string $type XML schema type of value (type or element)
   * @param mixed $value a native PHP value (parameter value)
   * @param string $use use for part (encoded|literal)
   * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
   * @param boolean $unqualified a kludge for what should be XML namespace form handling
   * @return string value serialized as an XML string
   * @access private
   */
  function serializeType($name, $type, $value, $use = 'encoded', $encodingStyle = false, $unqualified = false) {
    $this
      ->debug("in serializeType: name={$name}, type={$type}, use={$use}, encodingStyle={$encodingStyle}, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
    $this
      ->appendDebug("value=" . $this
      ->varDump($value));
    if ($use == 'encoded' && $encodingStyle) {
      $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
    }

    // if a soapval has been supplied, let its type override the WSDL
    if (is_object($value) && get_class($value) == 'soapval') {
      if ($value->type_ns) {
        $type = $value->type_ns . ':' . $value->type;
        $forceType = true;
        $this
          ->debug("in serializeType: soapval overrides type to {$type}");
      }
      elseif ($value->type) {
        $type = $value->type;
        $forceType = true;
        $this
          ->debug("in serializeType: soapval overrides type to {$type}");
      }
      else {
        $forceType = false;
        $this
          ->debug("in serializeType: soapval does not override type");
      }
      $attrs = $value->attributes;
      $value = $value->value;
      $this
        ->debug("in serializeType: soapval overrides value to {$value}");
      if ($attrs) {
        if (!is_array($value)) {
          $value['!'] = $value;
        }
        foreach ($attrs as $n => $v) {
          $value['!' . $n] = $v;
        }
        $this
          ->debug("in serializeType: soapval provides attributes");
      }
    }
    else {
      $forceType = false;
    }
    $xml = '';
    if (strpos($type, ':')) {
      $uqType = substr($type, strrpos($type, ':') + 1);
      $ns = substr($type, 0, strrpos($type, ':'));
      $this
        ->debug("in serializeType: got a prefixed type: {$uqType}, {$ns}");
      if ($this
        ->getNamespaceFromPrefix($ns)) {
        $ns = $this
          ->getNamespaceFromPrefix($ns);
        $this
          ->debug("in serializeType: expanded prefixed type: {$uqType}, {$ns}");
      }
      if ($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/') {
        $this
          ->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
        if ($unqualified && $use == 'literal') {
          $elementNS = " xmlns=\"\"";
        }
        else {
          $elementNS = '';
        }
        if (is_null($value)) {
          if ($use == 'literal') {

            // TODO: depends on minOccurs
            $xml = "<{$name}{$elementNS}/>";
          }
          else {

            // TODO: depends on nillable, which should be checked before calling this method
            $xml = "<{$name}{$elementNS} xsi:nil=\"true\" xsi:type=\"" . $this
              ->getPrefixFromNamespace($ns) . ":{$uqType}\"/>";
          }
          $this
            ->debug("in serializeType: returning: {$xml}");
          return $xml;
        }
        if ($uqType == 'boolean') {
          if (is_string($value) && $value == 'false' || !$value) {
            $value = 'false';
          }
          else {
            $value = 'true';
          }
        }
        if ($uqType == 'string' && gettype($value) == 'string') {
          $value = $this
            ->expandEntities($value);
        }
        if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
          $value = sprintf("%.0lf", $value);
        }

        // it's a scalar
        // TODO: what about null/nil values?
        // check type isn't a custom type extending xmlschema namespace
        if (!$this
          ->getTypeDef($uqType, $ns)) {
          if ($use == 'literal') {
            if ($forceType) {
              $xml = "<{$name}{$elementNS} xsi:type=\"" . $this
                ->getPrefixFromNamespace($ns) . ":{$uqType}\">{$value}</{$name}>";
            }
            else {
              $xml = "<{$name}{$elementNS}>{$value}</{$name}>";
            }
          }
          else {
            $xml = "<{$name}{$elementNS} xsi:type=\"" . $this
              ->getPrefixFromNamespace($ns) . ":{$uqType}\"{$encodingStyle}>{$value}</{$name}>";
          }
          $this
            ->debug("in serializeType: returning: {$xml}");
          return $xml;
        }
        $this
          ->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
      }
      else {
        if ($ns == 'http://xml.apache.org/xml-soap') {
          $this
            ->debug('in serializeType: appears to be Apache SOAP type');
          if ($uqType == 'Map') {
            $tt_prefix = $this
              ->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
            if (!$tt_prefix) {
              $this
                ->debug('in serializeType: Add namespace for Apache SOAP type');
              $tt_prefix = 'ns' . rand(1000, 9999);
              $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';

              // force this to be added to usedNamespaces
              $tt_prefix = $this
                ->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
            }
            $contents = '';
            foreach ($value as $k => $v) {
              $this
                ->debug("serializing map element: key {$k}, value {$v}");
              $contents .= '<item>';
              $contents .= $this
                ->serialize_val($k, 'key', false, false, false, false, $use);
              $contents .= $this
                ->serialize_val($v, 'value', false, false, false, false, $use);
              $contents .= '</item>';
            }
            if ($use == 'literal') {
              if ($forceType) {
                $xml = "<{$name} xsi:type=\"" . $tt_prefix . ":{$uqType}\">{$contents}</{$name}>";
              }
              else {
                $xml = "<{$name}>{$contents}</{$name}>";
              }
            }
            else {
              $xml = "<{$name} xsi:type=\"" . $tt_prefix . ":{$uqType}\"{$encodingStyle}>{$contents}</{$name}>";
            }
            $this
              ->debug("in serializeType: returning: {$xml}");
            return $xml;
          }
          $this
            ->debug('in serializeType: Apache SOAP type, but only support Map');
        }
      }
    }
    else {

      // TODO: should the type be compared to types in XSD, and the namespace
      // set to XSD if the type matches?
      $this
        ->debug("in serializeType: No namespace for type {$type}");
      $ns = '';
      $uqType = $type;
    }
    if (!($typeDef = $this
      ->getTypeDef($uqType, $ns))) {
      $this
        ->setError("{$type} ({$uqType}) is not a supported type.");
      $this
        ->debug("in serializeType: {$type} ({$uqType}) is not a supported type.");
      return false;
    }
    else {
      $this
        ->debug("in serializeType: found typeDef");
      $this
        ->appendDebug('typeDef=' . $this
        ->varDump($typeDef));
    }
    $phpType = $typeDef['phpType'];
    $this
      ->debug("in serializeType: uqType: {$uqType}, ns: {$ns}, phptype: {$phpType}, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));

    // if php type == struct, map value to the <all> element names
    if ($phpType == 'struct') {
      if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
        $elementName = $uqType;
        if (isset($typeDef['form']) && $typeDef['form'] == 'qualified') {
          $elementNS = " xmlns=\"{$ns}\"";
        }
        else {
          $elementNS = " xmlns=\"\"";
        }
      }
      else {
        $elementName = $name;
        if ($unqualified) {
          $elementNS = " xmlns=\"\"";
        }
        else {
          $elementNS = '';
        }
      }
      if (is_null($value)) {
        if ($use == 'literal') {

          // TODO: depends on minOccurs
          $xml = "<{$elementName}{$elementNS}/>";
        }
        else {
          $xml = "<{$elementName}{$elementNS} xsi:nil=\"true\" xsi:type=\"" . $this
            ->getPrefixFromNamespace($ns) . ":{$uqType}\"/>";
        }
        $this
          ->debug("in serializeType: returning: {$xml}");
        return $xml;
      }
      if (is_object($value)) {
        $value = get_object_vars($value);
      }
      if (is_array($value)) {
        $elementAttrs = $this
          ->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
        if ($use == 'literal') {
          if ($forceType) {
            $xml = "<{$elementName}{$elementNS}{$elementAttrs} xsi:type=\"" . $this
              ->getPrefixFromNamespace($ns) . ":{$uqType}\">";
          }
          else {
            $xml = "<{$elementName}{$elementNS}{$elementAttrs}>";
          }
        }
        else {
          $xml = "<{$elementName}{$elementNS}{$elementAttrs} xsi:type=\"" . $this
            ->getPrefixFromNamespace($ns) . ":{$uqType}\"{$encodingStyle}>";
        }
        $xml .= $this
          ->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
        $xml .= "</{$elementName}>";
      }
      else {
        $this
          ->debug("in serializeType: phpType is struct, but value is not an array");
        $this
          ->setError("phpType is struct, but value is not an array: see debug output for details");
        $xml = '';
      }
    }
    elseif ($phpType == 'array') {
      if (isset($typeDef['form']) && $typeDef['form'] == 'qualified') {
        $elementNS = " xmlns=\"{$ns}\"";
      }
      else {
        if ($unqualified) {
          $elementNS = " xmlns=\"\"";
        }
        else {
          $elementNS = '';
        }
      }
      if (is_null($value)) {
        if ($use == 'literal') {

          // TODO: depends on minOccurs
          $xml = "<{$name}{$elementNS}/>";
        }
        else {
          $xml = "<{$name}{$elementNS} xsi:nil=\"true\" xsi:type=\"" . $this
            ->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ":Array\" " . $this
            ->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':arrayType="' . $this
            ->getPrefixFromNamespace($this
            ->getPrefix($typeDef['arrayType'])) . ':' . $this
            ->getLocalPart($typeDef['arrayType']) . "[0]\"/>";
        }
        $this
          ->debug("in serializeType: returning: {$xml}");
        return $xml;
      }
      if (isset($typeDef['multidimensional'])) {
        $nv = array();
        foreach ($value as $v) {
          $cols = ',' . sizeof($v);
          $nv = array_merge($nv, $v);
        }
        $value = $nv;
      }
      else {
        $cols = '';
      }
      if (is_array($value) && sizeof($value) >= 1) {
        $rows = sizeof($value);
        $contents = '';
        foreach ($value as $k => $v) {
          $this
            ->debug("serializing array element: {$k}, {$v} of type: {$typeDef['arrayType']}");

          //if (strpos($typeDef['arrayType'], ':') ) {
          if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
            $contents .= $this
              ->serializeType('item', $typeDef['arrayType'], $v, $use);
          }
          else {
            $contents .= $this
              ->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
          }
        }
      }
      else {
        $rows = 0;
        $contents = null;
      }

      // TODO: for now, an empty value will be serialized as a zero element
      // array.  Revisit this when coding the handling of null/nil values.
      if ($use == 'literal') {
        $xml = "<{$name}{$elementNS}>" . $contents . "</{$name}>";
      }
      else {
        $xml = "<{$name}{$elementNS} xsi:type=\"" . $this
          ->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' . $this
          ->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':arrayType="' . $this
          ->getPrefixFromNamespace($this
          ->getPrefix($typeDef['arrayType'])) . ":" . $this
          ->getLocalPart($typeDef['arrayType']) . "[{$rows}{$cols}]\">" . $contents . "</{$name}>";
      }
    }
    elseif ($phpType == 'scalar') {
      if (isset($typeDef['form']) && $typeDef['form'] == 'qualified') {
        $elementNS = " xmlns=\"{$ns}\"";
      }
      else {
        if ($unqualified) {
          $elementNS = " xmlns=\"\"";
        }
        else {
          $elementNS = '';
        }
      }
      if ($use == 'literal') {
        if ($forceType) {
          $xml = "<{$name}{$elementNS} xsi:type=\"" . $this
            ->getPrefixFromNamespace($ns) . ":{$uqType}\">{$value}</{$name}>";
        }
        else {
          $xml = "<{$name}{$elementNS}>{$value}</{$name}>";
        }
      }
      else {
        $xml = "<{$name}{$elementNS} xsi:type=\"" . $this
          ->getPrefixFromNamespace($ns) . ":{$uqType}\"{$encodingStyle}>{$value}</{$name}>";
      }
    }
    $this
      ->debug("in serializeType: returning: {$xml}");
    return $xml;
  }

  /**
   * serializes the attributes for a complexType
   *
   * @param array $typeDef our internal representation of an XML schema type (or element)
   * @param mixed $value a native PHP value (parameter value)
   * @param string $ns the namespace of the type
   * @param string $uqType the local part of the type
   * @return string value serialized as an XML string
   * @access private
   */
  function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
    $xml = '';
    if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
      $this
        ->debug("serialize attributes for XML Schema type {$ns}:{$uqType}");
      if (is_array($value)) {
        $xvalue = $value;
      }
      elseif (is_object($value)) {
        $xvalue = get_object_vars($value);
      }
      else {
        $this
          ->debug("value is neither an array nor an object for XML Schema type {$ns}:{$uqType}");
        $xvalue = array();
      }
      foreach ($typeDef['attrs'] as $aName => $attrs) {
        if (isset($xvalue['!' . $aName])) {
          $xname = '!' . $aName;
          $this
            ->debug("value provided for attribute {$aName} with key {$xname}");
        }
        elseif (isset($xvalue[$aName])) {
          $xname = $aName;
          $this
            ->debug("value provided for attribute {$aName} with key {$xname}");
        }
        elseif (isset($attrs['default'])) {
          $xname = '!' . $aName;
          $xvalue[$xname] = $attrs['default'];
          $this
            ->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
        }
        else {
          $xname = '';
          $this
            ->debug("no value provided for attribute {$aName}");
        }
        if ($xname) {
          $xml .= " {$aName}=\"" . $this
            ->expandEntities($xvalue[$xname]) . "\"";
        }
      }
    }
    else {
      $this
        ->debug("no attributes to serialize for XML Schema type {$ns}:{$uqType}");
    }
    if (isset($typeDef['extensionBase'])) {
      $ns = $this
        ->getPrefix($typeDef['extensionBase']);
      $uqType = $this
        ->getLocalPart($typeDef['extensionBase']);
      if ($this
        ->getNamespaceFromPrefix($ns)) {
        $ns = $this
          ->getNamespaceFromPrefix($ns);
      }
      if ($typeDef = $this
        ->getTypeDef($uqType, $ns)) {
        $this
          ->debug("serialize attributes for extension base {$ns}:{$uqType}");
        $xml .= $this
          ->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
      }
      else {
        $this
          ->debug("extension base {$ns}:{$uqType} is not a supported type");
      }
    }
    return $xml;
  }

  /**
   * serializes the elements for a complexType
   *
   * @param array $typeDef our internal representation of an XML schema type (or element)
   * @param mixed $value a native PHP value (parameter value)
   * @param string $ns the namespace of the type
   * @param string $uqType the local part of the type
   * @param string $use use for part (encoded|literal)
   * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
   * @return string value serialized as an XML string
   * @access private
   */
  function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false) {
    $xml = '';
    if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
      $this
        ->debug("in serializeComplexTypeElements, serialize elements for XML Schema type {$ns}:{$uqType}");
      if (is_array($value)) {
        $xvalue = $value;
      }
      elseif (is_object($value)) {
        $xvalue = get_object_vars($value);
      }
      else {
        $this
          ->debug("value is neither an array nor an object for XML Schema type {$ns}:{$uqType}");
        $xvalue = array();
      }

      // toggle whether all elements are present - ideally should validate against schema
      if (count($typeDef['elements']) != count($xvalue)) {
        $optionals = true;
      }
      foreach ($typeDef['elements'] as $eName => $attrs) {
        if (!isset($xvalue[$eName])) {
          if (isset($attrs['default'])) {
            $xvalue[$eName] = $attrs['default'];
            $this
              ->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
          }
        }

        // if user took advantage of a minOccurs=0, then only serialize named parameters
        if (isset($optionals) && !isset($xvalue[$eName]) && (!isset($attrs['nillable']) || $attrs['nillable'] != 'true')) {
          if (isset($attrs['minOccurs']) && $attrs['minOccurs'] != '0') {
            $this
              ->debug("apparent error: no value provided for element {$eName} with minOccurs=" . $attrs['minOccurs']);
          }

          // do nothing
          $this
            ->debug("no value provided for complexType element {$eName} and element is not nillable, so serialize nothing");
        }
        else {

          // get value
          if (isset($xvalue[$eName])) {
            $v = $xvalue[$eName];
          }
          else {
            $v = null;
          }
          if (isset($attrs['form'])) {
            $unqualified = $attrs['form'] == 'unqualified';
          }
          else {
            $unqualified = false;
          }
          if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this
            ->isArraySimpleOrStruct($v) == 'arraySimple') {
            $vv = $v;
            foreach ($vv as $k => $v) {
              if (isset($attrs['type']) || isset($attrs['ref'])) {

                // serialize schema-defined type
                $xml .= $this
                  ->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
              }
              else {

                // serialize generic type (can this ever really happen?)
                $this
                  ->debug("calling serialize_val() for {$v}, {$eName}, false, false, false, false, {$use}");
                $xml .= $this
                  ->serialize_val($v, $eName, false, false, false, false, $use);
              }
            }
          }
          else {
            if (isset($attrs['type']) || isset($attrs['ref'])) {

              // serialize schema-defined type
              $xml .= $this
                ->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
            }
            else {

              // serialize generic type (can this ever really happen?)
              $this
                ->debug("calling serialize_val() for {$v}, {$eName}, false, false, false, false, {$use}");
              $xml .= $this
                ->serialize_val($v, $eName, false, false, false, false, $use);
            }
          }
        }
      }
    }
    else {
      $this
        ->debug("no elements to serialize for XML Schema type {$ns}:{$uqType}");
    }
    if (isset($typeDef['extensionBase'])) {
      $ns = $this
        ->getPrefix($typeDef['extensionBase']);
      $uqType = $this
        ->getLocalPart($typeDef['extensionBase']);
      if ($this
        ->getNamespaceFromPrefix($ns)) {
        $ns = $this
          ->getNamespaceFromPrefix($ns);
      }
      if ($typeDef = $this
        ->getTypeDef($uqType, $ns)) {
        $this
          ->debug("serialize elements for extension base {$ns}:{$uqType}");
        $xml .= $this
          ->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
      }
      else {
        $this
          ->debug("extension base {$ns}:{$uqType} is not a supported type");
      }
    }
    return $xml;
  }

  /**
   * adds an XML Schema complex type to the WSDL types
   *
   * @param string	name
   * @param string typeClass (complexType|simpleType|attribute)
   * @param string phpType: currently supported are array and struct (php assoc array)
   * @param string compositor (all|sequence|choice)
   * @param string restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   * @param array elements = array ( name => array(name=>'',type=>'') )
   * @param array attrs = 	array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
   * @param string arrayType: namespace:name (xsd:string)
   * @see xmlschema
   * @access public
   */
  function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = [], $attrs = [], $arrayType = '') {
    if (count($elements) > 0) {
      foreach ($elements as $n => $e) {

        // expand each element
        foreach ($e as $k => $v) {
          $k = strpos($k, ':') ? $this
            ->expandQname($k) : $k;
          $v = strpos($v, ':') ? $this
            ->expandQname($v) : $v;
          $ee[$k] = $v;
        }
        $eElements[$n] = $ee;
      }
      $elements = $eElements;
    }
    if (count($attrs) > 0) {
      foreach ($attrs as $n => $a) {

        // expand each attribute
        foreach ($a as $k => $v) {
          $k = strpos($k, ':') ? $this
            ->expandQname($k) : $k;
          $v = strpos($v, ':') ? $this
            ->expandQname($v) : $v;
          $aa[$k] = $v;
        }
        $eAttrs[$n] = $aa;
      }
      $attrs = $eAttrs;
    }
    $restrictionBase = strpos($restrictionBase, ':') ? $this
      ->expandQname($restrictionBase) : $restrictionBase;
    $arrayType = strpos($arrayType, ':') ? $this
      ->expandQname($arrayType) : $arrayType;
    $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
    $this->schemas[$typens][0]
      ->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
  }

  /**
   * adds an XML Schema simple type to the WSDL types
   *
   * @param string $name
   * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   * @param string $typeClass (should always be simpleType)
   * @param string $phpType (should always be scalar)
   * @param array $enumeration array of values
   * @see xmlschema
   * @access public
   */
  function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = []) {
    $restrictionBase = strpos($restrictionBase, ':') ? $this
      ->expandQname($restrictionBase) : $restrictionBase;
    $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
    $this->schemas[$typens][0]
      ->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
  }

  /**
   * adds an element to the WSDL types
   *
   * @param array $attrs attributes that must include name and type
   * @see xmlschema
   * @access public
   */
  function addElement($attrs) {
    $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
    $this->schemas[$typens][0]
      ->addElement($attrs);
  }

  /**
   * register an operation with the server
   *
   * @param string $name operation (method) name
   * @param array $in assoc array of input values: key = param name, value = param type
   * @param array $out assoc array of output values: key = param name, value = param type
   * @param string $namespace optional The namespace for the operation
   * @param string $soapaction optional The soapaction for the operation
   * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
   * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
   * @param string $documentation optional The description to include in the WSDL
   * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
   * @access public
   */
  function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = '') {
    if ($use == 'encoded' && $encodingStyle == '') {
      $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
    }
    if ($style == 'document') {
      $elements = array();
      foreach ($in as $n => $t) {
        $elements[$n] = array(
          'name' => $n,
          'type' => $t,
        );
      }
      $this
        ->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
      $this
        ->addElement(array(
        'name' => $name,
        'type' => $name . 'RequestType',
      ));
      $in = array(
        'parameters' => 'tns:' . $name,
      );
      $elements = array();
      foreach ($out as $n => $t) {
        $elements[$n] = array(
          'name' => $n,
          'type' => $t,
        );
      }
      $this
        ->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
      $this
        ->addElement(array(
        'name' => $name . 'Response',
        'type' => $name . 'ResponseType',
      ));
      $out = array(
        'parameters' => 'tns:' . $name . 'Response',
      );
    }

    // get binding
    $this->bindings[$this->serviceName . 'Binding']['operations'][$name] = array(
      'name' => $name,
      'binding' => $this->serviceName . 'Binding',
      'endpoint' => $this->endpoint,
      'soapAction' => $soapaction,
      'style' => $style,
      'input' => array(
        'use' => $use,
        'namespace' => $namespace,
        'encodingStyle' => $encodingStyle,
        'message' => $name . 'Request',
        'parts' => $in,
      ),
      'output' => array(
        'use' => $use,
        'namespace' => $namespace,
        'encodingStyle' => $encodingStyle,
        'message' => $name . 'Response',
        'parts' => $out,
      ),
      'namespace' => $namespace,
      'transport' => 'http://schemas.xmlsoap.org/soap/http',
      'documentation' => $documentation,
    );

    // add portTypes
    // add messages
    if ($in) {
      foreach ($in as $pName => $pType) {
        if (strpos($pType, ':')) {
          $pType = $this
            ->getNamespaceFromPrefix($this
            ->getPrefix($pType)) . ":" . $this
            ->getLocalPart($pType);
        }
        $this->messages[$name . 'Request'][$pName] = $pType;
      }
    }
    else {
      $this->messages[$name . 'Request'] = '0';
    }
    if ($out) {
      foreach ($out as $pName => $pType) {
        if (strpos($pType, ':')) {
          $pType = $this
            ->getNamespaceFromPrefix($this
            ->getPrefix($pType)) . ":" . $this
            ->getLocalPart($pType);
        }
        $this->messages[$name . 'Response'][$pName] = $pType;
      }
    }
    else {
      $this->messages[$name . 'Response'] = '0';
    }
    return true;
  }

}

/**
*
* soap_parser class parses SOAP XML messages into native PHP values
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class soap_parser extends nusoap_base {
  var $xml = '';
  var $xml_encoding = '';
  var $method = '';
  var $root_struct = '';
  var $root_struct_name = '';
  var $root_struct_namespace = '';
  var $root_header = '';
  var $document = '';

  // incoming SOAP body (text)
  // determines where in the message we are (envelope,header,body,method)
  var $status = '';
  var $position = 0;
  var $depth = 0;
  var $default_namespace = '';
  var $namespaces = [];
  var $message = [];
  var $parent = '';
  var $fault = false;
  var $fault_code = '';
  var $fault_str = '';
  var $fault_detail = '';
  var $depth_array = [];
  var $debug_flag = true;
  var $soapresponse = NULL;
  var $responseHeaders = '';

  // incoming SOAP headers (text)
  var $body_position = 0;

  // for multiref parsing:
  // array of id => pos
  var $ids = [];

  // array of id => hrefs => pos
  var $multirefs = [];

  // toggle for auto-decoding element content
  var $decode_utf8 = true;

  /**
   * constructor that actually does the parsing
   *
   * @param    string $xml SOAP message
   * @param    string $encoding character encoding scheme of message
   * @param    string $method method for which XML is parsed (unused?)
   * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
   * @access   public
   */
  function soap_parser($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true) {
    parent::nusoap_base();
    $this->xml = $xml;
    $this->xml_encoding = $encoding;
    $this->method = $method;
    $this->decode_utf8 = $decode_utf8;

    // Check whether content has been read.
    if (!empty($xml)) {

      // Check XML encoding
      $pos_xml = strpos($xml, '<?xml');
      if ($pos_xml !== FALSE) {
        $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
        if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
          $xml_encoding = $res[1];
          if (strtoupper($xml_encoding) != $encoding) {
            $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
            $this
              ->debug($err);
            if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
              $this
                ->setError($err);
              return;
            }

            // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
          }
          else {
            $this
              ->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
          }
        }
        else {
          $this
            ->debug('No encoding specified in XML declaration');
        }
      }
      else {
        $this
          ->debug('No XML declaration');
      }
      $this
        ->debug('Entering soap_parser(), length=' . strlen($xml) . ', encoding=' . $encoding);

      // Create an XML parser - why not xml_parser_create_ns?
      $this->parser = xml_parser_create($this->xml_encoding);

      // Set the options for parsing the XML data.

      //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
      xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
      xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);

      // Set the object for the parser.
      xml_set_object($this->parser, $this);

      // Set the element handlers for the parser.
      xml_set_element_handler($this->parser, 'start_element', 'end_element');
      xml_set_character_data_handler($this->parser, 'character_data');

      // Parse the XML file.
      if (!xml_parse($this->parser, $xml, true)) {

        // Display an error message.
        $err = sprintf('XML error parsing SOAP payload on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
        $this
          ->debug($err);
        $this
          ->debug("XML payload:\n" . $xml);
        $this
          ->setError($err);
      }
      else {
        $this
          ->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);

        // get final value
        $this->soapresponse = $this->message[$this->root_struct]['result'];

        // get header value: no, because this is documented as XML string
        //				if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
        //					$this->responseHeaders = $this->message[$this->root_header]['result'];
        //				}
        // resolve hrefs/ids
        if (sizeof($this->multirefs) > 0) {
          foreach ($this->multirefs as $id => $hrefs) {
            $this
              ->debug('resolving multirefs for id: ' . $id);
            $idVal = $this
              ->buildVal($this->ids[$id]);
            if (is_array($idVal) && isset($idVal['!id'])) {
              unset($idVal['!id']);
            }
            foreach ($hrefs as $refPos => $ref) {
              $this
                ->debug('resolving href at pos ' . $refPos);
              $this->multirefs[$id][$refPos] = $idVal;
            }
          }
        }
      }
      xml_parser_free($this->parser);
    }
    else {
      $this
        ->debug('xml was empty, didn\'t parse!');
      $this
        ->setError('xml was empty, didn\'t parse!');
    }
  }

  /**
   * start-element handler
   *
   * @param    resource $parser XML parser object
   * @param    string $name element name
   * @param    array $attrs associative array of attributes
   * @access   private
   */
  function start_element($parser, $name, $attrs) {

    // position in a total number of elements, starting from 0
    // update class level pos
    $pos = $this->position++;

    // and set mine
    $this->message[$pos] = array(
      'pos' => $pos,
      'children' => '',
      'cdata' => '',
    );

    // depth = how many levels removed from root?
    // set mine as current global depth and increment global depth value
    $this->message[$pos]['depth'] = $this->depth++;

    // else add self as child to whoever the current parent is
    if ($pos != 0) {
      $this->message[$this->parent]['children'] .= '|' . $pos;
    }

    // set my parent
    $this->message[$pos]['parent'] = $this->parent;

    // set self as current parent
    $this->parent = $pos;

    // set self as current value for this depth
    $this->depth_array[$this->depth] = $pos;

    // get element prefix
    if (strpos($name, ':')) {

      // get ns prefix
      $prefix = substr($name, 0, strpos($name, ':'));

      // get unqualified name
      $name = substr(strstr($name, ':'), 1);
    }

    // set status
    if ($name == 'Envelope') {
      $this->status = 'envelope';
    }
    elseif ($name == 'Header') {
      $this->root_header = $pos;
      $this->status = 'header';
    }
    elseif ($name == 'Body') {
      $this->status = 'body';
      $this->body_position = $pos;

      // set method
    }
    elseif ($this->status == 'body' && $pos == $this->body_position + 1) {
      $this->status = 'method';
      $this->root_struct_name = $name;
      $this->root_struct = $pos;
      $this->message[$pos]['type'] = 'struct';
      $this
        ->debug("found root struct {$this->root_struct_name}, pos {$this->root_struct}");
    }

    // set my status
    $this->message[$pos]['status'] = $this->status;

    // set name
    $this->message[$pos]['name'] = htmlspecialchars($name);

    // set attrs
    $this->message[$pos]['attrs'] = $attrs;

    // loop through atts, logging ns and type declarations
    $attstr = '';
    foreach ($attrs as $key => $value) {
      $key_prefix = $this
        ->getPrefix($key);
      $key_localpart = $this
        ->getLocalPart($key);

      // if ns declarations, add to class level array of valid namespaces
      if ($key_prefix == 'xmlns') {
        if (ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$', $value)) {
          $this->XMLSchemaVersion = $value;
          $this->namespaces['xsd'] = $this->XMLSchemaVersion;
          $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
        }
        $this->namespaces[$key_localpart] = $value;

        // set method namespace
        if ($name == $this->root_struct_name) {
          $this->methodNamespace = $value;
        }

        // if it's a type declaration, set type
      }
      elseif ($key_localpart == 'type') {
        $value_prefix = $this
          ->getPrefix($value);
        $value_localpart = $this
          ->getLocalPart($value);
        $this->message[$pos]['type'] = $value_localpart;
        $this->message[$pos]['typePrefix'] = $value_prefix;
        if (isset($this->namespaces[$value_prefix])) {
          $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
        }
        else {
          if (isset($attrs['xmlns:' . $value_prefix])) {
            $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
          }
        }

        // should do something here with the namespace of specified type?
      }
      elseif ($key_localpart == 'arrayType') {
        $this->message[$pos]['type'] = 'array';

        /* do arrayType ereg here
        			[1]    arrayTypeValue    ::=    atype asize
        			[2]    atype    ::=    QName rank*
        			[3]    rank    ::=    '[' (',')* ']'
        			[4]    asize    ::=    '[' length~ ']'
        			[5]    length    ::=    nextDimension* Digit+
        			[6]    nextDimension    ::=    Digit+ ','
        			*/
        $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\\[([0-9]+),?([0-9]*)\\]';
        if (ereg($expr, $value, $regs)) {
          $this->message[$pos]['typePrefix'] = $regs[1];
          $this->message[$pos]['arrayTypePrefix'] = $regs[1];
          if (isset($this->namespaces[$regs[1]])) {
            $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
          }
          else {
            if (isset($attrs['xmlns:' . $regs[1]])) {
              $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
            }
          }
          $this->message[$pos]['arrayType'] = $regs[2];
          $this->message[$pos]['arraySize'] = $regs[3];
          $this->message[$pos]['arrayCols'] = $regs[4];
        }

        // specifies nil value (or not)
      }
      elseif ($key_localpart == 'nil') {
        $this->message[$pos]['nil'] = $value == 'true' || $value == '1';

        // some other attribute
      }
      elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
        $this->message[$pos]['xattrs']['!' . $key] = $value;
      }
      if ($key == 'xmlns') {
        $this->default_namespace = $value;
      }

      // log id
      if ($key == 'id') {
        $this->ids[$value] = $pos;
      }

      // root
      if ($key_localpart == 'root' && $value == 1) {
        $this->status = 'method';
        $this->root_struct_name = $name;
        $this->root_struct = $pos;
        $this
          ->debug("found root struct {$this->root_struct_name}, pos {$pos}");
      }

      // for doclit
      $attstr .= " {$key}=\"{$value}\"";
    }

    // get namespace - must be done after namespace atts are processed
    if (isset($prefix)) {
      $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
      $this->default_namespace = $this->namespaces[$prefix];
    }
    else {
      $this->message[$pos]['namespace'] = $this->default_namespace;
    }
    if ($this->status == 'header') {
      if ($this->root_header != $pos) {
        $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "{$name}{$attstr}>";
      }
    }
    elseif ($this->root_struct_name != '') {
      $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "{$name}{$attstr}>";
    }
  }

  /**
   * end-element handler
   *
   * @param    resource $parser XML parser object
   * @param    string $name element name
   * @access   private
   */
  function end_element($parser, $name) {

    // position of current element is equal to the last value left in depth_array for my depth
    $pos = $this->depth_array[$this->depth--];

    // get element prefix
    if (strpos($name, ':')) {

      // get ns prefix
      $prefix = substr($name, 0, strpos($name, ':'));

      // get unqualified name
      $name = substr(strstr($name, ':'), 1);
    }

    // build to native type
    if (isset($this->body_position) && $pos > $this->body_position) {

      // deal w/ multirefs
      if (isset($this->message[$pos]['attrs']['href'])) {

        // get id
        $id = substr($this->message[$pos]['attrs']['href'], 1);

        // add placeholder to href array
        $this->multirefs[$id][$pos] = 'placeholder';

        // add set a reference to it as the result value
        $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];

        // build complexType values
      }
      elseif ($this->message[$pos]['children'] != '') {

        // if result has already been generated (struct/array)
        if (!isset($this->message[$pos]['result'])) {
          $this->message[$pos]['result'] = $this
            ->buildVal($pos);
        }

        // build complexType values of attributes and possibly simpleContent
      }
      elseif (isset($this->message[$pos]['xattrs'])) {
        if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
          $this->message[$pos]['xattrs']['!'] = null;
        }
        elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
          if (isset($this->message[$pos]['type'])) {
            $this->message[$pos]['xattrs']['!'] = $this
              ->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
          }
          else {
            $parent = $this->message[$pos]['parent'];
            if (isset($this->message[$parent]['type']) && $this->message[$parent]['type'] == 'array' && isset($this->message[$parent]['arrayType'])) {
              $this->message[$pos]['xattrs']['!'] = $this
                ->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
            }
            else {
              $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
            }
          }
        }
        $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];

        // set value of simpleType (or nil complexType)
      }
      else {

        //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
        if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
          $this->message[$pos]['xattrs']['!'] = null;
        }
        elseif (isset($this->message[$pos]['type'])) {
          $this->message[$pos]['result'] = $this
            ->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
        }
        else {
          $parent = $this->message[$pos]['parent'];
          if (isset($this->message[$parent]['type']) && $this->message[$parent]['type'] == 'array' && isset($this->message[$parent]['arrayType'])) {
            $this->message[$pos]['result'] = $this
              ->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
          }
          else {
            $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
          }
        }

        /* add value to parent's result, if parent is struct/array
        			$parent = $this->message[$pos]['parent'];
        			if($this->message[$parent]['type'] != 'map'){
        				if(strtolower($this->message[$parent]['type']) == 'array'){
        					$this->message[$parent]['result'][] = $this->message[$pos]['result'];
        				} else {
        					$this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
        				}
        			}
        			*/
      }
    }

    // for doclit
    if ($this->status == 'header') {
      if ($this->root_header != $pos) {
        $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "{$name}>";
      }
    }
    elseif ($pos >= $this->root_struct) {
      $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "{$name}>";
    }

    // switch status
    if ($pos == $this->root_struct) {
      $this->status = 'body';
      $this->root_struct_namespace = $this->message[$pos]['namespace'];
    }
    elseif ($name == 'Body') {
      $this->status = 'envelope';
    }
    elseif ($name == 'Header') {
      $this->status = 'envelope';
    }
    elseif ($name == 'Envelope') {

      //
    }

    // set parent back to my parent
    $this->parent = $this->message[$pos]['parent'];
  }

  /**
   * element content handler
   *
   * @param    resource $parser XML parser object
   * @param    string $data element content
   * @access   private
   */
  function character_data($parser, $data) {
    $pos = $this->depth_array[$this->depth];
    if ($this->xml_encoding == 'UTF-8') {

      // TODO: add an option to disable this for folks who want
      // raw UTF-8 that, e.g., might not map to iso-8859-1
      // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
      if ($this->decode_utf8) {
        $data = utf8_decode($data);
      }
    }
    $this->message[$pos]['cdata'] .= $data;

    // for doclit
    if ($this->status == 'header') {
      $this->responseHeaders .= $data;
    }
    else {
      $this->document .= $data;
    }
  }

  /**
   * get the parsed message
   *
   * @return	mixed
   * @access   public
   */
  function get_response() {
    return $this->soapresponse;
  }

  /**
   * get the parsed headers
   *
   * @return	string XML or empty if no headers
   * @access   public
   */
  function getHeaders() {
    return $this->responseHeaders;
  }

  /**
   * decodes simple types into PHP variables
   *
   * @param    string $value value to decode
   * @param    string $type XML type to decode
   * @param    string $typens XML type namespace to decode
   * @return	mixed PHP value
   * @access   private
   */
  function decodeSimple($value, $type, $typens) {

    // TODO: use the namespace!
    if (!isset($type) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
      return (string) $value;
    }
    if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
      return (int) $value;
    }
    if ($type == 'float' || $type == 'double' || $type == 'decimal') {
      return (double) $value;
    }
    if ($type == 'boolean') {
      if (strtolower($value) == 'false' || strtolower($value) == 'f') {
        return false;
      }
      return (bool) $value;
    }
    if ($type == 'base64' || $type == 'base64Binary') {
      $this
        ->debug('Decode base64 value');
      return base64_decode($value);
    }

    // obscure numeric types
    if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' || $type == 'nonNegativeInteger' || $type == 'positiveInteger' || $type == 'unsignedInt' || $type == 'unsignedShort' || $type == 'unsignedByte') {
      return (int) $value;
    }

    // bogus: parser treats array with no elements as a simple type
    if ($type == 'array') {
      return array();
    }

    // everything else
    return (string) $value;
  }

  /**
   * builds response structures for compound values (arrays/structs)
   * and scalars
   *
   * @param    integer $pos position in node tree
   * @return	mixed	PHP value
   * @access   private
   */
  function buildVal($pos) {
    if (!isset($this->message[$pos]['type'])) {
      $this->message[$pos]['type'] = '';
    }
    $this
      ->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos {$pos}) of type " . $this->message[$pos]['type']);

    // if there are children...
    if ($this->message[$pos]['children'] != '') {
      $this
        ->debug('in buildVal, there are children');
      $children = explode('|', $this->message[$pos]['children']);
      array_shift($children);

      // knock off empty
      // md array
      if (isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') {
        $r = 0;

        // rowcount
        $c = 0;

        // colcount
        foreach ($children as $child_pos) {
          $this
            ->debug("in buildVal, got an MD array element: {$r}, {$c}");
          $params[$r][] = $this->message[$child_pos]['result'];
          $c++;
          if ($c == $this->message[$pos]['arrayCols']) {
            $c = 0;
            $r++;
          }
        }

        // array
      }
      elseif ($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') {
        $this
          ->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
        foreach ($children as $child_pos) {
          $params[] =& $this->message[$child_pos]['result'];
        }

        // apache Map type: java hashtable
      }
      elseif ($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
        $this
          ->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
        foreach ($children as $child_pos) {
          $kv = explode("|", $this->message[$child_pos]['children']);
          $params[$this->message[$kv[1]]['result']] =& $this->message[$kv[2]]['result'];
        }

        // generic compound type

        //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
      }
      else {

        // Apache Vector type: treat as an array
        $this
          ->debug('in buildVal, adding Java Vector ' . $this->message[$pos]['name']);
        if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
          $notstruct = 1;
        }
        else {
          $notstruct = 0;
        }

        //
        foreach ($children as $child_pos) {
          if ($notstruct) {
            $params[] =& $this->message[$child_pos]['result'];
          }
          else {
            if (isset($params[$this->message[$child_pos]['name']])) {

              // de-serialize repeated element name into an array
              if (!is_array($params[$this->message[$child_pos]['name']]) || !isset($params[$this->message[$child_pos]['name']][0])) {
                $params[$this->message[$child_pos]['name']] = array(
                  $params[$this->message[$child_pos]['name']],
                );
              }
              $params[$this->message[$child_pos]['name']][] =& $this->message[$child_pos]['result'];
            }
            else {
              $params[$this->message[$child_pos]['name']] =& $this->message[$child_pos]['result'];
            }
          }
        }
      }
      if (isset($this->message[$pos]['xattrs'])) {
        $this
          ->debug('in buildVal, handling attributes');
        foreach ($this->message[$pos]['xattrs'] as $n => $v) {
          $params[$n] = $v;
        }
      }

      // handle simpleContent
      if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
        $this
          ->debug('in buildVal, handling simpleContent');
        if (isset($this->message[$pos]['type'])) {
          $params['!'] = $this
            ->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
        }
        else {
          $parent = $this->message[$pos]['parent'];
          if (isset($this->message[$parent]['type']) && $this->message[$parent]['type'] == 'array' && isset($this->message[$parent]['arrayType'])) {
            $params['!'] = $this
              ->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
          }
          else {
            $params['!'] = $this->message[$pos]['cdata'];
          }
        }
      }
      return is_array($params) ? $params : array();
    }
    else {
      $this
        ->debug('in buildVal, no children, building scalar');
      $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
      if (isset($this->message[$pos]['type'])) {
        return $this
          ->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
      }
      $parent = $this->message[$pos]['parent'];
      if (isset($this->message[$parent]['type']) && $this->message[$parent]['type'] == 'array' && isset($this->message[$parent]['arrayType'])) {
        return $this
          ->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
      }
      return $this->message[$pos]['cdata'];
    }
  }

}

/**
*
* soapclient higher level class for easy usage.
*
* usage:
*
* // instantiate client with server info
* $soapclient = new soapclient( string path [ ,boolean wsdl] );
*
* // call method, get results
* echo $soapclient->call( string methodname [ ,array parameters] );
*
* // bye bye client
* unset($soapclient);
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @access   public
*/
class soapclient extends nusoap_base {
  var $username = '';
  var $password = '';
  var $authtype = '';
  var $certRequest = [];
  var $requestHeaders = false;

  // SOAP headers in request (text)
  var $responseHeaders = '';

  // SOAP headers from response (incomplete namespace resolution) (text)
  var $document = '';

  // SOAP body response portion (incomplete namespace resolution) (text)
  var $endpoint;
  var $forceEndpoint = '';

  // overrides WSDL endpoint
  var $proxyhost = '';
  var $proxyport = '';
  var $proxyusername = '';
  var $proxypassword = '';
  var $xml_encoding = '';

  // character set encoding of incoming (response) messages
  var $http_encoding = false;
  var $timeout = 0;

  // HTTP connection timeout
  var $response_timeout = 30;

  // HTTP response timeout
  var $endpointType = '';

  // soap|wsdl, empty for WSDL initialization error
  var $persistentConnection = false;
  var $defaultRpcParams = false;

  // This is no longer used
  var $request = '';

  // HTTP request
  var $response = '';

  // HTTP response
  var $responseData = '';

  // SOAP payload of response
  var $cookies = [];

  // Cookies from response or for request
  var $decode_utf8 = true;

  // toggles whether the parser decodes element content w/ utf8_decode()
  var $operations = [];

  // WSDL operations, empty for WSDL initialization error

  /*
   * fault related variables
   */

  /**
   * @var      fault
   * @access   public
   */
  var $fault;

  /**
   * @var      faultcode
   * @access   public
   */
  var $faultcode;

  /**
   * @var      faultstring
   * @access   public
   */
  var $faultstring;

  /**
   * @var      faultdetail
   * @access   public
   */
  var $faultdetail;

  /**
   * constructor
   *
   * @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
   * @param    bool $wsdl optional, set to true if using WSDL
   * @param	int $portName optional portName in WSDL document
   * @param    string $proxyhost
   * @param    string $proxyport
   * @param	string $proxyusername
   * @param	string $proxypassword
   * @param	integer $timeout set the connection timeout
   * @param	integer $response_timeout set the response timeout
   * @access   public
   */
  function soapclient($endpoint, $wsdl = false, $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30) {
    parent::nusoap_base();
    $this->endpoint = $endpoint;
    $this->proxyhost = $proxyhost;
    $this->proxyport = $proxyport;
    $this->proxyusername = $proxyusername;
    $this->proxypassword = $proxypassword;
    $this->timeout = $timeout;
    $this->response_timeout = $response_timeout;

    // make values
    if ($wsdl) {
      if (is_object($endpoint) && get_class($endpoint) == 'wsdl') {
        $this->wsdl = $endpoint;
        $this->endpoint = $this->wsdl->wsdl;
        $this->wsdlFile = $this->endpoint;
        $this
          ->debug('existing wsdl instance created from ' . $this->endpoint);
      }
      else {
        $this->wsdlFile = $this->endpoint;

        // instantiate wsdl object and parse wsdl file
        $this
          ->debug('instantiating wsdl class with doc: ' . $endpoint);
        $this->wsdl =& new wsdl($this->wsdlFile, $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword, $this->timeout, $this->response_timeout);
      }
      $this
        ->appendDebug($this->wsdl
        ->getDebug());
      $this->wsdl
        ->clearDebug();

      // catch errors
      if ($errstr = $this->wsdl
        ->getError()) {
        $this
          ->debug('got wsdl error: ' . $errstr);
        $this
          ->setError('wsdl error: ' . $errstr);
      }
      elseif ($this->operations = $this->wsdl
        ->getOperations()) {
        $this
          ->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile);
        $this->endpointType = 'wsdl';
      }
      else {
        $this
          ->debug('getOperations returned false');
        $this
          ->setError('no operations defined in the WSDL document!');
      }
    }
    else {
      $this
        ->debug("instantiate SOAP with endpoint at {$endpoint}");
      $this->endpointType = 'soap';
    }
  }

  /**
   * calls method, returns PHP native type
   *
   * @param    string $method SOAP server URL or path
   * @param    mixed $params An array, associative or simple, of the parameters
   *			              for the method call, or a string that is the XML
   *			              for the call.  For rpc style, this call will
   *			              wrap the XML in a tag named after the method, as
   *			              well as the SOAP Envelope and Body.  For document
   *			              style, this will only wrap with the Envelope and Body.
   *			              IMPORTANT: when using an array with document style,
   *			              in which case there
   *                         is really one parameter, the root of the fragment
   *                         used in the call, which encloses what programmers
   *                         normally think of parameters.  A parameter array
   *                         *must* include the wrapper.
   * @param	string $namespace optional method namespace (WSDL can override)
   * @param	string $soapAction optional SOAPAction value (WSDL can override)
   * @param	mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
   * @param	boolean $rpcParams optional (no longer used)
   * @param	string	$style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
   * @param	string	$use optional (encoded|literal) the use when serializing parameters (WSDL can override)
   * @return	mixed	response from SOAP call
   * @access   public
   */
  function call($operation, $params = [], $namespace = 'http://tempuri.org', $soapAction = '', $headers = false, $rpcParams = null, $style = 'rpc', $use = 'encoded') {
    $this->operation = $operation;
    $this->fault = false;
    $this
      ->setError('');
    $this->request = '';
    $this->response = '';
    $this->responseData = '';
    $this->faultstring = '';
    $this->faultcode = '';
    $this->opData = array();
    $this
      ->debug("call: operation={$operation}, namespace={$namespace}, soapAction={$soapAction}, rpcParams={$rpcParams}, style={$style}, use={$use}, endpointType={$this->endpointType}");
    $this
      ->appendDebug('params=' . $this
      ->varDump($params));
    $this
      ->appendDebug('headers=' . $this
      ->varDump($headers));
    if ($headers) {
      $this->requestHeaders = $headers;
    }

    // serialize parameters
    if ($this->endpointType == 'wsdl' && ($opData = $this
      ->getOperationData($operation))) {

      // use WSDL for operation
      $this->opData = $opData;
      $this
        ->debug("found operation");
      $this
        ->appendDebug('opData=' . $this
        ->varDump($opData));
      if (isset($opData['soapAction'])) {
        $soapAction = $opData['soapAction'];
      }
      if (!$this->forceEndpoint) {
        $this->endpoint = $opData['endpoint'];
      }
      else {
        $this->endpoint = $this->forceEndpoint;
      }
      $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
      $style = $opData['style'];
      $use = $opData['input']['use'];

      // add ns to ns array
      if ($namespace != '' && !isset($this->wsdl->namespaces[$namespace])) {
        $nsPrefix = 'ns' . rand(1000, 9999);
        $this->wsdl->namespaces[$nsPrefix] = $namespace;
      }
      $nsPrefix = $this->wsdl
        ->getPrefixFromNamespace($namespace);

      // serialize payload
      if (is_string($params)) {
        $this
          ->debug("serializing param string for WSDL operation {$operation}");
        $payload = $params;
      }
      elseif (is_array($params)) {
        $this
          ->debug("serializing param array for WSDL operation {$operation}");
        $payload = $this->wsdl
          ->serializeRPCParameters($operation, 'input', $params);
      }
      else {
        $this
          ->debug('params must be array or string');
        $this
          ->setError('params must be array or string');
        return false;
      }
      $usedNamespaces = $this->wsdl->usedNamespaces;
      if (isset($opData['input']['encodingStyle'])) {
        $encodingStyle = $opData['input']['encodingStyle'];
      }
      else {
        $encodingStyle = '';
      }
      $this
        ->appendDebug($this->wsdl
        ->getDebug());
      $this->wsdl
        ->clearDebug();
      if ($errstr = $this->wsdl
        ->getError()) {
        $this
          ->debug('got wsdl error: ' . $errstr);
        $this
          ->setError('wsdl error: ' . $errstr);
        return false;
      }
    }
    elseif ($this->endpointType == 'wsdl') {

      // operation not in WSDL
      $this
        ->appendDebug($this->wsdl
        ->getDebug());
      $this->wsdl
        ->clearDebug();
      $this
        ->setError('operation ' . $operation . ' not present.');
      $this
        ->debug("operation '{$operation}' not present.");
      return false;
    }
    else {

      // no WSDL

      //$this->namespaces['ns1'] = $namespace;
      $nsPrefix = 'ns' . rand(1000, 9999);

      // serialize
      $payload = '';
      if (is_string($params)) {
        $this
          ->debug("serializing param string for operation {$operation}");
        $payload = $params;
      }
      elseif (is_array($params)) {
        $this
          ->debug("serializing param array for operation {$operation}");
        foreach ($params as $k => $v) {
          $payload .= $this
            ->serialize_val($v, $k, false, false, false, false, $use);
        }
      }
      else {
        $this
          ->debug('params must be array or string');
        $this
          ->setError('params must be array or string');
        return false;
      }
      $usedNamespaces = array();
      if ($use == 'encoded') {
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
      }
      else {
        $encodingStyle = '';
      }
    }

    // wrap RPC calls with method element
    if ($style == 'rpc') {
      if ($use == 'literal') {
        $this
          ->debug("wrapping RPC request with literal method element");
        if ($namespace) {
          $payload = "<{$operation} xmlns=\"{$namespace}\">" . $payload . "</{$operation}>";
        }
        else {
          $payload = "<{$operation}>" . $payload . "</{$operation}>";
        }
      }
      else {
        $this
          ->debug("wrapping RPC request with encoded method element");
        if ($namespace) {
          $payload = "<{$nsPrefix}:{$operation} xmlns:{$nsPrefix}=\"{$namespace}\">" . $payload . "</{$nsPrefix}:{$operation}>";
        }
        else {
          $payload = "<{$operation}>" . $payload . "</{$operation}>";
        }
      }
    }

    // serialize envelope
    $soapmsg = $this
      ->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
    $this
      ->debug("endpoint={$this->endpoint}, soapAction={$soapAction}, namespace={$namespace}, style={$style}, use={$use}, encodingStyle={$encodingStyle}");
    $this
      ->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));

    // send
    $return = $this
      ->send($this
      ->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
    if ($errstr = $this
      ->getError()) {
      $this
        ->debug('Error: ' . $errstr);
      return false;
    }
    else {
      $this->return = $return;
      $this
        ->debug('sent message successfully and got a(n) ' . gettype($return));
      $this
        ->appendDebug('return=' . $this
        ->varDump($return));

      // fault?
      if (is_array($return) && isset($return['faultcode'])) {
        $this
          ->debug('got fault');
        $this
          ->setError($return['faultcode'] . ': ' . $return['faultstring']);
        $this->fault = true;
        foreach ($return as $k => $v) {
          $this->{$k} = $v;
          $this
            ->debug("{$k} = {$v}<br>");
        }
        return $return;
      }
      elseif ($style == 'document') {

        // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
        // we are only going to return the first part here...sorry about that
        return $return;
      }
      else {

        // array of return values
        if (is_array($return)) {

          // multiple 'out' parameters, which we return wrapped up
          // in the array
          if (sizeof($return) > 1) {
            return $return;
          }

          // single 'out' parameter (normally the return value)
          $return = array_shift($return);
          $this
            ->debug('return shifted value: ');
          $this
            ->appendDebug($this
            ->varDump($return));
          return $return;

          // nothing returned (ie, echoVoid)
        }
        else {
          return "";
        }
      }
    }
  }

  /**
   * get available data pertaining to an operation
   *
   * @param    string $operation operation name
   * @return	array array of data pertaining to the operation
   * @access   public
   */
  function getOperationData($operation) {
    if (isset($this->operations[$operation])) {
      return $this->operations[$operation];
    }
    $this
      ->debug("No data for operation: {$operation}");
  }

  /**
   * send the SOAP message
   *
   * Note: if the operation has multiple return values
   * the return value of this method will be an array
   * of those values.
   *
   * @param    string $msg a SOAPx4 soapmsg object
   * @param    string $soapaction SOAPAction value
   * @param    integer $timeout set connection timeout in seconds
   * @param	integer $response_timeout set response timeout in seconds
   * @return	mixed native PHP types.
   * @access   private
   */
  function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30) {
    $this
      ->checkCookies();

    // detect transport
    switch (true) {

      // http(s)
      case ereg('^http', $this->endpoint):
        $this
          ->debug('transporting via HTTP');
        if ($this->persistentConnection == true && is_object($this->persistentConnection)) {
          $http =& $this->persistentConnection;
        }
        else {
          $http = new soap_transport_http($this->endpoint);
          if ($this->persistentConnection) {
            $http
              ->usePersistentConnection();
          }
        }
        $http
          ->setContentType($this
          ->getHTTPContentType(), $this
          ->getHTTPContentTypeCharset());
        $http
          ->setSOAPAction($soapaction);
        if ($this->proxyhost && $this->proxyport) {
          $http
            ->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
        }
        if ($this->authtype != '') {
          $http
            ->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
        }
        if ($this->http_encoding != '') {
          $http
            ->setEncoding($this->http_encoding);
        }
        $this
          ->debug('sending message, length=' . strlen($msg));
        if (ereg('^http:', $this->endpoint)) {

          //if(strpos($this->endpoint,'http:')){
          $this->responseData = $http
            ->send($msg, $timeout, $response_timeout, $this->cookies);
        }
        elseif (ereg('^https', $this->endpoint)) {

          //} elseif(strpos($this->endpoint,'https:')){

          //if(phpversion() == '4.3.0-dev'){

          //$response = $http->send($msg,$timeout,$response_timeout);

          //$this->request = $http->outgoing_payload;

          //$this->response = $http->incoming_payload;

          //} else
          $this->responseData = $http
            ->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
        }
        else {
          $this
            ->setError('no http/s in endpoint url');
        }
        $this->request = $http->outgoing_payload;
        $this->response = $http->incoming_payload;
        $this
          ->appendDebug($http
          ->getDebug());
        $this
          ->UpdateCookies($http->incoming_cookies);

        // save transport object if using persistent connections
        if ($this->persistentConnection) {
          $http
            ->clearDebug();
          if (!is_object($this->persistentConnection)) {
            $this->persistentConnection = $http;
          }
        }
        if ($err = $http
          ->getError()) {
          $this
            ->setError('HTTP Error: ' . $err);
          return false;
        }
        elseif ($this
          ->getError()) {
          return false;
        }
        else {
          $this
            ->debug('got response, length=' . strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
          return $this
            ->parseResponse($http->incoming_headers, $this->responseData);
        }
        break;
      default:
        $this
          ->setError('no transport found, or selected transport is not yet supported!');
        return false;
        break;
    }
  }

  /**
   * processes SOAP message returned from server
   *
   * @param	array	$headers	The HTTP headers
   * @param	string	$data		unprocessed response data from server
   * @return	mixed	value of the message, decoded into a PHP type
   * @access   private
   */
  function parseResponse($headers, $data) {
    $this
      ->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
    if (!strstr($headers['content-type'], 'text/xml')) {
      $this
        ->setError('Response not of type text/xml');
      return false;
    }
    if (strpos($headers['content-type'], '=')) {
      $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
      $this
        ->debug('Got response encoding: ' . $enc);
      if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
        $this->xml_encoding = strtoupper($enc);
      }
      else {
        $this->xml_encoding = 'US-ASCII';
      }
    }
    else {

      // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
      $this->xml_encoding = 'ISO-8859-1';
    }
    $this
      ->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
    $parser = new soap_parser($data, $this->xml_encoding, $this->operation, $this->decode_utf8);

    // add parser debug data to our debug
    $this
      ->appendDebug($parser
      ->getDebug());

    // if parse errors
    if ($errstr = $parser
      ->getError()) {
      $this
        ->setError($errstr);

      // destroy the parser object
      unset($parser);
      return false;
    }
    else {

      // get SOAP headers
      $this->responseHeaders = $parser
        ->getHeaders();

      // get decoded message
      $return = $parser
        ->get_response();

      // add document for doclit support
      $this->document = $parser->document;

      // destroy the parser object
      unset($parser);

      // return decode message
      return $return;
    }
  }

  /**
   * sets the SOAP endpoint, which can override WSDL
   *
   * @param	$endpoint string The endpoint URL to use, or empty string or false to prevent override
   * @access   public
   */
  function setEndpoint($endpoint) {
    $this->forceEndpoint = $endpoint;
  }

  /**
   * set the SOAP headers
   *
   * @param	$headers mixed String of XML with SOAP header content, or array of soapval objects for SOAP headers
   * @access   public
   */
  function setHeaders($headers) {
    $this->requestHeaders = $headers;
  }

  /**
   * get the SOAP response headers (namespace resolution incomplete)
   *
   * @return	string
   * @access   public
   */
  function getHeaders() {
    return $this->responseHeaders;
  }

  /**
   * set proxy info here
   *
   * @param    string $proxyhost
   * @param    string $proxyport
   * @param	string $proxyusername
   * @param	string $proxypassword
   * @access   public
   */
  function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
    $this->proxyhost = $proxyhost;
    $this->proxyport = $proxyport;
    $this->proxyusername = $proxyusername;
    $this->proxypassword = $proxypassword;
  }

  /**
   * if authenticating, set user credentials here
   *
   * @param    string $username
   * @param    string $password
   * @param	string $authtype (basic|digest|certificate)
   * @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
   * @access   public
   */
  function setCredentials($username, $password, $authtype = 'basic', $certRequest = []) {
    $this->username = $username;
    $this->password = $password;
    $this->authtype = $authtype;
    $this->certRequest = $certRequest;
  }

  /**
   * use HTTP encoding
   *
   * @param    string $enc
   * @access   public
   */
  function setHTTPEncoding($enc = 'gzip, deflate') {
    $this->http_encoding = $enc;
  }

  /**
   * use HTTP persistent connections if possible
   *
   * @access   public
   */
  function useHTTPPersistentConnection() {
    $this->persistentConnection = true;
  }

  /**
   * gets the default RPC parameter setting.
   * If true, default is that call params are like RPC even for document style.
   * Each call() can override this value.
   *
   * This is no longer used.
   *
   * @return boolean
   * @access public
   * @deprecated
   */
  function getDefaultRpcParams() {
    return $this->defaultRpcParams;
  }

  /**
   * sets the default RPC parameter setting.
   * If true, default is that call params are like RPC even for document style
   * Each call() can override this value.
   *
   * This is no longer used.
   *
   * @param    boolean $rpcParams
   * @access public
   * @deprecated
   */
  function setDefaultRpcParams($rpcParams) {
    $this->defaultRpcParams = $rpcParams;
  }

  /**
   * dynamically creates an instance of a proxy class,
   * allowing user to directly call methods from wsdl
   *
   * @return   object soap_proxy object
   * @access   public
   */
  function getProxy() {
    $r = rand();
    $evalStr = $this
      ->_getProxyClassCode($r);

    //$this->debug("proxy class: $evalStr";

    // eval the class
    eval($evalStr);

    // instantiate proxy object
    eval("\$proxy = new soap_proxy_{$r}('');");

    // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
    $proxy->endpointType = 'wsdl';
    $proxy->wsdlFile = $this->wsdlFile;
    $proxy->wsdl = $this->wsdl;
    $proxy->operations = $this->operations;
    $proxy->defaultRpcParams = $this->defaultRpcParams;

    // transfer other state
    $proxy->username = $this->username;
    $proxy->password = $this->password;
    $proxy->authtype = $this->authtype;
    $proxy->proxyhost = $this->proxyhost;
    $proxy->proxyport = $this->proxyport;
    $proxy->proxyusername = $this->proxyusername;
    $proxy->proxypassword = $this->proxypassword;
    $proxy->timeout = $this->timeout;
    $proxy->response_timeout = $this->response_timeout;
    $proxy->http_encoding = $this->http_encoding;
    $proxy->persistentConnection = $this->persistentConnection;
    $proxy->requestHeaders = $this->requestHeaders;
    $proxy->soap_defencoding = $this->soap_defencoding;
    $proxy->endpoint = $this->endpoint;
    $proxy->forceEndpoint = $this->forceEndpoint;
    return $proxy;
  }

  /**
   * dynamically creates proxy class code
   *
   * @return   string PHP/NuSOAP code for the proxy class
   * @access   private
   */
  function _getProxyClassCode($r) {
    if ($this->endpointType != 'wsdl') {
      $evalStr = 'A proxy can only be created for a WSDL client';
      $this
        ->setError($evalStr);
      return $evalStr;
    }
    $evalStr = '';
    foreach ($this->operations as $operation => $opData) {
      if ($operation != '') {

        // create param string and param comment string
        if (sizeof($opData['input']['parts']) > 0) {
          $paramStr = '';
          $paramArrayStr = '';
          $paramCommentStr = '';
          foreach ($opData['input']['parts'] as $name => $type) {
            $paramStr .= "\${$name}, ";
            $paramArrayStr .= "'{$name}' => \${$name}, ";
            $paramCommentStr .= "{$type} \${$name}, ";
          }
          $paramStr = substr($paramStr, 0, strlen($paramStr) - 2);
          $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr) - 2);
          $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr) - 2);
        }
        else {
          $paramStr = '';
          $paramCommentStr = 'void';
        }
        $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
        $evalStr .= "// {$paramCommentStr}\n\tfunction " . str_replace('.', '__', $operation) . "({$paramStr}) {\n\t\t\$params = array({$paramArrayStr});\n\t\treturn \$this->call('{$operation}', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');\n\t}\n\t";
        unset($paramStr);
        unset($paramCommentStr);
      }
    }
    $evalStr = 'class soap_proxy_' . $r . ' extends soapclient {
	' . $evalStr . '
}';
    return $evalStr;
  }

  /**
   * dynamically creates proxy class code
   *
   * @return   string PHP/NuSOAP code for the proxy class
   * @access   public
   */
  function getProxyClassCode() {
    $r = rand();
    return $this
      ->_getProxyClassCode($r);
  }

  /**
   * gets the HTTP body for the current request.
   *
   * @param string $soapmsg The SOAP payload
   * @return string The HTTP body, which includes the SOAP payload
   * @access private
   */
  function getHTTPBody($soapmsg) {
    return $soapmsg;
  }

  /**
   * gets the HTTP content type for the current request.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type for the current request.
   * @access private
   */
  function getHTTPContentType() {
    return 'text/xml';
  }

  /**
   * gets the HTTP content type charset for the current request.
   * returns false for non-text content types.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type charset for the current request.
   * @access private
   */
  function getHTTPContentTypeCharset() {
    return $this->soap_defencoding;
  }

  /*
   * whether or not parser should decode utf8 element content
   *
   * @return   always returns true
   * @access   public
   */
  function decodeUTF8($bool) {
    $this->decode_utf8 = $bool;
    return true;
  }

  /**
   * adds a new Cookie into $this->cookies array
   *
   * @param	string $name Cookie Name
   * @param	string $value Cookie Value
   * @return	if cookie-set was successful returns true, else false
   * @access	public
   */
  function setCookie($name, $value) {
    if (strlen($name) == 0) {
      return false;
    }
    $this->cookies[] = array(
      'name' => $name,
      'value' => $value,
    );
    return true;
  }

  /**
   * gets all Cookies
   *
   * @return   array with all internal cookies
   * @access   public
   */
  function getCookies() {
    return $this->cookies;
  }

  /**
   * checks all Cookies and delete those which are expired
   *
   * @return   always return true
   * @access   private
   */
  function checkCookies() {
    if (sizeof($this->cookies) == 0) {
      return true;
    }
    $this
      ->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
    $curr_cookies = $this->cookies;
    $this->cookies = array();
    foreach ($curr_cookies as $cookie) {
      if (!is_array($cookie)) {
        $this
          ->debug('Remove cookie that is not an array');
        continue;
      }
      if (isset($cookie['expires']) && !empty($cookie['expires'])) {
        if (strtotime($cookie['expires']) > time()) {
          $this->cookies[] = $cookie;
        }
        else {
          $this
            ->debug('Remove expired cookie ' . $cookie['name']);
        }
      }
      else {
        $this->cookies[] = $cookie;
      }
    }
    $this
      ->debug('checkCookie: ' . sizeof($this->cookies) . ' cookies left in array');
    return true;
  }

  /**
   * updates the current cookies with a new set
   *
   * @param	array $cookies new cookies with which to update current ones
   * @return	always return true
   * @access	private
   */
  function UpdateCookies($cookies) {
    if (sizeof($this->cookies) == 0) {

      // no existing cookies: take whatever is new
      if (sizeof($cookies) > 0) {
        $this
          ->debug('Setting new cookie(s)');
        $this->cookies = $cookies;
      }
      return true;
    }
    if (sizeof($cookies) == 0) {

      // no new cookies: keep what we've got
      return true;
    }

    // merge
    foreach ($cookies as $newCookie) {
      if (!is_array($newCookie)) {
        continue;
      }
      if (!isset($newCookie['name']) || !isset($newCookie['value'])) {
        continue;
      }
      $newName = $newCookie['name'];
      $found = false;
      for ($i = 0; $i < count($this->cookies); $i++) {
        $cookie = $this->cookies[$i];
        if (!is_array($cookie)) {
          continue;
        }
        if (!isset($cookie['name'])) {
          continue;
        }
        if ($newName != $cookie['name']) {
          continue;
        }
        $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
        $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
        if ($newDomain != $domain) {
          continue;
        }
        $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
        $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
        if ($newPath != $path) {
          continue;
        }
        $this->cookies[$i] = $newCookie;
        $found = true;
        $this
          ->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
        break;
      }
      if (!$found) {
        $this
          ->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
        $this->cookies[] = $newCookie;
      }
    }
    return true;
  }

}

Functions

Namesort descending Description
iso8601_to_timestamp convert ISO 8601 compliant date string to unix timestamp
timestamp_to_iso8601 convert unix timestamp to ISO 8601 compliant date string
usleepWindows sleeps some number of microseconds

Classes

Namesort descending Description
nusoap_base nusoap_base
soapclient soapclient higher level class for easy usage.
soapval For creating serializable abstractions of native PHP types. This class allows element name/namespace, XSD type, and XML attributes to be associated with a value. This is extremely useful when WSDL is not used, but is also useful when WSDL is used…
soap_fault Contains information for a SOAP fault. Mainly used for returning faults from deployed functions in a server instance. @author Dietrich Ayala <dietrich@ganx4.com> @access public
soap_parser soap_parser class parses SOAP XML messages into native PHP values
soap_server soap_server allows the user to create a SOAP server that is capable of receiving messages and returning responses
soap_transport_http transport class for sending/receiving data via HTTP and HTTPS NOTE: PHP must be compiled with the CURL extension for HTTPS support
wsdl parses a WSDL file, allows access to it's data, other utility methods
XMLSchema parses an XML Schema, allows access to it's data, other utility methods no validation... yet. very experimental and limited. As is discussed on XML-DEV, I'm one of the people that just doesn't have time to read the spec(s) thoroughly,…