4 $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6 NuSOAP - Web Services Toolkit for PHP
8 Copyright (c) 2002 NuSphere Corporation
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 The NuSOAP project home is:
25 http://sourceforge.net/projects/nusoap/
27 The primary support for NuSOAP is the Help forum on the project home page.
29 If you have any questions or comments, please email:
33 http://dietrich.ganx4.com/nusoap
36 http://www.nusphere.com
41 * Some of the standards implmented in whole or part by NuSOAP:
43 * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
44 * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
45 * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
46 * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
47 * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
48 * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
49 * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
50 * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
51 * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
57 require_once('class.soapclient.php');
58 require_once('class.soap_val.php');
59 require_once('class.soap_parser.php');
60 require_once('class.soap_fault.php');
63 require_once('class.soap_transport_http.php');
65 // optional add-on classes
66 require_once('class.xmlschema.php');
67 require_once('class.wsdl.php');
70 require_once('class.soap_server.php');*/
72 // class variable emulation
73 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
74 $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
80 * @author Dietrich Ayala <dietrich@ganx4.com>
81 * @author Scott Nichol <snichol@users.sourceforge.net>
82 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
87 * Identification for HTTP headers.
92 var $title = 'NuSOAP';
94 * Version for HTTP headers.
99 var $version = '0.9.5';
101 * CVS revision for HTTP headers.
106 var $revision = '$Revision: 1.123 $';
108 * Current error string (manipulated by getError/setError)
115 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
122 * toggles automatic encoding of special characters as entities
123 * (should always be true, I think)
128 var $charencoding = true;
130 * the debug level for this instance
143 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
146 * charset encoding for outgoing messages
151 var $soap_defencoding = 'ISO-8859-1';
152 //var $soap_defencoding = 'UTF-8';
155 * namespaces in an array of prefix => uri
157 * this is "seeded" by a set of constants, but it may be altered by code
162 var $namespaces = array(
163 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
164 'xsd' => 'http://www.w3.org/2001/XMLSchema',
165 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
166 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
170 * namespaces used in the current context, e.g. during serialization
175 var $usedNamespaces = array();
178 * XML Schema types in an array of uri => (array of xml type => php type)
179 * is this legacy yet?
180 * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
184 var $typemap = array(
185 'http://www.w3.org/2001/XMLSchema' => array(
186 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
187 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
188 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
189 // abstract "any" types
190 'anyType'=>'string','anySimpleType'=>'string',
192 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
193 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
194 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
195 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
196 'http://www.w3.org/2000/10/XMLSchema' => array(
197 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
198 'float'=>'double','dateTime'=>'string',
199 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
200 'http://www.w3.org/1999/XMLSchema' => array(
201 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
202 'float'=>'double','dateTime'=>'string',
203 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
204 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
205 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
206 'http://xml.apache.org/xml-soap' => array('Map')
210 * XML entities to convert
215 * @see expandEntities
217 var $xmlEntities = array('quot' => '"','amp' => '&',
218 'lt' => '<','gt' => '>','apos' => "'");
225 function nusoap_base() {
226 $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
230 * gets the global debug level, which applies to future instances
232 * @return integer Debug level 0-9, where 0 turns off
235 function getGlobalDebugLevel() {
236 return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
240 * sets the global debug level, which applies to future instances
242 * @param int $level Debug level 0-9, where 0 turns off
245 function setGlobalDebugLevel($level) {
246 $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
250 * gets the debug level for this instance
252 * @return int Debug level 0-9, where 0 turns off
255 function getDebugLevel() {
256 return $this->debugLevel;
260 * sets the debug level for this instance
262 * @param int $level Debug level 0-9, where 0 turns off
265 function setDebugLevel($level) {
266 $this->debugLevel = $level;
270 * adds debug data to the instance debug string with formatting
272 * @param string $string debug data
275 function debug($string){
276 if ($this->debugLevel > 0) {
277 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
282 * adds debug data to the instance debug string without formatting
284 * @param string $string debug data
287 function appendDebug($string){
288 if ($this->debugLevel > 0) {
289 // it would be nice to use a memory stream here to use
290 // memory more efficiently
291 $this->debug_str .= $string;
296 * clears the current debug data for this instance
300 function clearDebug() {
301 // it would be nice to use a memory stream here to use
302 // memory more efficiently
303 $this->debug_str = '';
307 * gets the current debug data for this instance
312 function &getDebug() {
313 // it would be nice to use a memory stream here to use
314 // memory more efficiently
315 return $this->debug_str;
319 * gets the current debug data for this instance as an XML comment
320 * this may change the contents of the debug data
322 * @return debug data as an XML comment
325 function &getDebugAsXMLComment() {
326 // it would be nice to use a memory stream here to use
327 // memory more efficiently
328 while (strpos($this->debug_str, '--')) {
329 $this->debug_str = str_replace('--', '- -', $this->debug_str);
331 $ret = "<!--\n" . $this->debug_str . "\n-->";
336 * expands entities, e.g. changes '<' to '<'.
338 * @param string $val The string in which to expand entities.
341 function expandEntities($val) {
342 if ($this->charencoding) {
343 $val = str_replace('&', '&', $val);
344 $val = str_replace("'", ''', $val);
345 $val = str_replace('"', '"', $val);
346 $val = str_replace('<', '<', $val);
347 $val = str_replace('>', '>', $val);
353 * returns error string if present
355 * @return mixed error string or false
359 if($this->error_str != ''){
360 return $this->error_str;
368 * @return boolean $string error string
371 function setError($str){
372 $this->error_str = $str;
376 * detect if array is a simple array or a struct (associative array)
378 * @param mixed $val The PHP array
379 * @return string (arraySimple|arrayStruct)
382 function isArraySimpleOrStruct($val) {
383 $keyList = array_keys($val);
384 foreach ($keyList as $keyListValue) {
385 if (!is_int($keyListValue)) {
386 return 'arrayStruct';
389 return 'arraySimple';
393 * serializes PHP values in accordance w/ section 5. Type information is
394 * not serialized if $use == 'literal'.
396 * @param mixed $val The value to serialize
397 * @param string $name The name (local part) of the XML element
398 * @param string $type The XML schema type (local part) for the element
399 * @param string $name_ns The namespace for the name of the XML element
400 * @param string $type_ns The namespace for the type of the element
401 * @param array $attributes The attributes to serialize as name=>value pairs
402 * @param string $use The WSDL "use" (encoded|literal)
403 * @param boolean $soapval Whether this is called from soapval.
404 * @return string The serialized element, possibly with child elements
407 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
408 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
409 $this->appendDebug('value=' . $this->varDump($val));
410 $this->appendDebug('attributes=' . $this->varDump($attributes));
412 if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
413 $this->debug("serialize_val: serialize soapval");
414 $xml = $val->serialize($use);
415 $this->appendDebug($val->getDebug());
417 $this->debug("serialize_val of soapval returning $xml");
420 // force valid name if necessary
421 if (is_numeric($name)) {
422 $name = '__numeric_' . $name;
426 // if name has ns, add ns prefix to name
429 $prefix = 'nu'.rand(1000,9999);
430 $name = $prefix.':'.$name;
431 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
433 // if type is prefixed, create type prefix
434 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
435 // need to fix this. shouldn't default to xsd if no ns specified
436 // w/o checking against typemap
437 $type_prefix = 'xsd';
439 $type_prefix = 'ns'.rand(1000,9999);
440 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
442 // serialize attributes if present
445 foreach($attributes as $k => $v){
446 $atts .= " $k=\"".$this->expandEntities($v).'"';
449 // serialize null value
451 $this->debug("serialize_val: serialize null");
452 if ($use == 'literal') {
453 // TODO: depends on minOccurs
454 $xml = "<$name$xmlns$atts/>";
455 $this->debug("serialize_val returning $xml");
458 if (isset($type) && isset($type_prefix)) {
459 $type_str = " xsi:type=\"$type_prefix:$type\"";
463 $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
464 $this->debug("serialize_val returning $xml");
468 // serialize if an xsd built-in primitive type
469 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
470 $this->debug("serialize_val: serialize xsd built-in primitive type");
472 if ($type == 'boolean') {
473 $val = $val ? 'true' : 'false';
477 } else if (is_string($val)) {
478 $val = $this->expandEntities($val);
480 if ($use == 'literal') {
481 $xml = "<$name$xmlns$atts>$val</$name>";
482 $this->debug("serialize_val returning $xml");
485 $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
486 $this->debug("serialize_val returning $xml");
490 // detect type and serialize
493 case (is_bool($val) || $type == 'boolean'):
494 $this->debug("serialize_val: serialize boolean");
495 if ($type == 'boolean') {
496 $val = $val ? 'true' : 'false';
500 if ($use == 'literal') {
501 $xml .= "<$name$xmlns$atts>$val</$name>";
503 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
506 case (is_int($val) || is_long($val) || $type == 'int'):
507 $this->debug("serialize_val: serialize int");
508 if ($use == 'literal') {
509 $xml .= "<$name$xmlns$atts>$val</$name>";
511 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
514 case (is_float($val)|| is_double($val) || $type == 'float'):
515 $this->debug("serialize_val: serialize float");
516 if ($use == 'literal') {
517 $xml .= "<$name$xmlns$atts>$val</$name>";
519 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
522 case (is_string($val) || $type == 'string'):
523 $this->debug("serialize_val: serialize string");
524 $val = $this->expandEntities($val);
525 if ($use == 'literal') {
526 $xml .= "<$name$xmlns$atts>$val</$name>";
528 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
531 case is_object($val):
532 $this->debug("serialize_val: serialize object");
533 if (get_class($val) == 'soapval') {
534 $this->debug("serialize_val: serialize soapval object");
535 $pXml = $val->serialize($use);
536 $this->appendDebug($val->getDebug());
540 $name = get_class($val);
541 $this->debug("In serialize_val, used class name $name as element name");
543 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
545 foreach(get_object_vars($val) as $k => $v){
546 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
549 if(isset($type) && isset($type_prefix)){
550 $type_str = " xsi:type=\"$type_prefix:$type\"";
554 if ($use == 'literal') {
555 $xml .= "<$name$xmlns$atts>$pXml</$name>";
557 $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
561 case (is_array($val) || $type):
562 // detect if struct or array
563 $valueType = $this->isArraySimpleOrStruct($val);
564 if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
565 $this->debug("serialize_val: serialize array");
567 if(is_array($val) && count($val)> 0){
569 if(is_object($v) && get_class($v) == 'soapval'){
570 $tt_ns = $v->type_ns;
572 } elseif (is_array($v)) {
573 $tt = $this->isArraySimpleOrStruct($v);
577 $array_types[$tt] = 1;
578 // TODO: for literal, the name should be $name
579 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
582 if(count($array_types) > 1){
583 $array_typename = 'xsd:anyType';
584 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
585 if ($tt == 'integer') {
588 $array_typename = 'xsd:'.$tt;
589 } elseif(isset($tt) && $tt == 'arraySimple'){
590 $array_typename = 'SOAP-ENC:Array';
591 } elseif(isset($tt) && $tt == 'arrayStruct'){
592 $array_typename = 'unnamed_struct_use_soapval';
594 // if type is prefixed, create type prefix
595 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
596 $array_typename = 'xsd:' . $tt;
598 $tt_prefix = 'ns' . rand(1000, 9999);
599 $array_typename = "$tt_prefix:$tt";
600 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
602 $array_typename = $tt;
606 if ($use == 'literal') {
608 } else if (isset($type) && isset($type_prefix)) {
609 $type_str = " xsi:type=\"$type_prefix:$type\"";
611 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
615 if ($use == 'literal') {
617 } else if (isset($type) && isset($type_prefix)) {
618 $type_str = " xsi:type=\"$type_prefix:$type\"";
620 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
623 // TODO: for array in literal, there is no wrapper here
624 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
627 $this->debug("serialize_val: serialize struct");
628 if(isset($type) && isset($type_prefix)){
629 $type_str = " xsi:type=\"$type_prefix:$type\"";
633 if ($use == 'literal') {
634 $xml .= "<$name$xmlns$atts>";
636 $xml .= "<$name$xmlns$type_str$atts>";
638 foreach($val as $k => $v){
640 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
642 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
643 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
646 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
653 $this->debug("serialize_val: serialize unknown");
654 $xml .= 'not detected, got '.gettype($val).' for '.$val;
657 $this->debug("serialize_val returning $xml");
662 * serializes a message
664 * @param string $body the XML of the SOAP body
665 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
666 * @param array $namespaces optional the namespaces used in generating the body and headers
667 * @param string $style optional (rpc|document)
668 * @param string $use optional (encoded|literal)
669 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
670 * @return string the message
673 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
674 // TODO: add an option to automatically run utf8_encode on $body and $headers
675 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
676 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
678 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
679 $this->debug("headers:");
680 $this->appendDebug($this->varDump($headers));
681 $this->debug("namespaces:");
682 $this->appendDebug($this->varDump($namespaces));
684 // serialize namespaces
686 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
687 $ns_string .= " xmlns:$k=\"$v\"";
690 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
695 if (is_array($headers)) {
697 foreach ($headers as $k => $v) {
698 if (is_object($v) && get_class($v) == 'soapval') {
699 $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
701 $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
705 $this->debug("In serializeEnvelope, serialized array of headers to $headers");
707 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
709 // serialize envelope
711 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
712 '<SOAP-ENV:Envelope'.$ns_string.">".
717 "</SOAP-ENV:Envelope>";
721 * formats a string to be inserted into an HTML stream
723 * @param string $str The string to format
724 * @return string The formatted string
728 function formatDump($str){
729 $str = htmlspecialchars($str);
734 * contracts (changes namespace to prefix) a qualified name
736 * @param string $qname qname
737 * @return string contracted qname
740 function contractQname($qname){
741 // get element namespace
742 //$this->xdebug("Contract $qname");
743 if (strrpos($qname, ':')) {
744 // get unqualified name
745 $name = substr($qname, strrpos($qname, ':') + 1);
747 $ns = substr($qname, 0, strrpos($qname, ':'));
748 $p = $this->getPrefixFromNamespace($ns);
750 return $p . ':' . $name;
759 * expands (changes prefix to namespace) a qualified name
761 * @param string $qname qname
762 * @return string expanded qname
765 function expandQname($qname){
766 // get element prefix
767 if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
768 // get unqualified name
769 $name = substr(strstr($qname,':'),1);
771 $prefix = substr($qname,0,strpos($qname,':'));
772 if(isset($this->namespaces[$prefix])){
773 return $this->namespaces[$prefix].':'.$name;
783 * returns the local part of a prefixed string
784 * returns the original string, if not prefixed
786 * @param string $str The prefixed string
787 * @return string The local part
790 function getLocalPart($str){
791 if($sstr = strrchr($str,':')){
792 // get unqualified name
793 return substr( $sstr, 1 );
800 * returns the prefix part of a prefixed string
801 * returns false, if not prefixed
803 * @param string $str The prefixed string
804 * @return mixed The prefix or false if there is no prefix
807 function getPrefix($str){
808 if($pos = strrpos($str,':')){
810 return substr($str,0,$pos);
816 * pass it a prefix, it returns a namespace
818 * @param string $prefix The prefix
819 * @return mixed The namespace, false if no namespace has the specified prefix
822 function getNamespaceFromPrefix($prefix){
823 if (isset($this->namespaces[$prefix])) {
824 return $this->namespaces[$prefix];
826 //$this->setError("No namespace registered for prefix '$prefix'");
831 * returns the prefix for a given namespace (or prefix)
832 * or false if no prefixes registered for the given namespace
834 * @param string $ns The namespace
835 * @return mixed The prefix, false if the namespace has no prefixes
838 function getPrefixFromNamespace($ns) {
839 foreach ($this->namespaces as $p => $n) {
840 if ($ns == $n || $ns == $p) {
841 $this->usedNamespaces[$p] = $n;
849 * returns the time in ODBC canonical form with microseconds
851 * @return string The time in ODBC canonical form with microseconds
854 function getmicrotime() {
855 if (function_exists('gettimeofday')) {
856 $tod = gettimeofday();
858 $usec = $tod['usec'];
863 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
867 * Returns a string with the output of var_dump
869 * @param mixed $data The variable to var_dump
870 * @return string The output of var_dump
873 function varDump($data) {
876 $ret_val = ob_get_contents();
882 * represents the object as a string
887 function __toString() {
888 return $this->varDump($this);
892 // XML Schema Datatype Helper Functions
894 //xsd:dateTime helpers
897 * convert unix timestamp to ISO 8601 compliant date string
899 * @param int $timestamp Unix time stamp
900 * @param boolean $utc Whether the time stamp is UTC or local
901 * @return mixed ISO 8601 date string or false
904 function timestamp_to_iso8601($timestamp,$utc=true){
905 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
906 $pos = strrpos($datestr, "+");
907 if ($pos === FALSE) {
908 $pos = strrpos($datestr, "-");
910 if ($pos !== FALSE) {
911 if (strlen($datestr) == $pos + 5) {
912 $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
917 '([0-9]{4})-'. // centuries & years CCYY-
918 '([0-9]{2})-'. // months MM-
919 '([0-9]{2})'. // days DD
921 '([0-9]{2}):'. // hours hh:
922 '([0-9]{2}):'. // minutes mm:
923 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
924 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
927 if(preg_match($pattern,$datestr,$regs)){
928 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
937 * convert ISO 8601 compliant date string to unix timestamp
939 * @param string $datestr ISO 8601 compliant date string
940 * @return mixed Unix timestamp (int) or false
943 function iso8601_to_timestamp($datestr){
945 '([0-9]{4})-'. // centuries & years CCYY-
946 '([0-9]{2})-'. // months MM-
947 '([0-9]{2})'. // days DD
949 '([0-9]{2}):'. // hours hh:
950 '([0-9]{2}):'. // minutes mm:
951 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
952 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
954 if(preg_match($pattern,$datestr,$regs)){
957 $op = substr($regs[8],0,1);
958 $h = substr($regs[8],1,2);
959 $m = substr($regs[8],strlen($regs[8])-2,2);
961 $regs[4] = $regs[4] + $h;
962 $regs[5] = $regs[5] + $m;
963 } elseif($op == '+'){
964 $regs[4] = $regs[4] - $h;
965 $regs[5] = $regs[5] - $m;
968 return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
969 // return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
976 * sleeps some number of microseconds
978 * @param string $usec the number of microseconds to sleep
982 function usleepWindows($usec)
984 $start = gettimeofday();
988 $stop = gettimeofday();
989 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
990 + $stop['usec'] - $start['usec'];
992 while ($timePassed < $usec);
1000 * Contains information for a SOAP fault.
1001 * Mainly used for returning faults from deployed functions
1002 * in a server instance.
1003 * @author Dietrich Ayala <dietrich@ganx4.com>
1004 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1007 class nusoap_fault extends nusoap_base {
1009 * The fault code (client|server)
1021 * The fault string, a description of the fault
1027 * The fault detail, typically a string or array of string
1036 * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1037 * @param string $faultactor only used when msg routed between multiple actors
1038 * @param string $faultstring human readable error message
1039 * @param mixed $faultdetail detail, typically a string or array of string
1041 function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1042 parent::nusoap_base();
1043 $this->faultcode = $faultcode;
1044 $this->faultactor = $faultactor;
1045 $this->faultstring = $faultstring;
1046 $this->faultdetail = $faultdetail;
1052 * @return string The serialization of the fault instance.
1055 function serialize(){
1057 foreach($this->namespaces as $k => $v){
1058 $ns_string .= "\n xmlns:$k=\"$v\"";
1061 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1062 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1065 $this->serialize_val($this->faultcode, 'faultcode').
1066 $this->serialize_val($this->faultactor, 'faultactor').
1067 $this->serialize_val($this->faultstring, 'faultstring').
1068 $this->serialize_val($this->faultdetail, 'detail').
1069 '</SOAP-ENV:Fault>'.
1071 '</SOAP-ENV:Envelope>';
1077 * Backward compatibility
1079 class soap_fault extends nusoap_fault {
1087 * parses an XML Schema, allows access to it's data, other utility methods.
1088 * imperfect, no validation... yet, but quite functional.
1090 * @author Dietrich Ayala <dietrich@ganx4.com>
1091 * @author Scott Nichol <snichol@users.sourceforge.net>
1092 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1095 class nusoap_xmlschema extends nusoap_base {
1101 var $enclosingNamespaces;
1103 var $schemaInfo = array();
1104 var $schemaTargetNamespace = '';
1105 // types, elements, attributes defined by the schema
1106 var $attributes = array();
1107 var $complexTypes = array();
1108 var $complexTypeStack = array();
1109 var $currentComplexType = null;
1110 var $elements = array();
1111 var $elementStack = array();
1112 var $currentElement = null;
1113 var $simpleTypes = array();
1114 var $simpleTypeStack = array();
1115 var $currentSimpleType = null;
1117 var $imports = array();
1122 var $depth_array = array();
1123 var $message = array();
1124 var $defaultNamespace = array();
1129 * @param string $schema schema document URI
1130 * @param string $xml xml document URI
1131 * @param string $namespaces namespaces defined in enclosing XML
1134 function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1135 parent::nusoap_base();
1136 $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1138 $this->schema = $schema;
1142 $this->enclosingNamespaces = $namespaces;
1143 $this->namespaces = array_merge($this->namespaces, $namespaces);
1145 // parse schema file
1147 $this->debug('initial schema file: '.$schema);
1148 $this->parseFile($schema, 'schema');
1153 $this->debug('initial xml file: '.$xml);
1154 $this->parseFile($xml, 'xml');
1162 * @param string $xml path/URL to XML file
1163 * @param string $type (schema | xml)
1167 function parseFile($xml,$type){
1170 $xmlStr = @join("",@file($xml));
1172 $msg = 'Error reading XML from '.$xml;
1173 $this->setError($msg);
1177 $this->debug("parsing $xml");
1178 $this->parseString($xmlStr,$type);
1179 $this->debug("done parsing $xml");
1187 * parse an XML string
1189 * @param string $xml path or URL
1190 * @param string $type (schema|xml)
1193 function parseString($xml,$type){
1197 // Create an XML parser.
1198 $this->parser = xml_parser_create();
1199 // Set the options for parsing the XML data.
1200 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1202 // Set the object for the parser.
1203 xml_set_object($this->parser, $this);
1205 // Set the element handlers for the parser.
1206 if($type == "schema"){
1207 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1208 xml_set_character_data_handler($this->parser,'schemaCharacterData');
1209 } elseif($type == "xml"){
1210 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1211 xml_set_character_data_handler($this->parser,'xmlCharacterData');
1214 // Parse the XML file.
1215 if(!xml_parse($this->parser,$xml,true)){
1216 // Display an error message.
1217 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1218 xml_get_current_line_number($this->parser),
1219 xml_error_string(xml_get_error_code($this->parser))
1221 $this->debug($errstr);
1222 $this->debug("XML payload:\n" . $xml);
1223 $this->setError($errstr);
1226 xml_parser_free($this->parser);
1228 $this->debug('no xml passed to parseString()!!');
1229 $this->setError('no xml passed to parseString()!!');
1234 * gets a type name for an unnamed type
1236 * @param string Element name
1237 * @return string A type name for an unnamed type
1240 function CreateTypeName($ename) {
1242 for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1243 $scope .= $this->complexTypeStack[$i] . '_';
1245 return $scope . $ename . '_ContainedType';
1249 * start-element handler
1251 * @param string $parser XML parser object
1252 * @param string $name element name
1253 * @param string $attrs associative array of attributes
1256 function schemaStartElement($parser, $name, $attrs) {
1258 // position in the total number of elements, starting from 0
1259 $pos = $this->position++;
1260 $depth = $this->depth++;
1261 // set self as current value for this depth
1262 $this->depth_array[$depth] = $pos;
1263 $this->message[$pos] = array('cdata' => '');
1265 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1267 $this->defaultNamespace[$pos] = false;
1270 // get element prefix
1271 if($prefix = $this->getPrefix($name)){
1272 // get unqualified name
1273 $name = $this->getLocalPart($name);
1278 // loop thru attributes, expanding, and registering namespace declarations
1279 if(count($attrs) > 0){
1280 foreach($attrs as $k => $v){
1281 // if ns declarations, add to class level array of valid namespaces
1282 if(preg_match('/^xmlns/',$k)){
1283 //$this->xdebug("$k: $v");
1284 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1285 if($ns_prefix = substr(strrchr($k,':'),1)){
1286 //$this->xdebug("Add namespace[$ns_prefix] = $v");
1287 $this->namespaces[$ns_prefix] = $v;
1289 $this->defaultNamespace[$pos] = $v;
1290 if (! $this->getPrefixFromNamespace($v)) {
1291 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1294 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1295 $this->XMLSchemaVersion = $v;
1296 $this->namespaces['xsi'] = $v.'-instance';
1300 foreach($attrs as $k => $v){
1301 // expand each attribute
1302 $k = strpos($k,':') ? $this->expandQname($k) : $k;
1303 $v = strpos($v,':') ? $this->expandQname($v) : $v;
1310 // find status, register data
1312 case 'all': // (optional) compositor content for a complexType
1316 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1317 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1318 //if($name == 'all' || $name == 'sequence'){
1319 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1322 case 'attribute': // complexType attribute
1323 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1324 $this->xdebug("parsing attribute:");
1325 $this->appendDebug($this->varDump($attrs));
1326 if (!isset($attrs['form'])) {
1327 // TODO: handle globals
1328 $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1330 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1331 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1332 if (!strpos($v, ':')) {
1333 // no namespace in arrayType attribute value...
1334 if ($this->defaultNamespace[$pos]) {
1335 // ...so use the default
1336 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1340 if(isset($attrs['name'])){
1341 $this->attributes[$attrs['name']] = $attrs;
1342 $aname = $attrs['name'];
1343 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1344 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1345 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1349 } elseif(isset($attrs['ref'])){
1350 $aname = $attrs['ref'];
1351 $this->attributes[$attrs['ref']] = $attrs;
1354 if($this->currentComplexType){ // This should *always* be
1355 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1357 // arrayType attribute
1358 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1359 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1360 $prefix = $this->getPrefix($aname);
1361 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1362 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1366 if(strpos($v,'[,]')){
1367 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1369 $v = substr($v,0,strpos($v,'[')); // clip the []
1370 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1371 $v = $this->XMLSchemaVersion.':'.$v;
1373 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1376 case 'complexContent': // (optional) content for a complexType
1377 $this->xdebug("do nothing for element $name");
1380 array_push($this->complexTypeStack, $this->currentComplexType);
1381 if(isset($attrs['name'])){
1382 // TODO: what is the scope of named complexTypes that appear
1383 // nested within other c complexTypes?
1384 $this->xdebug('processing named complexType '.$attrs['name']);
1385 //$this->currentElement = false;
1386 $this->currentComplexType = $attrs['name'];
1387 $this->complexTypes[$this->currentComplexType] = $attrs;
1388 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1389 // This is for constructs like
1390 // <complexType name="ListOfString" base="soap:Array">
1392 // <element name="string" type="xsd:string"
1393 // minOccurs="0" maxOccurs="unbounded" />
1396 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1397 $this->xdebug('complexType is unusual array');
1398 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1400 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1403 $name = $this->CreateTypeName($this->currentElement);
1404 $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1405 $this->currentComplexType = $name;
1406 //$this->currentElement = false;
1407 $this->complexTypes[$this->currentComplexType] = $attrs;
1408 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1409 // This is for constructs like
1410 // <complexType name="ListOfString" base="soap:Array">
1412 // <element name="string" type="xsd:string"
1413 // minOccurs="0" maxOccurs="unbounded" />
1416 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1417 $this->xdebug('complexType is unusual array');
1418 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1420 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1423 $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
1426 array_push($this->elementStack, $this->currentElement);
1427 if (!isset($attrs['form'])) {
1428 if ($this->currentComplexType) {
1429 $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1432 $attrs['form'] = 'qualified';
1435 if(isset($attrs['type'])){
1436 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1437 if (! $this->getPrefix($attrs['type'])) {
1438 if ($this->defaultNamespace[$pos]) {
1439 $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1440 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1443 // This is for constructs like
1444 // <complexType name="ListOfString" base="soap:Array">
1446 // <element name="string" type="xsd:string"
1447 // minOccurs="0" maxOccurs="unbounded" />
1450 if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1451 $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1452 $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1454 $this->currentElement = $attrs['name'];
1455 $ename = $attrs['name'];
1456 } elseif(isset($attrs['ref'])){
1457 $this->xdebug("processing element as ref to ".$attrs['ref']);
1458 $this->currentElement = "ref to ".$attrs['ref'];
1459 $ename = $this->getLocalPart($attrs['ref']);
1461 $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1462 $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1463 $this->currentElement = $attrs['name'];
1464 $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1465 $ename = $attrs['name'];
1467 if (isset($ename) && $this->currentComplexType) {
1468 $this->xdebug("add element $ename to complexType $this->currentComplexType");
1469 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1470 } elseif (!isset($attrs['ref'])) {
1471 $this->xdebug("add element $ename to elements array");
1472 $this->elements[ $attrs['name'] ] = $attrs;
1473 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1476 case 'enumeration': // restriction value list member
1477 $this->xdebug('enumeration ' . $attrs['value']);
1478 if ($this->currentSimpleType) {
1479 $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1480 } elseif ($this->currentComplexType) {
1481 $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1484 case 'extension': // simpleContent or complexContent type extension
1485 $this->xdebug('extension ' . $attrs['base']);
1486 if ($this->currentComplexType) {
1487 $ns = $this->getPrefix($attrs['base']);
1489 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
1491 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1494 $this->xdebug('no current complexType to set extensionBase');
1498 if (isset($attrs['schemaLocation'])) {
1499 $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1500 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1502 $this->xdebug('import namespace ' . $attrs['namespace']);
1503 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1504 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1505 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1510 if (isset($attrs['schemaLocation'])) {
1511 $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
1512 $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1514 $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
1517 case 'list': // simpleType value list
1518 $this->xdebug("do nothing for element $name");
1520 case 'restriction': // simpleType, simpleContent or complexContent value restriction
1521 $this->xdebug('restriction ' . $attrs['base']);
1522 if($this->currentSimpleType){
1523 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1524 } elseif($this->currentComplexType){
1525 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1526 if(strstr($attrs['base'],':') == ':Array'){
1527 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1532 $this->schemaInfo = $attrs;
1533 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1534 if (isset($attrs['targetNamespace'])) {
1535 $this->schemaTargetNamespace = $attrs['targetNamespace'];
1537 if (!isset($attrs['elementFormDefault'])) {
1538 $this->schemaInfo['elementFormDefault'] = 'unqualified';
1540 if (!isset($attrs['attributeFormDefault'])) {
1541 $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1544 case 'simpleContent': // (optional) content for a complexType
1545 if ($this->currentComplexType) { // This should *always* be
1546 $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
1548 $this->xdebug("do nothing for element $name because there is no current complexType");
1552 array_push($this->simpleTypeStack, $this->currentSimpleType);
1553 if(isset($attrs['name'])){
1554 $this->xdebug("processing simpleType for name " . $attrs['name']);
1555 $this->currentSimpleType = $attrs['name'];
1556 $this->simpleTypes[ $attrs['name'] ] = $attrs;
1557 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1558 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1560 $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1561 $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1562 $this->currentSimpleType = $name;
1563 //$this->currentElement = false;
1564 $this->simpleTypes[$this->currentSimpleType] = $attrs;
1565 $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1568 case 'union': // simpleType type list
1569 $this->xdebug("do nothing for element $name");
1572 $this->xdebug("do not have any logic to process element $name");
1577 * end-element handler
1579 * @param string $parser XML parser object
1580 * @param string $name element name
1583 function schemaEndElement($parser, $name) {
1584 // bring depth down a notch
1586 // position of current element is equal to the last value left in depth_array for my depth
1587 if(isset($this->depth_array[$this->depth])){
1588 $pos = $this->depth_array[$this->depth];
1590 // get element prefix
1591 if ($prefix = $this->getPrefix($name)){
1592 // get unqualified name
1593 $name = $this->getLocalPart($name);
1598 if($name == 'complexType'){
1599 $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1600 $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1601 $this->currentComplexType = array_pop($this->complexTypeStack);
1602 //$this->currentElement = false;
1604 if($name == 'element'){
1605 $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1606 $this->currentElement = array_pop($this->elementStack);
1608 if($name == 'simpleType'){
1609 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1610 $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1611 $this->currentSimpleType = array_pop($this->simpleTypeStack);
1616 * element content handler
1618 * @param string $parser XML parser object
1619 * @param string $data element content
1622 function schemaCharacterData($parser, $data){
1623 $pos = $this->depth_array[$this->depth - 1];
1624 $this->message[$pos]['cdata'] .= $data;
1628 * serialize the schema
1632 function serializeSchema(){
1634 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1637 if (sizeof($this->imports) > 0) {
1638 foreach($this->imports as $ns => $list) {
1639 foreach ($list as $ii) {
1640 if ($ii['location'] != '') {
1641 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1643 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1649 foreach($this->complexTypes as $typeName => $attrs){
1651 // serialize child elements
1652 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1653 foreach($attrs['elements'] as $element => $eParts){
1654 if(isset($eParts['ref'])){
1655 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1657 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1658 foreach ($eParts as $aName => $aValue) {
1659 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1660 if ($aName != 'name' && $aName != 'type') {
1661 $contentStr .= " $aName=\"$aValue\"";
1664 $contentStr .= "/>\n";
1667 // compositor wraps elements
1668 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1669 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1673 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1674 foreach($attrs['attrs'] as $attr => $aParts){
1675 $contentStr .= " <$schemaPrefix:attribute";
1676 foreach ($aParts as $a => $v) {
1677 if ($a == 'ref' || $a == 'type') {
1678 $contentStr .= " $a=\"".$this->contractQName($v).'"';
1679 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1680 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1681 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1683 $contentStr .= " $a=\"$v\"";
1686 $contentStr .= "/>\n";
1690 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1691 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1692 // complex or simple content
1693 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1694 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1697 // finalize complex type
1698 if($contentStr != ''){
1699 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1701 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1703 $xml .= $contentStr;
1706 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1707 foreach($this->simpleTypes as $typeName => $eParts){
1708 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1709 if (isset($eParts['enumeration'])) {
1710 foreach ($eParts['enumeration'] as $e) {
1711 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
1714 $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1718 if(isset($this->elements) && count($this->elements) > 0){
1719 foreach($this->elements as $element => $eParts){
1720 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1724 if(isset($this->attributes) && count($this->attributes) > 0){
1725 foreach($this->attributes as $attr => $aParts){
1726 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1731 foreach ($this->schemaInfo as $k => $v) {
1732 if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1733 $attr .= " $k=\"$v\"";
1736 $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1737 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1738 $el .= " xmlns:$nsp=\"$ns\"";
1740 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1745 * adds debug data to the clas level debug string
1747 * @param string $string debug data
1750 function xdebug($string){
1751 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1755 * get the PHP type of a user defined type in the schema
1756 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1757 * returns false if no type exists, or not w/ the given namespace
1758 * else returns a string that is either a native php type, or 'struct'
1760 * @param string $type name of defined type
1761 * @param string $ns namespace of type
1766 function getPHPType($type,$ns){
1767 if(isset($this->typemap[$ns][$type])){
1768 //print "found type '$type' and ns $ns in typemap<br>";
1769 return $this->typemap[$ns][$type];
1770 } elseif(isset($this->complexTypes[$type])){
1771 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1772 return $this->complexTypes[$type]['phpType'];
1778 * returns an associative array of information about a given type
1779 * returns false if no type exists by the given name
1781 * For a complexType typeDef = array(
1782 * 'restrictionBase' => '',
1784 * 'compositor' => '(sequence|all)',
1785 * 'elements' => array(), // refs to elements array
1786 * 'attrs' => array() // refs to attributes array
1787 * ... and so on (see addComplexType)
1790 * For simpleType or element, the array has different keys.
1792 * @param string $type
1795 * @see addComplexType
1796 * @see addSimpleType
1799 function getTypeDef($type){
1800 //$this->debug("in getTypeDef for type $type");
1801 if (substr($type, -1) == '^') {
1803 $type = substr($type, 0, -1);
1808 if((! $is_element) && isset($this->complexTypes[$type])){
1809 $this->xdebug("in getTypeDef, found complexType $type");
1810 return $this->complexTypes[$type];
1811 } elseif((! $is_element) && isset($this->simpleTypes[$type])){
1812 $this->xdebug("in getTypeDef, found simpleType $type");
1813 if (!isset($this->simpleTypes[$type]['phpType'])) {
1814 // get info for type to tack onto the simple type
1815 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1816 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1817 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1818 $etype = $this->getTypeDef($uqType);
1820 $this->xdebug("in getTypeDef, found type for simpleType $type:");
1821 $this->xdebug($this->varDump($etype));
1822 if (isset($etype['phpType'])) {
1823 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1825 if (isset($etype['elements'])) {
1826 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1830 return $this->simpleTypes[$type];
1831 } elseif(isset($this->elements[$type])){
1832 $this->xdebug("in getTypeDef, found element $type");
1833 if (!isset($this->elements[$type]['phpType'])) {
1834 // get info for type to tack onto the element
1835 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1836 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1837 $etype = $this->getTypeDef($uqType);
1839 $this->xdebug("in getTypeDef, found type for element $type:");
1840 $this->xdebug($this->varDump($etype));
1841 if (isset($etype['phpType'])) {
1842 $this->elements[$type]['phpType'] = $etype['phpType'];
1844 if (isset($etype['elements'])) {
1845 $this->elements[$type]['elements'] = $etype['elements'];
1847 if (isset($etype['extensionBase'])) {
1848 $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1850 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1851 $this->xdebug("in getTypeDef, element $type is an XSD type");
1852 $this->elements[$type]['phpType'] = 'scalar';
1855 return $this->elements[$type];
1856 } elseif(isset($this->attributes[$type])){
1857 $this->xdebug("in getTypeDef, found attribute $type");
1858 return $this->attributes[$type];
1859 } elseif (preg_match('/_ContainedType$/', $type)) {
1860 $this->xdebug("in getTypeDef, have an untyped element $type");
1861 $typeDef['typeClass'] = 'simpleType';
1862 $typeDef['phpType'] = 'scalar';
1863 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1866 $this->xdebug("in getTypeDef, did not find $type");
1871 * returns a sample serialization of a given type, or false if no type by the given name
1873 * @param string $type name of type
1878 function serializeTypeDef($type){
1879 //print "in sTD() for type $type<br>";
1880 if($typeDef = $this->getTypeDef($type)){
1882 if(is_array($typeDef['attrs'])){
1883 foreach($typeDef['attrs'] as $attName => $data){
1884 $str .= " $attName=\"{type = ".$data['type']."}\"";
1887 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1888 if(count($typeDef['elements']) > 0){
1890 foreach($typeDef['elements'] as $element => $eData){
1891 $str .= $this->serializeTypeDef($element);
1894 } elseif($typeDef['typeClass'] == 'element') {
1895 $str .= "></$type>";
1905 * returns HTML form elements that allow a user
1906 * to enter values for creating an instance of the given type.
1908 * @param string $name name for type instance
1909 * @param string $type name of type
1914 function typeToForm($name,$type){
1916 if($typeDef = $this->getTypeDef($type)){
1918 if($typeDef['phpType'] == 'struct'){
1919 $buffer .= '<table>';
1920 foreach($typeDef['elements'] as $child => $childDef){
1922 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1923 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1925 $buffer .= '</table>';
1927 } elseif($typeDef['phpType'] == 'array'){
1928 $buffer .= '<table>';
1929 for($i=0;$i < 3; $i++){
1931 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1932 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1934 $buffer .= '</table>';
1937 $buffer .= "<input type='text' name='parameters[$name]'>";
1940 $buffer .= "<input type='text' name='parameters[$name]'>";
1946 * adds a complex type to the schema
1956 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1960 * example: PHP associative array ( SOAP Struct )
1967 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1971 * @param typeClass (complexType|simpleType|attribute)
1972 * @param phpType: currently supported are array and struct (php assoc array)
1973 * @param compositor (all|sequence|choice)
1974 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1975 * @param elements = array ( name = array(name=>'',type=>'') )
1976 * @param attrs = array(
1978 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1979 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1982 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1986 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1987 $this->complexTypes[$name] = array(
1989 'typeClass' => $typeClass,
1990 'phpType' => $phpType,
1991 'compositor'=> $compositor,
1992 'restrictionBase' => $restrictionBase,
1993 'elements' => $elements,
1995 'arrayType' => $arrayType
1998 $this->xdebug("addComplexType $name:");
1999 $this->appendDebug($this->varDump($this->complexTypes[$name]));
2003 * adds a simple type to the schema
2005 * @param string $name
2006 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2007 * @param string $typeClass (should always be simpleType)
2008 * @param string $phpType (should always be scalar)
2009 * @param array $enumeration array of values
2011 * @see nusoap_xmlschema
2014 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
2015 $this->simpleTypes[$name] = array(
2017 'typeClass' => $typeClass,
2018 'phpType' => $phpType,
2019 'type' => $restrictionBase,
2020 'enumeration' => $enumeration
2023 $this->xdebug("addSimpleType $name:");
2024 $this->appendDebug($this->varDump($this->simpleTypes[$name]));
2028 * adds an element to the schema
2030 * @param array $attrs attributes that must include name and type
2031 * @see nusoap_xmlschema
2034 function addElement($attrs) {
2035 if (! $this->getPrefix($attrs['type'])) {
2036 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
2038 $this->elements[ $attrs['name'] ] = $attrs;
2039 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
2041 $this->xdebug("addElement " . $attrs['name']);
2042 $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
2047 * Backward compatibility
2049 class XMLSchema extends nusoap_xmlschema {
2057 * For creating serializable abstractions of native PHP types. This class
2058 * allows element name/namespace, XSD type, and XML attributes to be
2059 * associated with a value. This is extremely useful when WSDL is not
2060 * used, but is also useful when WSDL is used with polymorphic types, including
2061 * xsd:anyType and user-defined types.
2063 * @author Dietrich Ayala <dietrich@ganx4.com>
2064 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2067 class soapval extends nusoap_base {
2069 * The XML element name
2076 * The XML type name (string or false)
2090 * The XML element namespace (string or false)
2097 * The XML type namespace (string or false)
2104 * The XML element attributes (array or false)
2114 * @param string $name optional name
2115 * @param mixed $type optional type name
2116 * @param mixed $value optional value
2117 * @param mixed $element_ns optional namespace of value
2118 * @param mixed $type_ns optional namespace of type
2119 * @param mixed $attributes associative array of attributes to add to element serialization
2122 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2123 parent::nusoap_base();
2124 $this->name = $name;
2125 $this->type = $type;
2126 $this->value = $value;
2127 $this->element_ns = $element_ns;
2128 $this->type_ns = $type_ns;
2129 $this->attributes = $attributes;
2133 * return serialized value
2135 * @param string $use The WSDL use value (encoded|literal)
2136 * @return string XML data
2139 function serialize($use='encoded') {
2140 return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2144 * decodes a soapval object into a PHP native type
2150 return $this->value;
2161 * transport class for sending/receiving data via HTTP and HTTPS
2162 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2164 * @author Dietrich Ayala <dietrich@ganx4.com>
2165 * @author Scott Nichol <snichol@users.sourceforge.net>
2166 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2169 class soap_transport_http extends nusoap_base {
2173 var $digest_uri = '';
2178 var $request_method = 'POST';
2179 var $protocol_version = '1.0';
2181 var $outgoing_headers = array();
2182 var $incoming_headers = array();
2183 var $incoming_cookies = array();
2184 var $outgoing_payload = '';
2185 var $incoming_payload = '';
2186 var $response_status_line; // HTTP response status line
2187 var $useSOAPAction = true;
2188 var $persistentConnection = false;
2189 var $ch = false; // cURL handle
2190 var $ch_options = array(); // cURL custom options
2191 var $use_curl = false; // force cURL use
2192 var $proxy = null; // proxy information (associative array)
2196 var $digestRequest = array();
2197 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2198 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2199 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2200 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2201 // passphrase: SSL key password/passphrase
2202 // certpassword: SSL certificate password
2203 // verifypeer: default is 1
2204 // verifyhost: default is 1
2209 * @param string $url The URL to which to connect
2210 * @param array $curl_options User-specified cURL options
2211 * @param boolean $use_curl Whether to try to force cURL use
2214 function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2215 parent::nusoap_base();
2216 $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2217 $this->appendDebug($this->varDump($curl_options));
2218 $this->setURL($url);
2219 if (is_array($curl_options)) {
2220 $this->ch_options = $curl_options;
2222 $this->use_curl = $use_curl;
2223 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2224 $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2228 * sets a cURL option
2230 * @param mixed $option The cURL option (always integer?)
2231 * @param mixed $value The cURL option value
2234 function setCurlOption($option, $value) {
2235 $this->debug("setCurlOption option=$option, value=");
2236 $this->appendDebug($this->varDump($value));
2237 curl_setopt($this->ch, $option, $value);
2241 * sets an HTTP header
2243 * @param string $name The name of the header
2244 * @param string $value The value of the header
2247 function setHeader($name, $value) {
2248 $this->outgoing_headers[$name] = $value;
2249 $this->debug("set header $name: $value");
2253 * unsets an HTTP header
2255 * @param string $name The name of the header
2258 function unsetHeader($name) {
2259 if (isset($this->outgoing_headers[$name])) {
2260 $this->debug("unset header $name");
2261 unset($this->outgoing_headers[$name]);
2266 * sets the URL to which to connect
2268 * @param string $url The URL to which to connect
2271 function setURL($url) {
2274 $u = parse_url($url);
2275 foreach($u as $k => $v){
2276 $this->debug("parsed URL $k = $v");
2280 // add any GET params to path
2281 if(isset($u['query']) && $u['query'] != ''){
2282 $this->path .= '?' . $u['query'];
2286 if(!isset($u['port'])){
2287 if($u['scheme'] == 'https'){
2294 $this->uri = $this->path;
2295 $this->digest_uri = $this->uri;
2298 if (!isset($u['port'])) {
2299 $this->setHeader('Host', $this->host);
2301 $this->setHeader('Host', $this->host.':'.$this->port);
2304 if (isset($u['user']) && $u['user'] != '') {
2305 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2310 * gets the I/O method to use
2312 * @return string I/O method to use (socket|curl|unknown)
2315 function io_method() {
2316 if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2318 if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2324 * establish an HTTP connection
2326 * @param integer $timeout set connection timeout in seconds
2327 * @param integer $response_timeout set response timeout in seconds
2328 * @return boolean true if connected, false if not
2331 function connect($connection_timeout=0,$response_timeout=30){
2332 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2333 // "regular" socket.
2334 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2335 // loaded), and until PHP5 stream_get_wrappers is not available.
2336 // if ($this->scheme == 'https') {
2337 // if (version_compare(phpversion(), '4.3.0') >= 0) {
2338 // if (extension_loaded('openssl')) {
2339 // $this->scheme = 'ssl';
2340 // $this->debug('Using SSL over OpenSSL');
2344 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2345 if ($this->io_method() == 'socket') {
2346 if (!is_array($this->proxy)) {
2347 $host = $this->host;
2348 $port = $this->port;
2350 $host = $this->proxy['host'];
2351 $port = $this->proxy['port'];
2354 // use persistent connection
2355 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2356 if (!feof($this->fp)) {
2357 $this->debug('Re-use persistent connection');
2361 $this->debug('Closed persistent connection at EOF');
2364 // munge host if using OpenSSL
2365 if ($this->scheme == 'ssl') {
2366 $host = 'ssl://' . $host;
2368 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2371 if($connection_timeout > 0){
2372 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2374 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2379 $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2381 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2383 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
2386 $this->setError($msg);
2390 // set response timeout
2391 $this->debug('set response timeout to ' . $response_timeout);
2392 socket_set_timeout( $this->fp, $response_timeout);
2394 $this->debug('socket connected');
2396 } else if ($this->io_method() == 'curl') {
2397 if (!extension_loaded('curl')) {
2398 // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2399 $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
2402 // Avoid warnings when PHP does not have these options
2403 if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2404 $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2406 $CURLOPT_CONNECTIONTIMEOUT = 78;
2407 if (defined('CURLOPT_HTTPAUTH'))
2408 $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2410 $CURLOPT_HTTPAUTH = 107;
2411 if (defined('CURLOPT_PROXYAUTH'))
2412 $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2414 $CURLOPT_PROXYAUTH = 111;
2415 if (defined('CURLAUTH_BASIC'))
2416 $CURLAUTH_BASIC = CURLAUTH_BASIC;
2418 $CURLAUTH_BASIC = 1;
2419 if (defined('CURLAUTH_DIGEST'))
2420 $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2422 $CURLAUTH_DIGEST = 2;
2423 if (defined('CURLAUTH_NTLM'))
2424 $CURLAUTH_NTLM = CURLAUTH_NTLM;
2428 $this->debug('connect using cURL');
2430 $this->ch = curl_init();
2432 $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2434 $hostURL .= $this->path;
2435 $this->setCurlOption(CURLOPT_URL, $hostURL);
2436 // follow location headers (re-directs)
2437 if (ini_get('safe_mode') || ini_get('open_basedir')) {
2438 $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2439 $this->debug('safe_mode = ');
2440 $this->appendDebug($this->varDump(ini_get('safe_mode')));
2441 $this->debug('open_basedir = ');
2442 $this->appendDebug($this->varDump(ini_get('open_basedir')));
2444 $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2446 // ask for headers in the response output
2447 $this->setCurlOption(CURLOPT_HEADER, 1);
2448 // ask for the response output as the return value
2449 $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2451 // We manage this ourselves through headers and encoding
2452 // if(function_exists('gzuncompress')){
2453 // $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2455 // persistent connection
2456 if ($this->persistentConnection) {
2457 // I believe the following comment is now bogus, having applied to
2458 // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2459 // The way we send data, we cannot use persistent connections, since
2460 // there will be some "junk" at the end of our request.
2461 //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2462 $this->persistentConnection = false;
2463 $this->setHeader('Connection', 'close');
2466 if ($connection_timeout != 0) {
2467 $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2469 if ($response_timeout != 0) {
2470 $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2473 if ($this->scheme == 'https') {
2474 $this->debug('set cURL SSL verify options');
2475 // recent versions of cURL turn on peer/host checking by default,
2476 // while PHP binaries are not compiled with a default location for the
2477 // CA cert bundle, so disable peer/host checking.
2478 //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2479 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2480 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2482 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2483 if ($this->authtype == 'certificate') {
2484 $this->debug('set cURL certificate options');
2485 if (isset($this->certRequest['cainfofile'])) {
2486 $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2488 if (isset($this->certRequest['verifypeer'])) {
2489 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2491 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2493 if (isset($this->certRequest['verifyhost'])) {
2494 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2496 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2498 if (isset($this->certRequest['sslcertfile'])) {
2499 $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2501 if (isset($this->certRequest['sslkeyfile'])) {
2502 $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2504 if (isset($this->certRequest['passphrase'])) {
2505 $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2507 if (isset($this->certRequest['certpassword'])) {
2508 $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2512 if ($this->authtype && ($this->authtype != 'certificate')) {
2513 if ($this->username) {
2514 $this->debug('set cURL username/password');
2515 $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2517 if ($this->authtype == 'basic') {
2518 $this->debug('set cURL for Basic authentication');
2519 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2521 if ($this->authtype == 'digest') {
2522 $this->debug('set cURL for digest authentication');
2523 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2525 if ($this->authtype == 'ntlm') {
2526 $this->debug('set cURL for NTLM authentication');
2527 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2530 if (is_array($this->proxy)) {
2531 $this->debug('set cURL proxy options');
2532 if ($this->proxy['port'] != '') {
2533 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2535 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2537 if ($this->proxy['username'] || $this->proxy['password']) {
2538 $this->debug('set cURL proxy authentication options');
2539 $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2540 if ($this->proxy['authtype'] == 'basic') {
2541 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2543 if ($this->proxy['authtype'] == 'ntlm') {
2544 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2548 $this->debug('cURL connection set up');
2551 $this->setError('Unknown scheme ' . $this->scheme);
2552 $this->debug('Unknown scheme ' . $this->scheme);
2558 * sends the SOAP request and gets the SOAP response via HTTP[S]
2560 * @param string $data message data
2561 * @param integer $timeout set connection timeout in seconds
2562 * @param integer $response_timeout set response timeout in seconds
2563 * @param array $cookies cookies to send
2564 * @return string data
2567 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2569 $this->debug('entered send() with data of length: '.strlen($data));
2571 $this->tryagain = true;
2573 while ($this->tryagain) {
2574 $this->tryagain = false;
2577 if (!$this->connect($timeout, $response_timeout)){
2582 if (!$this->sendRequest($data, $cookies)){
2587 $respdata = $this->getResponse();
2589 $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2592 $this->debug('end of send()');
2598 * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2600 * @param string $data message data
2601 * @param integer $timeout set connection timeout in seconds
2602 * @param integer $response_timeout set response timeout in seconds
2603 * @param array $cookies cookies to send
2604 * @return string data
2608 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2609 return $this->send($data, $timeout, $response_timeout, $cookies);
2613 * if authenticating, set user credentials here
2615 * @param string $username
2616 * @param string $password
2617 * @param string $authtype (basic|digest|certificate|ntlm)
2618 * @param array $digestRequest (keys must be nonce, nc, realm, qop)
2619 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2622 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2623 $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2624 $this->appendDebug($this->varDump($digestRequest));
2625 $this->debug("certRequest=");
2626 $this->appendDebug($this->varDump($certRequest));
2628 if ($authtype == 'basic') {
2629 $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2630 } elseif ($authtype == 'digest') {
2631 if (isset($digestRequest['nonce'])) {
2632 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2634 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2636 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2637 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2642 // A2 = Method ":" digest-uri-value
2643 $A2 = $this->request_method . ':' . $this->digest_uri;
2648 // KD(secret, data) = H(concat(secret, ":", data))
2650 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
2652 // ":" unq(cnonce-value)
2653 // ":" unq(qop-value)
2656 // if qop is missing,
2657 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2659 $unhashedDigest = '';
2660 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2662 if ($digestRequest['qop'] != '') {
2663 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2665 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2668 $hashedDigest = md5($unhashedDigest);
2671 if (isset($digestRequest['opaque'])) {
2672 $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2675 $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2677 } elseif ($authtype == 'certificate') {
2678 $this->certRequest = $certRequest;
2679 $this->debug('Authorization header not set for certificate');
2680 } elseif ($authtype == 'ntlm') {
2682 $this->debug('Authorization header not set for ntlm');
2684 $this->username = $username;
2685 $this->password = $password;
2686 $this->authtype = $authtype;
2687 $this->digestRequest = $digestRequest;
2691 * set the soapaction value
2693 * @param string $soapaction
2696 function setSOAPAction($soapaction) {
2697 $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2703 * @param string $enc encoding style. supported values: gzip, deflate, or both
2706 function setEncoding($enc='gzip, deflate') {
2707 if (function_exists('gzdeflate')) {
2708 $this->protocol_version = '1.1';
2709 $this->setHeader('Accept-Encoding', $enc);
2710 if (!isset($this->outgoing_headers['Connection'])) {
2711 $this->setHeader('Connection', 'close');
2712 $this->persistentConnection = false;
2714 // deprecated as of PHP 5.3.0
2715 //set_magic_quotes_runtime(0);
2716 $this->encoding = $enc;
2721 * set proxy info here
2723 * @param string $proxyhost use an empty string to remove proxy
2724 * @param string $proxyport
2725 * @param string $proxyusername
2726 * @param string $proxypassword
2727 * @param string $proxyauthtype (basic|ntlm)
2730 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2732 $this->proxy = array(
2733 'host' => $proxyhost,
2734 'port' => $proxyport,
2735 'username' => $proxyusername,
2736 'password' => $proxypassword,
2737 'authtype' => $proxyauthtype
2739 if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2740 $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2743 $this->debug('remove proxy');
2745 unsetHeader('Proxy-Authorization');
2751 * Test if the given string starts with a header that is to be skipped.
2752 * Skippable headers result from chunked transfer and proxy requests.
2754 * @param string $data The string to check.
2755 * @returns boolean Whether a skippable header was found.
2758 function isSkippableCurlHeader(&$data) {
2759 $skipHeaders = array( 'HTTP/1.1 100',
2766 'HTTP/1.0 200 Connection established');
2767 foreach ($skipHeaders as $hd) {
2768 $prefix = substr($data, 0, strlen($hd));
2769 if ($prefix == $hd) return true;
2776 * decode a string that is encoded w/ "chunked' transfer encoding
2777 * as defined in RFC2068 19.4.6
2779 * @param string $buffer
2785 function decodeChunked($buffer, $lb){
2790 // read chunk-size, chunk-extension (if any) and CRLF
2791 // get the position of the linebreak
2792 $chunkend = strpos($buffer, $lb);
2793 if ($chunkend == FALSE) {
2794 $this->debug('no linebreak found in decodeChunked');
2797 $temp = substr($buffer,0,$chunkend);
2798 $chunk_size = hexdec( trim($temp) );
2799 $chunkstart = $chunkend + strlen($lb);
2800 // while (chunk-size > 0) {
2801 while ($chunk_size > 0) {
2802 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2803 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2805 // Just in case we got a broken connection
2806 if ($chunkend == FALSE) {
2807 $chunk = substr($buffer,$chunkstart);
2808 // append chunk-data to entity-body
2810 $length += strlen($chunk);
2814 // read chunk-data and CRLF
2815 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2816 // append chunk-data to entity-body
2818 // length := length + chunk-size
2819 $length += strlen($chunk);
2820 // read chunk-size and CRLF
2821 $chunkstart = $chunkend + strlen($lb);
2823 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2824 if ($chunkend == FALSE) {
2825 break; //Just in case we got a broken connection
2827 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2828 $chunk_size = hexdec( trim($temp) );
2829 $chunkstart = $chunkend;
2835 * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2837 * @param string $data HTTP body
2838 * @param string $cookie_str data for HTTP Cookie header
2842 function buildPayload($data, $cookie_str = '') {
2843 // Note: for cURL connections, $this->outgoing_payload is ignored,
2844 // as is the Content-Length header, but these are still created as
2845 // debugging guides.
2847 // add content-length header
2848 if ($this->request_method != 'GET') {
2849 $this->setHeader('Content-Length', strlen($data));
2852 // start building outgoing payload:
2858 $req = "$this->request_method $uri HTTP/$this->protocol_version";
2859 $this->debug("HTTP request: $req");
2860 $this->outgoing_payload = "$req\r\n";
2862 // loop thru headers, serializing
2863 foreach($this->outgoing_headers as $k => $v){
2865 $this->debug("HTTP header: $hdr");
2866 $this->outgoing_payload .= "$hdr\r\n";
2870 if ($cookie_str != '') {
2871 $hdr = 'Cookie: '.$cookie_str;
2872 $this->debug("HTTP header: $hdr");
2873 $this->outgoing_payload .= "$hdr\r\n";
2876 // header/body separator
2877 $this->outgoing_payload .= "\r\n";
2880 $this->outgoing_payload .= $data;
2884 * sends the SOAP request via HTTP[S]
2886 * @param string $data message data
2887 * @param array $cookies cookies to send
2888 * @return boolean true if OK, false if problem
2891 function sendRequest($data, $cookies = NULL) {
2892 // build cookie string
2893 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2896 $this->buildPayload($data, $cookie_str);
2898 if ($this->io_method() == 'socket') {
2900 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2901 $this->setError('couldn\'t write message data to socket');
2902 $this->debug('couldn\'t write message data to socket');
2905 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2907 } else if ($this->io_method() == 'curl') {
2909 // cURL does say this should only be the verb, and in fact it
2910 // turns out that the URI and HTTP version are appended to this, which
2911 // some servers refuse to work with (so we no longer use this method!)
2912 //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2913 $curl_headers = array();
2914 foreach($this->outgoing_headers as $k => $v){
2915 if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2916 $this->debug("Skip cURL header $k: $v");
2918 $curl_headers[] = "$k: $v";
2921 if ($cookie_str != '') {
2922 $curl_headers[] = 'Cookie: ' . $cookie_str;
2924 $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2925 $this->debug('set cURL HTTP headers');
2926 if ($this->request_method == "POST") {
2927 $this->setCurlOption(CURLOPT_POST, 1);
2928 $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2929 $this->debug('set cURL POST data');
2932 // insert custom user-set cURL options
2933 foreach ($this->ch_options as $key => $val) {
2934 $this->setCurlOption($key, $val);
2937 $this->debug('set cURL payload');
2943 * gets the SOAP response via HTTP[S]
2945 * @return string the response (also sets member variables like incoming_payload)
2948 function getResponse(){
2949 $this->incoming_payload = '';
2951 if ($this->io_method() == 'socket') {
2952 // loop until headers have been retrieved
2954 while (!isset($lb)){
2956 // We might EOF during header read.
2957 if(feof($this->fp)) {
2958 $this->incoming_payload = $data;
2959 $this->debug('found no headers before EOF after length ' . strlen($data));
2960 $this->debug("received before EOF:\n" . $data);
2961 $this->setError('server failed to send headers');
2965 $tmp = fgets($this->fp, 256);
2966 $tmplen = strlen($tmp);
2967 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2970 $this->incoming_payload = $data;
2971 $this->debug('socket read of headers timed out after length ' . strlen($data));
2972 $this->debug("read before timeout: " . $data);
2973 $this->setError('socket read of headers timed out');
2978 $pos = strpos($data,"\r\n\r\n");
2982 $pos = strpos($data,"\n\n");
2987 // remove 100 headers
2988 if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
2993 // store header data
2994 $this->incoming_payload .= $data;
2995 $this->debug('found end of headers after length ' . strlen($data));
2997 $header_data = trim(substr($data,0,$pos));
2998 $header_array = explode($lb,$header_data);
2999 $this->incoming_headers = array();
3000 $this->incoming_cookies = array();
3001 foreach($header_array as $header_line){
3002 $arr = explode(':',$header_line, 2);
3003 if(count($arr) > 1){
3004 $header_name = strtolower(trim($arr[0]));
3005 $this->incoming_headers[$header_name] = trim($arr[1]);
3006 if ($header_name == 'set-cookie') {
3007 // TODO: allow multiple cookies from parseCookie
3008 $cookie = $this->parseCookie(trim($arr[1]));
3010 $this->incoming_cookies[] = $cookie;
3011 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3013 $this->debug('did not find cookie in ' . trim($arr[1]));
3016 } else if (isset($header_name)) {
3017 // append continuation line to previous header
3018 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3022 // loop until msg has been received
3023 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
3024 $content_length = 2147483647; // ignore any content-length header
3026 $this->debug("want to read chunked content");
3027 } elseif (isset($this->incoming_headers['content-length'])) {
3028 $content_length = $this->incoming_headers['content-length'];
3030 $this->debug("want to read content of length $content_length");
3032 $content_length = 2147483647;
3034 $this->debug("want to read content to EOF");
3039 $tmp = fgets($this->fp, 256);
3040 $tmplen = strlen($tmp);
3041 $this->debug("read chunk line of $tmplen bytes");
3043 $this->incoming_payload = $data;
3044 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
3045 $this->debug("read before timeout:\n" . $data);
3046 $this->setError('socket read of chunk length timed out');
3049 $content_length = hexdec(trim($tmp));
3050 $this->debug("chunk length $content_length");
3053 while (($strlen < $content_length) && (!feof($this->fp))) {
3054 $readlen = min(8192, $content_length - $strlen);
3055 $tmp = fread($this->fp, $readlen);
3056 $tmplen = strlen($tmp);
3057 $this->debug("read buffer of $tmplen bytes");
3058 if (($tmplen == 0) && (!feof($this->fp))) {
3059 $this->incoming_payload = $data;
3060 $this->debug('socket read of body timed out after length ' . strlen($data));
3061 $this->debug("read before timeout:\n" . $data);
3062 $this->setError('socket read of body timed out');
3068 if ($chunked && ($content_length > 0)) {
3069 $tmp = fgets($this->fp, 256);
3070 $tmplen = strlen($tmp);
3071 $this->debug("read chunk terminator of $tmplen bytes");
3073 $this->incoming_payload = $data;
3074 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3075 $this->debug("read before timeout:\n" . $data);
3076 $this->setError('socket read of chunk terminator timed out');
3080 } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3081 if (feof($this->fp)) {
3082 $this->debug('read to EOF');
3084 $this->debug('read body of length ' . strlen($data));
3085 $this->incoming_payload .= $data;
3086 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3088 // close filepointer
3090 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3091 (! $this->persistentConnection) || feof($this->fp)){
3094 $this->debug('closed socket');
3097 // connection was closed unexpectedly
3098 if($this->incoming_payload == ''){
3099 $this->setError('no response from server');
3103 // decode transfer-encoding
3104 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3105 // if(!$data = $this->decodeChunked($data, $lb)){
3106 // $this->setError('Decoding of chunked data failed');
3109 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3110 // set decoded payload
3111 // $this->incoming_payload = $header_data.$lb.$lb.$data;
3114 } else if ($this->io_method() == 'curl') {
3116 $this->debug('send and receive with cURL');
3117 $this->incoming_payload = curl_exec($this->ch);
3118 $data = $this->incoming_payload;
3120 $cErr = curl_error($this->ch);
3122 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3123 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3124 foreach(curl_getinfo($this->ch) as $k => $v){
3125 $err .= "$k: $v<br>";
3128 $this->setError($err);
3129 curl_close($this->ch);
3133 //var_dump(curl_getinfo($this->ch));
3137 $this->debug('No cURL error, closing cURL');
3138 curl_close($this->ch);
3140 // try removing skippable headers
3142 while ($this->isSkippableCurlHeader($data)) {
3143 $this->debug("Found HTTP header to skip");
3144 if ($pos = strpos($data,"\r\n\r\n")) {
3145 $data = ltrim(substr($data,$pos));
3146 } elseif($pos = strpos($data,"\n\n") ) {
3147 $data = ltrim(substr($data,$pos));
3152 // have nothing left; just remove 100 header(s)
3154 while (preg_match('/^HTTP\/1.1 100/',$data)) {
3155 if ($pos = strpos($data,"\r\n\r\n")) {
3156 $data = ltrim(substr($data,$pos));
3157 } elseif($pos = strpos($data,"\n\n") ) {
3158 $data = ltrim(substr($data,$pos));
3163 // separate content from HTTP headers
3164 if ($pos = strpos($data,"\r\n\r\n")) {
3166 } elseif( $pos = strpos($data,"\n\n")) {
3169 $this->debug('no proper separation of headers and document');
3170 $this->setError('no proper separation of headers and document');
3173 $header_data = trim(substr($data,0,$pos));
3174 $header_array = explode($lb,$header_data);
3175 $data = ltrim(substr($data,$pos));
3176 $this->debug('found proper separation of headers and document');
3177 $this->debug('cleaned data, stringlen: '.strlen($data));
3179 foreach ($header_array as $header_line) {
3180 $arr = explode(':',$header_line,2);
3181 if(count($arr) > 1){
3182 $header_name = strtolower(trim($arr[0]));
3183 $this->incoming_headers[$header_name] = trim($arr[1]);
3184 if ($header_name == 'set-cookie') {
3185 // TODO: allow multiple cookies from parseCookie
3186 $cookie = $this->parseCookie(trim($arr[1]));
3188 $this->incoming_cookies[] = $cookie;
3189 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3191 $this->debug('did not find cookie in ' . trim($arr[1]));
3194 } else if (isset($header_name)) {
3195 // append continuation line to previous header
3196 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3201 $this->response_status_line = $header_array[0];
3202 $arr = explode(' ', $this->response_status_line, 3);
3203 $http_version = $arr[0];
3204 $http_status = intval($arr[1]);
3205 $http_reason = count($arr) > 2 ? $arr[2] : '';
3207 // see if we need to resend the request with http digest authentication
3208 if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3209 $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3210 $this->setURL($this->incoming_headers['location']);
3211 $this->tryagain = true;
3215 // see if we need to resend the request with http digest authentication
3216 if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3217 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3218 if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3219 $this->debug('Server wants digest authentication');
3220 // remove "Digest " from our elements
3221 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3223 // parse elements into array
3224 $digestElements = explode(',', $digestString);
3225 foreach ($digestElements as $val) {
3226 $tempElement = explode('=', trim($val), 2);
3227 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3230 // should have (at least) qop, realm, nonce
3231 if (isset($digestRequest['nonce'])) {
3232 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3233 $this->tryagain = true;
3237 $this->debug('HTTP authentication failed');
3238 $this->setError('HTTP authentication failed');
3243 ($http_status >= 300 && $http_status <= 307) ||
3244 ($http_status >= 400 && $http_status <= 417) ||
3245 ($http_status >= 501 && $http_status <= 505)
3247 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3251 // decode content-encoding
3252 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3253 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3254 // if decoding works, use it. else assume data wasn't gzencoded
3255 if(function_exists('gzinflate')){
3256 //$timer->setMarker('starting decoding of gzip/deflated content');
3257 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3258 // this means there are no Zlib headers, although there should be
3259 $this->debug('The gzinflate function exists');
3260 $datalen = strlen($data);
3261 if ($this->incoming_headers['content-encoding'] == 'deflate') {
3262 if ($degzdata = @gzinflate($data)) {
3264 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3265 if (strlen($data) < $datalen) {
3266 // test for the case that the payload has been compressed twice
3267 $this->debug('The inflated payload is smaller than the gzipped one; try again');
3268 if ($degzdata = @gzinflate($data)) {
3270 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3274 $this->debug('Error using gzinflate to inflate the payload');
3275 $this->setError('Error using gzinflate to inflate the payload');
3277 } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3278 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
3280 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3281 if (strlen($data) < $datalen) {
3282 // test for the case that the payload has been compressed twice
3283 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3284 if ($degzdata = @gzinflate(substr($data, 10))) {
3286 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3290 $this->debug('Error using gzinflate to un-gzip the payload');
3291 $this->setError('Error using gzinflate to un-gzip the payload');
3294 //$timer->setMarker('finished decoding of gzip/deflated content');
3295 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3296 // set decoded payload
3297 $this->incoming_payload = $header_data.$lb.$lb.$data;
3299 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3300 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3303 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3304 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3307 $this->debug('No Content-Encoding header');
3310 if(strlen($data) == 0){
3311 $this->debug('no data after headers!');
3312 $this->setError('no data present after HTTP headers');
3320 * sets the content-type for the SOAP message to be sent
3322 * @param string $type the content type, MIME style
3323 * @param mixed $charset character set used for encoding (or false)
3326 function setContentType($type, $charset = false) {
3327 $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3331 * specifies that an HTTP persistent connection should be used
3333 * @return boolean whether the request was honored by this method.
3336 function usePersistentConnection(){
3337 if (isset($this->outgoing_headers['Accept-Encoding'])) {
3340 $this->protocol_version = '1.1';
3341 $this->persistentConnection = true;
3342 $this->setHeader('Connection', 'Keep-Alive');
3347 * parse an incoming Cookie into it's parts
3349 * @param string $cookie_str content of cookie
3350 * @return array with data of that cookie
3354 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3356 function parseCookie($cookie_str) {
3357 $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3358 $data = preg_split('/;/', $cookie_str);
3359 $value_str = $data[0];
3361 $cookie_param = 'domain=';
3362 $start = strpos($cookie_str, $cookie_param);
3364 $domain = substr($cookie_str, $start + strlen($cookie_param));
3365 $domain = substr($domain, 0, strpos($domain, ';'));
3370 $cookie_param = 'expires=';
3371 $start = strpos($cookie_str, $cookie_param);
3373 $expires = substr($cookie_str, $start + strlen($cookie_param));
3374 $expires = substr($expires, 0, strpos($expires, ';'));
3379 $cookie_param = 'path=';
3380 $start = strpos($cookie_str, $cookie_param);
3382 $path = substr($cookie_str, $start + strlen($cookie_param));
3383 $path = substr($path, 0, strpos($path, ';'));
3388 $cookie_param = ';secure;';
3389 if (strpos($cookie_str, $cookie_param) !== FALSE) {
3395 $sep_pos = strpos($value_str, '=');
3398 $name = substr($value_str, 0, $sep_pos);
3399 $value = substr($value_str, $sep_pos + 1);
3400 $cookie= array( 'name' => $name,
3402 'domain' => $domain,
3404 'expires' => $expires,
3413 * sort out cookies for the current request
3415 * @param array $cookies array with all cookies
3416 * @param boolean $secure is the send-content secure or not?
3417 * @return string for Cookie-HTTP-Header
3420 function getCookiesForRequest($cookies, $secure=false) {
3422 if ((! is_null($cookies)) && (is_array($cookies))) {
3423 foreach ($cookies as $cookie) {
3424 if (! is_array($cookie)) {
3427 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3428 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3429 if (strtotime($cookie['expires']) <= time()) {
3430 $this->debug('cookie has expired');
3434 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3435 $domain = preg_quote($cookie['domain']);
3436 if (! preg_match("'.*$domain$'i", $this->host)) {
3437 $this->debug('cookie has different domain');
3441 if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3442 $path = preg_quote($cookie['path']);
3443 if (! preg_match("'^$path.*'i", $this->path)) {
3444 $this->debug('cookie is for a different path');
3448 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3449 $this->debug('cookie is secure, transport is not');
3452 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3453 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3466 * nusoap_server allows the user to create a SOAP server
3467 * that is capable of receiving messages and returning responses
3469 * @author Dietrich Ayala <dietrich@ganx4.com>
3470 * @author Scott Nichol <snichol@users.sourceforge.net>
3471 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
3474 class nusoap_server extends nusoap_base {
3476 * HTTP headers of request
3480 var $headers = array();
3488 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3492 var $requestHeaders = '';
3494 * SOAP Headers from request (parsed)
3498 var $requestHeader = NULL;
3500 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3506 * SOAP payload for request (text)
3510 var $requestSOAP = '';
3512 * requested method namespace URI
3516 var $methodURI = '';
3518 * name of method requested
3522 var $methodname = '';
3524 * method parameters from request
3528 var $methodparams = array();
3530 * SOAP Action from request
3534 var $SOAPAction = '';
3536 * character set encoding of incoming (request) messages
3540 var $xml_encoding = '';
3542 * toggles whether the parser decodes element content w/ utf8_decode()
3546 var $decode_utf8 = true;
3549 * HTTP headers of response
3553 var $outgoing_headers = array();
3561 * SOAP headers for response (text or array of soapval or associative array)
3565 var $responseHeaders = '';
3567 * SOAP payload for response (text)
3571 var $responseSOAP = '';
3573 * method return value to place in response
3577 var $methodreturn = false;
3579 * whether $methodreturn is a string of literal XML
3583 var $methodreturnisliteralxml = false;
3585 * SOAP fault for response (or false)
3591 * text indication of result (for debugging)
3595 var $result = 'successful';
3598 * assoc array of operations => opData; operations are added by the register()
3599 * method or by parsing an external WSDL definition
3603 var $operations = array();
3605 * wsdl instance (if one)
3611 * URL for WSDL (if one)
3615 var $externalWSDLURL = false;
3617 * whether to append debug to response as XML comment
3621 var $debug_flag = false;
3626 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3628 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3631 function nusoap_server($wsdl=false){
3632 parent::nusoap_base();
3633 // turn on debugging?
3635 global $HTTP_SERVER_VARS;
3637 if (isset($_SERVER)) {
3638 $this->debug("_SERVER is defined:");
3639 $this->appendDebug($this->varDump($_SERVER));
3640 } elseif (isset($HTTP_SERVER_VARS)) {
3641 $this->debug("HTTP_SERVER_VARS is defined:");
3642 $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3644 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3647 if (isset($debug)) {
3648 $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3649 $this->debug_flag = $debug;
3650 } elseif (isset($_SERVER['QUERY_STRING'])) {
3651 $qs = explode('&', $_SERVER['QUERY_STRING']);
3652 foreach ($qs as $v) {
3653 if (substr($v, 0, 6) == 'debug=') {
3654 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3655 $this->debug_flag = substr($v, 6);
3658 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3659 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3660 foreach ($qs as $v) {
3661 if (substr($v, 0, 6) == 'debug=') {
3662 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3663 $this->debug_flag = substr($v, 6);
3670 $this->debug("In nusoap_server, WSDL is specified");
3671 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3672 $this->wsdl = $wsdl;
3673 $this->externalWSDLURL = $this->wsdl->wsdl;
3674 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3676 $this->debug('Create wsdl from ' . $wsdl);
3677 $this->wsdl = new wsdl($wsdl);
3678 $this->externalWSDLURL = $wsdl;
3680 $this->appendDebug($this->wsdl->getDebug());
3681 $this->wsdl->clearDebug();
3682 if($err = $this->wsdl->getError()){
3683 die('WSDL ERROR: '.$err);
3689 * processes request and returns response
3691 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
3694 function service($data){
3695 global $HTTP_SERVER_VARS;
3697 if (isset($_SERVER['REQUEST_METHOD'])) {
3698 $rm = $_SERVER['REQUEST_METHOD'];
3699 } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
3700 $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
3705 if (isset($_SERVER['QUERY_STRING'])) {
3706 $qs = $_SERVER['QUERY_STRING'];
3707 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3708 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3712 $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
3714 if ($rm == 'POST') {
3715 $this->debug("In service, invoke the request");
3716 $this->parse_request($data);
3717 if (! $this->fault) {
3718 $this->invoke_method();
3720 if (! $this->fault) {
3721 $this->serialize_return();
3723 $this->send_response();
3724 } elseif (preg_match('/wsdl/', $qs) ){
3725 $this->debug("In service, this is a request for WSDL");
3726 if ($this->externalWSDLURL){
3727 if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
3728 $this->debug("In service, re-direct for WSDL");
3729 header('Location: '.$this->externalWSDLURL);
3730 } else { // assume file
3731 $this->debug("In service, use file passthru for WSDL");
3732 header("Content-Type: text/xml\r\n");
3733 $pos = strpos($this->externalWSDLURL, "file://");
3734 if ($pos === false) {
3735 $filename = $this->externalWSDLURL;
3737 $filename = substr($this->externalWSDLURL, $pos + 7);
3739 $fp = fopen($this->externalWSDLURL, 'r');
3742 } elseif ($this->wsdl) {
3743 $this->debug("In service, serialize WSDL");
3744 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3745 print $this->wsdl->serialize($this->debug_flag);
3746 if ($this->debug_flag) {
3747 $this->debug('wsdl:');
3748 $this->appendDebug($this->varDump($this->wsdl));
3749 print $this->getDebugAsXMLComment();
3752 $this->debug("In service, there is no WSDL");
3753 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3754 print "This service does not provide WSDL";
3756 } elseif ($this->wsdl) {
3757 $this->debug("In service, return Web description");
3758 print $this->wsdl->webDescription();
3760 $this->debug("In service, no Web description");
3761 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3762 print "This service does not provide a Web description";
3767 * parses HTTP request headers.
3769 * The following fields are set by this function (when successful)
3778 function parse_http_headers() {
3779 global $HTTP_SERVER_VARS;
3781 $this->request = '';
3782 $this->SOAPAction = '';
3783 if(function_exists('getallheaders')){
3784 $this->debug("In parse_http_headers, use getallheaders");
3785 $headers = getallheaders();
3786 foreach($headers as $k=>$v){
3787 $k = strtolower($k);
3788 $this->headers[$k] = $v;
3789 $this->request .= "$k: $v\r\n";
3790 $this->debug("$k: $v");
3792 // get SOAPAction header
3793 if(isset($this->headers['soapaction'])){
3794 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3796 // get the character encoding of the incoming request
3797 if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3798 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3799 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
3800 $this->xml_encoding = strtoupper($enc);
3802 $this->xml_encoding = 'US-ASCII';
3805 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3806 $this->xml_encoding = 'ISO-8859-1';
3808 } elseif(isset($_SERVER) && is_array($_SERVER)){
3809 $this->debug("In parse_http_headers, use _SERVER");
3810 foreach ($_SERVER as $k => $v) {
3811 if (substr($k, 0, 5) == 'HTTP_') {
3812 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3814 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3816 if ($k == 'soapaction') {
3817 // get SOAPAction header
3819 $v = str_replace('"', '', $v);
3820 $v = str_replace('\\', '', $v);
3821 $this->SOAPAction = $v;
3822 } else if ($k == 'content-type') {
3823 // get the character encoding of the incoming request
3824 if (strpos($v, '=')) {
3825 $enc = substr(strstr($v, '='), 1);
3826 $enc = str_replace('"', '', $enc);
3827 $enc = str_replace('\\', '', $enc);
3828 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3829 $this->xml_encoding = strtoupper($enc);
3831 $this->xml_encoding = 'US-ASCII';
3834 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3835 $this->xml_encoding = 'ISO-8859-1';
3838 $this->headers[$k] = $v;
3839 $this->request .= "$k: $v\r\n";
3840 $this->debug("$k: $v");
3842 } elseif (is_array($HTTP_SERVER_VARS)) {
3843 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3844 foreach ($HTTP_SERVER_VARS as $k => $v) {
3845 if (substr($k, 0, 5) == 'HTTP_') {
3846 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
3848 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
3850 if ($k == 'soapaction') {
3851 // get SOAPAction header
3853 $v = str_replace('"', '', $v);
3854 $v = str_replace('\\', '', $v);
3855 $this->SOAPAction = $v;
3856 } else if ($k == 'content-type') {
3857 // get the character encoding of the incoming request
3858 if (strpos($v, '=')) {
3859 $enc = substr(strstr($v, '='), 1);
3860 $enc = str_replace('"', '', $enc);
3861 $enc = str_replace('\\', '', $enc);
3862 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3863 $this->xml_encoding = strtoupper($enc);
3865 $this->xml_encoding = 'US-ASCII';
3868 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3869 $this->xml_encoding = 'ISO-8859-1';
3872 $this->headers[$k] = $v;
3873 $this->request .= "$k: $v\r\n";
3874 $this->debug("$k: $v");
3877 $this->debug("In parse_http_headers, HTTP headers not accessible");
3878 $this->setError("HTTP headers not accessible");
3885 * The following fields are set by this function (when successful)
3899 * This sets the fault field on error
3901 * @param string $data XML string
3904 function parse_request($data='') {
3905 $this->debug('entering parse_request()');
3906 $this->parse_http_headers();
3907 $this->debug('got character encoding: '.$this->xml_encoding);
3908 // uncompress if necessary
3909 if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3910 $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3911 if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3912 // if decoding works, use it. else assume data wasn't gzencoded
3913 if (function_exists('gzuncompress')) {
3914 if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3916 } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3919 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3923 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3928 $this->request .= "\r\n".$data;
3929 $data = $this->parseRequest($this->headers, $data);
3930 $this->requestSOAP = $data;
3931 $this->debug('leaving parse_request');
3935 * invokes a PHP function for the requested SOAP method
3937 * The following fields are set by this function (when successful)
3941 * Note that the PHP function that is called may also set the following
3942 * fields to affect the response sent to the client
3947 * This sets the fault field on error
3951 function invoke_method() {
3952 $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3955 // if you are debugging in this area of the code, your service uses a class to implement methods,
3956 // you use SOAP RPC, and the client is .NET, please be aware of the following...
3957 // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
3958 // method name. that is fine for naming the .NET methods. it is not fine for properly constructing
3959 // the XML request and reading the XML response. you need to add the RequestElementName and
3960 // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
3961 // generates for the method. these parameters are used to specify the correct XML element names
3962 // for .NET to use, i.e. the names with the '.' in them.
3964 $orig_methodname = $this->methodname;
3966 if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3967 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3968 $this->appendDebug('opData=' . $this->varDump($this->opData));
3969 } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3970 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3971 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3972 $this->appendDebug('opData=' . $this->varDump($this->opData));
3973 $this->methodname = $this->opData['name'];
3975 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3976 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3980 $this->debug('in invoke_method, no WSDL to validate method');
3983 // if a . is present in $this->methodname, we see if there is a class in scope,
3984 // which could be referred to. We will also distinguish between two deliminators,
3985 // to allow methods to be called a the class or an instance
3986 if (strpos($this->methodname, '..') > 0) {
3988 } else if (strpos($this->methodname, '.') > 0) {
3993 $this->debug("in invoke_method, delim=$delim");
3997 if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
3998 $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3999 if (class_exists($try_class)) {
4000 // get the class and method name
4001 $class = $try_class;
4002 $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
4003 $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4005 $this->debug("in invoke_method, class=$try_class not found");
4009 $this->debug("in invoke_method, no class to try");
4012 // does method exist?
4014 if (!function_exists($this->methodname)) {
4015 $this->debug("in invoke_method, function '$this->methodname' not found!");
4016 $this->result = 'fault: method not found';
4017 $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
4021 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
4022 if (!in_array($method_to_compare, get_class_methods($class))) {
4023 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4024 $this->result = 'fault: method not found';
4025 $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4030 // evaluate message, getting back parameters
4031 // verify that request parameters match the method's signature
4032 if(! $this->verify_method($this->methodname,$this->methodparams)){
4034 $this->debug('ERROR: request not verified against method signature');
4035 $this->result = 'fault: request failed validation against method signature';
4037 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
4041 // if there are parameters to pass
4042 $this->debug('in invoke_method, params:');
4043 $this->appendDebug($this->varDump($this->methodparams));
4044 $this->debug("in invoke_method, calling '$this->methodname'");
4045 if (!function_exists('call_user_func_array')) {
4047 $this->debug('in invoke_method, calling function using eval()');
4048 $funcCall = "\$this->methodreturn = $this->methodname(";
4050 if ($delim == '..') {
4051 $this->debug('in invoke_method, calling class method using eval()');
4052 $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
4054 $this->debug('in invoke_method, calling instance method using eval()');
4055 // generate unique instance name
4056 $instname = "\$inst_".time();
4057 $funcCall = $instname." = new ".$class."(); ";
4058 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
4061 if ($this->methodparams) {
4062 foreach ($this->methodparams as $param) {
4063 if (is_array($param) || is_object($param)) {
4064 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4067 $funcCall .= "\"$param\",";
4069 $funcCall = substr($funcCall, 0, -1);
4072 $this->debug('in invoke_method, function call: '.$funcCall);
4076 $this->debug('in invoke_method, calling function using call_user_func_array()');
4077 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
4078 } elseif ($delim == '..') {
4079 $this->debug('in invoke_method, calling class method using call_user_func_array()');
4080 $call_arg = array ($class, $method);
4082 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4083 $instance = new $class ();
4084 $call_arg = array(&$instance, $method);
4086 if (is_array($this->methodparams)) {
4087 $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4089 $this->methodreturn = call_user_func_array($call_arg, array());
4092 $this->debug('in invoke_method, methodreturn:');
4093 $this->appendDebug($this->varDump($this->methodreturn));
4094 $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
4098 * serializes the return value from a PHP function into a full SOAP Envelope
4100 * The following fields are set by this function (when successful)
4104 * This sets the fault field on error
4108 function serialize_return() {
4109 $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4111 if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
4112 $this->debug('got a fault object from method');
4113 $this->fault = $this->methodreturn;
4115 } elseif ($this->methodreturnisliteralxml) {
4116 $return_val = $this->methodreturn;
4117 // returned value(s)
4119 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
4120 $this->debug('serializing return value');
4122 if (sizeof($this->opData['output']['parts']) > 1) {
4123 $this->debug('more than one output part, so use the method return unchanged');
4124 $opParams = $this->methodreturn;
4125 } elseif (sizeof($this->opData['output']['parts']) == 1) {
4126 $this->debug('exactly one output part, so wrap the method return in a simple array');
4127 // TODO: verify that it is not already wrapped!
4128 //foreach ($this->opData['output']['parts'] as $name => $type) {
4129 // $this->debug('wrap in element named ' . $name);
4131 $opParams = array($this->methodreturn);
4133 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
4134 $this->appendDebug($this->wsdl->getDebug());
4135 $this->wsdl->clearDebug();
4136 if($errstr = $this->wsdl->getError()){
4137 $this->debug('got wsdl error: '.$errstr);
4138 $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4142 if (isset($this->methodreturn)) {
4143 $return_val = $this->serialize_val($this->methodreturn, 'return');
4146 $this->debug('in absence of WSDL, assume void return for backward compatibility');
4150 $this->debug('return value:');
4151 $this->appendDebug($this->varDump($return_val));
4153 $this->debug('serializing response');
4155 $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4156 if ($this->opData['style'] == 'rpc') {
4157 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4158 if ($this->opData['output']['use'] == 'literal') {
4159 // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
4160 if ($this->methodURI) {
4161 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4163 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4166 if ($this->methodURI) {
4167 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4169 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4173 $this->debug('style is not rpc for serialization: assume document');
4174 $payload = $return_val;
4177 $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4178 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4180 $this->result = 'successful';
4182 //if($this->debug_flag){
4183 $this->appendDebug($this->wsdl->getDebug());
4185 if (isset($this->opData['output']['encodingStyle'])) {
4186 $encodingStyle = $this->opData['output']['encodingStyle'];
4188 $encodingStyle = '';
4190 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4191 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
4193 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
4195 $this->debug("Leaving serialize_return");
4199 * sends an HTTP response
4201 * The following fields are set by this function (when successful)
4208 function send_response() {
4209 $this->debug('Enter send_response');
4211 $payload = $this->fault->serialize();
4212 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4213 $this->outgoing_headers[] = "Status: 500 Internal Server Error";
4215 $payload = $this->responseSOAP;
4216 // Some combinations of PHP+Web server allow the Status
4217 // to come through as a header. Since OK is the default
4219 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4220 // $this->outgoing_headers[] = "Status: 200 OK";
4222 // add debug data if in debug mode
4223 if(isset($this->debug_flag) && $this->debug_flag){
4224 $payload .= $this->getDebugAsXMLComment();
4226 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4227 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4228 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
4229 // Let the Web server decide about this
4230 //$this->outgoing_headers[] = "Connection: Close\r\n";
4231 $payload = $this->getHTTPBody($payload);
4232 $type = $this->getHTTPContentType();
4233 $charset = $this->getHTTPContentTypeCharset();
4234 $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4235 //begin code to compress payload - by John
4236 // NOTE: there is no way to know whether the Web server will also compress
4238 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4239 if (strstr($this->headers['accept-encoding'], 'gzip')) {
4240 if (function_exists('gzencode')) {
4241 if (isset($this->debug_flag) && $this->debug_flag) {
4242 $payload .= "<!-- Content being gzipped -->";
4244 $this->outgoing_headers[] = "Content-Encoding: gzip";
4245 $payload = gzencode($payload);
4247 if (isset($this->debug_flag) && $this->debug_flag) {
4248 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
4251 } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
4252 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4253 // instead of gzcompress output,
4254 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4255 if (function_exists('gzdeflate')) {
4256 if (isset($this->debug_flag) && $this->debug_flag) {
4257 $payload .= "<!-- Content being deflated -->";
4259 $this->outgoing_headers[] = "Content-Encoding: deflate";
4260 $payload = gzdeflate($payload);
4262 if (isset($this->debug_flag) && $this->debug_flag) {
4263 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
4269 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
4270 reset($this->outgoing_headers);
4271 foreach($this->outgoing_headers as $hdr){
4272 header($hdr, false);
4275 $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
4279 * takes the value that was created by parsing the request
4280 * and compares to the method's signature, if available.
4282 * @param string $operation The operation to be invoked
4283 * @param array $request The array of parameter values
4284 * @return boolean Whether the operation was found
4287 function verify_method($operation,$request){
4288 if(isset($this->wsdl) && is_object($this->wsdl)){
4289 if($this->wsdl->getOperationData($operation)){
4292 } elseif(isset($this->operations[$operation])){
4299 * processes SOAP message received from client
4301 * @param array $headers The HTTP headers
4302 * @param string $data unprocessed request data from client
4303 * @return mixed value of the message, decoded into a PHP type
4306 function parseRequest($headers, $data) {
4307 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
4308 $this->appendDebug($this->varDump($headers));
4309 if (!isset($headers['content-type'])) {
4310 $this->setError('Request not of type text/xml (no content-type header)');
4313 if (!strstr($headers['content-type'], 'text/xml')) {
4314 $this->setError('Request not of type text/xml');
4317 if (strpos($headers['content-type'], '=')) {
4318 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
4319 $this->debug('Got response encoding: ' . $enc);
4320 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
4321 $this->xml_encoding = strtoupper($enc);
4323 $this->xml_encoding = 'US-ASCII';
4326 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4327 $this->xml_encoding = 'ISO-8859-1';
4329 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4330 // parse response, get soap parser obj
4331 $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
4333 $this->debug("parser debug: \n".$parser->getDebug());
4334 // if fault occurred during message parsing
4335 if($err = $parser->getError()){
4336 $this->result = 'fault: error in msg parsing: '.$err;
4337 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
4338 // else successfully parsed request into soapval object
4340 // get/set methodname
4341 $this->methodURI = $parser->root_struct_namespace;
4342 $this->methodname = $parser->root_struct_name;
4343 $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
4344 $this->debug('calling parser->get_soapbody()');
4345 $this->methodparams = $parser->get_soapbody();
4347 $this->requestHeaders = $parser->getHeaders();
4349 $this->requestHeader = $parser->get_soapheader();
4350 // add document for doclit support
4351 $this->document = $parser->document;
4356 * gets the HTTP body for the current response.
4358 * @param string $soapmsg The SOAP payload
4359 * @return string The HTTP body, which includes the SOAP payload
4362 function getHTTPBody($soapmsg) {
4367 * gets the HTTP content type for the current response.
4369 * Note: getHTTPBody must be called before this.
4371 * @return string the HTTP content type for the current response.
4374 function getHTTPContentType() {
4379 * gets the HTTP content type charset for the current response.
4380 * returns false for non-text content types.
4382 * Note: getHTTPBody must be called before this.
4384 * @return string the HTTP content type charset for the current response.
4387 function getHTTPContentTypeCharset() {
4388 return $this->soap_defencoding;
4392 * add a method to the dispatch map (this has been replaced by the register method)
4394 * @param string $methodname
4395 * @param string $in array of input values
4396 * @param string $out array of output values
4400 function add_to_map($methodname,$in,$out){
4401 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
4405 * register a service function with the server
4407 * @param string $name the name of the PHP function, class.method or class..method
4408 * @param array $in assoc array of input values: key = param name, value = param type
4409 * @param array $out assoc array of output values: key = param name, value = param type
4410 * @param mixed $namespace the element namespace for the method or false
4411 * @param mixed $soapaction the soapaction for the method or false
4412 * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4413 * @param mixed $use optional (encoded|literal) or false
4414 * @param string $documentation optional Description to include in WSDL
4415 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4418 function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
4419 global $HTTP_SERVER_VARS;
4421 if($this->externalWSDLURL){
4422 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
4425 die('You must specify a name when you register an operation');
4427 if (!is_array($in)) {
4428 die('You must provide an array for operation inputs');
4430 if (!is_array($out)) {
4431 die('You must provide an array for operation outputs');
4433 if(false == $namespace) {
4435 if(false == $soapaction) {
4436 if (isset($_SERVER)) {
4437 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4438 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4439 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4440 } elseif (isset($HTTP_SERVER_VARS)) {
4441 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4442 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4443 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4445 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4447 if ($HTTPS == '1' || $HTTPS == 'on') {
4452 $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
4454 if(false == $style) {
4460 if ($use == 'encoded' && $encodingStyle == '') {
4461 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4464 $this->operations[$name] = array(
4468 'namespace' => $namespace,
4469 'soapaction' => $soapaction,
4472 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
4478 * Specify a fault to be returned to the client.
4479 * This also acts as a flag to the server that a fault has occured.
4481 * @param string $faultcode
4482 * @param string $faultstring
4483 * @param string $faultactor
4484 * @param string $faultdetail
4487 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
4488 if ($faultdetail == '' && $this->debug_flag) {
4489 $faultdetail = $this->getDebug();
4491 $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
4492 $this->fault->soap_defencoding = $this->soap_defencoding;
4496 * Sets up wsdl object.
4497 * Acts as a flag to enable internal WSDL generation
4499 * @param string $serviceName, name of the service
4500 * @param mixed $namespace optional 'tns' service namespace or false
4501 * @param mixed $endpoint optional URL of service endpoint or false
4502 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4503 * @param string $transport optional SOAP transport
4504 * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4506 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4508 global $HTTP_SERVER_VARS;
4510 if (isset($_SERVER)) {
4511 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4512 $SERVER_PORT = $_SERVER['SERVER_PORT'];
4513 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4514 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4515 } elseif (isset($HTTP_SERVER_VARS)) {
4516 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4517 $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4518 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4519 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4521 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4523 // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4524 $colon = strpos($SERVER_NAME,":");
4526 $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4528 if ($SERVER_PORT == 80) {
4531 $SERVER_PORT = ':' . $SERVER_PORT;
4533 if(false == $namespace) {
4534 $namespace = "http://$SERVER_NAME/soap/$serviceName";
4537 if(false == $endpoint) {
4538 if ($HTTPS == '1' || $HTTPS == 'on') {
4543 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4546 if(false == $schemaTargetNamespace) {
4547 $schemaTargetNamespace = $namespace;
4550 $this->wsdl = new wsdl;
4551 $this->wsdl->serviceName = $serviceName;
4552 $this->wsdl->endpoint = $endpoint;
4553 $this->wsdl->namespaces['tns'] = $namespace;
4554 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4555 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4556 if ($schemaTargetNamespace != $namespace) {
4557 $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4559 $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
4560 if ($style == 'document') {
4561 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4563 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4564 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4565 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4566 $this->wsdl->bindings[$serviceName.'Binding'] = array(
4567 'name'=>$serviceName.'Binding',
4569 'transport'=>$transport,
4570 'portType'=>$serviceName.'PortType');
4571 $this->wsdl->ports[$serviceName.'Port'] = array(
4572 'binding'=>$serviceName.'Binding',
4573 'location'=>$endpoint,
4574 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4579 * Backward compatibility
4581 class soap_server extends nusoap_server {
4589 * parses a WSDL file, allows access to it's data, other utility methods.
4590 * also builds WSDL structures programmatically.
4592 * @author Dietrich Ayala <dietrich@ganx4.com>
4593 * @author Scott Nichol <snichol@users.sourceforge.net>
4594 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
4597 class wsdl extends nusoap_base {
4598 // URL or filename of the root of this WSDL
4600 // define internal arrays of bindings, ports, operations, messages, etc.
4601 var $schemas = array();
4603 var $message = array();
4604 var $complexTypes = array();
4605 var $messages = array();
4606 var $currentMessage;
4607 var $currentOperation;
4608 var $portTypes = array();
4609 var $currentPortType;
4610 var $bindings = array();
4611 var $currentBinding;
4612 var $ports = array();
4614 var $opData = array();
4616 var $documentation = false;
4618 // array of wsdl docs to import
4619 var $import = array();
4624 var $depth_array = array();
4626 var $proxyhost = '';
4627 var $proxyport = '';
4628 var $proxyusername = '';
4629 var $proxypassword = '';
4631 var $response_timeout = 30;
4632 var $curl_options = array(); // User-specified cURL options
4633 var $use_curl = false; // whether to always try to use cURL
4634 // for HTTP authentication
4635 var $username = ''; // Username for HTTP authentication
4636 var $password = ''; // Password for HTTP authentication
4637 var $authtype = ''; // Type of HTTP authentication
4638 var $certRequest = array(); // Certificate for HTTP SSL authentication
4643 * @param string $wsdl WSDL document URL
4644 * @param string $proxyhost
4645 * @param string $proxyport
4646 * @param string $proxyusername
4647 * @param string $proxypassword
4648 * @param integer $timeout set the connection timeout
4649 * @param integer $response_timeout set the response timeout
4650 * @param array $curl_options user-specified cURL options
4651 * @param boolean $use_curl try to use cURL
4654 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
4655 parent::nusoap_base();
4656 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4657 $this->proxyhost = $proxyhost;
4658 $this->proxyport = $proxyport;
4659 $this->proxyusername = $proxyusername;
4660 $this->proxypassword = $proxypassword;
4661 $this->timeout = $timeout;
4662 $this->response_timeout = $response_timeout;
4663 if (is_array($curl_options))
4664 $this->curl_options = $curl_options;
4665 $this->use_curl = $use_curl;
4666 $this->fetchWSDL($wsdl);
4670 * fetches the WSDL document and parses it
4674 function fetchWSDL($wsdl) {
4675 $this->debug("parse and process WSDL path=$wsdl");
4676 $this->wsdl = $wsdl;
4678 if ($this->wsdl != "") {
4679 $this->parseWSDL($this->wsdl);
4682 // TODO: handle imports more properly, grabbing them in-line and nesting them
4683 $imported_urls = array();
4685 while ($imported > 0) {
4688 foreach ($this->schemas as $ns => $list) {
4689 foreach ($list as $xs) {
4690 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4691 foreach ($xs->imports as $ns2 => $list2) {
4692 for ($ii = 0; $ii < count($list2); $ii++) {
4693 if (! $list2[$ii]['loaded']) {
4694 $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4695 $url = $list2[$ii]['location'];
4697 $urlparts = parse_url($url);
4698 if (!isset($urlparts['host'])) {
4699 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4700 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4702 if (! in_array($url, $imported_urls)) {
4703 $this->parseWSDL($url);
4705 $imported_urls[] = $url;
4708 $this->debug("Unexpected scenario: empty URL for unloaded import");
4716 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4717 foreach ($this->import as $ns => $list) {
4718 for ($ii = 0; $ii < count($list); $ii++) {
4719 if (! $list[$ii]['loaded']) {
4720 $this->import[$ns][$ii]['loaded'] = true;
4721 $url = $list[$ii]['location'];
4723 $urlparts = parse_url($url);
4724 if (!isset($urlparts['host'])) {
4725 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4726 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4728 if (! in_array($url, $imported_urls)) {
4729 $this->parseWSDL($url);
4731 $imported_urls[] = $url;
4734 $this->debug("Unexpected scenario: empty URL for unloaded import");
4740 // add new data to operation data
4741 foreach($this->bindings as $binding => $bindingData) {
4742 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4743 foreach($bindingData['operations'] as $operation => $data) {
4744 $this->debug('post-parse data gathering for ' . $operation);
4745 $this->bindings[$binding]['operations'][$operation]['input'] =
4746 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4747 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4748 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4749 $this->bindings[$binding]['operations'][$operation]['output'] =
4750 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4751 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4752 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4753 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4754 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4756 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4757 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4759 // Set operation style if necessary, but do not override one already provided
4760 if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4761 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4763 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4764 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4765 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4772 * parses the wsdl document
4774 * @param string $wsdl path or URL
4777 function parseWSDL($wsdl = '') {
4778 $this->debug("parse WSDL at path=$wsdl");
4781 $this->debug('no wsdl passed to parseWSDL()!!');
4782 $this->setError('no wsdl passed to parseWSDL()!!');
4786 // parse $wsdl for url format
4787 $wsdl_props = parse_url($wsdl);
4789 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4790 $this->debug('getting WSDL http(s) URL ' . $wsdl);
4792 $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4793 $tr->request_method = 'GET';
4794 $tr->useSOAPAction = false;
4795 if($this->proxyhost && $this->proxyport){
4796 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4798 if ($this->authtype != '') {
4799 $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
4801 $tr->setEncoding('gzip, deflate');
4802 $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4803 //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4804 //$this->debug("WSDL response\n" . $tr->incoming_payload);
4805 $this->appendDebug($tr->getDebug());
4807 if($err = $tr->getError() ){
4808 $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
4809 $this->debug($errstr);
4810 $this->setError($errstr);
4815 $this->debug("got WSDL URL");
4817 // $wsdl is not http(s), so treat it as a file URL or plain file path
4818 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4819 $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4823 $this->debug('getting WSDL file ' . $path);
4824 if ($fp = @fopen($path, 'r')) {
4826 while ($data = fread($fp, 32768)) {
4827 $wsdl_string .= $data;
4831 $errstr = "Bad path to WSDL file $path";
4832 $this->debug($errstr);
4833 $this->setError($errstr);
4837 $this->debug('Parse WSDL');
4838 // end new code added
4839 // Create an XML parser.
4840 $this->parser = xml_parser_create();
4841 // Set the options for parsing the XML data.
4842 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4843 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4844 // Set the object for the parser.
4845 xml_set_object($this->parser, $this);
4846 // Set the element handlers for the parser.
4847 xml_set_element_handler($this->parser, 'start_element', 'end_element');
4848 xml_set_character_data_handler($this->parser, 'character_data');
4849 // Parse the XML file.
4850 if (!xml_parse($this->parser, $wsdl_string, true)) {
4851 // Display an error message.
4853 'XML error parsing WSDL from %s on line %d: %s',
4855 xml_get_current_line_number($this->parser),
4856 xml_error_string(xml_get_error_code($this->parser))
4858 $this->debug($errstr);
4859 $this->debug("XML payload:\n" . $wsdl_string);
4860 $this->setError($errstr);
4864 xml_parser_free($this->parser);
4865 $this->debug('Parsing WSDL done');
4866 // catch wsdl parse errors
4867 if($this->getError()){
4874 * start-element handler
4876 * @param string $parser XML parser object
4877 * @param string $name element name
4878 * @param string $attrs associative array of attributes
4881 function start_element($parser, $name, $attrs)
4883 if ($this->status == 'schema') {
4884 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4885 $this->appendDebug($this->currentSchema->getDebug());
4886 $this->currentSchema->clearDebug();
4887 } elseif (preg_match('/schema$/', $name)) {
4888 $this->debug('Parsing WSDL schema');
4889 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4890 $this->status = 'schema';
4891 $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
4892 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4893 $this->appendDebug($this->currentSchema->getDebug());
4894 $this->currentSchema->clearDebug();
4896 // position in the total number of elements, starting from 0
4897 $pos = $this->position++;
4898 $depth = $this->depth++;
4899 // set self as current value for this depth
4900 $this->depth_array[$depth] = $pos;
4901 $this->message[$pos] = array('cdata' => '');
4902 // process attributes
4903 if (count($attrs) > 0) {
4904 // register namespace declarations
4905 foreach($attrs as $k => $v) {
4906 if (preg_match('/^xmlns/',$k)) {
4907 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4908 $this->namespaces[$ns_prefix] = $v;
4910 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4912 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4913 $this->XMLSchemaVersion = $v;
4914 $this->namespaces['xsi'] = $v . '-instance';
4918 // expand each attribute prefix to its namespace
4919 foreach($attrs as $k => $v) {
4920 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4921 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4922 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4930 // get element prefix, namespace and name
4931 if (preg_match('/:/', $name)) {
4933 $prefix = substr($name, 0, strpos($name, ':'));
4935 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4936 // get unqualified name
4937 $name = substr(strstr($name, ':'), 1);
4939 // process attributes, expanding any prefixes to namespaces
4940 // find status, register data
4941 switch ($this->status) {
4943 if ($name == 'part') {
4944 if (isset($attrs['type'])) {
4945 $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4946 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4948 if (isset($attrs['element'])) {
4949 $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4950 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4957 $this->currentPortOperation = $attrs['name'];
4958 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4959 if (isset($attrs['parameterOrder'])) {
4960 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4963 case 'documentation':
4964 $this->documentation = true;
4966 // merge input/output data
4968 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4969 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4977 if (isset($attrs['style'])) {
4978 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4980 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4983 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4986 if (isset($attrs['soapAction'])) {
4987 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4989 if (isset($attrs['style'])) {
4990 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4992 if (isset($attrs['name'])) {
4993 $this->currentOperation = $attrs['name'];
4994 $this->debug("current binding operation: $this->currentOperation");
4995 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4996 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4997 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
5001 $this->opStatus = 'input';
5004 $this->opStatus = 'output';
5007 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5008 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5010 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5018 $this->currentPort = $attrs['name'];
5019 $this->debug('current port: ' . $this->currentPort);
5020 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5024 $this->ports[$this->currentPort]['location'] = $attrs['location'];
5025 $this->ports[$this->currentPort]['bindingType'] = $namespace;
5026 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
5027 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
5035 if (isset($attrs['location'])) {
5036 $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
5037 $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
5039 $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
5040 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
5041 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
5043 $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
5048 // $this->status = 'schema';
5051 $this->status = 'message';
5052 $this->messages[$attrs['name']] = array();
5053 $this->currentMessage = $attrs['name'];
5056 $this->status = 'portType';
5057 $this->portTypes[$attrs['name']] = array();
5058 $this->currentPortType = $attrs['name'];
5061 if (isset($attrs['name'])) {
5063 if (strpos($attrs['name'], ':')) {
5064 $this->currentBinding = $this->getLocalPart($attrs['name']);
5066 $this->currentBinding = $attrs['name'];
5068 $this->status = 'binding';
5069 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5070 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5074 $this->serviceName = $attrs['name'];
5075 $this->status = 'service';
5076 $this->debug('current service: ' . $this->serviceName);
5079 foreach ($attrs as $name => $value) {
5080 $this->wsdl_info[$name] = $value;
5088 * end-element handler
5090 * @param string $parser XML parser object
5091 * @param string $name element name
5094 function end_element($parser, $name){
5095 // unset schema status
5096 if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
5098 $this->appendDebug($this->currentSchema->getDebug());
5099 $this->currentSchema->clearDebug();
5100 $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5101 $this->debug('Parsing WSDL schema done');
5103 if ($this->status == 'schema') {
5104 $this->currentSchema->schemaEndElement($parser, $name);
5106 // bring depth down a notch
5109 // end documentation
5110 if ($this->documentation) {
5111 //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5112 //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5113 $this->documentation = false;
5118 * element content handler
5120 * @param string $parser XML parser object
5121 * @param string $data element content
5124 function character_data($parser, $data)
5126 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5127 if (isset($this->message[$pos]['cdata'])) {
5128 $this->message[$pos]['cdata'] .= $data;
5130 if ($this->documentation) {
5131 $this->documentation .= $data;
5136 * if authenticating, set user credentials here
5138 * @param string $username
5139 * @param string $password
5140 * @param string $authtype (basic|digest|certificate|ntlm)
5141 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5144 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
5145 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5146 $this->appendDebug($this->varDump($certRequest));
5147 $this->username = $username;
5148 $this->password = $password;
5149 $this->authtype = $authtype;
5150 $this->certRequest = $certRequest;
5153 function getBindingData($binding)
5155 if (is_array($this->bindings[$binding])) {
5156 return $this->bindings[$binding];
5161 * returns an assoc array of operation names => operation data
5163 * @param string $portName WSDL port name
5164 * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5168 function getOperations($portName = '', $bindingType = 'soap') {
5170 if ($bindingType == 'soap') {
5171 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5172 } elseif ($bindingType == 'soap12') {
5173 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5175 $this->debug("getOperations bindingType $bindingType may not be supported");
5177 $this->debug("getOperations for port '$portName' bindingType $bindingType");
5179 foreach($this->ports as $port => $portData) {
5180 $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5181 if ($portName == '' || $port == $portName) {
5182 // binding type of port matches parameter
5183 if ($portData['bindingType'] == $bindingType) {
5184 $this->debug("getOperations found port $port bindingType $bindingType");
5185 //$this->debug("port data: " . $this->varDump($portData));
5186 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5188 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
5189 $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
5194 if (count($ops) == 0) {
5195 $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5201 * returns an associative array of data necessary for calling an operation
5203 * @param string $operation name of operation
5204 * @param string $bindingType type of binding eg: soap, soap12
5208 function getOperationData($operation, $bindingType = 'soap')
5210 if ($bindingType == 'soap') {
5211 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5212 } elseif ($bindingType == 'soap12') {
5213 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5216 foreach($this->ports as $port => $portData) {
5217 // binding type of port matches parameter
5218 if ($portData['bindingType'] == $bindingType) {
5220 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5221 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
5222 // note that we could/should also check the namespace here
5223 if ($operation == $bOperation) {
5224 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
5233 * returns an associative array of data necessary for calling an operation
5235 * @param string $soapAction soapAction for operation
5236 * @param string $bindingType type of binding eg: soap, soap12
5240 function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
5241 if ($bindingType == 'soap') {
5242 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5243 } elseif ($bindingType == 'soap12') {
5244 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5247 foreach($this->ports as $port => $portData) {
5248 // binding type of port matches parameter
5249 if ($portData['bindingType'] == $bindingType) {
5250 // loop through operations for the binding
5251 foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5252 if ($opData['soapAction'] == $soapAction) {
5261 * returns an array of information about a given type
5262 * returns false if no type exists by the given name
5265 * 'elements' => array(), // refs to elements array
5266 * 'restrictionBase' => '',
5268 * 'order' => '(sequence|all)',
5269 * 'attrs' => array() // refs to attributes array
5272 * @param string $type the type
5273 * @param string $ns namespace (not prefix) of the type
5276 * @see nusoap_xmlschema
5278 function getTypeDef($type, $ns) {
5279 $this->debug("in getTypeDef: type=$type, ns=$ns");
5280 if ((! $ns) && isset($this->namespaces['tns'])) {
5281 $ns = $this->namespaces['tns'];
5282 $this->debug("in getTypeDef: type namespace forced to $ns");
5284 if (!isset($this->schemas[$ns])) {
5285 foreach ($this->schemas as $ns0 => $schema0) {
5286 if (strcasecmp($ns, $ns0) == 0) {
5287 $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5293 if (isset($this->schemas[$ns])) {
5294 $this->debug("in getTypeDef: have schema for namespace $ns");
5295 for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
5296 $xs = &$this->schemas[$ns][$i];
5297 $t = $xs->getTypeDef($type);
5298 $this->appendDebug($xs->getDebug());
5301 $this->debug("in getTypeDef: found type $type");
5302 if (!isset($t['phpType'])) {
5303 // get info for type to tack onto the element
5304 $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5305 $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5306 $etype = $this->getTypeDef($uqType, $ns);
5308 $this->debug("found type for [element] $type:");
5309 $this->debug($this->varDump($etype));
5310 if (isset($etype['phpType'])) {
5311 $t['phpType'] = $etype['phpType'];
5313 if (isset($etype['elements'])) {
5314 $t['elements'] = $etype['elements'];
5316 if (isset($etype['attrs'])) {
5317 $t['attrs'] = $etype['attrs'];
5320 $this->debug("did not find type for [element] $type");
5326 $this->debug("in getTypeDef: did not find type $type");
5328 $this->debug("in getTypeDef: do not have schema for namespace $ns");
5334 * prints html description of services
5338 function webDescription(){
5339 global $HTTP_SERVER_VARS;
5341 if (isset($_SERVER)) {
5342 $PHP_SELF = $_SERVER['PHP_SELF'];
5343 } elseif (isset($HTTP_SERVER_VARS)) {
5344 $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5346 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
5350 <html><head><title>NuSOAP: '.$this->serviceName.'</title>
5351 <style type="text/css">
5352 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5353 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5354 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5355 ul { margin-top: 10px; margin-left: 20px; }
5356 li { list-style-type: none; margin-top: 10px; color: #000000; }
5358 margin-left: 0px; padding-bottom: 2em; }
5360 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5361 margin-top: 10px; margin-left: 0px; color: #000000;
5362 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5364 font-family: arial; font-size: 26px; color: #ffffff;
5365 background-color: #999999; width: 100%;
5366 margin-left: 0px; margin-right: 0px;
5367 padding-top: 10px; padding-bottom: 10px;}
5369 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5370 font-family: arial; overflow: hidden; width: 600;
5371 padding: 20px; font-size: 10px; background-color: #999999;
5372 layer-background-color:#FFFFFF; }
5373 a,a:active { color: charcoal; font-weight: bold; }
5374 a:visited { color: #666666; font-weight: bold; }
5375 a:hover { color: cc3300; font-weight: bold; }
5377 <script language="JavaScript" type="text/javascript">
5379 // POP-UP CAPTIONS...
5380 function lib_bwcheck(){ //Browsercheck (needed)
5381 this.ver=navigator.appVersion
5382 this.agent=navigator.userAgent
5383 this.dom=document.getElementById?1:0
5384 this.opera5=this.agent.indexOf("Opera 5")>-1
5385 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5386 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5387 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5388 this.ie=this.ie4||this.ie5||this.ie6
5389 this.mac=this.agent.indexOf("Mac")>-1
5390 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5391 this.ns4=(document.layers && !this.dom)?1:0;
5392 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5395 var bw = new lib_bwcheck()
5396 //Makes crossbrowser object.
5397 function makeObj(obj){
5398 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5399 if(!this.evnt) return false
5400 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5401 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5402 this.writeIt=b_writeIt;
5405 // A unit of measure that will be added when setting the position of a layer.
5406 //var px = bw.ns4||window.opera?"":"px";
5407 function b_writeIt(text){
5408 if (bw.ns4){this.wref.write(text);this.wref.close()}
5409 else this.wref.innerHTML = text
5411 //Shows the messages
5413 function popup(divid){
5414 if(oDesc = new makeObj(divid)){
5415 oDesc.css.visibility = "visible"
5418 function popout(){ // Hides message
5419 if(oDesc) oDesc.css.visibility = "hidden"
5427 <div class=title>'.$this->serviceName.'</div>
5429 <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
5430 Click on an operation name to view it's details.</p>
5432 foreach($this->getOperations() as $op => $data){
5433 $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5434 // create hidden div
5435 $b .= "<div id='$op' class='hidden'>
5436 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5437 foreach($data as $donnie => $marie){ // loop through opdata
5438 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
5439 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
5440 foreach($marie as $captain => $tenille){ // loop through data
5441 if($captain == 'parts'){ // loop thru parts
5442 $b .= " $captain:<br>";
5443 //if(is_array($tenille)){
5444 foreach($tenille as $joanie => $chachi){
5445 $b .= " $joanie: $chachi<br>";
5449 $b .= " $captain: $tenille<br>";
5453 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
5461 </div></body></html>';
5466 * serialize the parsed wsdl
5468 * @param mixed $debug whether to put debug=1 in endpoint URL
5469 * @return string serialization of WSDL
5472 function serialize($debug = 0)
5474 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5475 $xml .= "\n<definitions";
5476 foreach($this->namespaces as $k => $v) {
5477 $xml .= " xmlns:$k=\"$v\"";
5479 // 10.9.02 - add poulter fix for wsdl and tns declarations
5480 if (isset($this->namespaces['wsdl'])) {
5481 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
5483 if (isset($this->namespaces['tns'])) {
5484 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
5488 if (sizeof($this->import) > 0) {
5489 foreach($this->import as $ns => $list) {
5490 foreach ($list as $ii) {
5491 if ($ii['location'] != '') {
5492 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5494 $xml .= '<import namespace="' . $ns . '" />';
5500 if (count($this->schemas)>=1) {
5501 $xml .= "\n<types>\n";
5502 foreach ($this->schemas as $ns => $list) {
5503 foreach ($list as $xs) {
5504 $xml .= $xs->serializeSchema();
5510 if (count($this->messages) >= 1) {
5511 foreach($this->messages as $msgName => $msgParts) {
5512 $xml .= "\n<message name=\"" . $msgName . '">';
5513 if(is_array($msgParts)){
5514 foreach($msgParts as $partName => $partType) {
5515 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5516 if (strpos($partType, ':')) {
5517 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
5518 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5519 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5520 $typePrefix = 'xsd';
5522 foreach($this->typemap as $ns => $types) {
5523 if (isset($types[$partType])) {
5524 $typePrefix = $this->getPrefixFromNamespace($ns);
5527 if (!isset($typePrefix)) {
5528 die("$partType has no namespace!");
5531 $ns = $this->getNamespaceFromPrefix($typePrefix);
5532 $localPart = $this->getLocalPart($partType);
5533 $typeDef = $this->getTypeDef($localPart, $ns);
5534 if ($typeDef['typeClass'] == 'element') {
5535 $elementortype = 'element';
5536 if (substr($localPart, -1) == '^') {
5537 $localPart = substr($localPart, 0, -1);
5540 $elementortype = 'type';
5542 $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5545 $xml .= '</message>';
5548 // bindings & porttypes
5549 if (count($this->bindings) >= 1) {
5552 foreach($this->bindings as $bindingName => $attrs) {
5553 $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5554 $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5555 $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5556 foreach($attrs['operations'] as $opName => $opParts) {
5557 $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
5558 $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
5559 if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
5560 $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5564 $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5565 if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
5566 $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5570 $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5571 $binding_xml .= "\n" . ' </operation>';
5572 $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
5573 if (isset($opParts['parameterOrder'])) {
5574 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5576 $portType_xml .= '>';
5577 if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
5578 $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5580 $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
5581 $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
5582 $portType_xml .= "\n" . ' </operation>';
5584 $portType_xml .= "\n" . '</portType>';
5585 $binding_xml .= "\n" . '</binding>';
5587 $xml .= $portType_xml . $binding_xml;
5590 $xml .= "\n<service name=\"" . $this->serviceName . '">';
5591 if (count($this->ports) >= 1) {
5592 foreach($this->ports as $pName => $attrs) {
5593 $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5594 $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5595 $xml .= "\n" . ' </port>';
5598 $xml .= "\n" . '</service>';
5599 return $xml . "\n</definitions>";
5603 * determine whether a set of parameters are unwrapped
5604 * when they are expect to be wrapped, Microsoft-style.
5606 * @param string $type the type (element name) of the wrapper
5607 * @param array $parameters the parameter values for the SOAP call
5608 * @return boolean whether they parameters are unwrapped (and should be wrapped)
5611 function parametersMatchWrapped($type, &$parameters) {
5612 $this->debug("in parametersMatchWrapped type=$type, parameters=");
5613 $this->appendDebug($this->varDump($parameters));
5615 // split type into namespace:unqualified-type
5616 if (strpos($type, ':')) {
5617 $uqType = substr($type, strrpos($type, ':') + 1);
5618 $ns = substr($type, 0, strrpos($type, ':'));
5619 $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5620 if ($this->getNamespaceFromPrefix($ns)) {
5621 $ns = $this->getNamespaceFromPrefix($ns);
5622 $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5625 // TODO: should the type be compared to types in XSD, and the namespace
5626 // set to XSD if the type matches?
5627 $this->debug("in parametersMatchWrapped: No namespace for type $type");
5632 // get the type information
5633 if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5634 $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5637 $this->debug("in parametersMatchWrapped: found typeDef=");
5638 $this->appendDebug($this->varDump($typeDef));
5639 if (substr($uqType, -1) == '^') {
5640 $uqType = substr($uqType, 0, -1);
5642 $phpType = $typeDef['phpType'];
5643 $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5644 $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5646 // we expect a complexType or element of complexType
5647 if ($phpType != 'struct') {
5648 $this->debug("in parametersMatchWrapped: not a struct");
5652 // see whether the parameter names match the elements
5653 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5656 foreach ($typeDef['elements'] as $name => $attrs) {
5657 if (isset($parameters[$name])) {
5658 $this->debug("in parametersMatchWrapped: have parameter named $name");
5661 $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5666 $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5667 if ($matches == 0) {
5673 // since there are no elements for the type, if the user passed no
5674 // parameters, the parameters match wrapped.
5675 $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5676 return count($parameters) == 0;
5680 * serialize PHP values according to a WSDL message definition
5681 * contrary to the method name, this is not limited to RPC
5684 * - multi-ref serialization
5685 * - validate PHP values against type definitions, return errors if invalid
5687 * @param string $operation operation name
5688 * @param string $direction (input|output)
5689 * @param mixed $parameters parameter value(s)
5690 * @param string $bindingType (soap|soap12)
5691 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5694 function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
5695 $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5696 $this->appendDebug('parameters=' . $this->varDump($parameters));
5698 if ($direction != 'input' && $direction != 'output') {
5699 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5700 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5703 if (!$opData = $this->getOperationData($operation, $bindingType)) {
5704 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5705 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5708 $this->debug('in serializeRPCParameters: opData:');
5709 $this->appendDebug($this->varDump($opData));
5711 // Get encoding style for output and set to current
5712 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5713 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5714 $encodingStyle = $opData['output']['encodingStyle'];
5715 $enc_style = $encodingStyle;
5720 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5721 $parts = &$opData[$direction]['parts'];
5722 $part_count = sizeof($parts);
5723 $style = $opData['style'];
5724 $use = $opData[$direction]['use'];
5725 $this->debug("have $part_count part(s) to serialize using $style/$use");
5726 if (is_array($parameters)) {
5727 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5728 $parameter_count = count($parameters);
5729 $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5730 // check for Microsoft-style wrapped parameters
5731 if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
5732 $this->debug('check whether the caller has wrapped the parameters');
5733 if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
5734 // TODO: consider checking here for double-wrapping, when
5735 // service function wraps, then NuSOAP wraps again
5736 $this->debug("change simple array to associative with 'parameters' element");
5737 $parameters['parameters'] = $parameters[0];
5738 unset($parameters[0]);
5740 if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
5741 $this->debug('check whether caller\'s parameters match the wrapped ones');
5742 if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5743 $this->debug('wrap the parameters for the caller');
5744 $parameters = array('parameters' => $parameters);
5745 $parameter_count = 1;
5749 foreach ($parts as $name => $type) {
5750 $this->debug("serializing part $name of type $type");
5751 // Track encoding style
5752 if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5753 $encodingStyle = $opData[$direction]['encodingStyle'];
5754 $enc_style = $encodingStyle;
5758 // NOTE: add error handling here
5759 // if serializeType returns false, then catch global error and fault
5760 if ($parametersArrayType == 'arraySimple') {
5761 $p = array_shift($parameters);
5762 $this->debug('calling serializeType w/indexed param');
5763 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5764 } elseif (isset($parameters[$name])) {
5765 $this->debug('calling serializeType w/named param');
5766 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5768 // TODO: only send nillable
5769 $this->debug('calling serializeType w/null param');
5770 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5774 $this->debug('no parameters passed.');
5777 $this->debug("serializeRPCParameters returning: $xml");
5782 * serialize a PHP value according to a WSDL message definition
5785 * - multi-ref serialization
5786 * - validate PHP values against type definitions, return errors if invalid
5788 * @param string $operation operation name
5789 * @param string $direction (input|output)
5790 * @param mixed $parameters parameter value(s)
5791 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5795 function serializeParameters($operation, $direction, $parameters)
5797 $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5798 $this->appendDebug('parameters=' . $this->varDump($parameters));
5800 if ($direction != 'input' && $direction != 'output') {
5801 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5802 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5805 if (!$opData = $this->getOperationData($operation)) {
5806 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5807 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5810 $this->debug('opData:');
5811 $this->appendDebug($this->varDump($opData));
5813 // Get encoding style for output and set to current
5814 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5815 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5816 $encodingStyle = $opData['output']['encodingStyle'];
5817 $enc_style = $encodingStyle;
5822 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5824 $use = $opData[$direction]['use'];
5825 $this->debug("use=$use");
5826 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5827 if (is_array($parameters)) {
5828 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5829 $this->debug('have ' . $parametersArrayType . ' parameters');
5830 foreach($opData[$direction]['parts'] as $name => $type) {
5831 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5832 // Track encoding style
5833 if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5834 $encodingStyle = $opData[$direction]['encodingStyle'];
5835 $enc_style = $encodingStyle;
5839 // NOTE: add error handling here
5840 // if serializeType returns false, then catch global error and fault
5841 if ($parametersArrayType == 'arraySimple') {
5842 $p = array_shift($parameters);
5843 $this->debug('calling serializeType w/indexed param');
5844 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5845 } elseif (isset($parameters[$name])) {
5846 $this->debug('calling serializeType w/named param');
5847 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5849 // TODO: only send nillable
5850 $this->debug('calling serializeType w/null param');
5851 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5855 $this->debug('no parameters passed.');
5858 $this->debug("serializeParameters returning: $xml");
5863 * serializes a PHP value according a given type definition
5865 * @param string $name name of value (part or element)
5866 * @param string $type XML schema type of value (type or element)
5867 * @param mixed $value a native PHP value (parameter value)
5868 * @param string $use use for part (encoded|literal)
5869 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5870 * @param boolean $unqualified a kludge for what should be XML namespace form handling
5871 * @return string value serialized as an XML string
5874 function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5876 $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5877 $this->appendDebug("value=" . $this->varDump($value));
5878 if($use == 'encoded' && $encodingStyle) {
5879 $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5882 // if a soapval has been supplied, let its type override the WSDL
5883 if (is_object($value) && get_class($value) == 'soapval') {
5884 if ($value->type_ns) {
5885 $type = $value->type_ns . ':' . $value->type;
5887 $this->debug("in serializeType: soapval overrides type to $type");
5888 } elseif ($value->type) {
5889 $type = $value->type;
5891 $this->debug("in serializeType: soapval overrides type to $type");
5894 $this->debug("in serializeType: soapval does not override type");
5896 $attrs = $value->attributes;
5897 $value = $value->value;
5898 $this->debug("in serializeType: soapval overrides value to $value");
5900 if (!is_array($value)) {
5901 $value['!'] = $value;
5903 foreach ($attrs as $n => $v) {
5904 $value['!' . $n] = $v;
5906 $this->debug("in serializeType: soapval provides attributes");
5913 if (strpos($type, ':')) {
5914 $uqType = substr($type, strrpos($type, ':') + 1);
5915 $ns = substr($type, 0, strrpos($type, ':'));
5916 $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5917 if ($this->getNamespaceFromPrefix($ns)) {
5918 $ns = $this->getNamespaceFromPrefix($ns);
5919 $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5922 if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5923 $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5924 if ($unqualified && $use == 'literal') {
5925 $elementNS = " xmlns=\"\"";
5929 if (is_null($value)) {
5930 if ($use == 'literal') {
5931 // TODO: depends on minOccurs
5932 $xml = "<$name$elementNS/>";
5934 // TODO: depends on nillable, which should be checked before calling this method
5935 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5937 $this->debug("in serializeType: returning: $xml");
5940 if ($uqType == 'Array') {
5941 // JBoss/Axis does this sometimes
5942 return $this->serialize_val($value, $name, false, false, false, false, $use);
5944 if ($uqType == 'boolean') {
5945 if ((is_string($value) && $value == 'false') || (! $value)) {
5951 if ($uqType == 'string' && gettype($value) == 'string') {
5952 $value = $this->expandEntities($value);
5954 if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5955 $value = sprintf("%.0lf", $value);
5958 // TODO: what about null/nil values?
5959 // check type isn't a custom type extending xmlschema namespace
5960 if (!$this->getTypeDef($uqType, $ns)) {
5961 if ($use == 'literal') {
5963 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5965 $xml = "<$name$elementNS>$value</$name>";
5968 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5970 $this->debug("in serializeType: returning: $xml");
5973 $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5974 } else if ($ns == 'http://xml.apache.org/xml-soap') {
5975 $this->debug('in serializeType: appears to be Apache SOAP type');
5976 if ($uqType == 'Map') {
5977 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5979 $this->debug('in serializeType: Add namespace for Apache SOAP type');
5980 $tt_prefix = 'ns' . rand(1000, 9999);
5981 $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5982 // force this to be added to usedNamespaces
5983 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5986 foreach($value as $k => $v) {
5987 $this->debug("serializing map element: key $k, value $v");
5988 $contents .= '<item>';
5989 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5990 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5991 $contents .= '</item>';
5993 if ($use == 'literal') {
5995 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5997 $xml = "<$name>$contents</$name>";
6000 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6002 $this->debug("in serializeType: returning: $xml");
6005 $this->debug('in serializeType: Apache SOAP type, but only support Map');
6008 // TODO: should the type be compared to types in XSD, and the namespace
6009 // set to XSD if the type matches?
6010 $this->debug("in serializeType: No namespace for type $type");
6014 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
6015 $this->setError("$type ($uqType) is not a supported type.");
6016 $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6019 $this->debug("in serializeType: found typeDef");
6020 $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6021 if (substr($uqType, -1) == '^') {
6022 $uqType = substr($uqType, 0, -1);
6025 if (!isset($typeDef['phpType'])) {
6026 $this->setError("$type ($uqType) has no phpType.");
6027 $this->debug("in serializeType: $type ($uqType) has no phpType.");
6030 $phpType = $typeDef['phpType'];
6031 $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
6032 // if php type == struct, map value to the <all> element names
6033 if ($phpType == 'struct') {
6034 if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
6035 $elementName = $uqType;
6036 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6037 $elementNS = " xmlns=\"$ns\"";
6039 $elementNS = " xmlns=\"\"";
6042 $elementName = $name;
6044 $elementNS = " xmlns=\"\"";
6049 if (is_null($value)) {
6050 if ($use == 'literal') {
6051 // TODO: depends on minOccurs and nillable
6052 $xml = "<$elementName$elementNS/>";
6054 $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6056 $this->debug("in serializeType: returning: $xml");
6059 if (is_object($value)) {
6060 $value = get_object_vars($value);
6062 if (is_array($value)) {
6063 $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6064 if ($use == 'literal') {
6066 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6068 $xml = "<$elementName$elementNS$elementAttrs>";
6071 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6074 if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
6075 if (isset($value['!'])) {
6076 $xml .= $value['!'];
6077 $this->debug("in serializeType: serialized simpleContent for type $type");
6079 $this->debug("in serializeType: no simpleContent to serialize for type $type");
6083 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6085 $xml .= "</$elementName>";
6087 $this->debug("in serializeType: phpType is struct, but value is not an array");
6088 $this->setError("phpType is struct, but value is not an array: see debug output for details");
6091 } elseif ($phpType == 'array') {
6092 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6093 $elementNS = " xmlns=\"$ns\"";
6096 $elementNS = " xmlns=\"\"";
6101 if (is_null($value)) {
6102 if ($use == 'literal') {
6103 // TODO: depends on minOccurs
6104 $xml = "<$name$elementNS/>";
6106 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
6107 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6109 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6111 $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
6113 $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
6115 $this->debug("in serializeType: returning: $xml");
6118 if (isset($typeDef['multidimensional'])) {
6120 foreach($value as $v) {
6121 $cols = ',' . sizeof($v);
6122 $nv = array_merge($nv, $v);
6128 if (is_array($value) && sizeof($value) >= 1) {
6129 $rows = sizeof($value);
6131 foreach($value as $k => $v) {
6132 //$this->debug("serializing array element: $k, $v of type: ".$typeDef['arrayType']);
6133 //if (strpos($typeDef['arrayType'], ':') ) {
6134 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6135 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6137 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6144 // TODO: for now, an empty value will be serialized as a zero element
6145 // array. Revisit this when coding the handling of null/nil values.
6146 if ($use == 'literal') {
6147 $xml = "<$name$elementNS>"
6151 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
6152 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6154 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6155 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
6159 } elseif ($phpType == 'scalar') {
6160 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6161 $elementNS = " xmlns=\"$ns\"";
6164 $elementNS = " xmlns=\"\"";
6169 if ($use == 'literal') {
6171 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6173 $xml = "<$name$elementNS>$value</$name>";
6176 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6179 $this->debug("in serializeType: returning: $xml");
6184 * serializes the attributes for a complexType
6186 * @param array $typeDef our internal representation of an XML schema type (or element)
6187 * @param mixed $value a native PHP value (parameter value)
6188 * @param string $ns the namespace of the type
6189 * @param string $uqType the local part of the type
6190 * @return string value serialized as an XML string
6193 function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
6194 $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6196 if (isset($typeDef['extensionBase'])) {
6197 $nsx = $this->getPrefix($typeDef['extensionBase']);
6198 $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6199 if ($this->getNamespaceFromPrefix($nsx)) {
6200 $nsx = $this->getNamespaceFromPrefix($nsx);
6202 if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6203 $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6204 $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6206 $this->debug("extension base $nsx:$uqTypex is not a supported type");
6209 if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6210 $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6211 if (is_array($value)) {
6213 } elseif (is_object($value)) {
6214 $xvalue = get_object_vars($value);
6216 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6219 foreach ($typeDef['attrs'] as $aName => $attrs) {
6220 if (isset($xvalue['!' . $aName])) {
6221 $xname = '!' . $aName;
6222 $this->debug("value provided for attribute $aName with key $xname");
6223 } elseif (isset($xvalue[$aName])) {
6225 $this->debug("value provided for attribute $aName with key $xname");
6226 } elseif (isset($attrs['default'])) {
6227 $xname = '!' . $aName;
6228 $xvalue[$xname] = $attrs['default'];
6229 $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6232 $this->debug("no value provided for attribute $aName");
6235 $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
6239 $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6245 * serializes the elements for a complexType
6247 * @param array $typeDef our internal representation of an XML schema type (or element)
6248 * @param mixed $value a native PHP value (parameter value)
6249 * @param string $ns the namespace of the type
6250 * @param string $uqType the local part of the type
6251 * @param string $use use for part (encoded|literal)
6252 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6253 * @return string value serialized as an XML string
6256 function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
6257 $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6259 if (isset($typeDef['extensionBase'])) {
6260 $nsx = $this->getPrefix($typeDef['extensionBase']);
6261 $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6262 if ($this->getNamespaceFromPrefix($nsx)) {
6263 $nsx = $this->getNamespaceFromPrefix($nsx);
6265 if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6266 $this->debug("serialize elements for extension base $nsx:$uqTypex");
6267 $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6269 $this->debug("extension base $nsx:$uqTypex is not a supported type");
6272 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6273 $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6274 if (is_array($value)) {
6276 } elseif (is_object($value)) {
6277 $xvalue = get_object_vars($value);
6279 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6282 // toggle whether all elements are present - ideally should validate against schema
6283 if (count($typeDef['elements']) != count($xvalue)){
6286 foreach ($typeDef['elements'] as $eName => $attrs) {
6287 if (!isset($xvalue[$eName])) {
6288 if (isset($attrs['default'])) {
6289 $xvalue[$eName] = $attrs['default'];
6290 $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6293 // if user took advantage of a minOccurs=0, then only serialize named parameters
6294 if (isset($optionals)
6295 && (!isset($xvalue[$eName]))
6296 && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
6298 if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
6299 $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6302 $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6305 if (isset($xvalue[$eName])) {
6306 $v = $xvalue[$eName];
6310 if (isset($attrs['form'])) {
6311 $unqualified = ($attrs['form'] == 'unqualified');
6313 $unqualified = false;
6315 if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
6317 foreach ($vv as $k => $v) {
6318 if (isset($attrs['type']) || isset($attrs['ref'])) {
6319 // serialize schema-defined type
6320 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6322 // serialize generic type (can this ever really happen?)
6323 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6324 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6328 if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
6330 } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
6331 // TODO: serialize a nil correctly, but for now serialize schema-defined type
6332 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6333 } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6334 // serialize schema-defined type
6335 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6337 // serialize generic type (can this ever really happen?)
6338 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6339 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6345 $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6351 * adds an XML Schema complex type to the WSDL types
6353 * @param string $name
6354 * @param string $typeClass (complexType|simpleType|attribute)
6355 * @param string $phpType currently supported are array and struct (php assoc array)
6356 * @param string $compositor (all|sequence|choice)
6357 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6358 * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6359 * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6360 * @param string $arrayType as namespace:name (xsd:string)
6361 * @see nusoap_xmlschema
6364 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
6365 if (count($elements) > 0) {
6366 $eElements = array();
6367 foreach($elements as $n => $e){
6368 // expand each element
6370 foreach ($e as $k => $v) {
6371 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6372 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6375 $eElements[$n] = $ee;
6377 $elements = $eElements;
6380 if (count($attrs) > 0) {
6381 foreach($attrs as $n => $a){
6382 // expand each attribute
6383 foreach ($a as $k => $v) {
6384 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6385 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6393 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6394 $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
6396 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6397 $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
6401 * adds an XML Schema simple type to the WSDL types
6403 * @param string $name
6404 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6405 * @param string $typeClass (should always be simpleType)
6406 * @param string $phpType (should always be scalar)
6407 * @param array $enumeration array of values
6408 * @see nusoap_xmlschema
6411 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
6412 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6414 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6415 $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6419 * adds an element to the WSDL types
6421 * @param array $attrs attributes that must include name and type
6422 * @see nusoap_xmlschema
6425 function addElement($attrs) {
6426 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6427 $this->schemas[$typens][0]->addElement($attrs);
6431 * register an operation with the server
6433 * @param string $name operation (method) name
6434 * @param array $in assoc array of input values: key = param name, value = param type
6435 * @param array $out assoc array of output values: key = param name, value = param type
6436 * @param string $namespace optional The namespace for the operation
6437 * @param string $soapaction optional The soapaction for the operation
6438 * @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
6439 * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
6440 * @param string $documentation optional The description to include in the WSDL
6441 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6444 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
6445 if ($use == 'encoded' && $encodingStyle == '') {
6446 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6449 if ($style == 'document') {
6450 $elements = array();
6451 foreach ($in as $n => $t) {
6452 $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6454 $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6455 $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
6456 $in = array('parameters' => 'tns:' . $name . '^');
6458 $elements = array();
6459 foreach ($out as $n => $t) {
6460 $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6462 $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6463 $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
6464 $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
6468 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
6471 'binding' => $this->serviceName . 'Binding',
6472 'endpoint' => $this->endpoint,
6473 'soapAction' => $soapaction,
6477 'namespace' => $namespace,
6478 'encodingStyle' => $encodingStyle,
6479 'message' => $name . 'Request',
6483 'namespace' => $namespace,
6484 'encodingStyle' => $encodingStyle,
6485 'message' => $name . 'Response',
6487 'namespace' => $namespace,
6488 'transport' => 'http://schemas.xmlsoap.org/soap/http',
6489 'documentation' => $documentation);
6494 foreach($in as $pName => $pType)
6496 if(strpos($pType,':')) {
6497 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6499 $this->messages[$name.'Request'][$pName] = $pType;
6502 $this->messages[$name.'Request']= '0';
6506 foreach($out as $pName => $pType)
6508 if(strpos($pType,':')) {
6509 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6511 $this->messages[$name.'Response'][$pName] = $pType;
6514 $this->messages[$name.'Response']= '0';
6525 * nusoap_parser class parses SOAP XML messages into native PHP values
6527 * @author Dietrich Ayala <dietrich@ganx4.com>
6528 * @author Scott Nichol <snichol@users.sourceforge.net>
6529 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6532 class nusoap_parser extends nusoap_base {
6535 var $xml_encoding = '';
6537 var $root_struct = '';
6538 var $root_struct_name = '';
6539 var $root_struct_namespace = '';
6540 var $root_header = '';
6541 var $document = ''; // incoming SOAP body (text)
6542 // determines where in the message we are (envelope,header,body,method)
6546 var $default_namespace = '';
6547 var $namespaces = array();
6548 var $message = array();
6551 var $fault_code = '';
6552 var $fault_str = '';
6553 var $fault_detail = '';
6554 var $depth_array = array();
6555 var $debug_flag = true;
6556 var $soapresponse = NULL; // parsed SOAP Body
6557 var $soapheader = NULL; // parsed SOAP Header
6558 var $responseHeaders = ''; // incoming SOAP headers (text)
6559 var $body_position = 0;
6560 // for multiref parsing:
6561 // array of id => pos
6563 // array of id => hrefs => pos
6564 var $multirefs = array();
6565 // toggle for auto-decoding element content
6566 var $decode_utf8 = true;
6569 * constructor that actually does the parsing
6571 * @param string $xml SOAP message
6572 * @param string $encoding character encoding scheme of message
6573 * @param string $method method for which XML is parsed (unused?)
6574 * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6577 function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
6578 parent::nusoap_base();
6580 $this->xml_encoding = $encoding;
6581 $this->method = $method;
6582 $this->decode_utf8 = $decode_utf8;
6584 // Check whether content has been read.
6586 // Check XML encoding
6587 $pos_xml = strpos($xml, '<?xml');
6588 if ($pos_xml !== FALSE) {
6589 $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6590 if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6591 $xml_encoding = $res[1];
6592 if (strtoupper($xml_encoding) != $encoding) {
6593 $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6595 if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
6596 $this->setError($err);
6599 // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6601 $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6604 $this->debug('No encoding specified in XML declaration');
6607 $this->debug('No XML declaration');
6609 $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
6610 // Create an XML parser - why not xml_parser_create_ns?
6611 $this->parser = xml_parser_create($this->xml_encoding);
6612 // Set the options for parsing the XML data.
6613 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6614 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6615 xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6616 // Set the object for the parser.
6617 xml_set_object($this->parser, $this);
6618 // Set the element handlers for the parser.
6619 xml_set_element_handler($this->parser, 'start_element','end_element');
6620 xml_set_character_data_handler($this->parser,'character_data');
6622 // Parse the XML file.
6623 if(!xml_parse($this->parser,$xml,true)){
6624 // Display an error message.
6625 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
6626 xml_get_current_line_number($this->parser),
6627 xml_error_string(xml_get_error_code($this->parser)));
6629 $this->debug("XML payload:\n" . $xml);
6630 $this->setError($err);
6632 $this->debug('in nusoap_parser ctor, message:');
6633 $this->appendDebug($this->varDump($this->message));
6634 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
6636 $this->soapresponse = $this->message[$this->root_struct]['result'];
6638 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
6639 $this->soapheader = $this->message[$this->root_header]['result'];
6641 // resolve hrefs/ids
6642 if(sizeof($this->multirefs) > 0){
6643 foreach($this->multirefs as $id => $hrefs){
6644 $this->debug('resolving multirefs for id: '.$id);
6645 $idVal = $this->buildVal($this->ids[$id]);
6646 if (is_array($idVal) && isset($idVal['!id'])) {
6647 unset($idVal['!id']);
6649 foreach($hrefs as $refPos => $ref){
6650 $this->debug('resolving href at pos '.$refPos);
6651 $this->multirefs[$id][$refPos] = $idVal;
6656 xml_parser_free($this->parser);
6658 $this->debug('xml was empty, didn\'t parse!');
6659 $this->setError('xml was empty, didn\'t parse!');
6664 * start-element handler
6666 * @param resource $parser XML parser object
6667 * @param string $name element name
6668 * @param array $attrs associative array of attributes
6671 function start_element($parser, $name, $attrs) {
6672 // position in a total number of elements, starting from 0
6673 // update class level pos
6674 $pos = $this->position++;
6676 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
6677 // depth = how many levels removed from root?
6678 // set mine as current global depth and increment global depth value
6679 $this->message[$pos]['depth'] = $this->depth++;
6681 // else add self as child to whoever the current parent is
6683 $this->message[$this->parent]['children'] .= '|'.$pos;
6686 $this->message[$pos]['parent'] = $this->parent;
6687 // set self as current parent
6688 $this->parent = $pos;
6689 // set self as current value for this depth
6690 $this->depth_array[$this->depth] = $pos;
6691 // get element prefix
6692 if(strpos($name,':')){
6694 $prefix = substr($name,0,strpos($name,':'));
6695 // get unqualified name
6696 $name = substr(strstr($name,':'),1);
6699 if ($name == 'Envelope' && $this->status == '') {
6700 $this->status = 'envelope';
6701 } elseif ($name == 'Header' && $this->status == 'envelope') {
6702 $this->root_header = $pos;
6703 $this->status = 'header';
6704 } elseif ($name == 'Body' && $this->status == 'envelope'){
6705 $this->status = 'body';
6706 $this->body_position = $pos;
6708 } elseif($this->status == 'body' && $pos == ($this->body_position+1)) {
6709 $this->status = 'method';
6710 $this->root_struct_name = $name;
6711 $this->root_struct = $pos;
6712 $this->message[$pos]['type'] = 'struct';
6713 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6716 $this->message[$pos]['status'] = $this->status;
6718 $this->message[$pos]['name'] = htmlspecialchars($name);
6720 $this->message[$pos]['attrs'] = $attrs;
6722 // loop through atts, logging ns and type declarations
6724 foreach($attrs as $key => $value){
6725 $key_prefix = $this->getPrefix($key);
6726 $key_localpart = $this->getLocalPart($key);
6727 // if ns declarations, add to class level array of valid namespaces
6728 if($key_prefix == 'xmlns'){
6729 if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){
6730 $this->XMLSchemaVersion = $value;
6731 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6732 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
6734 $this->namespaces[$key_localpart] = $value;
6735 // set method namespace
6736 if($name == $this->root_struct_name){
6737 $this->methodNamespace = $value;
6739 // if it's a type declaration, set type
6740 } elseif($key_localpart == 'type'){
6741 if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
6742 // do nothing: already processed arrayType
6744 $value_prefix = $this->getPrefix($value);
6745 $value_localpart = $this->getLocalPart($value);
6746 $this->message[$pos]['type'] = $value_localpart;
6747 $this->message[$pos]['typePrefix'] = $value_prefix;
6748 if(isset($this->namespaces[$value_prefix])){
6749 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6750 } else if(isset($attrs['xmlns:'.$value_prefix])) {
6751 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6753 // should do something here with the namespace of specified type?
6755 } elseif($key_localpart == 'arrayType'){
6756 $this->message[$pos]['type'] = 'array';
6757 /* do arrayType ereg here
6758 [1] arrayTypeValue ::= atype asize
6759 [2] atype ::= QName rank*
6760 [3] rank ::= '[' (',')* ']'
6761 [4] asize ::= '[' length~ ']'
6762 [5] length ::= nextDimension* Digit+
6763 [6] nextDimension ::= Digit+ ','
6765 $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
6766 if(preg_match($expr,$value,$regs)){
6767 $this->message[$pos]['typePrefix'] = $regs[1];
6768 $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6769 if (isset($this->namespaces[$regs[1]])) {
6770 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6771 } else if (isset($attrs['xmlns:'.$regs[1]])) {
6772 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6774 $this->message[$pos]['arrayType'] = $regs[2];
6775 $this->message[$pos]['arraySize'] = $regs[3];
6776 $this->message[$pos]['arrayCols'] = $regs[4];
6778 // specifies nil value (or not)
6779 } elseif ($key_localpart == 'nil'){
6780 $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6781 // some other attribute
6782 } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6783 $this->message[$pos]['xattrs']['!' . $key] = $value;
6786 if ($key == 'xmlns') {
6787 $this->default_namespace = $value;
6791 $this->ids[$value] = $pos;
6794 if($key_localpart == 'root' && $value == 1){
6795 $this->status = 'method';
6796 $this->root_struct_name = $name;
6797 $this->root_struct = $pos;
6798 $this->debug("found root struct $this->root_struct_name, pos $pos");
6801 $attstr .= " $key=\"$value\"";
6803 // get namespace - must be done after namespace atts are processed
6805 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6806 $this->default_namespace = $this->namespaces[$prefix];
6808 $this->message[$pos]['namespace'] = $this->default_namespace;
6810 if($this->status == 'header'){
6811 if ($this->root_header != $pos) {
6812 $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6814 } elseif($this->root_struct_name != ''){
6815 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6820 * end-element handler
6822 * @param resource $parser XML parser object
6823 * @param string $name element name
6826 function end_element($parser, $name) {
6827 // position of current element is equal to the last value left in depth_array for my depth
6828 $pos = $this->depth_array[$this->depth--];
6830 // get element prefix
6831 if(strpos($name,':')){
6833 $prefix = substr($name,0,strpos($name,':'));
6834 // get unqualified name
6835 $name = substr(strstr($name,':'),1);
6838 // build to native type
6839 if(isset($this->body_position) && $pos > $this->body_position){
6840 // deal w/ multirefs
6841 if(isset($this->message[$pos]['attrs']['href'])){
6843 $id = substr($this->message[$pos]['attrs']['href'],1);
6844 // add placeholder to href array
6845 $this->multirefs[$id][$pos] = 'placeholder';
6846 // add set a reference to it as the result value
6847 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6848 // build complexType values
6849 } elseif($this->message[$pos]['children'] != ''){
6850 // if result has already been generated (struct/array)
6851 if(!isset($this->message[$pos]['result'])){
6852 $this->message[$pos]['result'] = $this->buildVal($pos);
6854 // build complexType values of attributes and possibly simpleContent
6855 } elseif (isset($this->message[$pos]['xattrs'])) {
6856 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6857 $this->message[$pos]['xattrs']['!'] = null;
6858 } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6859 if (isset($this->message[$pos]['type'])) {
6860 $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'] : '');
6862 $parent = $this->message[$pos]['parent'];
6863 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6864 $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6866 $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6870 $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6871 // set value of simpleType (or nil complexType)
6873 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6874 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6875 $this->message[$pos]['xattrs']['!'] = null;
6876 } elseif (isset($this->message[$pos]['type'])) {
6877 $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'] : '');
6879 $parent = $this->message[$pos]['parent'];
6880 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6881 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6883 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6887 /* add value to parent's result, if parent is struct/array
6888 $parent = $this->message[$pos]['parent'];
6889 if($this->message[$parent]['type'] != 'map'){
6890 if(strtolower($this->message[$parent]['type']) == 'array'){
6891 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6893 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6901 if($this->status == 'header'){
6902 if ($this->root_header != $pos) {
6903 $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6905 } elseif($pos >= $this->root_struct){
6906 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6909 if ($pos == $this->root_struct){
6910 $this->status = 'body';
6911 $this->root_struct_namespace = $this->message[$pos]['namespace'];
6912 } elseif ($pos == $this->root_header) {
6913 $this->status = 'envelope';
6914 } elseif ($name == 'Body' && $this->status == 'body') {
6915 $this->status = 'envelope';
6916 } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
6917 $this->status = 'envelope';
6918 } elseif ($name == 'Envelope' && $this->status == 'envelope') {
6921 // set parent back to my parent
6922 $this->parent = $this->message[$pos]['parent'];
6926 * element content handler
6928 * @param resource $parser XML parser object
6929 * @param string $data element content
6932 function character_data($parser, $data){
6933 $pos = $this->depth_array[$this->depth];
6934 if ($this->xml_encoding=='UTF-8'){
6935 // TODO: add an option to disable this for folks who want
6936 // raw UTF-8 that, e.g., might not map to iso-8859-1
6937 // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6938 if($this->decode_utf8){
6939 $data = utf8_decode($data);
6942 $this->message[$pos]['cdata'] .= $data;
6944 if($this->status == 'header'){
6945 $this->responseHeaders .= $data;
6947 $this->document .= $data;
6952 * get the parsed message (SOAP Body)
6956 * @deprecated use get_soapbody instead
6958 function get_response(){
6959 return $this->soapresponse;
6963 * get the parsed SOAP Body (NULL if there was none)
6968 function get_soapbody(){
6969 return $this->soapresponse;
6973 * get the parsed SOAP Header (NULL if there was none)
6978 function get_soapheader(){
6979 return $this->soapheader;
6983 * get the unparsed SOAP Header
6985 * @return string XML or empty if no Header
6988 function getHeaders(){
6989 return $this->responseHeaders;
6993 * decodes simple types into PHP variables
6995 * @param string $value value to decode
6996 * @param string $type XML type to decode
6997 * @param string $typens XML type namespace to decode
6998 * @return mixed PHP value
7001 function decodeSimple($value, $type, $typens) {
7002 // TODO: use the namespace!
7003 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
7004 return (string) $value;
7006 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
7007 return (int) $value;
7009 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
7010 return (double) $value;
7012 if ($type == 'boolean') {
7013 if (strtolower($value) == 'false' || strtolower($value) == 'f') {
7016 return (boolean) $value;
7018 if ($type == 'base64' || $type == 'base64Binary') {
7019 $this->debug('Decode base64 value');
7020 return base64_decode($value);
7022 // obscure numeric types
7023 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
7024 || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
7025 || $type == 'unsignedInt'
7026 || $type == 'unsignedShort' || $type == 'unsignedByte') {
7027 return (int) $value;
7029 // bogus: parser treats array with no elements as a simple type
7030 if ($type == 'array') {
7034 return (string) $value;
7038 * builds response structures for compound values (arrays/structs)
7041 * @param integer $pos position in node tree
7042 * @return mixed PHP value
7045 function buildVal($pos){
7046 if(!isset($this->message[$pos]['type'])){
7047 $this->message[$pos]['type'] = '';
7049 $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
7050 // if there are children...
7051 if($this->message[$pos]['children'] != ''){
7052 $this->debug('in buildVal, there are children');
7053 $children = explode('|',$this->message[$pos]['children']);
7054 array_shift($children); // knock off empty
7056 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
7059 foreach($children as $child_pos){
7060 $this->debug("in buildVal, got an MD array element: $r, $c");
7061 $params[$r][] = $this->message[$child_pos]['result'];
7063 if($c == $this->message[$pos]['arrayCols']){
7069 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
7070 $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
7071 foreach($children as $child_pos){
7072 $params[] = &$this->message[$child_pos]['result'];
7074 // apache Map type: java hashtable
7075 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
7076 $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
7077 foreach($children as $child_pos){
7078 $kv = explode("|",$this->message[$child_pos]['children']);
7079 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
7081 // generic compound type
7082 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7084 // Apache Vector type: treat as an array
7085 $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']);
7086 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
7092 foreach($children as $child_pos){
7094 $params[] = &$this->message[$child_pos]['result'];
7096 if (isset($params[$this->message[$child_pos]['name']])) {
7097 // de-serialize repeated element name into an array
7098 if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
7099 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
7101 $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7103 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7108 if (isset($this->message[$pos]['xattrs'])) {
7109 $this->debug('in buildVal, handling attributes');
7110 foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7114 // handle simpleContent
7115 if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
7116 $this->debug('in buildVal, handling simpleContent');
7117 if (isset($this->message[$pos]['type'])) {
7118 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7120 $parent = $this->message[$pos]['parent'];
7121 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7122 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7124 $params['!'] = $this->message[$pos]['cdata'];
7128 $ret = is_array($params) ? $params : array();
7129 $this->debug('in buildVal, return:');
7130 $this->appendDebug($this->varDump($ret));
7133 $this->debug('in buildVal, no children, building scalar');
7134 $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7135 if (isset($this->message[$pos]['type'])) {
7136 $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7137 $this->debug("in buildVal, return: $ret");
7140 $parent = $this->message[$pos]['parent'];
7141 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7142 $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7143 $this->debug("in buildVal, return: $ret");
7146 $ret = $this->message[$pos]['cdata'];
7147 $this->debug("in buildVal, return: $ret");
7154 * Backward compatibility
7156 class soap_parser extends nusoap_parser {
7165 * [nu]soapclient higher level class for easy usage.
7169 * // instantiate client with server info
7170 * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
7172 * // call method, get results
7173 * echo $soapclient->call( string methodname [ ,array parameters] );
7176 * unset($soapclient);
7178 * @author Dietrich Ayala <dietrich@ganx4.com>
7179 * @author Scott Nichol <snichol@users.sourceforge.net>
7180 * @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
7183 class nusoap_client extends nusoap_base {
7185 var $username = ''; // Username for HTTP authentication
7186 var $password = ''; // Password for HTTP authentication
7187 var $authtype = ''; // Type of HTTP authentication
7188 var $certRequest = array(); // Certificate for HTTP SSL authentication
7189 var $requestHeaders = false; // SOAP headers in request (text)
7190 var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
7191 var $responseHeader = NULL; // SOAP Header from response (parsed)
7192 var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
7194 var $forceEndpoint = ''; // overrides WSDL endpoint
7195 var $proxyhost = '';
7196 var $proxyport = '';
7197 var $proxyusername = '';
7198 var $proxypassword = '';
7199 var $portName = ''; // port name to use in WSDL
7200 var $xml_encoding = ''; // character set encoding of incoming (response) messages
7201 var $http_encoding = false;
7202 var $timeout = 0; // HTTP connection timeout
7203 var $response_timeout = 30; // HTTP response timeout
7204 var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
7205 var $persistentConnection = false;
7206 var $defaultRpcParams = false; // This is no longer used
7207 var $request = ''; // HTTP request
7208 var $response = ''; // HTTP response
7209 var $responseData = ''; // SOAP payload of response
7210 var $cookies = array(); // Cookies from response or for request
7211 var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
7212 var $operations = array(); // WSDL operations, empty for WSDL initialization error
7213 var $curl_options = array(); // User-specified cURL options
7214 var $bindingType = ''; // WSDL operation binding type
7215 var $use_curl = false; // whether to always try to use cURL
7218 * fault related variables
7244 * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
7245 * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL
7246 * @param string $proxyhost optional
7247 * @param string $proxyport optional
7248 * @param string $proxyusername optional
7249 * @param string $proxypassword optional
7250 * @param integer $timeout set the connection timeout
7251 * @param integer $response_timeout set the response timeout
7252 * @param string $portName optional portName in WSDL document
7255 function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){
7256 parent::nusoap_base();
7257 $this->endpoint = $endpoint;
7258 $this->proxyhost = $proxyhost;
7259 $this->proxyport = $proxyport;
7260 $this->proxyusername = $proxyusername;
7261 $this->proxypassword = $proxypassword;
7262 $this->timeout = $timeout;
7263 $this->response_timeout = $response_timeout;
7264 $this->portName = $portName;
7266 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7267 $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7271 if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
7272 $this->wsdl = $endpoint;
7273 $this->endpoint = $this->wsdl->wsdl;
7274 $this->wsdlFile = $this->endpoint;
7275 $this->debug('existing wsdl instance created from ' . $this->endpoint);
7278 $this->wsdlFile = $this->endpoint;
7280 $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7282 $this->endpointType = 'wsdl';
7284 $this->debug("instantiate SOAP with endpoint at $endpoint");
7285 $this->endpointType = 'soap';
7290 * calls method, returns PHP native type
7292 * @param string $operation SOAP server URL or path
7293 * @param mixed $params An array, associative or simple, of the parameters
7294 * for the method call, or a string that is the XML
7295 * for the call. For rpc style, this call will
7296 * wrap the XML in a tag named after the method, as
7297 * well as the SOAP Envelope and Body. For document
7298 * style, this will only wrap with the Envelope and Body.
7299 * IMPORTANT: when using an array with document style,
7300 * in which case there
7301 * is really one parameter, the root of the fragment
7302 * used in the call, which encloses what programmers
7303 * normally think of parameters. A parameter array
7304 * *must* include the wrapper.
7305 * @param string $namespace optional method namespace (WSDL can override)
7306 * @param string $soapAction optional SOAPAction value (WSDL can override)
7307 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7308 * @param boolean $rpcParams optional (no longer used)
7309 * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7310 * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
7311 * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7314 function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
7315 $this->operation = $operation;
7316 $this->fault = false;
7317 $this->setError('');
7318 $this->request = '';
7319 $this->response = '';
7320 $this->responseData = '';
7321 $this->faultstring = '';
7322 $this->faultcode = '';
7323 $this->opData = array();
7325 $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7326 $this->appendDebug('params=' . $this->varDump($params));
7327 $this->appendDebug('headers=' . $this->varDump($headers));
7329 $this->requestHeaders = $headers;
7331 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7333 if ($this->getError())
7336 // serialize parameters
7337 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
7338 // use WSDL for operation
7339 $this->opData = $opData;
7340 $this->debug("found operation");
7341 $this->appendDebug('opData=' . $this->varDump($opData));
7342 if (isset($opData['soapAction'])) {
7343 $soapAction = $opData['soapAction'];
7345 if (! $this->forceEndpoint) {
7346 $this->endpoint = $opData['endpoint'];
7348 $this->endpoint = $this->forceEndpoint;
7350 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7351 $style = $opData['style'];
7352 $use = $opData['input']['use'];
7353 // add ns to ns array
7354 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
7355 $nsPrefix = 'ns' . rand(1000, 9999);
7356 $this->wsdl->namespaces[$nsPrefix] = $namespace;
7358 $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7359 // serialize payload
7360 if (is_string($params)) {
7361 $this->debug("serializing param string for WSDL operation $operation");
7363 } elseif (is_array($params)) {
7364 $this->debug("serializing param array for WSDL operation $operation");
7365 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType);
7367 $this->debug('params must be array or string');
7368 $this->setError('params must be array or string');
7371 $usedNamespaces = $this->wsdl->usedNamespaces;
7372 if (isset($opData['input']['encodingStyle'])) {
7373 $encodingStyle = $opData['input']['encodingStyle'];
7375 $encodingStyle = '';
7377 $this->appendDebug($this->wsdl->getDebug());
7378 $this->wsdl->clearDebug();
7379 if ($errstr = $this->wsdl->getError()) {
7380 $this->debug('got wsdl error: '.$errstr);
7381 $this->setError('wsdl error: '.$errstr);
7384 } elseif($this->endpointType == 'wsdl') {
7385 // operation not in WSDL
7386 $this->appendDebug($this->wsdl->getDebug());
7387 $this->wsdl->clearDebug();
7388 $this->setError('operation '.$operation.' not present in WSDL.');
7389 $this->debug("operation '$operation' not present in WSDL.");
7393 //$this->namespaces['ns1'] = $namespace;
7394 $nsPrefix = 'ns' . rand(1000, 9999);
7397 if (is_string($params)) {
7398 $this->debug("serializing param string for operation $operation");
7400 } elseif (is_array($params)) {
7401 $this->debug("serializing param array for operation $operation");
7402 foreach($params as $k => $v){
7403 $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
7406 $this->debug('params must be array or string');
7407 $this->setError('params must be array or string');
7410 $usedNamespaces = array();
7411 if ($use == 'encoded') {
7412 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7414 $encodingStyle = '';
7417 // wrap RPC calls with method element
7418 if ($style == 'rpc') {
7419 if ($use == 'literal') {
7420 $this->debug("wrapping RPC request with literal method element");
7422 // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
7423 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7425 "</$nsPrefix:$operation>";
7427 $payload = "<$operation>" . $payload . "</$operation>";
7430 $this->debug("wrapping RPC request with encoded method element");
7432 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7434 "</$nsPrefix:$operation>";
7436 $payload = "<$operation>" .
7442 // serialize envelope
7443 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
7444 $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7445 $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
7447 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
7448 if($errstr = $this->getError()){
7449 $this->debug('Error: '.$errstr);
7452 $this->return = $return;
7453 $this->debug('sent message successfully and got a(n) '.gettype($return));
7454 $this->appendDebug('return=' . $this->varDump($return));
7457 if(is_array($return) && isset($return['faultcode'])){
7458 $this->debug('got fault');
7459 $this->setError($return['faultcode'].': '.$return['faultstring']);
7460 $this->fault = true;
7461 foreach($return as $k => $v){
7463 $this->debug("$k = $v<br>");
7466 } elseif ($style == 'document') {
7467 // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7468 // we are only going to return the first part here...sorry about that
7471 // array of return values
7472 if(is_array($return)){
7473 // multiple 'out' parameters, which we return wrapped up
7475 if(sizeof($return) > 1){
7478 // single 'out' parameter (normally the return value)
7479 $return = array_shift($return);
7480 $this->debug('return shifted value: ');
7481 $this->appendDebug($this->varDump($return));
7483 // nothing returned (ie, echoVoid)
7492 * check WSDL passed as an instance or pulled from an endpoint
7496 function checkWSDL() {
7497 $this->appendDebug($this->wsdl->getDebug());
7498 $this->wsdl->clearDebug();
7499 $this->debug('checkWSDL');
7501 if ($errstr = $this->wsdl->getError()) {
7502 $this->appendDebug($this->wsdl->getDebug());
7503 $this->wsdl->clearDebug();
7504 $this->debug('got wsdl error: '.$errstr);
7505 $this->setError('wsdl error: '.$errstr);
7506 } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
7507 $this->appendDebug($this->wsdl->getDebug());
7508 $this->wsdl->clearDebug();
7509 $this->bindingType = 'soap';
7510 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7511 } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
7512 $this->appendDebug($this->wsdl->getDebug());
7513 $this->wsdl->clearDebug();
7514 $this->bindingType = 'soap12';
7515 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7516 $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7518 $this->appendDebug($this->wsdl->getDebug());
7519 $this->wsdl->clearDebug();
7520 $this->debug('getOperations returned false');
7521 $this->setError('no operations defined in the WSDL document!');
7526 * instantiate wsdl object and parse wsdl file
7530 function loadWSDL() {
7531 $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile);
7532 $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl);
7533 $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7534 $this->wsdl->fetchWSDL($this->wsdlFile);
7539 * get available data pertaining to an operation
7541 * @param string $operation operation name
7542 * @return array array of data pertaining to the operation
7545 function getOperationData($operation){
7546 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7548 if ($this->getError())
7551 if(isset($this->operations[$operation])){
7552 return $this->operations[$operation];
7554 $this->debug("No data for operation: $operation");
7558 * send the SOAP message
7560 * Note: if the operation has multiple return values
7561 * the return value of this method will be an array
7564 * @param string $msg a SOAPx4 soapmsg object
7565 * @param string $soapaction SOAPAction value
7566 * @param integer $timeout set connection timeout in seconds
7567 * @param integer $response_timeout set response timeout in seconds
7568 * @return mixed native PHP types.
7571 function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
7572 $this->checkCookies();
7576 case preg_match('/^http/',$this->endpoint):
7577 $this->debug('transporting via HTTP');
7578 if($this->persistentConnection == true && is_object($this->persistentConnection)){
7579 $http =& $this->persistentConnection;
7581 $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7582 if ($this->persistentConnection) {
7583 $http->usePersistentConnection();
7586 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7587 $http->setSOAPAction($soapaction);
7588 if($this->proxyhost && $this->proxyport){
7589 $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
7591 if($this->authtype != '') {
7592 $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
7594 if($this->http_encoding != ''){
7595 $http->setEncoding($this->http_encoding);
7597 $this->debug('sending message, length='.strlen($msg));
7598 if(preg_match('/^http:/',$this->endpoint)){
7599 //if(strpos($this->endpoint,'http:')){
7600 $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
7601 } elseif(preg_match('/^https/',$this->endpoint)){
7602 //} elseif(strpos($this->endpoint,'https:')){
7603 //if(phpversion() == '4.3.0-dev'){
7604 //$response = $http->send($msg,$timeout,$response_timeout);
7605 //$this->request = $http->outgoing_payload;
7606 //$this->response = $http->incoming_payload;
7608 $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
7610 $this->setError('no http/s in endpoint url');
7612 $this->request = $http->outgoing_payload;
7613 $this->response = $http->incoming_payload;
7614 $this->appendDebug($http->getDebug());
7615 $this->UpdateCookies($http->incoming_cookies);
7617 // save transport object if using persistent connections
7618 if ($this->persistentConnection) {
7619 $http->clearDebug();
7620 if (!is_object($this->persistentConnection)) {
7621 $this->persistentConnection = $http;
7625 if($err = $http->getError()){
7626 $this->setError('HTTP Error: '.$err);
7628 } elseif($this->getError()){
7631 $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
7632 return $this->parseResponse($http->incoming_headers, $this->responseData);
7636 $this->setError('no transport found, or selected transport is not yet supported!');
7643 * processes SOAP message returned from server
7645 * @param array $headers The HTTP headers
7646 * @param string $data unprocessed response data from server
7647 * @return mixed value of the message, decoded into a PHP type
7650 function parseResponse($headers, $data) {
7651 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
7652 $this->appendDebug($this->varDump($headers));
7653 if (!isset($headers['content-type'])) {
7654 $this->setError('Response not of type text/xml (no content-type header)');
7657 if (!strstr($headers['content-type'], 'text/xml')) {
7658 $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7661 if (strpos($headers['content-type'], '=')) {
7662 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
7663 $this->debug('Got response encoding: ' . $enc);
7664 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
7665 $this->xml_encoding = strtoupper($enc);
7667 $this->xml_encoding = 'US-ASCII';
7670 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7671 $this->xml_encoding = 'ISO-8859-1';
7673 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
7674 $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
7675 // add parser debug data to our debug
7676 $this->appendDebug($parser->getDebug());
7678 if($errstr = $parser->getError()){
7679 $this->setError( $errstr);
7680 // destroy the parser object
7685 $this->responseHeaders = $parser->getHeaders();
7687 $this->responseHeader = $parser->get_soapheader();
7688 // get decoded message
7689 $return = $parser->get_soapbody();
7690 // add document for doclit support
7691 $this->document = $parser->document;
7692 // destroy the parser object
7694 // return decode message
7700 * sets user-specified cURL options
7702 * @param mixed $option The cURL option (always integer?)
7703 * @param mixed $value The cURL option value
7706 function setCurlOption($option, $value) {
7707 $this->debug("setCurlOption option=$option, value=");
7708 $this->appendDebug($this->varDump($value));
7709 $this->curl_options[$option] = $value;
7713 * sets the SOAP endpoint, which can override WSDL
7715 * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
7718 function setEndpoint($endpoint) {
7719 $this->debug("setEndpoint(\"$endpoint\")");
7720 $this->forceEndpoint = $endpoint;
7724 * set the SOAP headers
7726 * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
7729 function setHeaders($headers){
7730 $this->debug("setHeaders headers=");
7731 $this->appendDebug($this->varDump($headers));
7732 $this->requestHeaders = $headers;
7736 * get the SOAP response headers (namespace resolution incomplete)
7741 function getHeaders(){
7742 return $this->responseHeaders;
7746 * get the SOAP response Header (parsed)
7751 function getHeader(){
7752 return $this->responseHeader;
7756 * set proxy info here
7758 * @param string $proxyhost
7759 * @param string $proxyport
7760 * @param string $proxyusername
7761 * @param string $proxypassword
7764 function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
7765 $this->proxyhost = $proxyhost;
7766 $this->proxyport = $proxyport;
7767 $this->proxyusername = $proxyusername;
7768 $this->proxypassword = $proxypassword;
7772 * if authenticating, set user credentials here
7774 * @param string $username
7775 * @param string $password
7776 * @param string $authtype (basic|digest|certificate|ntlm)
7777 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
7780 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
7781 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
7782 $this->appendDebug($this->varDump($certRequest));
7783 $this->username = $username;
7784 $this->password = $password;
7785 $this->authtype = $authtype;
7786 $this->certRequest = $certRequest;
7792 * @param string $enc HTTP encoding
7795 function setHTTPEncoding($enc='gzip, deflate'){
7796 $this->debug("setHTTPEncoding(\"$enc\")");
7797 $this->http_encoding = $enc;
7801 * Set whether to try to use cURL connections if possible
7803 * @param boolean $use Whether to try to use cURL
7806 function setUseCURL($use) {
7807 $this->debug("setUseCURL($use)");
7808 $this->use_curl = $use;
7812 * use HTTP persistent connections if possible
7816 function useHTTPPersistentConnection(){
7817 $this->debug("useHTTPPersistentConnection");
7818 $this->persistentConnection = true;
7822 * gets the default RPC parameter setting.
7823 * If true, default is that call params are like RPC even for document style.
7824 * Each call() can override this value.
7826 * This is no longer used.
7832 function getDefaultRpcParams() {
7833 return $this->defaultRpcParams;
7837 * sets the default RPC parameter setting.
7838 * If true, default is that call params are like RPC even for document style
7839 * Each call() can override this value.
7841 * This is no longer used.
7843 * @param boolean $rpcParams
7847 function setDefaultRpcParams($rpcParams) {
7848 $this->defaultRpcParams = $rpcParams;
7852 * dynamically creates an instance of a proxy class,
7853 * allowing user to directly call methods from wsdl
7855 * @return object soap_proxy object
7858 function getProxy() {
7860 $evalStr = $this->_getProxyClassCode($r);
7861 //$this->debug("proxy class: $evalStr");
7862 if ($this->getError()) {
7863 $this->debug("Error from _getProxyClassCode, so return NULL");
7868 // instantiate proxy object
7869 eval("\$proxy = new nusoap_proxy_$r('');");
7870 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
7871 $proxy->endpointType = 'wsdl';
7872 $proxy->wsdlFile = $this->wsdlFile;
7873 $proxy->wsdl = $this->wsdl;
7874 $proxy->operations = $this->operations;
7875 $proxy->defaultRpcParams = $this->defaultRpcParams;
7876 // transfer other state
7877 $proxy->soap_defencoding = $this->soap_defencoding;
7878 $proxy->username = $this->username;
7879 $proxy->password = $this->password;
7880 $proxy->authtype = $this->authtype;
7881 $proxy->certRequest = $this->certRequest;
7882 $proxy->requestHeaders = $this->requestHeaders;
7883 $proxy->endpoint = $this->endpoint;
7884 $proxy->forceEndpoint = $this->forceEndpoint;
7885 $proxy->proxyhost = $this->proxyhost;
7886 $proxy->proxyport = $this->proxyport;
7887 $proxy->proxyusername = $this->proxyusername;
7888 $proxy->proxypassword = $this->proxypassword;
7889 $proxy->http_encoding = $this->http_encoding;
7890 $proxy->timeout = $this->timeout;
7891 $proxy->response_timeout = $this->response_timeout;
7892 $proxy->persistentConnection = &$this->persistentConnection;
7893 $proxy->decode_utf8 = $this->decode_utf8;
7894 $proxy->curl_options = $this->curl_options;
7895 $proxy->bindingType = $this->bindingType;
7896 $proxy->use_curl = $this->use_curl;
7901 * dynamically creates proxy class code
7903 * @return string PHP/NuSOAP code for the proxy class
7906 function _getProxyClassCode($r) {
7907 $this->debug("in getProxy endpointType=$this->endpointType");
7908 $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
7909 if ($this->endpointType != 'wsdl') {
7910 $evalStr = 'A proxy can only be created for a WSDL client';
7911 $this->setError($evalStr);
7912 $evalStr = "echo \"$evalStr\";";
7915 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7917 if ($this->getError()) {
7918 return "echo \"" . $this->getError() . "\";";
7922 foreach ($this->operations as $operation => $opData) {
7923 if ($operation != '') {
7924 // create param string and param comment string
7925 if (sizeof($opData['input']['parts']) > 0) {
7927 $paramArrayStr = '';
7928 $paramCommentStr = '';
7929 foreach ($opData['input']['parts'] as $name => $type) {
7930 $paramStr .= "\$$name, ";
7931 $paramArrayStr .= "'$name' => \$$name, ";
7932 $paramCommentStr .= "$type \$$name, ";
7934 $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
7935 $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
7936 $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
7939 $paramArrayStr = '';
7940 $paramCommentStr = 'void';
7942 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
7943 $evalStr .= "// $paramCommentStr
7944 function " . str_replace('.', '__', $operation) . "($paramStr) {
7945 \$params = array($paramArrayStr);
7946 return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
7950 unset($paramCommentStr);
7953 $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client {
7960 * dynamically creates proxy class code
7962 * @return string PHP/NuSOAP code for the proxy class
7965 function getProxyClassCode() {
7967 return $this->_getProxyClassCode($r);
7971 * gets the HTTP body for the current request.
7973 * @param string $soapmsg The SOAP payload
7974 * @return string The HTTP body, which includes the SOAP payload
7977 function getHTTPBody($soapmsg) {
7982 * gets the HTTP content type for the current request.
7984 * Note: getHTTPBody must be called before this.
7986 * @return string the HTTP content type for the current request.
7989 function getHTTPContentType() {
7994 * gets the HTTP content type charset for the current request.
7995 * returns false for non-text content types.
7997 * Note: getHTTPBody must be called before this.
7999 * @return string the HTTP content type charset for the current request.
8002 function getHTTPContentTypeCharset() {
8003 return $this->soap_defencoding;
8007 * whether or not parser should decode utf8 element content
8009 * @return always returns true
8012 function decodeUTF8($bool){
8013 $this->decode_utf8 = $bool;
8018 * adds a new Cookie into $this->cookies array
8020 * @param string $name Cookie Name
8021 * @param string $value Cookie Value
8022 * @return boolean if cookie-set was successful returns true, else false
8025 function setCookie($name, $value) {
8026 if (strlen($name) == 0) {
8029 $this->cookies[] = array('name' => $name, 'value' => $value);
8036 * @return array with all internal cookies
8039 function getCookies() {
8040 return $this->cookies;
8044 * checks all Cookies and delete those which are expired
8046 * @return boolean always return true
8049 function checkCookies() {
8050 if (sizeof($this->cookies) == 0) {
8053 $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
8054 $curr_cookies = $this->cookies;
8055 $this->cookies = array();
8056 foreach ($curr_cookies as $cookie) {
8057 if (! is_array($cookie)) {
8058 $this->debug('Remove cookie that is not an array');
8061 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
8062 if (strtotime($cookie['expires']) > time()) {
8063 $this->cookies[] = $cookie;
8065 $this->debug('Remove expired cookie ' . $cookie['name']);
8068 $this->cookies[] = $cookie;
8071 $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
8076 * updates the current cookies with a new set
8078 * @param array $cookies new cookies with which to update current ones
8079 * @return boolean always return true
8082 function UpdateCookies($cookies) {
8083 if (sizeof($this->cookies) == 0) {
8084 // no existing cookies: take whatever is new
8085 if (sizeof($cookies) > 0) {
8086 $this->debug('Setting new cookie(s)');
8087 $this->cookies = $cookies;
8091 if (sizeof($cookies) == 0) {
8092 // no new cookies: keep what we've got
8096 foreach ($cookies as $newCookie) {
8097 if (!is_array($newCookie)) {
8100 if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
8103 $newName = $newCookie['name'];
8106 for ($i = 0; $i < count($this->cookies); $i++) {
8107 $cookie = $this->cookies[$i];
8108 if (!is_array($cookie)) {
8111 if (!isset($cookie['name'])) {
8114 if ($newName != $cookie['name']) {
8117 $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8118 $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8119 if ($newDomain != $domain) {
8122 $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8123 $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8124 if ($newPath != $path) {
8127 $this->cookies[$i] = $newCookie;
8129 $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8133 $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8134 $this->cookies[] = $newCookie;
8141 if (!extension_loaded('soap')) {
8143 * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
8145 class soapclient extends nusoap_client {