class.nusoap.php

Go to the documentation of this file.
00001 <?php
00002 /*
00003 $Id: class.nusoap.php 2663 2007-11-05 09:22:23Z ingmars $
00004 
00005 NuSOAP - Web Services Toolkit for PHP
00006 
00007 Copyright (c) 2002 NuSphere Corporation
00008 
00009 This library is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU Lesser General Public
00011 License as published by the Free Software Foundation; either
00012 version 2.1 of the License, or (at your option) any later version.
00013 
00014 This library is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 Lesser General Public License for more details.
00018 
00019 You should have received a copy of the GNU Lesser General Public
00020 License along with this library; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 §§§§§
00023 If you have any questions or comments, please email:
00024 
00025 Dietrich Ayala
00026 dietrich@ganx4.com
00027 http://dietrich.ganx4.com/nusoap
00028 
00029 NuSphere Corporation
00030 http://www.nusphere.com
00031 
00032 */
00033 
00034 /* load classes
00035 
00036 // necessary classes
00037 require_once('class.soapclient.php');
00038 require_once('class.soap_val.php');
00039 require_once('class.soap_parser.php');
00040 require_once('class.soap_fault.php');
00041 
00042 // transport classes
00043 require_once('class.soap_transport_http.php');
00044 
00045 // optional add-on classes
00046 require_once('class.xmlschema.php');
00047 require_once('class.wsdl.php');
00048 */
00049 
00050 // class variable emulation
00051 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
00052 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
00053 
00054 /**
00055 *
00056 * nusoap_base
00057 *
00058 * @author   Dietrich Ayala <dietrich@ganx4.com>
00059 * @version  $Id: class.nusoap.php 2663 2007-11-05 09:22:23Z ingmars $
00060 * @access   public
00061 */
00062 class nusoap_base {
00063     /**
00064      * Identification for HTTP headers.
00065      *
00066      * @var string
00067      * @access private
00068      */
00069     var $title = 'NuSOAP';
00070     /**
00071      * Version for HTTP headers.
00072      *
00073      * @var string
00074      * @access private
00075      */
00076     var $version = '0.7.2';
00077     /**
00078      * CVS revision for HTTP headers.
00079      *
00080      * @var string
00081      * @access private
00082      */
00083     var $revision = '$Revision: 2663 $';
00084     /**
00085      * Current error string (manipulated by getError/setError)
00086      *
00087      * @var string
00088      * @access private
00089      */
00090     var $error_str = '';
00091     /**
00092      * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
00093      *
00094      * @var string
00095      * @access private
00096      */
00097     var $debug_str = '';
00098     /**
00099      * toggles automatic encoding of special characters as entities
00100      * (should always be true, I think)
00101      *
00102      * @var boolean
00103      * @access private
00104      */
00105     var $charencoding = true;
00106     /**
00107      * the debug level for this instance
00108      *
00109      * @var integer
00110      * @access private
00111      */
00112     var $debugLevel;
00113 
00114     /**
00115     * set schema version
00116     *
00117     * @var      string
00118     * @access   public
00119     */
00120     var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
00121 
00122     /**
00123     * charset encoding for outgoing messages
00124     *
00125     * @var      string
00126     * @access   public
00127     */
00128     var $soap_defencoding = 'ISO-8859-1';
00129     //var $soap_defencoding = 'UTF-8';
00130 
00131     /**
00132     * namespaces in an array of prefix => uri
00133     *
00134     * this is "seeded" by a set of constants, but it may be altered by code
00135     *
00136     * @var      array
00137     * @access   public
00138     */
00139     var $namespaces = array(
00140         'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
00141         'xsd' => 'http://www.w3.org/2001/XMLSchema',
00142         'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
00143         'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
00144         );
00145 
00146     /**
00147     * namespaces used in the current context, e.g. during serialization
00148     *
00149     * @var      array
00150     * @access   private
00151     */
00152     var $usedNamespaces = array();
00153 
00154     /**
00155     * XML Schema types in an array of uri => (array of xml type => php type)
00156     * is this legacy yet?
00157     * no, this is used by the xmlschema class to verify type => namespace mappings.
00158     * @var      array
00159     * @access   public
00160     */
00161     var $typemap = array(
00162     'http://www.w3.org/2001/XMLSchema' => array(
00163         'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
00164         'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
00165         'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
00166         // abstract "any" types
00167         'anyType'=>'string','anySimpleType'=>'string',
00168         // derived datatypes
00169         'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
00170         'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
00171         'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
00172         'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
00173     'http://www.w3.org/2000/10/XMLSchema' => array(
00174         'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
00175         'float'=>'double','dateTime'=>'string',
00176         'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
00177     'http://www.w3.org/1999/XMLSchema' => array(
00178         'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
00179         'float'=>'double','dateTime'=>'string',
00180         'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
00181     'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
00182     'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
00183     'http://xml.apache.org/xml-soap' => array('Map')
00184     );
00185 
00186     /**
00187     * XML entities to convert
00188     *
00189     * @var      array
00190     * @access   public
00191     * @deprecated
00192     * @see  expandEntities
00193     */
00194     var $xmlEntities = array('quot' => '"','amp' => '&',
00195         'lt' => '<','gt' => '>','apos' => "'");
00196 
00197     /**
00198     * constructor
00199     *
00200     * @access   public
00201     */
00202     function nusoap_base() {
00203         $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
00204     }
00205 
00206     /**
00207     * gets the global debug level, which applies to future instances
00208     *
00209     * @return   integer Debug level 0-9, where 0 turns off
00210     * @access   public
00211     */
00212     function getGlobalDebugLevel() {
00213         return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
00214     }
00215 
00216     /**
00217     * sets the global debug level, which applies to future instances
00218     *
00219     * @param    int $level  Debug level 0-9, where 0 turns off
00220     * @access   public
00221     */
00222     function setGlobalDebugLevel($level) {
00223         $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
00224     }
00225 
00226     /**
00227     * gets the debug level for this instance
00228     *
00229     * @return   int Debug level 0-9, where 0 turns off
00230     * @access   public
00231     */
00232     function getDebugLevel() {
00233         return $this->debugLevel;
00234     }
00235 
00236     /**
00237     * sets the debug level for this instance
00238     *
00239     * @param    int $level  Debug level 0-9, where 0 turns off
00240     * @access   public
00241     */
00242     function setDebugLevel($level) {
00243         $this->debugLevel = $level;
00244     }
00245 
00246     /**
00247     * adds debug data to the instance debug string with formatting
00248     *
00249     * @param    string $string debug data
00250     * @access   private
00251     */
00252     function debug($string){
00253         if ($this->debugLevel > 0) {
00254             $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
00255         }
00256     }
00257 
00258     /**
00259     * adds debug data to the instance debug string without formatting
00260     *
00261     * @param    string $string debug data
00262     * @access   public
00263     */
00264     function appendDebug($string){
00265         if ($this->debugLevel > 0) {
00266             // it would be nice to use a memory stream here to use
00267             // memory more efficiently
00268             $this->debug_str .= $string;
00269         }
00270     }
00271 
00272     /**
00273     * clears the current debug data for this instance
00274     *
00275     * @access   public
00276     */
00277     function clearDebug() {
00278         // it would be nice to use a memory stream here to use
00279         // memory more efficiently
00280         $this->debug_str = '';
00281     }
00282 
00283     /**
00284     * gets the current debug data for this instance
00285     *
00286     * @return   debug data
00287     * @access   public
00288     */
00289     function &getDebug() {
00290         // it would be nice to use a memory stream here to use
00291         // memory more efficiently
00292         return $this->debug_str;
00293     }
00294 
00295     /**
00296     * gets the current debug data for this instance as an XML comment
00297     * this may change the contents of the debug data
00298     *
00299     * @return   debug data as an XML comment
00300     * @access   public
00301     */
00302     function &getDebugAsXMLComment() {
00303         // it would be nice to use a memory stream here to use
00304         // memory more efficiently
00305         while (strpos($this->debug_str, '--')) {
00306             $this->debug_str = str_replace('--', '- -', $this->debug_str);
00307         }
00308         return "<!--\n" . $this->debug_str . "\n-->";
00309     }
00310 
00311     /**
00312     * expands entities, e.g. changes '<' to '&lt;'.
00313     *
00314     * @param    string  $val    The string in which to expand entities.
00315     * @access   private
00316     */
00317     function expandEntities($val) {
00318         if ($this->charencoding) {
00319             $val = str_replace('&', '&amp;', $val);
00320             $val = str_replace("'", '&apos;', $val);
00321             $val = str_replace('"', '&quot;', $val);
00322             $val = str_replace('<', '&lt;', $val);
00323             $val = str_replace('>', '&gt;', $val);
00324         }
00325         return $val;
00326     }
00327 
00328     /**
00329     * returns error string if present
00330     *
00331     * @return   mixed error string or false
00332     * @access   public
00333     */
00334     function getError(){
00335         if($this->error_str != ''){
00336             return $this->error_str;
00337         }
00338         return false;
00339     }
00340 
00341     /**
00342     * sets error string
00343     *
00344     * @return   boolean $string error string
00345     * @access   private
00346     */
00347     function setError($str){
00348         $this->error_str = $str;
00349     }
00350 
00351     /**
00352     * detect if array is a simple array or a struct (associative array)
00353     *
00354     * @param    mixed   $val    The PHP array
00355     * @return   string  (arraySimple|arrayStruct)
00356     * @access   private
00357     */
00358     function isArraySimpleOrStruct($val) {
00359         $keyList = array_keys($val);
00360         foreach ($keyList as $keyListValue) {
00361             if (!is_int($keyListValue)) {
00362                 return 'arrayStruct';
00363             }
00364         }
00365         return 'arraySimple';
00366     }
00367 
00368     /**
00369     * serializes PHP values in accordance w/ section 5. Type information is
00370     * not serialized if $use == 'literal'.
00371     *
00372     * @param    mixed   $val    The value to serialize
00373     * @param    string  $name   The name (local part) of the XML element
00374     * @param    string  $type   The XML schema type (local part) for the element
00375     * @param    string  $name_ns    The namespace for the name of the XML element
00376     * @param    string  $type_ns    The namespace for the type of the element
00377     * @param    array   $attributes The attributes to serialize as name=>value pairs
00378     * @param    string  $use    The WSDL "use" (encoded|literal)
00379     * @return   string  The serialized element, possibly with child elements
00380     * @access   public
00381     */
00382     function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
00383         $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
00384         $this->appendDebug('value=' . $this->varDump($val));
00385         $this->appendDebug('attributes=' . $this->varDump($attributes));
00386 
00387         if(is_object($val) && get_class($val) == 'soapval'){
00388             return $val->serialize($use);
00389         }
00390         // force valid name if necessary
00391         if (is_numeric($name)) {
00392             $name = '__numeric_' . $name;
00393         } elseif (! $name) {
00394             $name = 'noname';
00395         }
00396         // if name has ns, add ns prefix to name
00397         $xmlns = '';
00398         if($name_ns){
00399             $prefix = 'nu'.rand(1000,9999);
00400             $name = $prefix.':'.$name;
00401             $xmlns .= " xmlns:$prefix=\"$name_ns\"";
00402         }
00403         // if type is prefixed, create type prefix
00404         if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
00405             // need to fix this. shouldn't default to xsd if no ns specified
00406             // w/o checking against typemap
00407             $type_prefix = 'xsd';
00408         } elseif($type_ns){
00409             $type_prefix = 'ns'.rand(1000,9999);
00410             $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
00411         }
00412         // serialize attributes if present
00413         $atts = '';
00414         if($attributes){
00415             foreach($attributes as $k => $v){
00416                 $atts .= " $k=\"".$this->expandEntities($v).'"';
00417             }
00418         }
00419         // serialize null value
00420         if (is_null($val)) {
00421             if ($use == 'literal') {
00422                 // TODO: depends on minOccurs
00423                 return "<$name$xmlns $atts/>";
00424             } else {
00425                 if (isset($type) && isset($type_prefix)) {
00426                     $type_str = " xsi:type=\"$type_prefix:$type\"";
00427                 } else {
00428                     $type_str = '';
00429                 }
00430                 return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
00431             }
00432         }
00433         // serialize if an xsd built-in primitive type
00434         if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
00435             if (is_bool($val)) {
00436                 if ($type == 'boolean') {
00437                     $val = $val ? 'true' : 'false';
00438                 } elseif (! $val) {
00439                     $val = 0;
00440                 }
00441             } else if (is_string($val)) {
00442                 $val = $this->expandEntities($val);
00443             }
00444             if ($use == 'literal') {
00445                 return "<$name$xmlns $atts>$val</$name>";
00446             } else {
00447                 return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
00448             }
00449         }
00450         // detect type and serialize
00451         $xml = '';
00452         switch(true) {
00453             case (is_bool($val) || $type == 'boolean'):
00454                 if ($type == 'boolean') {
00455                     $val = $val ? 'true' : 'false';
00456                 } elseif (! $val) {
00457                     $val = 0;
00458                 }
00459                 if ($use == 'literal') {
00460                     $xml .= "<$name$xmlns $atts>$val</$name>";
00461                 } else {
00462                     $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
00463                 }
00464                 break;
00465             case (is_int($val) || is_long($val) || $type == 'int'):
00466                 if ($use == 'literal') {
00467                     $xml .= "<$name$xmlns $atts>$val</$name>";
00468                 } else {
00469                     $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
00470                 }
00471                 break;
00472             case (is_float($val)|| is_double($val) || $type == 'float'):
00473                 if ($use == 'literal') {
00474                     $xml .= "<$name$xmlns $atts>$val</$name>";
00475                 } else {
00476                     $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
00477                 }
00478                 break;
00479             case (is_string($val) || $type == 'string'):
00480                 $val = $this->expandEntities($val);
00481                 if ($use == 'literal') {
00482                     $xml .= "<$name$xmlns $atts>$val</$name>";
00483                 } else {
00484                     $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
00485                 }
00486                 break;
00487             case is_object($val):
00488                 if (! $name) {
00489                     $name = get_class($val);
00490                     $this->debug("In serialize_val, used class name $name as element name");
00491                 } else {
00492                     $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
00493                 }
00494                 foreach(get_object_vars($val) as $k => $v){
00495                     $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
00496                 }
00497                 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
00498                 break;
00499             break;
00500             case (is_array($val) || $type):
00501                 // detect if struct or array
00502                 $valueType = $this->isArraySimpleOrStruct($val);
00503                 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
00504                     $i = 0;
00505                     if(is_array($val) && count($val)> 0){
00506                         foreach($val as $v){
00507                             if(is_object($v) && get_class($v) ==  'soapval'){
00508                                 $tt_ns = $v->type_ns;
00509                                 $tt = $v->type;
00510                             } elseif (is_array($v)) {
00511                                 $tt = $this->isArraySimpleOrStruct($v);
00512                             } else {
00513                                 $tt = gettype($v);
00514                             }
00515                             $array_types[$tt] = 1;
00516                             // TODO: for literal, the name should be $name
00517                             $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
00518                             ++$i;
00519                         }
00520                         if(count($array_types) > 1){
00521                             $array_typename = 'xsd:anyType';
00522                         } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
00523                             if ($tt == 'integer') {
00524                                 $tt = 'int';
00525                             }
00526                             $array_typename = 'xsd:'.$tt;
00527                         } elseif(isset($tt) && $tt == 'arraySimple'){
00528                             $array_typename = 'SOAP-ENC:Array';
00529                         } elseif(isset($tt) && $tt == 'arrayStruct'){
00530                             $array_typename = 'unnamed_struct_use_soapval';
00531                         } else {
00532                             // if type is prefixed, create type prefix
00533                             if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
00534                                  $array_typename = 'xsd:' . $tt;
00535                             } elseif ($tt_ns) {
00536                                 $tt_prefix = 'ns' . rand(1000, 9999);
00537                                 $array_typename = "$tt_prefix:$tt";
00538                                 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
00539                             } else {
00540                                 $array_typename = $tt;
00541                             }
00542                         }
00543                         $array_type = $i;
00544                         if ($use == 'literal') {
00545                             $type_str = '';
00546                         } else if (isset($type) && isset($type_prefix)) {
00547                             $type_str = " xsi:type=\"$type_prefix:$type\"";
00548                         } else {
00549                             $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
00550                         }
00551                     // empty array
00552                     } else {
00553                         if ($use == 'literal') {
00554                             $type_str = '';
00555                         } else if (isset($type) && isset($type_prefix)) {
00556                             $type_str = " xsi:type=\"$type_prefix:$type\"";
00557                         } else {
00558                             $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
00559                         }
00560                     }
00561                     // TODO: for array in literal, there is no wrapper here
00562                     $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
00563                 } else {
00564                     // got a struct
00565                     if(isset($type) && isset($type_prefix)){
00566                         $type_str = " xsi:type=\"$type_prefix:$type\"";
00567                     } else {
00568                         $type_str = '';
00569                     }
00570                     if ($use == 'literal') {
00571                         $xml .= "<$name$xmlns $atts>";
00572                     } else {
00573                         $xml .= "<$name$xmlns$type_str$atts>";
00574                     }
00575                     foreach($val as $k => $v){
00576                         // Apache Map
00577                         if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
00578                             $xml .= '<item>';
00579                             $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
00580                             $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
00581                             $xml .= '</item>';
00582                         } else {
00583                             $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
00584                         }
00585                     }
00586                     $xml .= "</$name>";
00587                 }
00588                 break;
00589             default:
00590                 $xml .= 'not detected, got '.gettype($val).' for '.$val;
00591                 break;
00592         }
00593         return $xml;
00594     }
00595 
00596     /**
00597     * serializes a message
00598     *
00599     * @param string $body the XML of the SOAP body
00600     * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
00601     * @param array $namespaces optional the namespaces used in generating the body and headers
00602     * @param string $style optional (rpc|document)
00603     * @param string $use optional (encoded|literal)
00604     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
00605     * @return string the message
00606     * @access public
00607     */
00608     function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
00609     // TODO: add an option to automatically run utf8_encode on $body and $headers
00610     // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
00611     // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
00612 
00613     $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
00614     $this->debug("headers:");
00615     $this->appendDebug($this->varDump($headers));
00616     $this->debug("namespaces:");
00617     $this->appendDebug($this->varDump($namespaces));
00618 
00619     // serialize namespaces
00620     $ns_string = '';
00621     foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
00622         $ns_string .= " xmlns:$k=\"$v\"";
00623     }
00624     if($encodingStyle) {
00625         $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
00626     }
00627 
00628     // serialize headers
00629     if($headers){
00630         if (is_array($headers)) {
00631             $xml = '';
00632             foreach ($headers as $header) {
00633                 $xml .= $this->serialize_val($header, false, false, false, false, false, $use);
00634             }
00635             $headers = $xml;
00636             $this->debug("In serializeEnvelope, serialzied array of headers to $headers");
00637         }
00638         $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
00639     }
00640     // serialize envelope
00641     return
00642     '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
00643     '<SOAP-ENV:Envelope'.$ns_string.">".
00644     $headers.
00645     "<SOAP-ENV:Body>".
00646         $body.
00647     "</SOAP-ENV:Body>".
00648     "</SOAP-ENV:Envelope>";
00649     }
00650 
00651     /**
00652      * formats a string to be inserted into an HTML stream
00653      *
00654      * @param string $str The string to format
00655      * @return string The formatted string
00656      * @access public
00657      * @deprecated
00658      */
00659     function formatDump($str){
00660         $str = htmlspecialchars($str);
00661         return nl2br($str);
00662     }
00663 
00664     /**
00665     * contracts (changes namespace to prefix) a qualified name
00666     *
00667     * @param    string $qname qname
00668     * @return   string contracted qname
00669     * @access   private
00670     */
00671     function contractQname($qname){
00672         // get element namespace
00673         //$this->xdebug("Contract $qname");
00674         if (strrpos($qname, ':')) {
00675             // get unqualified name
00676             $name = substr($qname, strrpos($qname, ':') + 1);
00677             // get ns
00678             $ns = substr($qname, 0, strrpos($qname, ':'));
00679             $p = $this->getPrefixFromNamespace($ns);
00680             if ($p) {
00681                 return $p . ':' . $name;
00682             }
00683             return $qname;
00684         } else {
00685             return $qname;
00686         }
00687     }
00688 
00689     /**
00690     * expands (changes prefix to namespace) a qualified name
00691     *
00692     * @param    string $string qname
00693     * @return   string expanded qname
00694     * @access   private
00695     */
00696     function expandQname($qname){
00697         // get element prefix
00698         if(strpos($qname,':') && !ereg('^http://',$qname)){
00699             // get unqualified name
00700             $name = substr(strstr($qname,':'),1);
00701             // get ns prefix
00702             $prefix = substr($qname,0,strpos($qname,':'));
00703             if(isset($this->namespaces[$prefix])){
00704                 return $this->namespaces[$prefix].':'.$name;
00705             } else {
00706                 return $qname;
00707             }
00708         } else {
00709             return $qname;
00710         }
00711     }
00712 
00713     /**
00714     * returns the local part of a prefixed string
00715     * returns the original string, if not prefixed
00716     *
00717     * @param string $str The prefixed string
00718     * @return string The local part
00719     * @access public
00720     */
00721     function getLocalPart($str){
00722         if($sstr = strrchr($str,':')){
00723             // get unqualified name
00724             return substr( $sstr, 1 );
00725         } else {
00726             return $str;
00727         }
00728     }
00729 
00730     /**
00731     * returns the prefix part of a prefixed string
00732     * returns false, if not prefixed
00733     *
00734     * @param string $str The prefixed string
00735     * @return mixed The prefix or false if there is no prefix
00736     * @access public
00737     */
00738     function getPrefix($str){
00739         if($pos = strrpos($str,':')){
00740             // get prefix
00741             return substr($str,0,$pos);
00742         }
00743         return false;
00744     }
00745 
00746     /**
00747     * pass it a prefix, it returns a namespace
00748     *
00749     * @param string $prefix The prefix
00750     * @return mixed The namespace, false if no namespace has the specified prefix
00751     * @access public
00752     */
00753     function getNamespaceFromPrefix($prefix){
00754         if (isset($this->namespaces[$prefix])) {
00755             return $this->namespaces[$prefix];
00756         }
00757         //$this->setError("No namespace registered for prefix '$prefix'");
00758         return false;
00759     }
00760 
00761     /**
00762     * returns the prefix for a given namespace (or prefix)
00763     * or false if no prefixes registered for the given namespace
00764     *
00765     * @param string $ns The namespace
00766     * @return mixed The prefix, false if the namespace has no prefixes
00767     * @access public
00768     */
00769     function getPrefixFromNamespace($ns) {
00770         foreach ($this->namespaces as $p => $n) {
00771             if ($ns == $n || $ns == $p) {
00772                 $this->usedNamespaces[$p] = $n;
00773                 return $p;
00774             }
00775         }
00776         return false;
00777     }
00778 
00779     /**
00780     * returns the time in ODBC canonical form with microseconds
00781     *
00782     * @return string The time in ODBC canonical form with microseconds
00783     * @access public
00784     */
00785     function getmicrotime() {
00786         if (function_exists('gettimeofday')) {
00787             $tod = gettimeofday();
00788             $sec = $tod['sec'];
00789             $usec = $tod['usec'];
00790         } else {
00791             $sec = time();
00792             $usec = 0;
00793         }
00794         return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
00795     }
00796 
00797     /**
00798      * Returns a string with the output of var_dump
00799      *
00800      * @param mixed $data The variable to var_dump
00801      * @return string The output of var_dump
00802      * @access public
00803      */
00804     function varDump($data) {
00805         ob_start();
00806         var_dump($data);
00807         $ret_val = ob_get_contents();
00808         ob_end_clean();
00809         return $ret_val;
00810     }
00811 }
00812 
00813 // XML Schema Datatype Helper Functions
00814 
00815 //xsd:dateTime helpers
00816 
00817 /**
00818 * convert unix timestamp to ISO 8601 compliant date string
00819 *
00820 * @param    string $timestamp Unix time stamp
00821 * @access   public
00822 */
00823 function timestamp_to_iso8601($timestamp,$utc=true){
00824     $datestr = date('Y-m-d\TH:i:sO',$timestamp);
00825     if($utc){
00826         $eregStr =
00827         '([0-9]{4})-'.  // centuries & years CCYY-
00828         '([0-9]{2})-'.  // months MM-
00829         '([0-9]{2})'.   // days DD
00830         'T'.            // separator T
00831         '([0-9]{2}):'.  // hours hh:
00832         '([0-9]{2}):'.  // minutes mm:
00833         '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
00834         '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
00835 
00836         if(ereg($eregStr,$datestr,$regs)){
00837             return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
00838         }
00839         return false;
00840     } else {
00841         return $datestr;
00842     }
00843 }
00844 
00845 /**
00846 * convert ISO 8601 compliant date string to unix timestamp
00847 *
00848 * @param    string $datestr ISO 8601 compliant date string
00849 * @access   public
00850 */
00851 function iso8601_to_timestamp($datestr){
00852     $eregStr =
00853     '([0-9]{4})-'.  // centuries & years CCYY-
00854     '([0-9]{2})-'.  // months MM-
00855     '([0-9]{2})'.   // days DD
00856     'T'.            // separator T
00857     '([0-9]{2}):'.  // hours hh:
00858     '([0-9]{2}):'.  // minutes mm:
00859     '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
00860     '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
00861     if(ereg($eregStr,$datestr,$regs)){
00862         // not utc
00863         if($regs[8] != 'Z'){
00864             $op = substr($regs[8],0,1);
00865             $h = substr($regs[8],1,2);
00866             $m = substr($regs[8],strlen($regs[8])-2,2);
00867             if($op == '-'){
00868                 $regs[4] = $regs[4] + $h;
00869                 $regs[5] = $regs[5] + $m;
00870             } elseif($op == '+'){
00871                 $regs[4] = $regs[4] - $h;
00872                 $regs[5] = $regs[5] - $m;
00873             }
00874         }
00875         return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
00876     } else {
00877         return false;
00878     }
00879 }
00880 
00881 /**
00882 * sleeps some number of microseconds
00883 *
00884 * @param    string $usec the number of microseconds to sleep
00885 * @access   public
00886 * @deprecated
00887 */
00888 function usleepWindows($usec)
00889 {
00890     $start = gettimeofday();
00891 
00892     do
00893     {
00894         $stop = gettimeofday();
00895         $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
00896         + $stop['usec'] - $start['usec'];
00897     }
00898     while ($timePassed < $usec);
00899 }
00900 
00901 
00902 
00903 /**
00904 * Contains information for a SOAP fault.
00905 * Mainly used for returning faults from deployed functions
00906 * in a server instance.
00907 * @author   Dietrich Ayala <dietrich@ganx4.com>
00908 * @version  $Id: class.nusoap.php 2663 2007-11-05 09:22:23Z ingmars $
00909 * @access public
00910 */
00911 class soap_fault extends nusoap_base {
00912     /**
00913      * The fault code (client|server)
00914      * @var string
00915      * @access private
00916      */
00917     var $faultcode;
00918     /**
00919      * The fault actor
00920      * @var string
00921      * @access private
00922      */
00923     var $faultactor;
00924     /**
00925      * The fault string, a description of the fault
00926      * @var string
00927      * @access private
00928      */
00929     var $faultstring;
00930     /**
00931      * The fault detail, typically a string or array of string
00932      * @var mixed
00933      * @access private
00934      */
00935     var $faultdetail;
00936 
00937     /**
00938     * constructor
00939     *
00940     * @param string $faultcode (client | server)
00941     * @param string $faultactor only used when msg routed between multiple actors
00942     * @param string $faultstring human readable error message
00943     * @param mixed $faultdetail detail, typically a string or array of string
00944     */
00945     function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
00946         parent::nusoap_base();
00947         $this->faultcode = $faultcode;
00948         $this->faultactor = $faultactor;
00949         $this->faultstring = $faultstring;
00950         $this->faultdetail = $faultdetail;
00951     }
00952 
00953     /**
00954     * serialize a fault
00955     *
00956     * @return   string  The serialization of the fault instance.
00957     * @access   public
00958     */
00959     function serialize(){
00960         $ns_string = '';
00961         foreach($this->namespaces as $k => $v){
00962             $ns_string .= "\n  xmlns:$k=\"$v\"";
00963         }
00964         $return_msg =
00965             '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
00966             '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
00967                 '<SOAP-ENV:Body>'.
00968                 '<SOAP-ENV:Fault>'.
00969                     $this->serialize_val($this->faultcode, 'faultcode').
00970                     $this->serialize_val($this->faultactor, 'faultactor').
00971                     $this->serialize_val($this->faultstring, 'faultstring').
00972                     $this->serialize_val($this->faultdetail, 'detail').
00973                 '</SOAP-ENV:Fault>'.
00974                 '</SOAP-ENV:Body>'.
00975             '</SOAP-ENV:Envelope>';
00976         return $return_msg;
00977     }
00978 }
00979 
00980 
00981 
00982 /**
00983 * parses an XML Schema, allows access to it's data, other utility methods
00984 * no validation... yet.
00985 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
00986 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
00987 * tutorials I refer to :)
00988 *
00989 * @author   Dietrich Ayala <dietrich@ganx4.com>
00990 * @version  $Id: class.nusoap.php 2663 2007-11-05 09:22:23Z ingmars $
00991 * @access   public
00992 */
00993 class XMLSchema extends nusoap_base  {
00994 
00995     // files
00996     var $schema = '';
00997     var $xml = '';
00998     // namespaces
00999     var $enclosingNamespaces;
01000     // schema info
01001     var $schemaInfo = array();
01002     var $schemaTargetNamespace = '';
01003     // types, elements, attributes defined by the schema
01004     var $attributes = array();
01005     var $complexTypes = array();
01006     var $complexTypeStack = array();
01007     var $currentComplexType = null;
01008     var $elements = array();
01009     var $elementStack = array();
01010     var $currentElement = null;
01011     var $simpleTypes = array();
01012     var $simpleTypeStack = array();
01013     var $currentSimpleType = null;
01014     // imports
01015     var $imports = array();
01016     // parser vars
01017     var $parser;
01018     var $position = 0;
01019     var $depth = 0;
01020     var $depth_array = array();
01021     var $message = array();
01022     var $defaultNamespace = array();
01023 
01024     /**
01025     * constructor
01026     *
01027     * @param    string $schema schema document URI
01028     * @param    string $xml xml document URI
01029     * @param    string $namespaces namespaces defined in enclosing XML
01030     * @access   public
01031     */
01032     function XMLSchema($schema='',$xml='',$namespaces=array()){
01033         parent::nusoap_base();
01034         $this->debug('xmlschema class instantiated, inside constructor');
01035         // files
01036         $this->schema = $schema;
01037         $this->xml = $xml;
01038 
01039         // namespaces
01040         $this->enclosingNamespaces = $namespaces;
01041         $this->namespaces = array_merge($this->namespaces, $namespaces);
01042 
01043         // parse schema file
01044         if($schema != ''){
01045             $this->debug('initial schema file: '.$schema);
01046             $this->parseFile($schema, 'schema');
01047         }
01048 
01049         // parse xml file
01050         if($xml != ''){
01051             $this->debug('initial xml file: '.$xml);
01052             $this->parseFile($xml, 'xml');
01053         }
01054 
01055     }
01056 
01057     /**
01058     * parse an XML file
01059     *
01060     * @param string $xml, path/URL to XML file
01061     * @param string $type, (schema | xml)
01062     * @return boolean
01063     * @access public
01064     */
01065     function parseFile($xml,$type){
01066         // parse xml file
01067         if($xml != ""){
01068             $xmlStr = @join("",@file($xml));
01069             if($xmlStr == ""){
01070                 $msg = 'Error reading XML from '.$xml;
01071                 $this->setError($msg);
01072                 $this->debug($msg);
01073             return false;
01074             } else {
01075                 $this->debug("parsing $xml");
01076                 $this->parseString($xmlStr,$type);
01077                 $this->debug("done parsing $xml");
01078             return true;
01079             }
01080         }
01081         return false;
01082     }
01083 
01084     /**
01085     * parse an XML string
01086     *
01087     * @param    string $xml path or URL
01088     * @param string $type, (schema|xml)
01089     * @access   private
01090     */
01091     function parseString($xml,$type){
01092         // parse xml string
01093         if($xml != ""){
01094 
01095             // Create an XML parser.
01096             $this->parser = xml_parser_create();
01097             // Set the options for parsing the XML data.
01098             xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
01099 
01100             // Set the object for the parser.
01101             xml_set_object($this->parser, $this);
01102 
01103             // Set the element handlers for the parser.
01104             if($type == "schema"){
01105                 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
01106                 xml_set_character_data_handler($this->parser,'schemaCharacterData');
01107             } elseif($type == "xml"){
01108                 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
01109                 xml_set_character_data_handler($this->parser,'xmlCharacterData');
01110             }
01111 
01112             // Parse the XML file.
01113             if(!xml_parse($this->parser,$xml,true)){
01114             // Display an error message.
01115                 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
01116                 xml_get_current_line_number($this->parser),
01117                 xml_error_string(xml_get_error_code($this->parser))
01118                 );
01119                 $this->debug($errstr);
01120                 $this->debug("XML payload:\n" . $xml);
01121                 $this->setError($errstr);
01122             }
01123 
01124             xml_parser_free($this->parser);
01125         } else{
01126             $this->debug('no xml passed to parseString()!!');
01127             $this->setError('no xml passed to parseString()!!');
01128         }
01129     }
01130 
01131     /**
01132     * start-element handler
01133     *
01134     * @param    string $parser XML parser object
01135     * @param    string $name element name
01136     * @param    string $attrs associative array of attributes
01137     * @access   private
01138     */
01139     function schemaStartElement($parser, $name, $attrs) {
01140 
01141         // position in the total number of elements, starting from 0
01142         $pos = $this->position++;
01143         $depth = $this->depth++;
01144         // set self as current value for this depth
01145         $this->depth_array[$depth] = $pos;
01146         $this->message[$pos] = array('cdata' => '');
01147         if ($depth > 0) {
01148             $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
01149         } else {
01150             $this->defaultNamespace[$pos] = false;
01151         }
01152 
01153         // get element prefix
01154         if($prefix = $this->getPrefix($name)){
01155             // get unqualified name
01156             $name = $this->getLocalPart($name);
01157         } else {
01158             $prefix = '';
01159         }
01160 
01161         // loop thru attributes, expanding, and registering namespace declarations
01162         if(count($attrs) > 0){
01163             foreach($attrs as $k => $v){
01164                 // if ns declarations, add to class level array of valid namespaces
01165                 if(ereg("^xmlns",$k)){
01166                     //$this->xdebug("$k: $v");
01167                     //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
01168                     if($ns_prefix = substr(strrchr($k,':'),1)){
01169                         //$this->xdebug("Add namespace[$ns_prefix] = $v");
01170                         $this->namespaces[$ns_prefix] = $v;
01171                     } else {
01172                         $this->defaultNamespace[$pos] = $v;
01173                         if (! $this->getPrefixFromNamespace($v)) {
01174                             $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
01175                         }
01176                     }
01177                     if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
01178                         $this->XMLSchemaVersion = $v;
01179                         $this->namespaces['xsi'] = $v.'-instance';
01180                     }
01181                 }
01182             }
01183             foreach($attrs as $k => $v){
01184                 // expand each attribute
01185                 $k = strpos($k,':') ? $this->expandQname($k) : $k;
01186                 $v = strpos($v,':') ? $this->expandQname($v) : $v;
01187                 $eAttrs[$k] = $v;
01188             }
01189             $attrs = $eAttrs;
01190         } else {
01191             $attrs = array();
01192         }
01193         // find status, register data
01194         switch($name){
01195             case 'all':         // (optional) compositor content for a complexType
01196             case 'choice':
01197             case 'group':
01198             case 'sequence':
01199                 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
01200                 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
01201                 //if($name == 'all' || $name == 'sequence'){
01202