4 $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 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 mailing list:
28 nusoap-general@lists.sourceforge.net
30 If you have any questions or comments, please email:
34 http://dietrich.ganx4.com/nusoap
37 http://www.nusphere.com
42 * Some of the standards implmented in whole or part by NuSOAP:
44 * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
45 * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
46 * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
47 * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
48 * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
49 * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
50 * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
51 * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
52 * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
58 require_once('class.soapclient.php');
59 require_once('class.soap_val.php');
60 require_once('class.soap_parser.php');
61 require_once('class.soap_fault.php');
64 require_once('class.soap_transport_http.php');
66 // optional add-on classes
67 require_once('class.xmlschema.php');
68 require_once('class.wsdl.php');
71 require_once('class.soap_server.php');*/
73 // class variable emulation
74 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
75 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
81 * @author Dietrich Ayala <dietrich@ganx4.com>
82 * @author Scott Nichol <snichol@users.sourceforge.net>
83 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
88 * Identification for HTTP headers.
93 var $title = 'NuSOAP';
95 * Version for HTTP headers.
100 var $version = '0.7.3';
102 * CVS revision for HTTP headers.
107 var $revision = '$Revision: 1.114 $';
109 * Current error string (manipulated by getError/setError)
116 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
123 * toggles automatic encoding of special characters as entities
124 * (should always be true, I think)
129 var $charencoding = true;
131 * the debug level for this instance
144 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
147 * charset encoding for outgoing messages
152 var $soap_defencoding = 'ISO-8859-1';
153 //var $soap_defencoding = 'UTF-8';
156 * namespaces in an array of prefix => uri
158 * this is "seeded" by a set of constants, but it may be altered by code
163 var $namespaces = array(
164 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
165 'xsd' => 'http://www.w3.org/2001/XMLSchema',
166 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
167 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
171 * namespaces used in the current context, e.g. during serialization
176 var $usedNamespaces = array();
179 * XML Schema types in an array of uri => (array of xml type => php type)
180 * is this legacy yet?
181 * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
185 var $typemap = array(
186 'http://www.w3.org/2001/XMLSchema' => array(
187 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
188 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
189 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
190 // abstract "any" types
191 'anyType'=>'string','anySimpleType'=>'string',
193 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
194 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
195 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
196 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
197 'http://www.w3.org/2000/10/XMLSchema' => array(
198 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
199 'float'=>'double','dateTime'=>'string',
200 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
201 'http://www.w3.org/1999/XMLSchema' => array(
202 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
203 'float'=>'double','dateTime'=>'string',
204 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
205 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
206 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
207 'http://xml.apache.org/xml-soap' => array('Map')
211 * XML entities to convert
216 * @see expandEntities
218 var $xmlEntities = array('quot' => '"','amp' => '&',
219 'lt' => '<','gt' => '>','apos' => "'");
226 function nusoap_base() {
227 $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
231 * gets the global debug level, which applies to future instances
233 * @return integer Debug level 0-9, where 0 turns off
236 function getGlobalDebugLevel() {
237 return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
241 * sets the global debug level, which applies to future instances
243 * @param int $level Debug level 0-9, where 0 turns off
246 function setGlobalDebugLevel($level) {
247 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
251 * gets the debug level for this instance
253 * @return int Debug level 0-9, where 0 turns off
256 function getDebugLevel() {
257 return $this->debugLevel;
261 * sets the debug level for this instance
263 * @param int $level Debug level 0-9, where 0 turns off
266 function setDebugLevel($level) {
267 $this->debugLevel = $level;
271 * adds debug data to the instance debug string with formatting
273 * @param string $string debug data
276 function debug($string){
277 if ($this->debugLevel > 0) {
278 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
283 * adds debug data to the instance debug string without formatting
285 * @param string $string debug data
288 function appendDebug($string){
289 if ($this->debugLevel > 0) {
290 // it would be nice to use a memory stream here to use
291 // memory more efficiently
292 $this->debug_str .= $string;
297 * clears the current debug data for this instance
301 function clearDebug() {
302 // it would be nice to use a memory stream here to use
303 // memory more efficiently
304 $this->debug_str = '';
308 * gets the current debug data for this instance
313 function &getDebug() {
314 // it would be nice to use a memory stream here to use
315 // memory more efficiently
316 return $this->debug_str;
320 * gets the current debug data for this instance as an XML comment
321 * this may change the contents of the debug data
323 * @return debug data as an XML comment
326 function &getDebugAsXMLComment() {
327 // it would be nice to use a memory stream here to use
328 // memory more efficiently
329 while (strpos($this->debug_str, '--')) {
330 $this->debug_str = str_replace('--', '- -', $this->debug_str);
332 $ret = "<!--\n" . $this->debug_str . "\n-->";
337 * expands entities, e.g. changes '<' to '<'.
339 * @param string $val The string in which to expand entities.
342 function expandEntities($val) {
343 if ($this->charencoding) {
344 $val = str_replace('&', '&', $val);
345 $val = str_replace("'", ''', $val);
346 $val = str_replace('"', '"', $val);
347 $val = str_replace('<', '<', $val);
348 $val = str_replace('>', '>', $val);
354 * returns error string if present
356 * @return mixed error string or false
360 if($this->error_str != ''){
361 return $this->error_str;
369 * @return boolean $string error string
372 function setError($str){
373 $this->error_str = $str;
377 * detect if array is a simple array or a struct (associative array)
379 * @param mixed $val The PHP array
380 * @return string (arraySimple|arrayStruct)
383 function isArraySimpleOrStruct($val) {
384 $keyList = array_keys($val);
385 foreach ($keyList as $keyListValue) {
386 if (!is_int($keyListValue)) {
387 return 'arrayStruct';
390 return 'arraySimple';
394 * serializes PHP values in accordance w/ section 5. Type information is
395 * not serialized if $use == 'literal'.
397 * @param mixed $val The value to serialize
398 * @param string $name The name (local part) of the XML element
399 * @param string $type The XML schema type (local part) for the element
400 * @param string $name_ns The namespace for the name of the XML element
401 * @param string $type_ns The namespace for the type of the element
402 * @param array $attributes The attributes to serialize as name=>value pairs
403 * @param string $use The WSDL "use" (encoded|literal)
404 * @param boolean $soapval Whether this is called from soapval.
405 * @return string The serialized element, possibly with child elements
408 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
409 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
410 $this->appendDebug('value=' . $this->varDump($val));
411 $this->appendDebug('attributes=' . $this->varDump($attributes));
413 if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
414 $this->debug("serialize_val: serialize soapval");
415 $xml = $val->serialize($use);
416 $this->appendDebug($val->getDebug());
418 $this->debug("serialize_val of soapval returning $xml");
421 // force valid name if necessary
422 if (is_numeric($name)) {
423 $name = '__numeric_' . $name;
427 // if name has ns, add ns prefix to name
430 $prefix = 'nu'.rand(1000,9999);
431 $name = $prefix.':'.$name;
432 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
434 // if type is prefixed, create type prefix
435 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
436 // need to fix this. shouldn't default to xsd if no ns specified
437 // w/o checking against typemap
438 $type_prefix = 'xsd';
440 $type_prefix = 'ns'.rand(1000,9999);
441 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
443 // serialize attributes if present
446 foreach($attributes as $k => $v){
447 $atts .= " $k=\"".$this->expandEntities($v).'"';
450 // serialize null value
452 $this->debug("serialize_val: serialize null");
453 if ($use == 'literal') {
454 // TODO: depends on minOccurs
455 $xml = "<$name$xmlns$atts/>";
456 $this->debug("serialize_val returning $xml");
459 if (isset($type) && isset($type_prefix)) {
460 $type_str = " xsi:type=\"$type_prefix:$type\"";
464 $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
465 $this->debug("serialize_val returning $xml");
469 // serialize if an xsd built-in primitive type
470 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
471 $this->debug("serialize_val: serialize xsd built-in primitive type");
473 if ($type == 'boolean') {
474 $val = $val ? 'true' : 'false';
478 } else if (is_string($val)) {
479 $val = $this->expandEntities($val);
481 if ($use == 'literal') {
482 $xml = "<$name$xmlns$atts>$val</$name>";
483 $this->debug("serialize_val returning $xml");
486 $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
487 $this->debug("serialize_val returning $xml");
491 // detect type and serialize
494 case (is_bool($val) || $type == 'boolean'):
495 $this->debug("serialize_val: serialize boolean");
496 if ($type == 'boolean') {
497 $val = $val ? 'true' : 'false';
501 if ($use == 'literal') {
502 $xml .= "<$name$xmlns$atts>$val</$name>";
504 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
507 case (is_int($val) || is_long($val) || $type == 'int'):
508 $this->debug("serialize_val: serialize int");
509 if ($use == 'literal') {
510 $xml .= "<$name$xmlns$atts>$val</$name>";
512 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
515 case (is_float($val)|| is_double($val) || $type == 'float'):
516 $this->debug("serialize_val: serialize float");
517 if ($use == 'literal') {
518 $xml .= "<$name$xmlns$atts>$val</$name>";
520 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
523 case (is_string($val) || $type == 'string'):
524 $this->debug("serialize_val: serialize string");
525 $val = $this->expandEntities($val);
526 if ($use == 'literal') {
527 $xml .= "<$name$xmlns$atts>$val</$name>";
529 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
532 case is_object($val):
533 $this->debug("serialize_val: serialize object");
534 if (get_class($val) == 'soapval') {
535 $this->debug("serialize_val: serialize soapval object");
536 $pXml = $val->serialize($use);
537 $this->appendDebug($val->getDebug());
541 $name = get_class($val);
542 $this->debug("In serialize_val, used class name $name as element name");
544 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
546 foreach(get_object_vars($val) as $k => $v){
547 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
550 if(isset($type) && isset($type_prefix)){
551 $type_str = " xsi:type=\"$type_prefix:$type\"";
555 if ($use == 'literal') {
556 $xml .= "<$name$xmlns$atts>$pXml</$name>";
558 $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
562 case (is_array($val) || $type):
563 // detect if struct or array
564 $valueType = $this->isArraySimpleOrStruct($val);
565 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
566 $this->debug("serialize_val: serialize array");
568 if(is_array($val) && count($val)> 0){
570 if(is_object($v) && get_class($v) == 'soapval'){
571 $tt_ns = $v->type_ns;
573 } elseif (is_array($v)) {
574 $tt = $this->isArraySimpleOrStruct($v);
578 $array_types[$tt] = 1;
579 // TODO: for literal, the name should be $name
580 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
583 if(count($array_types) > 1){
584 $array_typename = 'xsd:anyType';
585 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
586 if ($tt == 'integer') {
589 $array_typename = 'xsd:'.$tt;
590 } elseif(isset($tt) && $tt == 'arraySimple'){
591 $array_typename = 'SOAP-ENC:Array';
592 } elseif(isset($tt) && $tt == 'arrayStruct'){
593 $array_typename = 'unnamed_struct_use_soapval';
595 // if type is prefixed, create type prefix
596 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
597 $array_typename = 'xsd:' . $tt;
599 $tt_prefix = 'ns' . rand(1000, 9999);
600 $array_typename = "$tt_prefix:$tt";
601 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
603 $array_typename = $tt;
607 if ($use == 'literal') {
609 } else if (isset($type) && isset($type_prefix)) {
610 $type_str = " xsi:type=\"$type_prefix:$type\"";
612 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
616 if ($use == 'literal') {
618 } else if (isset($type) && isset($type_prefix)) {
619 $type_str = " xsi:type=\"$type_prefix:$type\"";
621 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
624 // TODO: for array in literal, there is no wrapper here
625 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
628 $this->debug("serialize_val: serialize struct");
629 if(isset($type) && isset($type_prefix)){
630 $type_str = " xsi:type=\"$type_prefix:$type\"";
634 if ($use == 'literal') {
635 $xml .= "<$name$xmlns$atts>";
637 $xml .= "<$name$xmlns$type_str$atts>";
639 foreach($val as $k => $v){
641 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
643 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
644 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
647 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
654 $this->debug("serialize_val: serialize unknown");
655 $xml .= 'not detected, got '.gettype($val).' for '.$val;
658 $this->debug("serialize_val returning $xml");
663 * serializes a message
665 * @param string $body the XML of the SOAP body
666 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
667 * @param array $namespaces optional the namespaces used in generating the body and headers
668 * @param string $style optional (rpc|document)
669 * @param string $use optional (encoded|literal)
670 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
671 * @return string the message
674 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
675 // TODO: add an option to automatically run utf8_encode on $body and $headers
676 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
677 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
679 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
680 $this->debug("headers:");
681 $this->appendDebug($this->varDump($headers));
682 $this->debug("namespaces:");
683 $this->appendDebug($this->varDump($namespaces));
685 // serialize namespaces
687 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
688 $ns_string .= " xmlns:$k=\"$v\"";
691 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
696 if (is_array($headers)) {
698 foreach ($headers as $k => $v) {
699 if (is_object($v) && get_class($v) == 'soapval') {
700 $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
702 $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
706 $this->debug("In serializeEnvelope, serialized array of headers to $headers");
708 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
710 // serialize envelope
712 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
713 '<SOAP-ENV:Envelope'.$ns_string.">".
718 "</SOAP-ENV:Envelope>";
722 * formats a string to be inserted into an HTML stream
724 * @param string $str The string to format
725 * @return string The formatted string
729 function formatDump($str){
730 $str = htmlspecialchars($str);
735 * contracts (changes namespace to prefix) a qualified name
737 * @param string $qname qname
738 * @return string contracted qname
741 function contractQname($qname){
742 // get element namespace
743 //$this->xdebug("Contract $qname");
744 if (strrpos($qname, ':')) {
745 // get unqualified name
746 $name = substr($qname, strrpos($qname, ':') + 1);
748 $ns = substr($qname, 0, strrpos($qname, ':'));
749 $p = $this->getPrefixFromNamespace($ns);
751 return $p . ':' . $name;
760 * expands (changes prefix to namespace) a qualified name
762 * @param string $qname qname
763 * @return string expanded qname
766 function expandQname($qname){
767 // get element prefix
768 if(strpos($qname,':') && !ereg('^http://',$qname)){
769 // get unqualified name
770 $name = substr(strstr($qname,':'),1);
772 $prefix = substr($qname,0,strpos($qname,':'));
773 if(isset($this->namespaces[$prefix])){
774 return $this->namespaces[$prefix].':'.$name;
784 * returns the local part of a prefixed string
785 * returns the original string, if not prefixed
787 * @param string $str The prefixed string
788 * @return string The local part
791 function getLocalPart($str){
792 if($sstr = strrchr($str,':')){
793 // get unqualified name
794 return substr( $sstr, 1 );
801 * returns the prefix part of a prefixed string
802 * returns false, if not prefixed
804 * @param string $str The prefixed string
805 * @return mixed The prefix or false if there is no prefix
808 function getPrefix($str){
809 if($pos = strrpos($str,':')){
811 return substr($str,0,$pos);
817 * pass it a prefix, it returns a namespace
819 * @param string $prefix The prefix
820 * @return mixed The namespace, false if no namespace has the specified prefix
823 function getNamespaceFromPrefix($prefix){
824 if (isset($this->namespaces[$prefix])) {
825 return $this->namespaces[$prefix];
827 //$this->setError("No namespace registered for prefix '$prefix'");
832 * returns the prefix for a given namespace (or prefix)
833 * or false if no prefixes registered for the given namespace
835 * @param string $ns The namespace
836 * @return mixed The prefix, false if the namespace has no prefixes
839 function getPrefixFromNamespace($ns) {
840 foreach ($this->namespaces as $p => $n) {
841 if ($ns == $n || $ns == $p) {
842 $this->usedNamespaces[$p] = $n;
850 * returns the time in ODBC canonical form with microseconds
852 * @return string The time in ODBC canonical form with microseconds
855 function getmicrotime() {
856 if (function_exists('gettimeofday')) {
857 $tod = gettimeofday();
859 $usec = $tod['usec'];
864 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
868 * Returns a string with the output of var_dump
870 * @param mixed $data The variable to var_dump
871 * @return string The output of var_dump
874 function varDump($data) {
877 $ret_val = ob_get_contents();
883 * represents the object as a string
888 function __toString() {
889 return $this->varDump($this);
893 // XML Schema Datatype Helper Functions
895 //xsd:dateTime helpers
898 * convert unix timestamp to ISO 8601 compliant date string
900 * @param string $timestamp Unix time stamp
901 * @param boolean $utc Whether the time stamp is UTC or local
904 function timestamp_to_iso8601($timestamp,$utc=true){
905 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
908 '([0-9]{4})-'. // centuries & years CCYY-
909 '([0-9]{2})-'. // months MM-
910 '([0-9]{2})'. // days DD
912 '([0-9]{2}):'. // hours hh:
913 '([0-9]{2}):'. // minutes mm:
914 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
915 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
917 if(ereg($eregStr,$datestr,$regs)){
918 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
927 * convert ISO 8601 compliant date string to unix timestamp
929 * @param string $datestr ISO 8601 compliant date string
932 function iso8601_to_timestamp($datestr){
934 '([0-9]{4})-'. // centuries & years CCYY-
935 '([0-9]{2})-'. // months MM-
936 '([0-9]{2})'. // days DD
938 '([0-9]{2}):'. // hours hh:
939 '([0-9]{2}):'. // minutes mm:
940 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
941 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
942 if(ereg($eregStr,$datestr,$regs)){
945 $op = substr($regs[8],0,1);
946 $h = substr($regs[8],1,2);
947 $m = substr($regs[8],strlen($regs[8])-2,2);
949 $regs[4] = $regs[4] + $h;
950 $regs[5] = $regs[5] + $m;
951 } elseif($op == '+'){
952 $regs[4] = $regs[4] - $h;
953 $regs[5] = $regs[5] - $m;
956 return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
957 // return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
964 * sleeps some number of microseconds
966 * @param string $usec the number of microseconds to sleep
970 function usleepWindows($usec)
972 $start = gettimeofday();
976 $stop = gettimeofday();
977 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
978 + $stop['usec'] - $start['usec'];
980 while ($timePassed < $usec);
988 * Contains information for a SOAP fault.
989 * Mainly used for returning faults from deployed functions
990 * in a server instance.
991 * @author Dietrich Ayala <dietrich@ganx4.com>
992 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
995 class nusoap_fault extends nusoap_base {
997 * The fault code (client|server)
1009 * The fault string, a description of the fault
1015 * The fault detail, typically a string or array of string
1024 * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1025 * @param string $faultactor only used when msg routed between multiple actors
1026 * @param string $faultstring human readable error message
1027 * @param mixed $faultdetail detail, typically a string or array of string
1029 function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1030 parent::nusoap_base();
1031 $this->faultcode = $faultcode;
1032 $this->faultactor = $faultactor;
1033 $this->faultstring = $faultstring;
1034 $this->faultdetail = $faultdetail;
1040 * @return string The serialization of the fault instance.
1043 function serialize(){
1045 foreach($this->namespaces as $k => $v){
1046 $ns_string .= "\n xmlns:$k=\"$v\"";
1049 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1050 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1053 $this->serialize_val($this->faultcode, 'faultcode').
1054 $this->serialize_val($this->faultactor, 'faultactor').
1055 $this->serialize_val($this->faultstring, 'faultstring').
1056 $this->serialize_val($this->faultdetail, 'detail').
1057 '</SOAP-ENV:Fault>'.
1059 '</SOAP-ENV:Envelope>';
1065 * Backward compatibility
1067 class soap_fault extends nusoap_fault {
1075 * parses an XML Schema, allows access to it's data, other utility methods.
1076 * imperfect, no validation... yet, but quite functional.
1078 * @author Dietrich Ayala <dietrich@ganx4.com>
1079 * @author Scott Nichol <snichol@users.sourceforge.net>
1080 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
1083 class nusoap_xmlschema extends nusoap_base {
1089 var $enclosingNamespaces;
1091 var $schemaInfo = array();
1092 var $schemaTargetNamespace = '';
1093 // types, elements, attributes defined by the schema
1094 var $attributes = array();
1095 var $complexTypes = array();
1096 var $complexTypeStack = array();
1097 var $currentComplexType = null;
1098 var $elements = array();
1099 var $elementStack = array();
1100 var $currentElement = null;
1101 var $simpleTypes = array();
1102 var $simpleTypeStack = array();
1103 var $currentSimpleType = null;
1105 var $imports = array();
1110 var $depth_array = array();
1111 var $message = array();
1112 var $defaultNamespace = array();
1117 * @param string $schema schema document URI
1118 * @param string $xml xml document URI
1119 * @param string $namespaces namespaces defined in enclosing XML
1122 function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1123 parent::nusoap_base();
1124 $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1126 $this->schema = $schema;
1130 $this->enclosingNamespaces = $namespaces;
1131 $this->namespaces = array_merge($this->namespaces, $namespaces);
1133 // parse schema file
1135 $this->debug('initial schema file: '.$schema);
1136 $this->parseFile($schema, 'schema');
1141 $this->debug('initial xml file: '.$xml);
1142 $this->parseFile($xml, 'xml');
1150 * @param string $xml path/URL to XML file
1151 * @param string $type (schema | xml)
1155 function parseFile($xml,$type){
1158 $xmlStr = @join("",@file($xml));
1160 $msg = 'Error reading XML from '.$xml;
1161 $this->setError($msg);
1165 $this->debug("parsing $xml");
1166 $this->parseString($xmlStr,$type);
1167 $this->debug("done parsing $xml");
1175 * parse an XML string
1177 * @param string $xml path or URL
1178 * @param string $type (schema|xml)
1181 function parseString($xml,$type){
1185 // Create an XML parser.
1186 $this->parser = xml_parser_create();
1187 // Set the options for parsing the XML data.
1188 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1190 // Set the object for the parser.
1191 xml_set_object($this->parser, $this);
1193 // Set the element handlers for the parser.
1194 if($type == "schema"){
1195 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1196 xml_set_character_data_handler($this->parser,'schemaCharacterData');
1197 } elseif($type == "xml"){
1198 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1199 xml_set_character_data_handler($this->parser,'xmlCharacterData');
1202 // Parse the XML file.
1203 if(!xml_parse($this->parser,$xml,true)){
1204 // Display an error message.
1205 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1206 xml_get_current_line_number($this->parser),
1207 xml_error_string(xml_get_error_code($this->parser))
1209 $this->debug($errstr);
1210 $this->debug("XML payload:\n" . $xml);
1211 $this->setError($errstr);
1214 xml_parser_free($this->parser);
1216 $this->debug('no xml passed to parseString()!!');
1217 $this->setError('no xml passed to parseString()!!');
1222 * gets a type name for an unnamed type
1224 * @param string Element name
1225 * @return string A type name for an unnamed type
1228 function CreateTypeName($ename) {
1230 for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1231 $scope .= $this->complexTypeStack[$i] . '_';
1233 return $scope . $ename . '_ContainedType';
1237 * start-element handler
1239 * @param string $parser XML parser object
1240 * @param string $name element name
1241 * @param string $attrs associative array of attributes
1244 function schemaStartElement($parser, $name, $attrs) {
1246 // position in the total number of elements, starting from 0
1247 $pos = $this->position++;
1248 $depth = $this->depth++;
1249 // set self as current value for this depth
1250 $this->depth_array[$depth] = $pos;
1251 $this->message[$pos] = array('cdata' => '');
1253 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1255 $this->defaultNamespace[$pos] = false;
1258 // get element prefix
1259 if($prefix = $this->getPrefix($name)){
1260 // get unqualified name
1261 $name = $this->getLocalPart($name);
1266 // loop thru attributes, expanding, and registering namespace declarations
1267 if(count($attrs) > 0){
1268 foreach($attrs as $k => $v){
1269 // if ns declarations, add to class level array of valid namespaces
1270 if(ereg("^xmlns",$k)){
1271 //$this->xdebug("$k: $v");
1272 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1273 if($ns_prefix = substr(strrchr($k,':'),1)){
1274 //$this->xdebug("Add namespace[$ns_prefix] = $v");
1275 $this->namespaces[$ns_prefix] = $v;
1277 $this->defaultNamespace[$pos] = $v;
1278 if (! $this->getPrefixFromNamespace($v)) {
1279 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1282 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1283 $this->XMLSchemaVersion = $v;
1284 $this->namespaces['xsi'] = $v.'-instance';
1288 foreach($attrs as $k => $v){
1289 // expand each attribute
1290 $k = strpos($k,':') ? $this->expandQname($k) : $k;
1291 $v = strpos($v,':') ? $this->expandQname($v) : $v;
1298 // find status, register data
1300 case 'all': // (optional) compositor content for a complexType
1304 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1305 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1306 //if($name == 'all' || $name == 'sequence'){
1307 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1310 case 'attribute': // complexType attribute
1311 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1312 $this->xdebug("parsing attribute:");
1313 $this->appendDebug($this->varDump($attrs));
1314 if (!isset($attrs['form'])) {
1315 $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1317 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1318 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1319 if (!strpos($v, ':')) {
1320 // no namespace in arrayType attribute value...
1321 if ($this->defaultNamespace[$pos]) {
1322 // ...so use the default
1323 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1327 if(isset($attrs['name'])){
1328 $this->attributes[$attrs['name']] = $attrs;
1329 $aname = $attrs['name'];
1330 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1331 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1332 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1336 } elseif(isset($attrs['ref'])){
1337 $aname = $attrs['ref'];
1338 $this->attributes[$attrs['ref']] = $attrs;
1341 if($this->currentComplexType){ // This should *always* be
1342 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1344 // arrayType attribute
1345 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1346 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1347 $prefix = $this->getPrefix($aname);
1348 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1349 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1353 if(strpos($v,'[,]')){
1354 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1356 $v = substr($v,0,strpos($v,'[')); // clip the []
1357 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1358 $v = $this->XMLSchemaVersion.':'.$v;
1360 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1363 case 'complexContent': // (optional) content for a complexType
1366 array_push($this->complexTypeStack, $this->currentComplexType);
1367 if(isset($attrs['name'])){
1368 // TODO: what is the scope of named complexTypes that appear
1369 // nested within other c complexTypes?
1370 $this->xdebug('processing named complexType '.$attrs['name']);
1371 //$this->currentElement = false;
1372 $this->currentComplexType = $attrs['name'];
1373 $this->complexTypes[$this->currentComplexType] = $attrs;
1374 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1375 // This is for constructs like
1376 // <complexType name="ListOfString" base="soap:Array">
1378 // <element name="string" type="xsd:string"
1379 // minOccurs="0" maxOccurs="unbounded" />
1382 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1383 $this->xdebug('complexType is unusual array');
1384 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1386 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1389 $name = $this->CreateTypeName($this->currentElement);
1390 $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1391 $this->currentComplexType = $name;
1392 //$this->currentElement = false;
1393 $this->complexTypes[$this->currentComplexType] = $attrs;
1394 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1395 // This is for constructs like
1396 // <complexType name="ListOfString" base="soap:Array">
1398 // <element name="string" type="xsd:string"
1399 // minOccurs="0" maxOccurs="unbounded" />
1402 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1403 $this->xdebug('complexType is unusual array');
1404 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1406 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1411 array_push($this->elementStack, $this->currentElement);
1412 if (!isset($attrs['form'])) {
1413 $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1415 if(isset($attrs['type'])){
1416 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1417 if (! $this->getPrefix($attrs['type'])) {
1418 if ($this->defaultNamespace[$pos]) {
1419 $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1420 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1423 // This is for constructs like
1424 // <complexType name="ListOfString" base="soap:Array">
1426 // <element name="string" type="xsd:string"
1427 // minOccurs="0" maxOccurs="unbounded" />
1430 if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1431 $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1432 $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1434 $this->currentElement = $attrs['name'];
1435 $ename = $attrs['name'];
1436 } elseif(isset($attrs['ref'])){
1437 $this->xdebug("processing element as ref to ".$attrs['ref']);
1438 $this->currentElement = "ref to ".$attrs['ref'];
1439 $ename = $this->getLocalPart($attrs['ref']);
1441 $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1442 $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1443 $this->currentElement = $attrs['name'];
1444 $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1445 $ename = $attrs['name'];
1447 if (isset($ename) && $this->currentComplexType) {
1448 $this->xdebug("add element $ename to complexType $this->currentComplexType");
1449 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1450 } elseif (!isset($attrs['ref'])) {
1451 $this->xdebug("add element $ename to elements array");
1452 $this->elements[ $attrs['name'] ] = $attrs;
1453 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1456 case 'enumeration': // restriction value list member
1457 $this->xdebug('enumeration ' . $attrs['value']);
1458 if ($this->currentSimpleType) {
1459 $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1460 } elseif ($this->currentComplexType) {
1461 $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1464 case 'extension': // simpleContent or complexContent type extension
1465 $this->xdebug('extension ' . $attrs['base']);
1466 if ($this->currentComplexType) {
1467 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1471 if (isset($attrs['schemaLocation'])) {
1472 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1473 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1475 //$this->xdebug('import namespace ' . $attrs['namespace']);
1476 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1477 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1478 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1482 case 'list': // simpleType value list
1484 case 'restriction': // simpleType, simpleContent or complexContent value restriction
1485 $this->xdebug('restriction ' . $attrs['base']);
1486 if($this->currentSimpleType){
1487 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1488 } elseif($this->currentComplexType){
1489 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1490 if(strstr($attrs['base'],':') == ':Array'){
1491 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1496 $this->schemaInfo = $attrs;
1497 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1498 if (isset($attrs['targetNamespace'])) {
1499 $this->schemaTargetNamespace = $attrs['targetNamespace'];
1501 if (!isset($attrs['elementFormDefault'])) {
1502 $this->schemaInfo['elementFormDefault'] = 'unqualified';
1504 if (!isset($attrs['attributeFormDefault'])) {
1505 $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1508 case 'simpleContent': // (optional) content for a complexType
1511 array_push($this->simpleTypeStack, $this->currentSimpleType);
1512 if(isset($attrs['name'])){
1513 $this->xdebug("processing simpleType for name " . $attrs['name']);
1514 $this->currentSimpleType = $attrs['name'];
1515 $this->simpleTypes[ $attrs['name'] ] = $attrs;
1516 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1517 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1519 $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1520 $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1521 $this->currentSimpleType = $name;
1522 //$this->currentElement = false;
1523 $this->simpleTypes[$this->currentSimpleType] = $attrs;
1524 $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1527 case 'union': // simpleType type list
1530 //$this->xdebug("do not have anything to do for element $name");
1535 * end-element handler
1537 * @param string $parser XML parser object
1538 * @param string $name element name
1541 function schemaEndElement($parser, $name) {
1542 // bring depth down a notch
1544 // position of current element is equal to the last value left in depth_array for my depth
1545 if(isset($this->depth_array[$this->depth])){
1546 $pos = $this->depth_array[$this->depth];
1548 // get element prefix
1549 if ($prefix = $this->getPrefix($name)){
1550 // get unqualified name
1551 $name = $this->getLocalPart($name);
1556 if($name == 'complexType'){
1557 $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1558 $this->currentComplexType = array_pop($this->complexTypeStack);
1559 //$this->currentElement = false;
1561 if($name == 'element'){
1562 $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1563 $this->currentElement = array_pop($this->elementStack);
1565 if($name == 'simpleType'){
1566 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1567 $this->currentSimpleType = array_pop($this->simpleTypeStack);
1572 * element content handler
1574 * @param string $parser XML parser object
1575 * @param string $data element content
1578 function schemaCharacterData($parser, $data){
1579 $pos = $this->depth_array[$this->depth - 1];
1580 $this->message[$pos]['cdata'] .= $data;
1584 * serialize the schema
1588 function serializeSchema(){
1590 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1593 if (sizeof($this->imports) > 0) {
1594 foreach($this->imports as $ns => $list) {
1595 foreach ($list as $ii) {
1596 if ($ii['location'] != '') {
1597 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1599 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1605 foreach($this->complexTypes as $typeName => $attrs){
1607 // serialize child elements
1608 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1609 foreach($attrs['elements'] as $element => $eParts){
1610 if(isset($eParts['ref'])){
1611 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1613 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1614 foreach ($eParts as $aName => $aValue) {
1615 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1616 if ($aName != 'name' && $aName != 'type') {
1617 $contentStr .= " $aName=\"$aValue\"";
1620 $contentStr .= "/>\n";
1623 // compositor wraps elements
1624 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1625 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1629 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1630 foreach($attrs['attrs'] as $attr => $aParts){
1631 $contentStr .= " <$schemaPrefix:attribute";
1632 foreach ($aParts as $a => $v) {
1633 if ($a == 'ref' || $a == 'type') {
1634 $contentStr .= " $a=\"".$this->contractQName($v).'"';
1635 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1636 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1637 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1639 $contentStr .= " $a=\"$v\"";
1642 $contentStr .= "/>\n";
1646 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1647 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1648 // complex or simple content
1649 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1650 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1653 // finalize complex type
1654 if($contentStr != ''){
1655 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1657 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1659 $xml .= $contentStr;
1662 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1663 foreach($this->simpleTypes as $typeName => $eParts){
1664 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1665 if (isset($eParts['enumeration'])) {
1666 foreach ($eParts['enumeration'] as $e) {
1667 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
1670 $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1674 if(isset($this->elements) && count($this->elements) > 0){
1675 foreach($this->elements as $element => $eParts){
1676 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1680 if(isset($this->attributes) && count($this->attributes) > 0){
1681 foreach($this->attributes as $attr => $aParts){
1682 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1687 foreach ($this->schemaInfo as $k => $v) {
1688 if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1689 $attr .= " $k=\"$v\"";
1692 $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1693 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1694 $el .= " xmlns:$nsp=\"$ns\"";
1696 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1701 * adds debug data to the clas level debug string
1703 * @param string $string debug data
1706 function xdebug($string){
1707 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1711 * get the PHP type of a user defined type in the schema
1712 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1713 * returns false if no type exists, or not w/ the given namespace
1714 * else returns a string that is either a native php type, or 'struct'
1716 * @param string $type name of defined type
1717 * @param string $ns namespace of type
1722 function getPHPType($type,$ns){
1723 if(isset($this->typemap[$ns][$type])){
1724 //print "found type '$type' and ns $ns in typemap<br>";
1725 return $this->typemap[$ns][$type];
1726 } elseif(isset($this->complexTypes[$type])){
1727 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1728 return $this->complexTypes[$type]['phpType'];
1734 * returns an associative array of information about a given type
1735 * returns false if no type exists by the given name
1737 * For a complexType typeDef = array(
1738 * 'restrictionBase' => '',
1740 * 'compositor' => '(sequence|all)',
1741 * 'elements' => array(), // refs to elements array
1742 * 'attrs' => array() // refs to attributes array
1743 * ... and so on (see addComplexType)
1746 * For simpleType or element, the array has different keys.
1748 * @param string $type
1751 * @see addComplexType
1752 * @see addSimpleType
1755 function getTypeDef($type){
1756 //$this->debug("in getTypeDef for type $type");
1757 if (substr($type, -1) == '^') {
1759 $type = substr($type, 0, -1);
1764 if((! $is_element) && isset($this->complexTypes[$type])){
1765 $this->xdebug("in getTypeDef, found complexType $type");
1766 return $this->complexTypes[$type];
1767 } elseif((! $is_element) && isset($this->simpleTypes[$type])){
1768 $this->xdebug("in getTypeDef, found simpleType $type");
1769 if (!isset($this->simpleTypes[$type]['phpType'])) {
1770 // get info for type to tack onto the simple type
1771 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1772 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1773 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1774 $etype = $this->getTypeDef($uqType);
1776 $this->xdebug("in getTypeDef, found type for simpleType $type:");
1777 $this->xdebug($this->varDump($etype));
1778 if (isset($etype['phpType'])) {
1779 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1781 if (isset($etype['elements'])) {
1782 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1786 return $this->simpleTypes[$type];
1787 } elseif(isset($this->elements[$type])){
1788 $this->xdebug("in getTypeDef, found element $type");
1789 if (!isset($this->elements[$type]['phpType'])) {
1790 // get info for type to tack onto the element
1791 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1792 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1793 $etype = $this->getTypeDef($uqType);
1795 $this->xdebug("in getTypeDef, found type for element $type:");
1796 $this->xdebug($this->varDump($etype));
1797 if (isset($etype['phpType'])) {
1798 $this->elements[$type]['phpType'] = $etype['phpType'];
1800 if (isset($etype['elements'])) {
1801 $this->elements[$type]['elements'] = $etype['elements'];
1803 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1804 $this->xdebug("in getTypeDef, element $type is an XSD type");
1805 $this->elements[$type]['phpType'] = 'scalar';
1808 return $this->elements[$type];
1809 } elseif(isset($this->attributes[$type])){
1810 $this->xdebug("in getTypeDef, found attribute $type");
1811 return $this->attributes[$type];
1812 } elseif (ereg('_ContainedType$', $type)) {
1813 $this->xdebug("in getTypeDef, have an untyped element $type");
1814 $typeDef['typeClass'] = 'simpleType';
1815 $typeDef['phpType'] = 'scalar';
1816 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1819 $this->xdebug("in getTypeDef, did not find $type");
1824 * returns a sample serialization of a given type, or false if no type by the given name
1826 * @param string $type name of type
1831 function serializeTypeDef($type){
1832 //print "in sTD() for type $type<br>";
1833 if($typeDef = $this->getTypeDef($type)){
1835 if(is_array($typeDef['attrs'])){
1836 foreach($typeDef['attrs'] as $attName => $data){
1837 $str .= " $attName=\"{type = ".$data['type']."}\"";
1840 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1841 if(count($typeDef['elements']) > 0){
1843 foreach($typeDef['elements'] as $element => $eData){
1844 $str .= $this->serializeTypeDef($element);
1847 } elseif($typeDef['typeClass'] == 'element') {
1848 $str .= "></$type>";
1858 * returns HTML form elements that allow a user
1859 * to enter values for creating an instance of the given type.
1861 * @param string $name name for type instance
1862 * @param string $type name of type
1867 function typeToForm($name,$type){
1869 if($typeDef = $this->getTypeDef($type)){
1871 if($typeDef['phpType'] == 'struct'){
1872 $buffer .= '<table>';
1873 foreach($typeDef['elements'] as $child => $childDef){
1875 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1876 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1878 $buffer .= '</table>';
1880 } elseif($typeDef['phpType'] == 'array'){
1881 $buffer .= '<table>';
1882 for($i=0;$i < 3; $i++){
1884 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1885 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1887 $buffer .= '</table>';
1890 $buffer .= "<input type='text' name='parameters[$name]'>";
1893 $buffer .= "<input type='text' name='parameters[$name]'>";
1899 * adds a complex type to the schema
1909 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1913 * example: PHP associative array ( SOAP Struct )
1920 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1924 * @param typeClass (complexType|simpleType|attribute)
1925 * @param phpType: currently supported are array and struct (php assoc array)
1926 * @param compositor (all|sequence|choice)
1927 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1928 * @param elements = array ( name = array(name=>'',type=>'') )
1929 * @param attrs = array(
1931 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1932 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1935 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1939 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1940 $this->complexTypes[$name] = array(
1942 'typeClass' => $typeClass,
1943 'phpType' => $phpType,
1944 'compositor'=> $compositor,
1945 'restrictionBase' => $restrictionBase,
1946 'elements' => $elements,
1948 'arrayType' => $arrayType
1951 $this->xdebug("addComplexType $name:");
1952 $this->appendDebug($this->varDump($this->complexTypes[$name]));
1956 * adds a simple type to the schema
1958 * @param string $name
1959 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1960 * @param string $typeClass (should always be simpleType)
1961 * @param string $phpType (should always be scalar)
1962 * @param array $enumeration array of values
1964 * @see nusoap_xmlschema
1967 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1968 $this->simpleTypes[$name] = array(
1970 'typeClass' => $typeClass,
1971 'phpType' => $phpType,
1972 'type' => $restrictionBase,
1973 'enumeration' => $enumeration
1976 $this->xdebug("addSimpleType $name:");
1977 $this->appendDebug($this->varDump($this->simpleTypes[$name]));
1981 * adds an element to the schema
1983 * @param array $attrs attributes that must include name and type
1984 * @see nusoap_xmlschema
1987 function addElement($attrs) {
1988 if (! $this->getPrefix($attrs['type'])) {
1989 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1991 $this->elements[ $attrs['name'] ] = $attrs;
1992 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1994 $this->xdebug("addElement " . $attrs['name']);
1995 $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
2000 * Backward compatibility
2002 class XMLSchema extends nusoap_xmlschema {
2010 * For creating serializable abstractions of native PHP types. This class
2011 * allows element name/namespace, XSD type, and XML attributes to be
2012 * associated with a value. This is extremely useful when WSDL is not
2013 * used, but is also useful when WSDL is used with polymorphic types, including
2014 * xsd:anyType and user-defined types.
2016 * @author Dietrich Ayala <dietrich@ganx4.com>
2017 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2020 class soapval extends nusoap_base {
2022 * The XML element name
2029 * The XML type name (string or false)
2043 * The XML element namespace (string or false)
2050 * The XML type namespace (string or false)
2057 * The XML element attributes (array or false)
2067 * @param string $name optional name
2068 * @param mixed $type optional type name
2069 * @param mixed $value optional value
2070 * @param mixed $element_ns optional namespace of value
2071 * @param mixed $type_ns optional namespace of type
2072 * @param mixed $attributes associative array of attributes to add to element serialization
2075 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2076 parent::nusoap_base();
2077 $this->name = $name;
2078 $this->type = $type;
2079 $this->value = $value;
2080 $this->element_ns = $element_ns;
2081 $this->type_ns = $type_ns;
2082 $this->attributes = $attributes;
2086 * return serialized value
2088 * @param string $use The WSDL use value (encoded|literal)
2089 * @return string XML data
2092 function serialize($use='encoded') {
2093 return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2097 * decodes a soapval object into a PHP native type
2103 return $this->value;
2114 * transport class for sending/receiving data via HTTP and HTTPS
2115 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2117 * @author Dietrich Ayala <dietrich@ganx4.com>
2118 * @author Scott Nichol <snichol@users.sourceforge.net>
2119 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2122 class soap_transport_http extends nusoap_base {
2126 var $digest_uri = '';
2131 var $request_method = 'POST';
2132 var $protocol_version = '1.0';
2134 var $outgoing_headers = array();
2135 var $incoming_headers = array();
2136 var $incoming_cookies = array();
2137 var $outgoing_payload = '';
2138 var $incoming_payload = '';
2139 var $response_status_line; // HTTP response status line
2140 var $useSOAPAction = true;
2141 var $persistentConnection = false;
2142 var $ch = false; // cURL handle
2143 var $ch_options = array(); // cURL custom options
2144 var $use_curl = false; // force cURL use
2145 var $proxy = null; // proxy information (associative array)
2149 var $digestRequest = array();
2150 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2151 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2152 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2153 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2154 // passphrase: SSL key password/passphrase
2155 // certpassword: SSL certificate password
2156 // verifypeer: default is 1
2157 // verifyhost: default is 1
2162 * @param string $url The URL to which to connect
2163 * @param array $curl_options User-specified cURL options
2164 * @param boolean $use_curl Whether to try to force cURL use
2167 function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2168 parent::nusoap_base();
2169 $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2170 $this->appendDebug($this->varDump($curl_options));
2171 $this->setURL($url);
2172 if (is_array($curl_options)) {
2173 $this->ch_options = $curl_options;
2175 $this->use_curl = $use_curl;
2176 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2177 $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2181 * sets a cURL option
2183 * @param mixed $option The cURL option (always integer?)
2184 * @param mixed $value The cURL option value
2187 function setCurlOption($option, $value) {
2188 $this->debug("setCurlOption option=$option, value=");
2189 $this->appendDebug($this->varDump($value));
2190 curl_setopt($this->ch, $option, $value);
2194 * sets an HTTP header
2196 * @param string $name The name of the header
2197 * @param string $value The value of the header
2200 function setHeader($name, $value) {
2201 $this->outgoing_headers[$name] = $value;
2202 $this->debug("set header $name: $value");
2206 * unsets an HTTP header
2208 * @param string $name The name of the header
2211 function unsetHeader($name) {
2212 if (isset($this->outgoing_headers[$name])) {
2213 $this->debug("unset header $name");
2214 unset($this->outgoing_headers[$name]);
2219 * sets the URL to which to connect
2221 * @param string $url The URL to which to connect
2224 function setURL($url) {
2227 $u = parse_url($url);
2228 foreach($u as $k => $v){
2229 $this->debug("parsed URL $k = $v");
2233 // add any GET params to path
2234 if(isset($u['query']) && $u['query'] != ''){
2235 $this->path .= '?' . $u['query'];
2239 if(!isset($u['port'])){
2240 if($u['scheme'] == 'https'){
2247 $this->uri = $this->path;
2248 $this->digest_uri = $this->uri;
2251 if (!isset($u['port'])) {
2252 $this->setHeader('Host', $this->host);
2254 $this->setHeader('Host', $this->host.':'.$this->port);
2257 if (isset($u['user']) && $u['user'] != '') {
2258 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2263 * gets the I/O method to use
2265 * @return string I/O method to use (socket|curl|unknown)
2268 function io_method() {
2269 if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2271 if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2277 * establish an HTTP connection
2279 * @param integer $timeout set connection timeout in seconds
2280 * @param integer $response_timeout set response timeout in seconds
2281 * @return boolean true if connected, false if not
2284 function connect($connection_timeout=0,$response_timeout=30){
2285 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2286 // "regular" socket.
2287 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2288 // loaded), and until PHP5 stream_get_wrappers is not available.
2289 // if ($this->scheme == 'https') {
2290 // if (version_compare(phpversion(), '4.3.0') >= 0) {
2291 // if (extension_loaded('openssl')) {
2292 // $this->scheme = 'ssl';
2293 // $this->debug('Using SSL over OpenSSL');
2297 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2298 if ($this->io_method() == 'socket') {
2299 if (!is_array($this->proxy)) {
2300 $host = $this->host;
2301 $port = $this->port;
2303 $host = $this->proxy['host'];
2304 $port = $this->proxy['port'];
2307 // use persistent connection
2308 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2309 if (!feof($this->fp)) {
2310 $this->debug('Re-use persistent connection');
2314 $this->debug('Closed persistent connection at EOF');
2317 // munge host if using OpenSSL
2318 if ($this->scheme == 'ssl') {
2319 $host = 'ssl://' . $host;
2321 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2324 if($connection_timeout > 0){
2325 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2327 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2332 $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2334 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2336 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
2339 $this->setError($msg);
2343 // set response timeout
2344 $this->debug('set response timeout to ' . $response_timeout);
2345 socket_set_timeout( $this->fp, $response_timeout);
2347 $this->debug('socket connected');
2349 } else if ($this->io_method() == 'curl') {
2350 if (!extension_loaded('curl')) {
2351 // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2352 $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to included cURL.');
2355 // Avoid warnings when PHP does not have these options
2356 if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2357 $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2359 $CURLOPT_CONNECTIONTIMEOUT = 78;
2360 if (defined('CURLOPT_HTTPAUTH'))
2361 $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2363 $CURLOPT_HTTPAUTH = 107;
2364 if (defined('CURLOPT_PROXYAUTH'))
2365 $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2367 $CURLOPT_PROXYAUTH = 111;
2368 if (defined('CURLAUTH_BASIC'))
2369 $CURLAUTH_BASIC = CURLAUTH_BASIC;
2371 $CURLAUTH_BASIC = 1;
2372 if (defined('CURLAUTH_DIGEST'))
2373 $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2375 $CURLAUTH_DIGEST = 2;
2376 if (defined('CURLAUTH_NTLM'))
2377 $CURLAUTH_NTLM = CURLAUTH_NTLM;
2381 $this->debug('connect using cURL');
2383 $this->ch = curl_init();
2385 $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2387 $hostURL .= $this->path;
2388 $this->setCurlOption(CURLOPT_URL, $hostURL);
2389 // follow location headers (re-directs)
2390 if (ini_get('safe_mode') || ini_get('open_basedir')) {
2391 $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2392 $this->debug('safe_mode = ');
2393 $this->appendDebug($this->varDump(ini_get('safe_mode')));
2394 $this->debug('open_basedir = ');
2395 $this->appendDebug($this->varDump(ini_get('open_basedir')));
2397 $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2399 // ask for headers in the response output
2400 $this->setCurlOption(CURLOPT_HEADER, 1);
2401 // ask for the response output as the return value
2402 $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2404 // We manage this ourselves through headers and encoding
2405 // if(function_exists('gzuncompress')){
2406 // $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2408 // persistent connection
2409 if ($this->persistentConnection) {
2410 // I believe the following comment is now bogus, having applied to
2411 // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2412 // The way we send data, we cannot use persistent connections, since
2413 // there will be some "junk" at the end of our request.
2414 //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2415 $this->persistentConnection = false;
2416 $this->setHeader('Connection', 'close');
2419 if ($connection_timeout != 0) {
2420 $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2422 if ($response_timeout != 0) {
2423 $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2426 if ($this->scheme == 'https') {
2427 $this->debug('set cURL SSL verify options');
2428 // recent versions of cURL turn on peer/host checking by default,
2429 // while PHP binaries are not compiled with a default location for the
2430 // CA cert bundle, so disable peer/host checking.
2431 //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2432 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2433 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2435 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2436 if ($this->authtype == 'certificate') {
2437 $this->debug('set cURL certificate options');
2438 if (isset($this->certRequest['cainfofile'])) {
2439 $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2441 if (isset($this->certRequest['verifypeer'])) {
2442 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2444 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2446 if (isset($this->certRequest['verifyhost'])) {
2447 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2449 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2451 if (isset($this->certRequest['sslcertfile'])) {
2452 $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2454 if (isset($this->certRequest['sslkeyfile'])) {
2455 $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2457 if (isset($this->certRequest['passphrase'])) {
2458 $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2460 if (isset($this->certRequest['certpassword'])) {
2461 $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2465 if ($this->authtype && ($this->authtype != 'certificate')) {
2466 if ($this->username) {
2467 $this->debug('set cURL username/password');
2468 $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2470 if ($this->authtype == 'basic') {
2471 $this->debug('set cURL for Basic authentication');
2472 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2474 if ($this->authtype == 'digest') {
2475 $this->debug('set cURL for digest authentication');
2476 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2478 if ($this->authtype == 'ntlm') {
2479 $this->debug('set cURL for NTLM authentication');
2480 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2483 if (is_array($this->proxy)) {
2484 $this->debug('set cURL proxy options');
2485 if ($this->proxy['port'] != '') {
2486 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2488 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2490 if ($this->proxy['username'] || $this->proxy['password']) {
2491 $this->debug('set cURL proxy authentication options');
2492 $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2493 if ($this->proxy['authtype'] == 'basic') {
2494 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2496 if ($this->proxy['authtype'] == 'ntlm') {
2497 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2501 $this->debug('cURL connection set up');
2504 $this->setError('Unknown scheme ' . $this->scheme);
2505 $this->debug('Unknown scheme ' . $this->scheme);
2511 * sends the SOAP request and gets the SOAP response via HTTP[S]
2513 * @param string $data message data
2514 * @param integer $timeout set connection timeout in seconds
2515 * @param integer $response_timeout set response timeout in seconds
2516 * @param array $cookies cookies to send
2517 * @return string data
2520 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2522 $this->debug('entered send() with data of length: '.strlen($data));
2524 $this->tryagain = true;
2526 while ($this->tryagain) {
2527 $this->tryagain = false;
2530 if (!$this->connect($timeout, $response_timeout)){
2535 if (!$this->sendRequest($data, $cookies)){
2540 $respdata = $this->getResponse();
2542 $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2545 $this->debug('end of send()');
2551 * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2553 * @param string $data message data
2554 * @param integer $timeout set connection timeout in seconds
2555 * @param integer $response_timeout set response timeout in seconds
2556 * @param array $cookies cookies to send
2557 * @return string data
2561 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2562 return $this->send($data, $timeout, $response_timeout, $cookies);
2566 * if authenticating, set user credentials here
2568 * @param string $username
2569 * @param string $password
2570 * @param string $authtype (basic|digest|certificate|ntlm)
2571 * @param array $digestRequest (keys must be nonce, nc, realm, qop)
2572 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2575 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2576 $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2577 $this->appendDebug($this->varDump($digestRequest));
2578 $this->debug("certRequest=");
2579 $this->appendDebug($this->varDump($certRequest));
2581 if ($authtype == 'basic') {
2582 $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2583 } elseif ($authtype == 'digest') {
2584 if (isset($digestRequest['nonce'])) {
2585 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2587 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2589 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2590 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2595 // A2 = Method ":" digest-uri-value
2596 $A2 = $this->request_method . ':' . $this->digest_uri;
2601 // KD(secret, data) = H(concat(secret, ":", data))
2603 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
2605 // ":" unq(cnonce-value)
2606 // ":" unq(qop-value)
2609 // if qop is missing,
2610 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2612 $unhashedDigest = '';
2613 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2615 if ($digestRequest['qop'] != '') {
2616 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2618 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2621 $hashedDigest = md5($unhashedDigest);
2624 if (isset($digestRequest['opaque'])) {
2625 $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2628 $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 . '"');
2630 } elseif ($authtype == 'certificate') {
2631 $this->certRequest = $certRequest;
2632 $this->debug('Authorization header not set for certificate');
2633 } elseif ($authtype == 'ntlm') {
2635 $this->debug('Authorization header not set for ntlm');
2637 $this->username = $username;
2638 $this->password = $password;
2639 $this->authtype = $authtype;
2640 $this->digestRequest = $digestRequest;
2644 * set the soapaction value
2646 * @param string $soapaction
2649 function setSOAPAction($soapaction) {
2650 $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2656 * @param string $enc encoding style. supported values: gzip, deflate, or both
2659 function setEncoding($enc='gzip, deflate') {
2660 if (function_exists('gzdeflate')) {
2661 $this->protocol_version = '1.1';
2662 $this->setHeader('Accept-Encoding', $enc);
2663 if (!isset($this->outgoing_headers['Connection'])) {
2664 $this->setHeader('Connection', 'close');
2665 $this->persistentConnection = false;
2667 set_magic_quotes_runtime(0);
2669 $this->encoding = $enc;
2674 * set proxy info here
2676 * @param string $proxyhost use an empty string to remove proxy
2677 * @param string $proxyport
2678 * @param string $proxyusername
2679 * @param string $proxypassword
2680 * @param string $proxyauthtype (basic|ntlm)
2683 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2685 $this->proxy = array(
2686 'host' => $proxyhost,
2687 'port' => $proxyport,
2688 'username' => $proxyusername,
2689 'password' => $proxypassword,
2690 'authtype' => $proxyauthtype
2692 if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2693 $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2696 $this->debug('remove proxy');
2698 unsetHeader('Proxy-Authorization');
2704 * Test if the given string starts with a header that is to be skipped.
2705 * Skippable headers result from chunked transfer and proxy requests.
2707 * @param string $data The string to check.
2708 * @returns boolean Whether a skippable header was found.
2711 function isSkippableCurlHeader(&$data) {
2712 $skipHeaders = array( 'HTTP/1.1 100',
2719 'HTTP/1.0 200 Connection established');
2720 foreach ($skipHeaders as $hd) {
2721 $prefix = substr($data, 0, strlen($hd));
2722 if ($prefix == $hd) return true;
2729 * decode a string that is encoded w/ "chunked' transfer encoding
2730 * as defined in RFC2068 19.4.6
2732 * @param string $buffer
2738 function decodeChunked($buffer, $lb){
2743 // read chunk-size, chunk-extension (if any) and CRLF
2744 // get the position of the linebreak
2745 $chunkend = strpos($buffer, $lb);
2746 if ($chunkend == FALSE) {
2747 $this->debug('no linebreak found in decodeChunked');
2750 $temp = substr($buffer,0,$chunkend);
2751 $chunk_size = hexdec( trim($temp) );
2752 $chunkstart = $chunkend + strlen($lb);
2753 // while (chunk-size > 0) {
2754 while ($chunk_size > 0) {
2755 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2756 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2758 // Just in case we got a broken connection
2759 if ($chunkend == FALSE) {
2760 $chunk = substr($buffer,$chunkstart);
2761 // append chunk-data to entity-body
2763 $length += strlen($chunk);
2767 // read chunk-data and CRLF
2768 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2769 // append chunk-data to entity-body
2771 // length := length + chunk-size
2772 $length += strlen($chunk);
2773 // read chunk-size and CRLF
2774 $chunkstart = $chunkend + strlen($lb);
2776 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2777 if ($chunkend == FALSE) {
2778 break; //Just in case we got a broken connection
2780 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2781 $chunk_size = hexdec( trim($temp) );
2782 $chunkstart = $chunkend;
2788 * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2790 * @param string $data HTTP body
2791 * @param string $cookie_str data for HTTP Cookie header
2795 function buildPayload($data, $cookie_str = '') {
2796 // Note: for cURL connections, $this->outgoing_payload is ignored,
2797 // as is the Content-Length header, but these are still created as
2798 // debugging guides.
2800 // add content-length header
2801 $this->setHeader('Content-Length', strlen($data));
2803 // start building outgoing payload:
2809 $req = "$this->request_method $uri HTTP/$this->protocol_version";
2810 $this->debug("HTTP request: $req");
2811 $this->outgoing_payload = "$req\r\n";
2813 // loop thru headers, serializing
2814 foreach($this->outgoing_headers as $k => $v){
2816 $this->debug("HTTP header: $hdr");
2817 $this->outgoing_payload .= "$hdr\r\n";
2821 if ($cookie_str != '') {
2822 $hdr = 'Cookie: '.$cookie_str;
2823 $this->debug("HTTP header: $hdr");
2824 $this->outgoing_payload .= "$hdr\r\n";
2827 // header/body separator
2828 $this->outgoing_payload .= "\r\n";
2831 $this->outgoing_payload .= $data;
2835 * sends the SOAP request via HTTP[S]
2837 * @param string $data message data
2838 * @param array $cookies cookies to send
2839 * @return boolean true if OK, false if problem
2842 function sendRequest($data, $cookies = NULL) {
2843 // build cookie string
2844 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2847 $this->buildPayload($data, $cookie_str);
2849 if ($this->io_method() == 'socket') {
2851 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2852 $this->setError('couldn\'t write message data to socket');
2853 $this->debug('couldn\'t write message data to socket');
2856 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2858 } else if ($this->io_method() == 'curl') {
2860 // cURL does say this should only be the verb, and in fact it
2861 // turns out that the URI and HTTP version are appended to this, which
2862 // some servers refuse to work with (so we no longer use this method!)
2863 //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2864 $curl_headers = array();
2865 foreach($this->outgoing_headers as $k => $v){
2866 if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2867 $this->debug("Skip cURL header $k: $v");
2869 $curl_headers[] = "$k: $v";
2872 if ($cookie_str != '') {
2873 $curl_headers[] = 'Cookie: ' . $cookie_str;
2875 $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2876 $this->debug('set cURL HTTP headers');
2877 if ($this->request_method == "POST") {
2878 $this->setCurlOption(CURLOPT_POST, 1);
2879 $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2880 $this->debug('set cURL POST data');
2883 // insert custom user-set cURL options
2884 foreach ($this->ch_options as $key => $val) {
2885 $this->setCurlOption($key, $val);
2888 $this->debug('set cURL payload');
2894 * gets the SOAP response via HTTP[S]
2896 * @return string the response (also sets member variables like incoming_payload)
2899 function getResponse(){
2900 $this->incoming_payload = '';
2902 if ($this->io_method() == 'socket') {
2903 // loop until headers have been retrieved
2905 while (!isset($lb)){
2907 // We might EOF during header read.
2908 if(feof($this->fp)) {
2909 $this->incoming_payload = $data;
2910 $this->debug('found no headers before EOF after length ' . strlen($data));
2911 $this->debug("received before EOF:\n" . $data);
2912 $this->setError('server failed to send headers');
2916 $tmp = fgets($this->fp, 256);
2917 $tmplen = strlen($tmp);
2918 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2921 $this->incoming_payload = $data;
2922 $this->debug('socket read of headers timed out after length ' . strlen($data));
2923 $this->debug("read before timeout: " . $data);
2924 $this->setError('socket read of headers timed out');
2929 $pos = strpos($data,"\r\n\r\n");
2933 $pos = strpos($data,"\n\n");
2938 // remove 100 headers
2939 if (isset($lb) && ereg('^HTTP/1.1 100',$data)) {
2944 // store header data
2945 $this->incoming_payload .= $data;
2946 $this->debug('found end of headers after length ' . strlen($data));
2948 $header_data = trim(substr($data,0,$pos));
2949 $header_array = explode($lb,$header_data);
2950 $this->incoming_headers = array();
2951 $this->incoming_cookies = array();
2952 foreach($header_array as $header_line){
2953 $arr = explode(':',$header_line, 2);
2954 if(count($arr) > 1){
2955 $header_name = strtolower(trim($arr[0]));
2956 $this->incoming_headers[$header_name] = trim($arr[1]);
2957 if ($header_name == 'set-cookie') {
2958 // TODO: allow multiple cookies from parseCookie
2959 $cookie = $this->parseCookie(trim($arr[1]));
2961 $this->incoming_cookies[] = $cookie;
2962 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2964 $this->debug('did not find cookie in ' . trim($arr[1]));
2967 } else if (isset($header_name)) {
2968 // append continuation line to previous header
2969 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2973 // loop until msg has been received
2974 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2975 $content_length = 2147483647; // ignore any content-length header
2977 $this->debug("want to read chunked content");
2978 } elseif (isset($this->incoming_headers['content-length'])) {
2979 $content_length = $this->incoming_headers['content-length'];
2981 $this->debug("want to read content of length $content_length");
2983 $content_length = 2147483647;
2985 $this->debug("want to read content to EOF");
2990 $tmp = fgets($this->fp, 256);
2991 $tmplen = strlen($tmp);
2992 $this->debug("read chunk line of $tmplen bytes");
2994 $this->incoming_payload = $data;
2995 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2996 $this->debug("read before timeout:\n" . $data);
2997 $this->setError('socket read of chunk length timed out');
3000 $content_length = hexdec(trim($tmp));
3001 $this->debug("chunk length $content_length");
3004 while (($strlen < $content_length) && (!feof($this->fp))) {
3005 $readlen = min(8192, $content_length - $strlen);
3006 $tmp = fread($this->fp, $readlen);
3007 $tmplen = strlen($tmp);
3008 $this->debug("read buffer of $tmplen bytes");
3009 if (($tmplen == 0) && (!feof($this->fp))) {
3010 $this->incoming_payload = $data;
3011 $this->debug('socket read of body timed out after length ' . strlen($data));
3012 $this->debug("read before timeout:\n" . $data);
3013 $this->setError('socket read of body timed out');
3019 if ($chunked && ($content_length > 0)) {
3020 $tmp = fgets($this->fp, 256);
3021 $tmplen = strlen($tmp);
3022 $this->debug("read chunk terminator of $tmplen bytes");
3024 $this->incoming_payload = $data;
3025 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3026 $this->debug("read before timeout:\n" . $data);
3027 $this->setError('socket read of chunk terminator timed out');
3031 } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3032 if (feof($this->fp)) {
3033 $this->debug('read to EOF');
3035 $this->debug('read body of length ' . strlen($data));
3036 $this->incoming_payload .= $data;
3037 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3039 // close filepointer
3041 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3042 (! $this->persistentConnection) || feof($this->fp)){
3045 $this->debug('closed socket');
3048 // connection was closed unexpectedly
3049 if($this->incoming_payload == ''){
3050 $this->setError('no response from server');
3054 // decode transfer-encoding
3055 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3056 // if(!$data = $this->decodeChunked($data, $lb)){
3057 // $this->setError('Decoding of chunked data failed');
3060 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3061 // set decoded payload
3062 // $this->incoming_payload = $header_data.$lb.$lb.$data;
3065 } else if ($this->io_method() == 'curl') {
3067 $this->debug('send and receive with cURL');
3068 $this->incoming_payload = curl_exec($this->ch);
3069 $data = $this->incoming_payload;
3071 $cErr = curl_error($this->ch);
3073 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3074 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3075 foreach(curl_getinfo($this->ch) as $k => $v){
3076 $err .= "$k: $v<br>";
3079 $this->setError($err);
3080 curl_close($this->ch);
3084 //var_dump(curl_getinfo($this->ch));
3088 $this->debug('No cURL error, closing cURL');
3089 curl_close($this->ch);
3091 // try removing skippable headers
3093 while ($this->isSkippableCurlHeader($data)) {
3094 $this->debug("Found HTTP header to skip");
3095 if ($pos = strpos($data,"\r\n\r\n")) {
3096 $data = ltrim(substr($data,$pos));
3097 } elseif($pos = strpos($data,"\n\n") ) {
3098 $data = ltrim(substr($data,$pos));
3103 // have nothing left; just remove 100 header(s)
3105 while (ereg('^HTTP/1.1 100',$data)) {
3106 if ($pos = strpos($data,"\r\n\r\n")) {
3107 $data = ltrim(substr($data,$pos));
3108 } elseif($pos = strpos($data,"\n\n") ) {
3109 $data = ltrim(substr($data,$pos));
3114 // separate content from HTTP headers
3115 if ($pos = strpos($data,"\r\n\r\n")) {
3117 } elseif( $pos = strpos($data,"\n\n")) {
3120 $this->debug('no proper separation of headers and document');
3121 $this->setError('no proper separation of headers and document');
3124 $header_data = trim(substr($data,0,$pos));
3125 $header_array = explode($lb,$header_data);
3126 $data = ltrim(substr($data,$pos));
3127 $this->debug('found proper separation of headers and document');
3128 $this->debug('cleaned data, stringlen: '.strlen($data));
3130 foreach ($header_array as $header_line) {
3131 $arr = explode(':',$header_line,2);
3132 if(count($arr) > 1){
3133 $header_name = strtolower(trim($arr[0]));
3134 $this->incoming_headers[$header_name] = trim($arr[1]);
3135 if ($header_name == 'set-cookie') {
3136 // TODO: allow multiple cookies from parseCookie
3137 $cookie = $this->parseCookie(trim($arr[1]));
3139 $this->incoming_cookies[] = $cookie;
3140 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3142 $this->debug('did not find cookie in ' . trim($arr[1]));
3145 } else if (isset($header_name)) {
3146 // append continuation line to previous header
3147 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3152 $this->response_status_line = $header_array[0];
3153 $arr = explode(' ', $this->response_status_line, 3);
3154 $http_version = $arr[0];
3155 $http_status = intval($arr[1]);
3156 $http_reason = count($arr) > 2 ? $arr[2] : '';
3158 // see if we need to resend the request with http digest authentication
3159 if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3160 $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3161 $this->setURL($this->incoming_headers['location']);
3162 $this->tryagain = true;
3166 // see if we need to resend the request with http digest authentication
3167 if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3168 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3169 if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3170 $this->debug('Server wants digest authentication');
3171 // remove "Digest " from our elements
3172 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3174 // parse elements into array
3175 $digestElements = explode(',', $digestString);
3176 foreach ($digestElements as $val) {
3177 $tempElement = explode('=', trim($val), 2);
3178 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3181 // should have (at least) qop, realm, nonce
3182 if (isset($digestRequest['nonce'])) {
3183 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3184 $this->tryagain = true;
3188 $this->debug('HTTP authentication failed');
3189 $this->setError('HTTP authentication failed');
3194 ($http_status >= 300 && $http_status <= 307) ||
3195 ($http_status >= 400 && $http_status <= 417) ||
3196 ($http_status >= 501 && $http_status <= 505)
3198 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3202 // decode content-encoding
3203 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3204 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3205 // if decoding works, use it. else assume data wasn't gzencoded
3206 if(function_exists('gzinflate')){
3207 //$timer->setMarker('starting decoding of gzip/deflated content');
3208 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3209 // this means there are no Zlib headers, although there should be
3210 $this->debug('The gzinflate function exists');
3211 $datalen = strlen($data);
3212 if ($this->incoming_headers['content-encoding'] == 'deflate') {
3213 if ($degzdata = @gzinflate($data)) {
3215 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3216 if (strlen($data) < $datalen) {
3217 // test for the case that the payload has been compressed twice
3218 $this->debug('The inflated payload is smaller than the gzipped one; try again');
3219 if ($degzdata = @gzinflate($data)) {
3221 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3225 $this->debug('Error using gzinflate to inflate the payload');
3226 $this->setError('Error using gzinflate to inflate the payload');
3228 } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3229 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
3231 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3232 if (strlen($data) < $datalen) {
3233 // test for the case that the payload has been compressed twice
3234 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3235 if ($degzdata = @gzinflate(substr($data, 10))) {
3237 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3241 $this->debug('Error using gzinflate to un-gzip the payload');
3242 $this->setError('Error using gzinflate to un-gzip the payload');
3245 //$timer->setMarker('finished decoding of gzip/deflated content');
3246 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3247 // set decoded payload
3248 $this->incoming_payload = $header_data.$lb.$lb.$data;
3250 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3251 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3254 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3255 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3258 $this->debug('No Content-Encoding header');
3261 if(strlen($data) == 0){
3262 $this->debug('no data after headers!');
3263 $this->setError('no data present after HTTP headers');
3271 * sets the content-type for the SOAP message to be sent
3273 * @param string $type the content type, MIME style
3274 * @param mixed $charset character set used for encoding (or false)
3277 function setContentType($type, $charset = false) {
3278 $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3282 * specifies that an HTTP persistent connection should be used
3284 * @return boolean whether the request was honored by this method.
3287 function usePersistentConnection(){
3288 if (isset($this->outgoing_headers['Accept-Encoding'])) {
3291 $this->protocol_version = '1.1';
3292 $this->persistentConnection = true;
3293 $this->setHeader('Connection', 'Keep-Alive');
3298 * parse an incoming Cookie into it's parts
3300 * @param string $cookie_str content of cookie
3301 * @return array with data of that cookie
3305 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3307 function parseCookie($cookie_str) {
3308 $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3309 $data = split(';', $cookie_str);
3310 $value_str = $data[0];
3312 $cookie_param = 'domain=';
3313 $start = strpos($cookie_str, $cookie_param);
3315 $domain = substr($cookie_str, $start + strlen($cookie_param));
3316 $domain = substr($domain, 0, strpos($domain, ';'));
3321 $cookie_param = 'expires=';
3322 $start = strpos($cookie_str, $cookie_param);
3324 $expires = substr($cookie_str, $start + strlen($cookie_param));
3325 $expires = substr($expires, 0, strpos($expires, ';'));
3330 $cookie_param = 'path=';
3331 $start = strpos($cookie_str, $cookie_param);
3333 $path = substr($cookie_str, $start + strlen($cookie_param));
3334 $path = substr($path, 0, strpos($path, ';'));
3339 $cookie_param = ';secure;';
3340 if (strpos($cookie_str, $cookie_param) !== FALSE) {
3346 $sep_pos = strpos($value_str, '=');
3349 $name = substr($value_str, 0, $sep_pos);
3350 $value = substr($value_str, $sep_pos + 1);
3351 $cookie= array( 'name' => $name,
3353 'domain' => $domain,
3355 'expires' => $expires,
3364 * sort out cookies for the current request
3366 * @param array $cookies array with all cookies
3367 * @param boolean $secure is the send-content secure or not?
3368 * @return string for Cookie-HTTP-Header
3371 function getCookiesForRequest($cookies, $secure=false) {
3373 if ((! is_null($cookies)) && (is_array($cookies))) {
3374 foreach ($cookies as $cookie) {
3375 if (! is_array($cookie)) {
3378 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3379 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3380 if (strtotime($cookie['expires']) <= time()) {
3381 $this->debug('cookie has expired');
3385 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3386 $domain = preg_quote($cookie['domain']);
3387 if (! preg_match("'.*$domain$'i", $this->host)) {
3388 $this->debug('cookie has different domain');
3392 if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3393 $path = preg_quote($cookie['path']);
3394 if (! preg_match("'^$path.*'i", $this->path)) {
3395 $this->debug('cookie is for a different path');
3399 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3400 $this->debug('cookie is secure, transport is not');
3403 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3404 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3417 * nusoap_server allows the user to create a SOAP server
3418 * that is capable of receiving messages and returning responses
3420 * @author Dietrich Ayala <dietrich@ganx4.com>
3421 * @author Scott Nichol <snichol@users.sourceforge.net>
3422 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
3425 class nusoap_server extends nusoap_base {
3427 * HTTP headers of request
3431 var $headers = array();
3439 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3443 var $requestHeaders = '';
3445 * SOAP Headers from request (parsed)
3449 var $requestHeader = NULL;
3451 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3457 * SOAP payload for request (text)
3461 var $requestSOAP = '';
3463 * requested method namespace URI
3467 var $methodURI = '';
3469 * name of method requested
3473 var $methodname = '';
3475 * method parameters from request
3479 var $methodparams = array();
3481 * SOAP Action from request
3485 var $SOAPAction = '';
3487 * character set encoding of incoming (request) messages
3491 var $xml_encoding = '';
3493 * toggles whether the parser decodes element content w/ utf8_decode()
3497 var $decode_utf8 = true;
3500 * HTTP headers of response
3504 var $outgoing_headers = array();
3512 * SOAP headers for response (text or array of soapval or associative array)
3516 var $responseHeaders = '';
3518 * SOAP payload for response (text)
3522 var $responseSOAP = '';
3524 * method return value to place in response
3528 var $methodreturn = false;
3530 * whether $methodreturn is a string of literal XML
3534 var $methodreturnisliteralxml = false;
3536 * SOAP fault for response (or false)
3542 * text indication of result (for debugging)
3546 var $result = 'successful';
3549 * assoc array of operations => opData; operations are added by the register()
3550 * method or by parsing an external WSDL definition
3554 var $operations = array();
3556 * wsdl instance (if one)
3562 * URL for WSDL (if one)
3566 var $externalWSDLURL = false;
3568 * whether to append debug to response as XML comment
3572 var $debug_flag = false;
3577 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3579 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3582 function nusoap_server($wsdl=false){
3583 parent::nusoap_base();
3584 // turn on debugging?
3586 global $HTTP_SERVER_VARS;
3588 if (isset($_SERVER)) {
3589 $this->debug("_SERVER is defined:");
3590 $this->appendDebug($this->varDump($_SERVER));
3591 } elseif (isset($HTTP_SERVER_VARS)) {
3592 $this->debug("HTTP_SERVER_VARS is defined:");
3593 $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3595 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3598 if (isset($debug)) {
3599 $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3600 $this->debug_flag = $debug;
3601 } elseif (isset($_SERVER['QUERY_STRING'])) {
3602 $qs = explode('&', $_SERVER['QUERY_STRING']);
3603 foreach ($qs as $v) {
3604 if (substr($v, 0, 6) == 'debug=') {
3605 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3606 $this->debug_flag = substr($v, 6);
3609 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3610 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3611 foreach ($qs as $v) {
3612 if (substr($v, 0, 6) == 'debug=') {
3613 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3614 $this->debug_flag = substr($v, 6);
3621 $this->debug("In nusoap_server, WSDL is specified");
3622 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3623 $this->wsdl = $wsdl;
3624 $this->externalWSDLURL = $this->wsdl->wsdl;
3625 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3627 $this->debug('Create wsdl from ' . $wsdl);
3628 $this->wsdl = new wsdl($wsdl);
3629 $this->externalWSDLURL = $wsdl;
3631 $this->appendDebug($this->wsdl->getDebug());
3632 $this->wsdl->clearDebug();
3633 if($err = $this->wsdl->getError()){
3634 die('WSDL ERROR: '.$err);
3640 * processes request and returns response
3642 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
3645 function service($data){
3646 global $HTTP_SERVER_VARS;
3648 if (isset($_SERVER['QUERY_STRING'])) {
3649 $qs = $_SERVER['QUERY_STRING'];
3650 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3651 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3655 $this->debug("In service, query string=$qs");
3657 if (ereg('wsdl', $qs) ){
3658 $this->debug("In service, this is a request for WSDL");
3659 if($this->externalWSDLURL){
3660 if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3661 header('Location: '.$this->externalWSDLURL);
3662 } else { // assume file
3663 header("Content-Type: text/xml\r\n");
3664 $fp = fopen($this->externalWSDLURL, 'r');
3667 } elseif ($this->wsdl) {
3668 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3669 print $this->wsdl->serialize($this->debug_flag);
3670 if ($this->debug_flag) {
3671 $this->debug('wsdl:');
3672 $this->appendDebug($this->varDump($this->wsdl));
3673 print $this->getDebugAsXMLComment();
3676 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3677 print "This service does not provide WSDL";
3679 } elseif ($data == '' && $this->wsdl) {
3680 $this->debug("In service, there is no data, so return Web description");
3681 print $this->wsdl->webDescription();
3683 $this->debug("In service, invoke the request");
3684 $this->parse_request($data);
3685 if (! $this->fault) {
3686 $this->invoke_method();
3688 if (! $this->fault) {
3689 $this->serialize_return();
3691 $this->send_response();
3696 * parses HTTP request headers.
3698 * The following fields are set by this function (when successful)
3707 function parse_http_headers() {
3708 global $HTTP_SERVER_VARS;
3710 $this->request = '';
3711 $this->SOAPAction = '';
3712 if(function_exists('getallheaders')){
3713 $this->debug("In parse_http_headers, use getallheaders");
3714 $headers = getallheaders();
3715 foreach($headers as $k=>$v){
3716 $k = strtolower($k);
3717 $this->headers[$k] = $v;
3718 $this->request .= "$k: $v\r\n";
3719 $this->debug("$k: $v");
3721 // get SOAPAction header
3722 if(isset($this->headers['soapaction'])){
3723 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3725 // get the character encoding of the incoming request
3726 if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3727 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3728 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3729 $this->xml_encoding = strtoupper($enc);
3731 $this->xml_encoding = 'US-ASCII';
3734 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3735 $this->xml_encoding = 'ISO-8859-1';
3737 } elseif(isset($_SERVER) && is_array($_SERVER)){
3738 $this->debug("In parse_http_headers, use _SERVER");
3739 foreach ($_SERVER as $k => $v) {
3740 if (substr($k, 0, 5) == 'HTTP_') {
3741 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3743 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3745 if ($k == 'soapaction') {
3746 // get SOAPAction header
3748 $v = str_replace('"', '', $v);
3749 $v = str_replace('\\', '', $v);
3750 $this->SOAPAction = $v;
3751 } else if ($k == 'content-type') {
3752 // get the character encoding of the incoming request
3753 if (strpos($v, '=')) {
3754 $enc = substr(strstr($v, '='), 1);
3755 $enc = str_replace('"', '', $enc);
3756 $enc = str_replace('\\', '', $enc);
3757 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3758 $this->xml_encoding = strtoupper($enc);
3760 $this->xml_encoding = 'US-ASCII';
3763 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3764 $this->xml_encoding = 'ISO-8859-1';
3767 $this->headers[$k] = $v;
3768 $this->request .= "$k: $v\r\n";
3769 $this->debug("$k: $v");
3771 } elseif (is_array($HTTP_SERVER_VARS)) {
3772 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3773 foreach ($HTTP_SERVER_VARS as $k => $v) {
3774 if (substr($k, 0, 5) == 'HTTP_') {
3775 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
3777 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
3779 if ($k == 'soapaction') {
3780 // get SOAPAction header
3782 $v = str_replace('"', '', $v);
3783 $v = str_replace('\\', '', $v);
3784 $this->SOAPAction = $v;
3785 } else if ($k == 'content-type') {
3786 // get the character encoding of the incoming request
3787 if (strpos($v, '=')) {
3788 $enc = substr(strstr($v, '='), 1);
3789 $enc = str_replace('"', '', $enc);
3790 $enc = str_replace('\\', '', $enc);
3791 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3792 $this->xml_encoding = strtoupper($enc);
3794 $this->xml_encoding = 'US-ASCII';
3797 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3798 $this->xml_encoding = 'ISO-8859-1';
3801 $this->headers[$k] = $v;
3802 $this->request .= "$k: $v\r\n";
3803 $this->debug("$k: $v");
3806 $this->debug("In parse_http_headers, HTTP headers not accessible");
3807 $this->setError("HTTP headers not accessible");
3814 * The following fields are set by this function (when successful)
3828 * This sets the fault field on error
3830 * @param string $data XML string
3833 function parse_request($data='') {
3834 $this->debug('entering parse_request()');
3835 $this->parse_http_headers();
3836 $this->debug('got character encoding: '.$this->xml_encoding);
3837 // uncompress if necessary
3838 if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3839 $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3840 if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3841 // if decoding works, use it. else assume data wasn't gzencoded
3842 if (function_exists('gzuncompress')) {
3843 if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3845 } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3848 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3852 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3857 $this->request .= "\r\n".$data;
3858 $data = $this->parseRequest($this->headers, $data);
3859 $this->requestSOAP = $data;
3860 $this->debug('leaving parse_request');
3864 * invokes a PHP function for the requested SOAP method
3866 * The following fields are set by this function (when successful)
3870 * Note that the PHP function that is called may also set the following
3871 * fields to affect the response sent to the client
3876 * This sets the fault field on error
3880 function invoke_method() {
3881 $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3884 if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3885 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3886 $this->appendDebug('opData=' . $this->varDump($this->opData));
3887 } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3888 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3889 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3890 $this->appendDebug('opData=' . $this->varDump($this->opData));
3891 $this->methodname = $this->opData['name'];
3893 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3894 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3898 $this->debug('in invoke_method, no WSDL to validate method');
3901 // if a . is present in $this->methodname, we see if there is a class in scope,
3902 // which could be referred to. We will also distinguish between two deliminators,
3903 // to allow methods to be called a the class or an instance
3906 if (strpos($this->methodname, '..') > 0) {
3908 } else if (strpos($this->methodname, '.') > 0) {
3914 if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
3915 class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
3916 // get the class and method name
3917 $class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3918 $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
3919 $this->debug("in invoke_method, class=$class method=$method delim=$delim");
3922 // does method exist?
3924 if (!function_exists($this->methodname)) {
3925 $this->debug("in invoke_method, function '$this->methodname' not found!");
3926 $this->result = 'fault: method not found';
3927 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3931 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
3932 if (!in_array($method_to_compare, get_class_methods($class))) {
3933 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3934 $this->result = 'fault: method not found';
3935 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3940 // evaluate message, getting back parameters
3941 // verify that request parameters match the method's signature
3942 if(! $this->verify_method($this->methodname,$this->methodparams)){
3944 $this->debug('ERROR: request not verified against method signature');
3945 $this->result = 'fault: request failed validation against method signature';
3947 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
3951 // if there are parameters to pass
3952 $this->debug('in invoke_method, params:');
3953 $this->appendDebug($this->varDump($this->methodparams));
3954 $this->debug("in invoke_method, calling '$this->methodname'");
3955 if (!function_exists('call_user_func_array')) {
3957 $this->debug('in invoke_method, calling function using eval()');
3958 $funcCall = "\$this->methodreturn = $this->methodname(";
3960 if ($delim == '..') {
3961 $this->debug('in invoke_method, calling class method using eval()');
3962 $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3964 $this->debug('in invoke_method, calling instance method using eval()');
3965 // generate unique instance name
3966 $instname = "\$inst_".time();
3967 $funcCall = $instname." = new ".$class."(); ";
3968 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3971 if ($this->methodparams) {
3972 foreach ($this->methodparams as $param) {
3973 if (is_array($param) || is_object($param)) {
3974 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3977 $funcCall .= "\"$param\",";
3979 $funcCall = substr($funcCall, 0, -1);
3982 $this->debug('in invoke_method, function call: '.$funcCall);
3986 $this->debug('in invoke_method, calling function using call_user_func_array()');
3987 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
3988 } elseif ($delim == '..') {
3989 $this->debug('in invoke_method, calling class method using call_user_func_array()');
3990 $call_arg = array ($class, $method);
3992 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
3993 $instance = new $class ();
3994 $call_arg = array(&$instance, $method);
3996 if (is_array($this->methodparams)) {
3997 $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
3999 $this->methodreturn = call_user_func_array($call_arg, array());
4002 $this->debug('in invoke_method, methodreturn:');
4003 $this->appendDebug($this->varDump($this->methodreturn));
4004 $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
4008 * serializes the return value from a PHP function into a full SOAP Envelope
4010 * The following fields are set by this function (when successful)
4014 * This sets the fault field on error
4018 function serialize_return() {
4019 $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4021 if (isset($this->methodreturn) && ((@get_class($this->methodreturn) == 'soap_fault') || (@get_class($this->methodreturn) == 'nusoap_fault'))) {
4022 $this->debug('got a fault object from method');
4023 $this->fault = $this->methodreturn;
4025 } elseif ($this->methodreturnisliteralxml) {
4026 $return_val = $this->methodreturn;
4027 // returned value(s)
4029 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
4030 $this->debug('serializing return value');
4032 if (sizeof($this->opData['output']['parts']) > 1) {
4033 $this->debug('more than one output part, so use the method return unchanged');
4034 $opParams = $this->methodreturn;
4035 } elseif (sizeof($this->opData['output']['parts']) == 1) {
4036 $this->debug('exactly one output part, so wrap the method return in a simple array');
4037 // TODO: verify that it is not already wrapped!
4038 //foreach ($this->opData['output']['parts'] as $name => $type) {
4039 // $this->debug('wrap in element named ' . $name);
4041 $opParams = array($this->methodreturn);
4043 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
4044 $this->appendDebug($this->wsdl->getDebug());
4045 $this->wsdl->clearDebug();
4046 if($errstr = $this->wsdl->getError()){
4047 $this->debug('got wsdl error: '.$errstr);
4048 $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4052 if (isset($this->methodreturn)) {
4053 $return_val = $this->serialize_val($this->methodreturn, 'return');
4056 $this->debug('in absence of WSDL, assume void return for backward compatibility');
4060 $this->debug('return value:');
4061 $this->appendDebug($this->varDump($return_val));
4063 $this->debug('serializing response');
4065 $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4066 if ($this->opData['style'] == 'rpc') {
4067 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4068 if ($this->opData['output']['use'] == 'literal') {
4069 // 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
4070 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4072 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4075 $this->debug('style is not rpc for serialization: assume document');
4076 $payload = $return_val;
4079 $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4080 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4082 $this->result = 'successful';
4084 //if($this->debug_flag){
4085 $this->appendDebug($this->wsdl->getDebug());
4087 if (isset($opData['output']['encodingStyle'])) {
4088 $encodingStyle = $opData['output']['encodingStyle'];
4090 $encodingStyle = '';
4092 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4093 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
4095 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
4097 $this->debug("Leaving serialize_return");
4101 * sends an HTTP response
4103 * The following fields are set by this function (when successful)
4110 function send_response() {
4111 $this->debug('Enter send_response');
4113 $payload = $this->fault->serialize();
4114 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4115 $this->outgoing_headers[] = "Status: 500 Internal Server Error";
4117 $payload = $this->responseSOAP;
4118 // Some combinations of PHP+Web server allow the Status
4119 // to come through as a header. Since OK is the default
4121 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4122 // $this->outgoing_headers[] = "Status: 200 OK";
4124 // add debug data if in debug mode
4125 if(isset($this->debug_flag) && $this->debug_flag){
4126 $payload .= $this->getDebugAsXMLComment();
4128 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4129 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
4130 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
4131 // Let the Web server decide about this
4132 //$this->outgoing_headers[] = "Connection: Close\r\n";
4133 $payload = $this->getHTTPBody($payload);
4134 $type = $this->getHTTPContentType();
4135 $charset = $this->getHTTPContentTypeCharset();
4136 $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4137 //begin code to compress payload - by John
4138 // NOTE: there is no way to know whether the Web server will also compress
4140 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4141 if (strstr($this->headers['accept-encoding'], 'gzip')) {
4142 if (function_exists('gzencode')) {
4143 if (isset($this->debug_flag) && $this->debug_flag) {
4144 $payload .= "<!-- Content being gzipped -->";
4146 $this->outgoing_headers[] = "Content-Encoding: gzip";
4147 $payload = gzencode($payload);
4149 if (isset($this->debug_flag) && $this->debug_flag) {
4150 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
4153 } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
4154 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4155 // instead of gzcompress output,
4156 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4157 if (function_exists('gzdeflate')) {
4158 if (isset($this->debug_flag) && $this->debug_flag) {
4159 $payload .= "<!-- Content being deflated -->";
4161 $this->outgoing_headers[] = "Content-Encoding: deflate";
4162 $payload = gzdeflate($payload);
4164 if (isset($this->debug_flag) && $this->debug_flag) {
4165 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
4171 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
4172 reset($this->outgoing_headers);
4173 foreach($this->outgoing_headers as $hdr){
4174 header($hdr, false);
4177 $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
4181 * takes the value that was created by parsing the request
4182 * and compares to the method's signature, if available.
4184 * @param string $operation The operation to be invoked
4185 * @param array $request The array of parameter values
4186 * @return boolean Whether the operation was found
4189 function verify_method($operation,$request){
4190 if(isset($this->wsdl) && is_object($this->wsdl)){
4191 if($this->wsdl->getOperationData($operation)){
4194 } elseif(isset($this->operations[$operation])){
4201 * processes SOAP message received from client
4203 * @param array $headers The HTTP headers
4204 * @param string $data unprocessed request data from client
4205 * @return mixed value of the message, decoded into a PHP type
4208 function parseRequest($headers, $data) {
4209 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
4210 if (!strstr($headers['content-type'], 'text/xml')) {
4211 $this->setError('Request not of type text/xml');
4214 if (strpos($headers['content-type'], '=')) {
4215 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
4216 $this->debug('Got response encoding: ' . $enc);
4217 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
4218 $this->xml_encoding = strtoupper($enc);
4220 $this->xml_encoding = 'US-ASCII';
4223 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4224 $this->xml_encoding = 'ISO-8859-1';
4226 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4227 // parse response, get soap parser obj
4228 $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
4230 $this->debug("parser debug: \n".$parser->getDebug());
4231 // if fault occurred during message parsing
4232 if($err = $parser->getError()){
4233 $this->result = 'fault: error in msg parsing: '.$err;
4234 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
4235 // else successfully parsed request into soapval object
4237 // get/set methodname
4238 $this->methodURI = $parser->root_struct_namespace;
4239 $this->methodname = $parser->root_struct_name;
4240 $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
4241 $this->debug('calling parser->get_soapbody()');
4242 $this->methodparams = $parser->get_soapbody();
4244 $this->requestHeaders = $parser->getHeaders();
4246 $this->requestHeader = $parser->get_soapheader();
4247 // add document for doclit support
4248 $this->document = $parser->document;
4253 * gets the HTTP body for the current response.
4255 * @param string $soapmsg The SOAP payload
4256 * @return string The HTTP body, which includes the SOAP payload
4259 function getHTTPBody($soapmsg) {
4264 * gets the HTTP content type for the current response.
4266 * Note: getHTTPBody must be called before this.
4268 * @return string the HTTP content type for the current response.
4271 function getHTTPContentType() {
4276 * gets the HTTP content type charset for the current response.
4277 * returns false for non-text content types.
4279 * Note: getHTTPBody must be called before this.
4281 * @return string the HTTP content type charset for the current response.
4284 function getHTTPContentTypeCharset() {
4285 return $this->soap_defencoding;
4289 * add a method to the dispatch map (this has been replaced by the register method)
4291 * @param string $methodname
4292 * @param string $in array of input values
4293 * @param string $out array of output values
4297 function add_to_map($methodname,$in,$out){
4298 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
4302 * register a service function with the server
4304 * @param string $name the name of the PHP function, class.method or class..method
4305 * @param array $in assoc array of input values: key = param name, value = param type
4306 * @param array $out assoc array of output values: key = param name, value = param type
4307 * @param mixed $namespace the element namespace for the method or false
4308 * @param mixed $soapaction the soapaction for the method or false
4309 * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4310 * @param mixed $use optional (encoded|literal) or false
4311 * @param string $documentation optional Description to include in WSDL
4312 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4315 function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
4316 global $HTTP_SERVER_VARS;
4318 if($this->externalWSDLURL){
4319 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
4322 die('You must specify a name when you register an operation');
4324 if (!is_array($in)) {
4325 die('You must provide an array for operation inputs');
4327 if (!is_array($out)) {
4328 die('You must provide an array for operation outputs');
4330 if(false == $namespace) {
4332 if(false == $soapaction) {
4333 if (isset($_SERVER)) {
4334 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4335 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4336 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4337 } elseif (isset($HTTP_SERVER_VARS)) {
4338 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4339 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4340 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4342 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4344 if ($HTTPS == '1' || $HTTPS == 'on') {
4349 $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
4351 if(false == $style) {
4357 if ($use == 'encoded' && $encodingStyle = '') {
4358 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4361 $this->operations[$name] = array(
4365 'namespace' => $namespace,
4366 'soapaction' => $soapaction,
4369 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
4375 * Specify a fault to be returned to the client.
4376 * This also acts as a flag to the server that a fault has occured.
4378 * @param string $faultcode
4379 * @param string $faultstring
4380 * @param string $faultactor
4381 * @param string $faultdetail
4384 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
4385 if ($faultdetail == '' && $this->debug_flag) {
4386 $faultdetail = $this->getDebug();
4388 $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
4389 $this->fault->soap_defencoding = $this->soap_defencoding;
4393 * Sets up wsdl object.
4394 * Acts as a flag to enable internal WSDL generation
4396 * @param string $serviceName, name of the service
4397 * @param mixed $namespace optional 'tns' service namespace or false
4398 * @param mixed $endpoint optional URL of service endpoint or false
4399 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4400 * @param string $transport optional SOAP transport
4401 * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4403 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4405 global $HTTP_SERVER_VARS;
4407 if (isset($_SERVER)) {
4408 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4409 $SERVER_PORT = $_SERVER['SERVER_PORT'];
4410 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4411 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4412 } elseif (isset($HTTP_SERVER_VARS)) {
4413 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4414 $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4415 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4416 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4418 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4420 // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4421 $colon = strpos($SERVER_NAME,":");
4423 $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4425 if ($SERVER_PORT == 80) {
4428 $SERVER_PORT = ':' . $SERVER_PORT;
4430 if(false == $namespace) {
4431 $namespace = "http://$SERVER_NAME/soap/$serviceName";
4434 if(false == $endpoint) {
4435 if ($HTTPS == '1' || $HTTPS == 'on') {
4440 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4443 if(false == $schemaTargetNamespace) {
4444 $schemaTargetNamespace = $namespace;
4447 $this->wsdl = new wsdl;
4448 $this->wsdl->serviceName = $serviceName;
4449 $this->wsdl->endpoint = $endpoint;
4450 $this->wsdl->namespaces['tns'] = $namespace;
4451 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4452 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4453 if ($schemaTargetNamespace != $namespace) {
4454 $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4456 $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
4457 if ($style == 'document') {
4458 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4460 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4461 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4462 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4463 $this->wsdl->bindings[$serviceName.'Binding'] = array(
4464 'name'=>$serviceName.'Binding',
4466 'transport'=>$transport,
4467 'portType'=>$serviceName.'PortType');
4468 $this->wsdl->ports[$serviceName.'Port'] = array(
4469 'binding'=>$serviceName.'Binding',
4470 'location'=>$endpoint,
4471 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4476 * Backward compatibility
4478 class soap_server extends nusoap_server {
4486 * parses a WSDL file, allows access to it's data, other utility methods.
4487 * also builds WSDL structures programmatically.
4489 * @author Dietrich Ayala <dietrich@ganx4.com>
4490 * @author Scott Nichol <snichol@users.sourceforge.net>
4491 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
4494 class wsdl extends nusoap_base {
4495 // URL or filename of the root of this WSDL
4497 // define internal arrays of bindings, ports, operations, messages, etc.
4498 var $schemas = array();
4500 var $message = array();
4501 var $complexTypes = array();
4502 var $messages = array();
4503 var $currentMessage;
4504 var $currentOperation;
4505 var $portTypes = array();
4506 var $currentPortType;
4507 var $bindings = array();
4508 var $currentBinding;
4509 var $ports = array();
4511 var $opData = array();
4513 var $documentation = false;
4515 // array of wsdl docs to import
4516 var $import = array();
4521 var $depth_array = array();
4523 var $proxyhost = '';
4524 var $proxyport = '';
4525 var $proxyusername = '';
4526 var $proxypassword = '';
4528 var $response_timeout = 30;
4529 var $curl_options = array(); // User-specified cURL options
4530 var $use_curl = false; // whether to always try to use cURL
4531 // for HTTP authentication
4532 var $username = ''; // Username for HTTP authentication
4533 var $password = ''; // Password for HTTP authentication
4534 var $authtype = ''; // Type of HTTP authentication
4535 var $certRequest = array(); // Certificate for HTTP SSL authentication
4540 * @param string $wsdl WSDL document URL
4541 * @param string $proxyhost
4542 * @param string $proxyport
4543 * @param string $proxyusername
4544 * @param string $proxypassword
4545 * @param integer $timeout set the connection timeout
4546 * @param integer $response_timeout set the response timeout
4547 * @param array $curl_options user-specified cURL options
4548 * @param boolean $use_curl try to use cURL
4551 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
4552 parent::nusoap_base();
4553 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4554 $this->proxyhost = $proxyhost;
4555 $this->proxyport = $proxyport;
4556 $this->proxyusername = $proxyusername;
4557 $this->proxypassword = $proxypassword;
4558 $this->timeout = $timeout;
4559 $this->response_timeout = $response_timeout;
4560 if (is_array($curl_options))
4561 $this->curl_options = $curl_options;
4562 $this->use_curl = $use_curl;
4563 $this->fetchWSDL($wsdl);
4567 * fetches the WSDL document and parses it
4571 function fetchWSDL($wsdl) {
4572 $this->debug("parse and process WSDL path=$wsdl");
4573 $this->wsdl = $wsdl;
4575 if ($this->wsdl != "") {
4576 $this->parseWSDL($this->wsdl);
4579 // TODO: handle imports more properly, grabbing them in-line and nesting them
4580 $imported_urls = array();
4582 while ($imported > 0) {
4585 foreach ($this->schemas as $ns => $list) {
4586 foreach ($list as $xs) {
4587 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4588 foreach ($xs->imports as $ns2 => $list2) {
4589 for ($ii = 0; $ii < count($list2); $ii++) {
4590 if (! $list2[$ii]['loaded']) {
4591 $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4592 $url = $list2[$ii]['location'];
4594 $urlparts = parse_url($url);
4595 if (!isset($urlparts['host'])) {
4596 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4597 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4599 if (! in_array($url, $imported_urls)) {
4600 $this->parseWSDL($url);
4602 $imported_urls[] = $url;
4605 $this->debug("Unexpected scenario: empty URL for unloaded import");
4613 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4614 foreach ($this->import as $ns => $list) {
4615 for ($ii = 0; $ii < count($list); $ii++) {
4616 if (! $list[$ii]['loaded']) {
4617 $this->import[$ns][$ii]['loaded'] = true;
4618 $url = $list[$ii]['location'];
4620 $urlparts = parse_url($url);
4621 if (!isset($urlparts['host'])) {
4622 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4623 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4625 if (! in_array($url, $imported_urls)) {
4626 $this->parseWSDL($url);
4628 $imported_urls[] = $url;
4631 $this->debug("Unexpected scenario: empty URL for unloaded import");
4637 // add new data to operation data
4638 foreach($this->bindings as $binding => $bindingData) {
4639 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4640 foreach($bindingData['operations'] as $operation => $data) {
4641 $this->debug('post-parse data gathering for ' . $operation);
4642 $this->bindings[$binding]['operations'][$operation]['input'] =
4643 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4644 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4645 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4646 $this->bindings[$binding]['operations'][$operation]['output'] =
4647 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4648 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4649 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4650 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4651 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4653 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4654 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4656 // Set operation style if necessary, but do not override one already provided
4657 if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4658 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4660 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4661 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4662 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4669 * parses the wsdl document
4671 * @param string $wsdl path or URL
4674 function parseWSDL($wsdl = '') {
4675 $this->debug("parse WSDL at path=$wsdl");
4678 $this->debug('no wsdl passed to parseWSDL()!!');
4679 $this->setError('no wsdl passed to parseWSDL()!!');
4683 // parse $wsdl for url format
4684 $wsdl_props = parse_url($wsdl);
4686 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4687 $this->debug('getting WSDL http(s) URL ' . $wsdl);
4689 $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4690 $tr->request_method = 'GET';
4691 $tr->useSOAPAction = false;
4692 if($this->proxyhost && $this->proxyport){
4693 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4695 if ($this->authtype != '') {
4696 $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
4698 $tr->setEncoding('gzip, deflate');
4699 $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4700 //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4701 //$this->debug("WSDL response\n" . $tr->incoming_payload);
4702 $this->appendDebug($tr->getDebug());
4704 if($err = $tr->getError() ){
4705 $errstr = 'HTTP ERROR: '.$err;
4706 $this->debug($errstr);
4707 $this->setError($errstr);
4712 $this->debug("got WSDL URL");
4714 // $wsdl is not http(s), so treat it as a file URL or plain file path
4715 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4716 $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4720 $this->debug('getting WSDL file ' . $path);
4721 if ($fp = @fopen($path, 'r')) {
4723 while ($data = fread($fp, 32768)) {
4724 $wsdl_string .= $data;
4728 $errstr = "Bad path to WSDL file $path";
4729 $this->debug($errstr);
4730 $this->setError($errstr);
4734 $this->debug('Parse WSDL');
4735 // end new code added
4736 // Create an XML parser.
4737 $this->parser = xml_parser_create();
4738 // Set the options for parsing the XML data.
4739 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4740 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4741 // Set the object for the parser.
4742 xml_set_object($this->parser, $this);
4743 // Set the element handlers for the parser.
4744 xml_set_element_handler($this->parser, 'start_element', 'end_element');
4745 xml_set_character_data_handler($this->parser, 'character_data');
4746 // Parse the XML file.
4747 if (!xml_parse($this->parser, $wsdl_string, true)) {
4748 // Display an error message.
4750 'XML error parsing WSDL from %s on line %d: %s',
4752 xml_get_current_line_number($this->parser),
4753 xml_error_string(xml_get_error_code($this->parser))
4755 $this->debug($errstr);
4756 $this->debug("XML payload:\n" . $wsdl_string);
4757 $this->setError($errstr);
4761 xml_parser_free($this->parser);
4762 $this->debug('Parsing WSDL done');
4763 // catch wsdl parse errors
4764 if($this->getError()){
4771 * start-element handler
4773 * @param string $parser XML parser object
4774 * @param string $name element name
4775 * @param string $attrs associative array of attributes
4778 function start_element($parser, $name, $attrs)
4780 if ($this->status == 'schema') {
4781 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4782 $this->appendDebug($this->currentSchema->getDebug());
4783 $this->currentSchema->clearDebug();
4784 } elseif (ereg('schema$', $name)) {
4785 $this->debug('Parsing WSDL schema');
4786 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4787 $this->status = 'schema';
4788 $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
4789 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4790 $this->appendDebug($this->currentSchema->getDebug());
4791 $this->currentSchema->clearDebug();
4793 // position in the total number of elements, starting from 0
4794 $pos = $this->position++;
4795 $depth = $this->depth++;
4796 // set self as current value for this depth
4797 $this->depth_array[$depth] = $pos;
4798 $this->message[$pos] = array('cdata' => '');
4799 // process attributes
4800 if (count($attrs) > 0) {
4801 // register namespace declarations
4802 foreach($attrs as $k => $v) {
4803 if (ereg("^xmlns", $k)) {
4804 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4805 $this->namespaces[$ns_prefix] = $v;
4807 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4809 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4810 $this->XMLSchemaVersion = $v;
4811 $this->namespaces['xsi'] = $v . '-instance';
4815 // expand each attribute prefix to its namespace
4816 foreach($attrs as $k => $v) {
4817 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4818 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4819 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4827 // get element prefix, namespace and name
4828 if (ereg(':', $name)) {
4830 $prefix = substr($name, 0, strpos($name, ':'));
4832 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4833 // get unqualified name
4834 $name = substr(strstr($name, ':'), 1);
4836 // process attributes, expanding any prefixes to namespaces
4837 // find status, register data
4838 switch ($this->status) {
4840 if ($name == 'part') {
4841 if (isset($attrs['type'])) {
4842 $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4843 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4845 if (isset($attrs['element'])) {
4846 $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4847 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4854 $this->currentPortOperation = $attrs['name'];
4855 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4856 if (isset($attrs['parameterOrder'])) {
4857 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4860 case 'documentation':
4861 $this->documentation = true;
4863 // merge input/output data
4865 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4866 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4874 if (isset($attrs['style'])) {
4875 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4877 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4880 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4883 if (isset($attrs['soapAction'])) {
4884 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4886 if (isset($attrs['style'])) {
4887 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4889 if (isset($attrs['name'])) {
4890 $this->currentOperation = $attrs['name'];
4891 $this->debug("current binding operation: $this->currentOperation");
4892 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4893 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4894 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4898 $this->opStatus = 'input';
4901 $this->opStatus = 'output';
4904 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
4905 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
4907 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
4915 $this->currentPort = $attrs['name'];
4916 $this->debug('current port: ' . $this->currentPort);
4917 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
4921 $this->ports[$this->currentPort]['location'] = $attrs['location'];
4922 $this->ports[$this->currentPort]['bindingType'] = $namespace;
4923 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
4924 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
4932 if (isset($attrs['location'])) {
4933 $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
4934 $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
4936 $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
4937 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
4938 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
4940 $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
4945 // $this->status = 'schema';
4948 $this->status = 'message';
4949 $this->messages[$attrs['name']] = array();
4950 $this->currentMessage = $attrs['name'];
4953 $this->status = 'portType';
4954 $this->portTypes[$attrs['name']] = array();
4955 $this->currentPortType = $attrs['name'];
4958 if (isset($attrs['name'])) {
4960 if (strpos($attrs['name'], ':')) {
4961 $this->currentBinding = $this->getLocalPart($attrs['name']);
4963 $this->currentBinding = $attrs['name'];
4965 $this->status = 'binding';
4966 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
4967 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
4971 $this->serviceName = $attrs['name'];
4972 $this->status = 'service';
4973 $this->debug('current service: ' . $this->serviceName);
4976 foreach ($attrs as $name => $value) {
4977 $this->wsdl_info[$name] = $value;
4985 * end-element handler
4987 * @param string $parser XML parser object
4988 * @param string $name element name
4991 function end_element($parser, $name){
4992 // unset schema status
4993 if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
4995 $this->appendDebug($this->currentSchema->getDebug());
4996 $this->currentSchema->clearDebug();
4997 $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
4998 $this->debug('Parsing WSDL schema done');
5000 if ($this->status == 'schema') {
5001 $this->currentSchema->schemaEndElement($parser, $name);
5003 // bring depth down a notch
5006 // end documentation
5007 if ($this->documentation) {
5008 //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5009 //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5010 $this->documentation = false;
5015 * element content handler
5017 * @param string $parser XML parser object
5018 * @param string $data element content
5021 function character_data($parser, $data)
5023 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5024 if (isset($this->message[$pos]['cdata'])) {
5025 $this->message[$pos]['cdata'] .= $data;
5027 if ($this->documentation) {
5028 $this->documentation .= $data;
5033 * if authenticating, set user credentials here
5035 * @param string $username
5036 * @param string $password
5037 * @param string $authtype (basic|digest|certificate|ntlm)
5038 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5041 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
5042 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5043 $this->appendDebug($this->varDump($certRequest));
5044 $this->username = $username;
5045 $this->password = $password;
5046 $this->authtype = $authtype;
5047 $this->certRequest = $certRequest;
5050 function getBindingData($binding)
5052 if (is_array($this->bindings[$binding])) {
5053 return $this->bindings[$binding];
5058 * returns an assoc array of operation names => operation data
5060 * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5064 function getOperations($bindingType = 'soap') {
5066 if ($bindingType == 'soap') {
5067 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5068 } elseif ($bindingType == 'soap12') {
5069 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5072 foreach($this->ports as $port => $portData) {
5073 // binding type of port matches parameter
5074 if ($portData['bindingType'] == $bindingType) {
5075 //$this->debug("getOperations for port $port");
5076 //$this->debug("port data: " . $this->varDump($portData));
5077 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5079 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
5080 $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
5088 * returns an associative array of data necessary for calling an operation
5090 * @param string $operation name of operation
5091 * @param string $bindingType type of binding eg: soap, soap12
5095 function getOperationData($operation, $bindingType = 'soap')
5097 if ($bindingType == 'soap') {
5098 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5099 } elseif ($bindingType == 'soap12') {
5100 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5103 foreach($this->ports as $port => $portData) {
5104 // binding type of port matches parameter
5105 if ($portData['bindingType'] == $bindingType) {
5107 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5108 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
5109 // note that we could/should also check the namespace here
5110 if ($operation == $bOperation) {
5111 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
5120 * returns an associative array of data necessary for calling an operation
5122 * @param string $soapAction soapAction for operation
5123 * @param string $bindingType type of binding eg: soap, soap12
5127 function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
5128 if ($bindingType == 'soap') {
5129 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5130 } elseif ($bindingType == 'soap12') {
5131 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5134 foreach($this->ports as $port => $portData) {
5135 // binding type of port matches parameter
5136 if ($portData['bindingType'] == $bindingType) {
5137 // loop through operations for the binding
5138 foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5139 if ($opData['soapAction'] == $soapAction) {
5148 * returns an array of information about a given type
5149 * returns false if no type exists by the given name
5152 * 'elements' => array(), // refs to elements array
5153 * 'restrictionBase' => '',
5155 * 'order' => '(sequence|all)',
5156 * 'attrs' => array() // refs to attributes array
5159 * @param string $type the type
5160 * @param string $ns namespace (not prefix) of the type
5163 * @see nusoap_xmlschema
5165 function getTypeDef($type, $ns) {
5166 $this->debug("in getTypeDef: type=$type, ns=$ns");
5167 if ((! $ns) && isset($this->namespaces['tns'])) {
5168 $ns = $this->namespaces['tns'];
5169 $this->debug("in getTypeDef: type namespace forced to $ns");
5171 if (!isset($this->schemas[$ns])) {
5172 foreach ($this->schemas as $ns0 => $schema0) {
5173 if (strcasecmp($ns, $ns0) == 0) {
5174 $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5180 if (isset($this->schemas[$ns])) {
5181 $this->debug("in getTypeDef: have schema for namespace $ns");
5182 for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
5183 $xs = &$this->schemas[$ns][$i];
5184 $t = $xs->getTypeDef($type);
5185 //$this->appendDebug($xs->getDebug());
5186 //$xs->clearDebug();
5188 if (!isset($t['phpType'])) {
5189 // get info for type to tack onto the element
5190 $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5191 $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5192 $etype = $this->getTypeDef($uqType, $ns);
5194 $this->debug("found type for [element] $type:");
5195 $this->debug($this->varDump($etype));
5196 if (isset($etype['phpType'])) {
5197 $t['phpType'] = $etype['phpType'];
5199 if (isset($etype['elements'])) {
5200 $t['elements'] = $etype['elements'];
5202 if (isset($etype['attrs'])) {
5203 $t['attrs'] = $etype['attrs'];
5211 $this->debug("in getTypeDef: do not have schema for namespace $ns");
5217 * prints html description of services
5221 function webDescription(){
5222 global $HTTP_SERVER_VARS;
5224 if (isset($_SERVER)) {
5225 $PHP_SELF = $_SERVER['PHP_SELF'];
5226 } elseif (isset($HTTP_SERVER_VARS)) {
5227 $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5229 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
5233 <html><head><title>NuSOAP: '.$this->serviceName.'</title>
5234 <style type="text/css">
5235 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5236 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5237 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5238 ul { margin-top: 10px; margin-left: 20px; }
5239 li { list-style-type: none; margin-top: 10px; color: #000000; }
5241 margin-left: 0px; padding-bottom: 2em; }
5243 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5244 margin-top: 10px; margin-left: 0px; color: #000000;
5245 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5247 font-family: arial; font-size: 26px; color: #ffffff;
5248 background-color: #999999; width: 105%; margin-left: 0px;
5249 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
5251 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5252 font-family: arial; overflow: hidden; width: 600;
5253 padding: 20px; font-size: 10px; background-color: #999999;
5254 layer-background-color:#FFFFFF; }
5255 a,a:active { color: charcoal; font-weight: bold; }
5256 a:visited { color: #666666; font-weight: bold; }
5257 a:hover { color: cc3300; font-weight: bold; }
5259 <script language="JavaScript" type="text/javascript">
5261 // POP-UP CAPTIONS...
5262 function lib_bwcheck(){ //Browsercheck (needed)
5263 this.ver=navigator.appVersion
5264 this.agent=navigator.userAgent
5265 this.dom=document.getElementById?1:0
5266 this.opera5=this.agent.indexOf("Opera 5")>-1
5267 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5268 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5269 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5270 this.ie=this.ie4||this.ie5||this.ie6
5271 this.mac=this.agent.indexOf("Mac")>-1
5272 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5273 this.ns4=(document.layers && !this.dom)?1:0;
5274 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5277 var bw = new lib_bwcheck()
5278 //Makes crossbrowser object.
5279 function makeObj(obj){
5280 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5281 if(!this.evnt) return false
5282 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5283 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5284 this.writeIt=b_writeIt;
5287 // A unit of measure that will be added when setting the position of a layer.
5288 //var px = bw.ns4||window.opera?"":"px";
5289 function b_writeIt(text){
5290 if (bw.ns4){this.wref.write(text);this.wref.close()}
5291 else this.wref.innerHTML = text
5293 //Shows the messages
5295 function popup(divid){
5296 if(oDesc = new makeObj(divid)){
5297 oDesc.css.visibility = "visible"
5300 function popout(){ // Hides message
5301 if(oDesc) oDesc.css.visibility = "hidden"
5309 <div class=title>'.$this->serviceName.'</div>
5311 <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
5312 Click on an operation name to view it's details.</p>
5314 foreach($this->getOperations() as $op => $data){
5315 $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5316 // create hidden div
5317 $b .= "<div id='$op' class='hidden'>
5318 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5319 foreach($data as $donnie => $marie){ // loop through opdata
5320 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
5321 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
5322 foreach($marie as $captain => $tenille){ // loop through data
5323 if($captain == 'parts'){ // loop thru parts
5324 $b .= " $captain:<br>";
5325 //if(is_array($tenille)){
5326 foreach($tenille as $joanie => $chachi){
5327 $b .= " $joanie: $chachi<br>";
5331 $b .= " $captain: $tenille<br>";
5335 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
5343 </div></body></html>';
5348 * serialize the parsed wsdl
5350 * @param mixed $debug whether to put debug=1 in endpoint URL
5351 * @return string serialization of WSDL
5354 function serialize($debug = 0)
5356 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5357 $xml .= "\n<definitions";
5358 foreach($this->namespaces as $k => $v) {
5359 $xml .= " xmlns:$k=\"$v\"";
5361 // 10.9.02 - add poulter fix for wsdl and tns declarations
5362 if (isset($this->namespaces['wsdl'])) {
5363 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
5365 if (isset($this->namespaces['tns'])) {
5366 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
5370 if (sizeof($this->import) > 0) {
5371 foreach($this->import as $ns => $list) {
5372 foreach ($list as $ii) {
5373 if ($ii['location'] != '') {
5374 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5376 $xml .= '<import namespace="' . $ns . '" />';
5382 if (count($this->schemas)>=1) {
5383 $xml .= "\n<types>\n";
5384 foreach ($this->schemas as $ns => $list) {
5385 foreach ($list as $xs) {
5386 $xml .= $xs->serializeSchema();
5392 if (count($this->messages) >= 1) {
5393 foreach($this->messages as $msgName => $msgParts) {
5394 $xml .= "\n<message name=\"" . $msgName . '">';
5395 if(is_array($msgParts)){
5396 foreach($msgParts as $partName => $partType) {
5397 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5398 if (strpos($partType, ':')) {
5399 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
5400 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5401 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5402 $typePrefix = 'xsd';
5404 foreach($this->typemap as $ns => $types) {
5405 if (isset($types[$partType])) {
5406 $typePrefix = $this->getPrefixFromNamespace($ns);
5409 if (!isset($typePrefix)) {
5410 die("$partType has no namespace!");
5413 $ns = $this->getNamespaceFromPrefix($typePrefix);
5414 $localPart = $this->getLocalPart($partType);
5415 $typeDef = $this->getTypeDef($localPart, $ns);
5416 if ($typeDef['typeClass'] == 'element') {
5417 $elementortype = 'element';
5418 if (substr($localPart, -1) == '^') {
5419 $localPart = substr($localPart, 0, -1);
5422 $elementortype = 'type';
5424 $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5427 $xml .= '</message>';
5430 // bindings & porttypes
5431 if (count($this->bindings) >= 1) {
5434 foreach($this->bindings as $bindingName => $attrs) {
5435 $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5436 $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5437 $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5438 foreach($attrs['operations'] as $opName => $opParts) {
5439 $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
5440 $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
5441 if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
5442 $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5446 $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5447 if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
5448 $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5452 $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5453 $binding_xml .= "\n" . ' </operation>';
5454 $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
5455 if (isset($opParts['parameterOrder'])) {
5456 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5458 $portType_xml .= '>';
5459 if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
5460 $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5462 $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
5463 $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
5464 $portType_xml .= "\n" . ' </operation>';
5466 $portType_xml .= "\n" . '</portType>';
5467 $binding_xml .= "\n" . '</binding>';
5469 $xml .= $portType_xml . $binding_xml;
5472 $xml .= "\n<service name=\"" . $this->serviceName . '">';
5473 if (count($this->ports) >= 1) {
5474 foreach($this->ports as $pName => $attrs) {
5475 $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5476 $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5477 $xml .= "\n" . ' </port>';
5480 $xml .= "\n" . '</service>';
5481 return $xml . "\n</definitions>";
5485 * determine whether a set of parameters are unwrapped
5486 * when they are expect to be wrapped, Microsoft-style.
5488 * @param string $type the type (element name) of the wrapper
5489 * @param array $parameters the parameter values for the SOAP call
5490 * @return boolean whether they parameters are unwrapped (and should be wrapped)
5493 function parametersMatchWrapped($type, &$parameters) {
5494 $this->debug("in parametersMatchWrapped type=$type, parameters=");
5495 $this->appendDebug($this->varDump($parameters));
5497 // split type into namespace:unqualified-type
5498 if (strpos($type, ':')) {
5499 $uqType = substr($type, strrpos($type, ':') + 1);
5500 $ns = substr($type, 0, strrpos($type, ':'));
5501 $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5502 if ($this->getNamespaceFromPrefix($ns)) {
5503 $ns = $this->getNamespaceFromPrefix($ns);
5504 $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5507 // TODO: should the type be compared to types in XSD, and the namespace
5508 // set to XSD if the type matches?
5509 $this->debug("in parametersMatchWrapped: No namespace for type $type");
5514 // get the type information
5515 if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5516 $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5519 $this->debug("in parametersMatchWrapped: found typeDef=");
5520 $this->appendDebug($this->varDump($typeDef));
5521 if (substr($uqType, -1) == '^') {
5522 $uqType = substr($uqType, 0, -1);
5524 $phpType = $typeDef['phpType'];
5525 $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5526 $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5528 // we expect a complexType or element of complexType
5529 if ($phpType != 'struct') {
5530 $this->debug("in parametersMatchWrapped: not a struct");
5534 // see whether the parameter names match the elements
5535 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5539 if ($this->isArraySimpleOrStruct($parameters) == 'arraySimple' && count($parameters) == count($typeDef['elements'])) {
5540 $this->debug("in parametersMatchWrapped: (wrapped return value kludge) correct number of elements in simple array, so change array and wrap");
5543 foreach ($typeDef['elements'] as $name => $attrs) {
5545 $this->debug("in parametersMatchWrapped: change parameter $element to name $name");
5546 $parameters[$name] = $parameters[$elements];
5547 unset($parameters[$elements]);
5549 } elseif (isset($parameters[$name])) {
5550 $this->debug("in parametersMatchWrapped: have parameter named $name");
5553 $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5558 $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5559 if ($matches == 0) {
5565 // since there are no elements for the type, if the user passed no
5566 // parameters, the parameters match wrapped.
5567 $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5568 return count($parameters) == 0;
5572 * serialize PHP values according to a WSDL message definition
5573 * contrary to the method name, this is not limited to RPC
5576 * - multi-ref serialization
5577 * - validate PHP values against type definitions, return errors if invalid
5579 * @param string $operation operation name
5580 * @param string $direction (input|output)
5581 * @param mixed $parameters parameter value(s)
5582 * @param string $bindingType (soap|soap12)
5583 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5586 function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
5587 $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5588 $this->appendDebug('parameters=' . $this->varDump($parameters));
5590 if ($direction != 'input' && $direction != 'output') {
5591 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5592 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5595 if (!$opData = $this->getOperationData($operation, $bindingType)) {
5596 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5597 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5600 $this->debug('in serializeRPCParameters: opData:');
5601 $this->appendDebug($this->varDump($opData));
5603 // Get encoding style for output and set to current
5604 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5605 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5606 $encodingStyle = $opData['output']['encodingStyle'];
5607 $enc_style = $encodingStyle;
5612 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5613 $parts = &$opData[$direction]['parts'];
5614 $part_count = sizeof($parts);
5615 $style = $opData['style'];
5616 $use = $opData[$direction]['use'];
5617 $this->debug("have $part_count part(s) to serialize using $style/$use");
5618 if (is_array($parameters)) {
5619 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5620 $parameter_count = count($parameters);
5621 $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5622 // check for Microsoft-style wrapped parameters
5623 if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
5624 $this->debug('check whether the caller has wrapped the parameters');
5625 if ((($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) || ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1)) {
5626 $this->debug('check whether caller\'s parameters match the wrapped ones');
5627 if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5628 $this->debug('wrap the parameters for the caller');
5629 $parameters = array('parameters' => $parameters);
5630 $parameter_count = 1;
5634 foreach ($parts as $name => $type) {
5635 $this->debug("serializing part $name of type $type");
5636 // Track encoding style
5637 if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5638 $encodingStyle = $opData[$direction]['encodingStyle'];
5639 $enc_style = $encodingStyle;
5643 // NOTE: add error handling here
5644 // if serializeType returns false, then catch global error and fault
5645 if ($parametersArrayType == 'arraySimple') {
5646 $p = array_shift($parameters);
5647 $this->debug('calling serializeType w/indexed param');
5648 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5649 } elseif (isset($parameters[$name])) {
5650 $this->debug('calling serializeType w/named param');
5651 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5653 // TODO: only send nillable
5654 $this->debug('calling serializeType w/null param');
5655 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5659 $this->debug('no parameters passed.');
5662 $this->debug("serializeRPCParameters returning: $xml");
5667 * serialize a PHP value according to a WSDL message definition
5670 * - multi-ref serialization
5671 * - validate PHP values against type definitions, return errors if invalid
5673 * @param string $operation operation name
5674 * @param string $direction (input|output)
5675 * @param mixed $parameters parameter value(s)
5676 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5680 function serializeParameters($operation, $direction, $parameters)
5682 $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5683 $this->appendDebug('parameters=' . $this->varDump($parameters));
5685 if ($direction != 'input' && $direction != 'output') {
5686 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5687 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5690 if (!$opData = $this->getOperationData($operation)) {
5691 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5692 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5695 $this->debug('opData:');
5696 $this->appendDebug($this->varDump($opData));
5698 // Get encoding style for output and set to current
5699 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5700 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5701 $encodingStyle = $opData['output']['encodingStyle'];
5702 $enc_style = $encodingStyle;
5707 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5709 $use = $opData[$direction]['use'];
5710 $this->debug("use=$use");
5711 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5712 if (is_array($parameters)) {
5713 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5714 $this->debug('have ' . $parametersArrayType . ' parameters');
5715 foreach($opData[$direction]['parts'] as $name => $type) {
5716 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5717 // Track encoding style
5718 if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5719 $encodingStyle = $opData[$direction]['encodingStyle'];
5720 $enc_style = $encodingStyle;
5724 // NOTE: add error handling here
5725 // if serializeType returns false, then catch global error and fault
5726 if ($parametersArrayType == 'arraySimple') {
5727 $p = array_shift($parameters);
5728 $this->debug('calling serializeType w/indexed param');
5729 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5730 } elseif (isset($parameters[$name])) {
5731 $this->debug('calling serializeType w/named param');
5732 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5734 // TODO: only send nillable
5735 $this->debug('calling serializeType w/null param');
5736 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5740 $this->debug('no parameters passed.');
5743 $this->debug("serializeParameters returning: $xml");
5748 * serializes a PHP value according a given type definition
5750 * @param string $name name of value (part or element)
5751 * @param string $type XML schema type of value (type or element)
5752 * @param mixed $value a native PHP value (parameter value)
5753 * @param string $use use for part (encoded|literal)
5754 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5755 * @param boolean $unqualified a kludge for what should be XML namespace form handling
5756 * @return string value serialized as an XML string
5759 function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5761 $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5762 $this->appendDebug("value=" . $this->varDump($value));
5763 if($use == 'encoded' && $encodingStyle) {
5764 $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5767 // if a soapval has been supplied, let its type override the WSDL
5768 if (is_object($value) && get_class($value) == 'soapval') {
5769 if ($value->type_ns) {
5770 $type = $value->type_ns . ':' . $value->type;
5772 $this->debug("in serializeType: soapval overrides type to $type");
5773 } elseif ($value->type) {
5774 $type = $value->type;
5776 $this->debug("in serializeType: soapval overrides type to $type");
5779 $this->debug("in serializeType: soapval does not override type");
5781 $attrs = $value->attributes;
5782 $value = $value->value;
5783 $this->debug("in serializeType: soapval overrides value to $value");
5785 if (!is_array($value)) {
5786 $value['!'] = $value;
5788 foreach ($attrs as $n => $v) {
5789 $value['!' . $n] = $v;
5791 $this->debug("in serializeType: soapval provides attributes");
5798 if (strpos($type, ':')) {
5799 $uqType = substr($type, strrpos($type, ':') + 1);
5800 $ns = substr($type, 0, strrpos($type, ':'));
5801 $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5802 if ($this->getNamespaceFromPrefix($ns)) {
5803 $ns = $this->getNamespaceFromPrefix($ns);
5804 $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5807 if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5808 $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5809 if ($unqualified && $use == 'literal') {
5810 $elementNS = " xmlns=\"\"";
5814 if (is_null($value)) {
5815 if ($use == 'literal') {
5816 // TODO: depends on minOccurs
5817 $xml = "<$name$elementNS/>";
5819 // TODO: depends on nillable, which should be checked before calling this method
5820 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5822 $this->debug("in serializeType: returning: $xml");
5825 if ($uqType == 'Array') {
5826 // JBoss/Axis does this sometimes
5827 return $this->serialize_val($value, $name, false, false, false, false, $use);
5829 if ($uqType == 'boolean') {
5830 if ((is_string($value) && $value == 'false') || (! $value)) {
5836 if ($uqType == 'string' && gettype($value) == 'string') {
5837 $value = $this->expandEntities($value);
5839 if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5840 $value = sprintf("%.0lf", $value);
5843 // TODO: what about null/nil values?
5844 // check type isn't a custom type extending xmlschema namespace
5845 if (!$this->getTypeDef($uqType, $ns)) {
5846 if ($use == 'literal') {
5848 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5850 $xml = "<$name$elementNS>$value</$name>";
5853 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5855 $this->debug("in serializeType: returning: $xml");
5858 $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5859 } else if ($ns == 'http://xml.apache.org/xml-soap') {
5860 $this->debug('in serializeType: appears to be Apache SOAP type');
5861 if ($uqType == 'Map') {
5862 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5864 $this->debug('in serializeType: Add namespace for Apache SOAP type');
5865 $tt_prefix = 'ns' . rand(1000, 9999);
5866 $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5867 // force this to be added to usedNamespaces
5868 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5871 foreach($value as $k => $v) {
5872 $this->debug("serializing map element: key $k, value $v");
5873 $contents .= '<item>';
5874 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5875 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5876 $contents .= '</item>';
5878 if ($use == 'literal') {
5880 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5882 $xml = "<$name>$contents</$name>";
5885 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
5887 $this->debug("in serializeType: returning: $xml");
5890 $this->debug('in serializeType: Apache SOAP type, but only support Map');
5893 // TODO: should the type be compared to types in XSD, and the namespace
5894 // set to XSD if the type matches?
5895 $this->debug("in serializeType: No namespace for type $type");
5899 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
5900 $this->setError("$type ($uqType) is not a supported type.");
5901 $this->debug("in serializeType: $type ($uqType) is not a supported type.");
5904 $this->debug("in serializeType: found typeDef");
5905 $this->appendDebug('typeDef=' . $this->varDump($typeDef));
5906 if (substr($uqType, -1) == '^') {
5907 $uqType = substr($uqType, 0, -1);
5910 $phpType = $typeDef['phpType'];
5911 $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
5912 // if php type == struct, map value to the <all> element names
5913 if ($phpType == 'struct') {
5914 if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
5915 $elementName = $uqType;
5916 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5917 $elementNS = " xmlns=\"$ns\"";
5919 $elementNS = " xmlns=\"\"";
5922 $elementName = $name;
5924 $elementNS = " xmlns=\"\"";
5929 if (is_null($value)) {
5930 if ($use == 'literal') {
5931 // TODO: depends on minOccurs
5932 $xml = "<$elementName$elementNS/>";
5934 $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5936 $this->debug("in serializeType: returning: $xml");
5939 if (is_object($value)) {
5940 $value = get_object_vars($value);
5942 if (is_array($value)) {
5943 $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5944 if ($use == 'literal') {
5946 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
5948 $xml = "<$elementName$elementNS$elementAttrs>";
5951 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
5954 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5955 $xml .= "</$elementName>";
5957 $this->debug("in serializeType: phpType is struct, but value is not an array");
5958 $this->setError("phpType is struct, but value is not an array: see debug output for details");
5961 } elseif ($phpType == 'array') {
5962 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5963 $elementNS = " xmlns=\"$ns\"";
5966 $elementNS = " xmlns=\"\"";
5971 if (is_null($value)) {
5972 if ($use == 'literal') {
5973 // TODO: depends on minOccurs
5974 $xml = "<$name$elementNS/>";
5976 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
5977 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5979 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5981 $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
5983 $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
5985 $this->debug("in serializeType: returning: $xml");
5988 if (isset($typeDef['multidimensional'])) {
5990 foreach($value as $v) {
5991 $cols = ',' . sizeof($v);
5992 $nv = array_merge($nv, $v);
5998 if (is_array($value) && sizeof($value) >= 1) {
5999 $rows = sizeof($value);
6001 foreach($value as $k => $v) {
6002 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
6003 //if (strpos($typeDef['arrayType'], ':') ) {
6004 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6005 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6007 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6014 // TODO: for now, an empty value will be serialized as a zero element
6015 // array. Revisit this when coding the handling of null/nil values.
6016 if ($use == 'literal') {
6017 $xml = "<$name$elementNS>"
6021 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
6022 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6024 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6025 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
6029 } elseif ($phpType == 'scalar') {
6030 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6031 $elementNS = " xmlns=\"$ns\"";
6034 $elementNS = " xmlns=\"\"";
6039 if ($use == 'literal') {
6041 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6043 $xml = "<$name$elementNS>$value</$name>";
6046 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6049 $this->debug("in serializeType: returning: $xml");
6054 * serializes the attributes for a complexType
6056 * @param array $typeDef our internal representation of an XML schema type (or element)
6057 * @param mixed $value a native PHP value (parameter value)
6058 * @param string $ns the namespace of the type
6059 * @param string $uqType the local part of the type
6060 * @return string value serialized as an XML string
6063 function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
6065 if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6066 $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6067 if (is_array($value)) {
6069 } elseif (is_object($value)) {
6070 $xvalue = get_object_vars($value);
6072 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6075 foreach ($typeDef['attrs'] as $aName => $attrs) {
6076 if (isset($xvalue['!' . $aName])) {
6077 $xname = '!' . $aName;
6078 $this->debug("value provided for attribute $aName with key $xname");
6079 } elseif (isset($xvalue[$aName])) {
6081 $this->debug("value provided for attribute $aName with key $xname");
6082 } elseif (isset($attrs['default'])) {
6083 $xname = '!' . $aName;
6084 $xvalue[$xname] = $attrs['default'];
6085 $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6088 $this->debug("no value provided for attribute $aName");
6091 $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
6095 $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6097 if (isset($typeDef['extensionBase'])) {
6098 $ns = $this->getPrefix($typeDef['extensionBase']);
6099 $uqType = $this->getLocalPart($typeDef['extensionBase']);
6100 if ($this->getNamespaceFromPrefix($ns)) {
6101 $ns = $this->getNamespaceFromPrefix($ns);
6103 if ($typeDef = $this->getTypeDef($uqType, $ns)) {
6104 $this->debug("serialize attributes for extension base $ns:$uqType");
6105 $xml .= $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6107 $this->debug("extension base $ns:$uqType is not a supported type");
6114 * serializes the elements for a complexType
6116 * @param array $typeDef our internal representation of an XML schema type (or element)
6117 * @param mixed $value a native PHP value (parameter value)
6118 * @param string $ns the namespace of the type
6119 * @param string $uqType the local part of the type
6120 * @param string $use use for part (encoded|literal)
6121 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6122 * @return string value serialized as an XML string
6125 function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
6127 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6128 $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6129 if (is_array($value)) {
6131 } elseif (is_object($value)) {
6132 $xvalue = get_object_vars($value);
6134 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6137 // toggle whether all elements are present - ideally should validate against schema
6138 if (count($typeDef['elements']) != count($xvalue)){
6141 foreach ($typeDef['elements'] as $eName => $attrs) {
6142 if (!isset($xvalue[$eName])) {
6143 if (isset($attrs['default'])) {
6144 $xvalue[$eName] = $attrs['default'];
6145 $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6148 // if user took advantage of a minOccurs=0, then only serialize named parameters
6149 if (isset($optionals)
6150 && (!isset($xvalue[$eName]))
6151 && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
6153 if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
6154 $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6157 $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6160 if (isset($xvalue[$eName])) {
6161 $v = $xvalue[$eName];
6165 if (isset($attrs['form'])) {
6166 $unqualified = ($attrs['form'] == 'unqualified');
6168 $unqualified = false;
6170 if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
6172 foreach ($vv as $k => $v) {
6173 if (isset($attrs['type']) || isset($attrs['ref'])) {
6174 // serialize schema-defined type
6175 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6177 // serialize generic type (can this ever really happen?)
6178 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6179 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6183 if (isset($attrs['type']) || isset($attrs['ref'])) {
6184 // serialize schema-defined type
6185 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6187 // serialize generic type (can this ever really happen?)
6188 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6189 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6195 $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6197 if (isset($typeDef['extensionBase'])) {
6198 $ns = $this->getPrefix($typeDef['extensionBase']);
6199 $uqType = $this->getLocalPart($typeDef['extensionBase']);
6200 if ($this->getNamespaceFromPrefix($ns)) {
6201 $ns = $this->getNamespaceFromPrefix($ns);
6203 if ($typeDef = $this->getTypeDef($uqType, $ns)) {
6204 $this->debug("serialize elements for extension base $ns:$uqType");
6205 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6207 $this->debug("extension base $ns:$uqType is not a supported type");
6214 * adds an XML Schema complex type to the WSDL types
6216 * @param string $name
6217 * @param string $typeClass (complexType|simpleType|attribute)
6218 * @param string $phpType currently supported are array and struct (php assoc array)
6219 * @param string $compositor (all|sequence|choice)
6220 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6221 * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6222 * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6223 * @param string $arrayType as namespace:name (xsd:string)
6224 * @see nusoap_xmlschema
6227 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
6228 if (count($elements) > 0) {
6229 $eElements = array();
6230 foreach($elements as $n => $e){
6231 // expand each element
6233 foreach ($e as $k => $v) {
6234 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6235 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6238 $eElements[$n] = $ee;
6240 $elements = $eElements;
6243 if (count($attrs) > 0) {
6244 foreach($attrs as $n => $a){
6245 // expand each attribute
6246 foreach ($a as $k => $v) {
6247 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6248 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6256 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6257 $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
6259 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6260 $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
6264 * adds an XML Schema simple type to the WSDL types
6266 * @param string $name
6267 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6268 * @param string $typeClass (should always be simpleType)
6269 * @param string $phpType (should always be scalar)
6270 * @param array $enumeration array of values
6271 * @see nusoap_xmlschema
6274 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
6275 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6277 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6278 $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6282 * adds an element to the WSDL types
6284 * @param array $attrs attributes that must include name and type
6285 * @see nusoap_xmlschema
6288 function addElement($attrs) {
6289 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6290 $this->schemas[$typens][0]->addElement($attrs);
6294 * register an operation with the server
6296 * @param string $name operation (method) name
6297 * @param array $in assoc array of input values: key = param name, value = param type
6298 * @param array $out assoc array of output values: key = param name, value = param type
6299 * @param string $namespace optional The namespace for the operation
6300 * @param string $soapaction optional The soapaction for the operation
6301 * @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
6302 * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
6303 * @param string $documentation optional The description to include in the WSDL
6304 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6307 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
6308 if ($use == 'encoded' && $encodingStyle == '') {
6309 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6312 if ($style == 'document') {
6313 $elements = array();
6314 foreach ($in as $n => $t) {
6315 $elements[$n] = array('name' => $n, 'type' => $t);
6317 $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6318 $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
6319 $in = array('parameters' => 'tns:' . $name . '^');
6321 $elements = array();
6322 foreach ($out as $n => $t) {
6323 $elements[$n] = array('name' => $n, 'type' => $t);
6325 $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6326 $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
6327 $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
6331 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
6334 'binding' => $this->serviceName . 'Binding',
6335 'endpoint' => $this->endpoint,
6336 'soapAction' => $soapaction,
6340 'namespace' => $namespace,
6341 'encodingStyle' => $encodingStyle,
6342 'message' => $name . 'Request',
6346 'namespace' => $namespace,
6347 'encodingStyle' => $encodingStyle,
6348 'message' => $name . 'Response',
6350 'namespace' => $namespace,
6351 'transport' => 'http://schemas.xmlsoap.org/soap/http',
6352 'documentation' => $documentation);
6357 foreach($in as $pName => $pType)
6359 if(strpos($pType,':')) {
6360 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6362 $this->messages[$name.'Request'][$pName] = $pType;
6365 $this->messages[$name.'Request']= '0';
6369 foreach($out as $pName => $pType)
6371 if(strpos($pType,':')) {
6372 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6374 $this->messages[$name.'Response'][$pName] = $pType;
6377 $this->messages[$name.'Response']= '0';
6388 * nusoap_parser class parses SOAP XML messages into native PHP values
6390 * @author Dietrich Ayala <dietrich@ganx4.com>
6391 * @author Scott Nichol <snichol@users.sourceforge.net>
6392 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
6395 class nusoap_parser extends nusoap_base {
6398 var $xml_encoding = '';
6400 var $root_struct = '';
6401 var $root_struct_name = '';
6402 var $root_struct_namespace = '';
6403 var $root_header = '';
6404 var $document = ''; // incoming SOAP body (text)
6405 // determines where in the message we are (envelope,header,body,method)
6409 var $default_namespace = '';
6410 var $namespaces = array();
6411 var $message = array();
6414 var $fault_code = '';
6415 var $fault_str = '';
6416 var $fault_detail = '';
6417 var $depth_array = array();
6418 var $debug_flag = true;
6419 var $soapresponse = NULL; // parsed SOAP Body
6420 var $soapheader = NULL; // parsed SOAP Header
6421 var $responseHeaders = ''; // incoming SOAP headers (text)
6422 var $body_position = 0;
6423 // for multiref parsing:
6424 // array of id => pos
6426 // array of id => hrefs => pos
6427 var $multirefs = array();
6428 // toggle for auto-decoding element content
6429 var $decode_utf8 = true;
6432 * constructor that actually does the parsing
6434 * @param string $xml SOAP message
6435 * @param string $encoding character encoding scheme of message
6436 * @param string $method method for which XML is parsed (unused?)
6437 * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6440 function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
6441 parent::nusoap_base();
6443 $this->xml_encoding = $encoding;
6444 $this->method = $method;
6445 $this->decode_utf8 = $decode_utf8;
6447 // Check whether content has been read.
6449 // Check XML encoding
6450 $pos_xml = strpos($xml, '<?xml');
6451 if ($pos_xml !== FALSE) {
6452 $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6453 if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6454 $xml_encoding = $res[1];
6455 if (strtoupper($xml_encoding) != $encoding) {
6456 $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6458 if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
6459 $this->setError($err);
6462 // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6464 $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6467 $this->debug('No encoding specified in XML declaration');
6470 $this->debug('No XML declaration');
6472 $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
6473 // Create an XML parser - why not xml_parser_create_ns?
6474 $this->parser = xml_parser_create($this->xml_encoding);
6475 // Set the options for parsing the XML data.
6476 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6477 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6478 xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6479 // Set the object for the parser.
6480 xml_set_object($this->parser, $this);
6481 // Set the element handlers for the parser.
6482 xml_set_element_handler($this->parser, 'start_element','end_element');
6483 xml_set_character_data_handler($this->parser,'character_data');
6485 // Parse the XML file.
6486 if(!xml_parse($this->parser,$xml,true)){
6487 // Display an error message.
6488 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
6489 xml_get_current_line_number($this->parser),
6490 xml_error_string(xml_get_error_code($this->parser)));
6492 $this->debug("XML payload:\n" . $xml);
6493 $this->setError($err);
6495 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
6497 $this->soapresponse = $this->message[$this->root_struct]['result'];
6499 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
6500 $this->soapheader = $this->message[$this->root_header]['result'];
6502 // resolve hrefs/ids
6503 if(sizeof($this->multirefs) > 0){
6504 foreach($this->multirefs as $id => $hrefs){
6505 $this->debug('resolving multirefs for id: '.$id);
6506 $idVal = $this->buildVal($this->ids[$id]);
6507 if (is_array($idVal) && isset($idVal['!id'])) {
6508 unset($idVal['!id']);
6510 foreach($hrefs as $refPos => $ref){
6511 $this->debug('resolving href at pos '.$refPos);
6512 $this->multirefs[$id][$refPos] = $idVal;
6517 xml_parser_free($this->parser);
6519 $this->debug('xml was empty, didn\'t parse!');
6520 $this->setError('xml was empty, didn\'t parse!');
6525 * start-element handler
6527 * @param resource $parser XML parser object
6528 * @param string $name element name
6529 * @param array $attrs associative array of attributes
6532 function start_element($parser, $name, $attrs) {
6533 // position in a total number of elements, starting from 0
6534 // update class level pos
6535 $pos = $this->position++;
6537 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
6538 // depth = how many levels removed from root?
6539 // set mine as current global depth and increment global depth value
6540 $this->message[$pos]['depth'] = $this->depth++;
6542 // else add self as child to whoever the current parent is
6544 $this->message[$this->parent]['children'] .= '|'.$pos;
6547 $this->message[$pos]['parent'] = $this->parent;
6548 // set self as current parent
6549 $this->parent = $pos;
6550 // set self as current value for this depth
6551 $this->depth_array[$this->depth] = $pos;
6552 // get element prefix
6553 if(strpos($name,':')){
6555 $prefix = substr($name,0,strpos($name,':'));
6556 // get unqualified name
6557 $name = substr(strstr($name,':'),1);
6560 if($name == 'Envelope'){
6561 $this->status = 'envelope';
6562 } elseif($name == 'Header' && $this->status = 'envelope'){
6563 $this->root_header = $pos;
6564 $this->status = 'header';
6565 } elseif($name == 'Body' && $this->status = 'envelope'){
6566 $this->status = 'body';
6567 $this->body_position = $pos;
6569 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
6570 $this->status = 'method';
6571 $this->root_struct_name = $name;
6572 $this->root_struct = $pos;
6573 $this->message[$pos]['type'] = 'struct';
6574 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6577 $this->message[$pos]['status'] = $this->status;
6579 $this->message[$pos]['name'] = htmlspecialchars($name);
6581 $this->message[$pos]['attrs'] = $attrs;
6583 // loop through atts, logging ns and type declarations
6585 foreach($attrs as $key => $value){
6586 $key_prefix = $this->getPrefix($key);
6587 $key_localpart = $this->getLocalPart($key);
6588 // if ns declarations, add to class level array of valid namespaces
6589 if($key_prefix == 'xmlns'){
6590 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
6591 $this->XMLSchemaVersion = $value;
6592 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6593 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
6595 $this->namespaces[$key_localpart] = $value;
6596 // set method namespace
6597 if($name == $this->root_struct_name){
6598 $this->methodNamespace = $value;
6600 // if it's a type declaration, set type
6601 } elseif($key_localpart == 'type'){
6602 if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
6603 // do nothing: already processed arrayType
6605 $value_prefix = $this->getPrefix($value);
6606 $value_localpart = $this->getLocalPart($value);
6607 $this->message[$pos]['type'] = $value_localpart;
6608 $this->message[$pos]['typePrefix'] = $value_prefix;
6609 if(isset($this->namespaces[$value_prefix])){
6610 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6611 } else if(isset($attrs['xmlns:'.$value_prefix])) {
6612 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6614 // should do something here with the namespace of specified type?
6616 } elseif($key_localpart == 'arrayType'){
6617 $this->message[$pos]['type'] = 'array';
6618 /* do arrayType ereg here
6619 [1] arrayTypeValue ::= atype asize
6620 [2] atype ::= QName rank*
6621 [3] rank ::= '[' (',')* ']'
6622 [4] asize ::= '[' length~ ']'
6623 [5] length ::= nextDimension* Digit+
6624 [6] nextDimension ::= Digit+ ','
6626 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
6627 if(ereg($expr,$value,$regs)){
6628 $this->message[$pos]['typePrefix'] = $regs[1];
6629 $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6630 if (isset($this->namespaces[$regs[1]])) {
6631 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6632 } else if (isset($attrs['xmlns:'.$regs[1]])) {
6633 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6635 $this->message[$pos]['arrayType'] = $regs[2];
6636 $this->message[$pos]['arraySize'] = $regs[3];
6637 $this->message[$pos]['arrayCols'] = $regs[4];
6639 // specifies nil value (or not)
6640 } elseif ($key_localpart == 'nil'){
6641 $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6642 // some other attribute
6643 } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6644 $this->message[$pos]['xattrs']['!' . $key] = $value;
6647 if ($key == 'xmlns') {
6648 $this->default_namespace = $value;
6652 $this->ids[$value] = $pos;
6655 if($key_localpart == 'root' && $value == 1){
6656 $this->status = 'method';
6657 $this->root_struct_name = $name;
6658 $this->root_struct = $pos;
6659 $this->debug("found root struct $this->root_struct_name, pos $pos");
6662 $attstr .= " $key=\"$value\"";
6664 // get namespace - must be done after namespace atts are processed
6666 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6667 $this->default_namespace = $this->namespaces[$prefix];
6669 $this->message[$pos]['namespace'] = $this->default_namespace;
6671 if($this->status == 'header'){
6672 if ($this->root_header != $pos) {
6673 $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6675 } elseif($this->root_struct_name != ''){
6676 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6681 * end-element handler
6683 * @param resource $parser XML parser object
6684 * @param string $name element name
6687 function end_element($parser, $name) {
6688 // position of current element is equal to the last value left in depth_array for my depth
6689 $pos = $this->depth_array[$this->depth--];
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 // build to native type
6700 if(isset($this->body_position) && $pos > $this->body_position){
6701 // deal w/ multirefs
6702 if(isset($this->message[$pos]['attrs']['href'])){
6704 $id = substr($this->message[$pos]['attrs']['href'],1);
6705 // add placeholder to href array
6706 $this->multirefs[$id][$pos] = 'placeholder';
6707 // add set a reference to it as the result value
6708 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6709 // build complexType values
6710 } elseif($this->message[$pos]['children'] != ''){
6711 // if result has already been generated (struct/array)
6712 if(!isset($this->message[$pos]['result'])){
6713 $this->message[$pos]['result'] = $this->buildVal($pos);
6715 // build complexType values of attributes and possibly simpleContent
6716 } elseif (isset($this->message[$pos]['xattrs'])) {
6717 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6718 $this->message[$pos]['xattrs']['!'] = null;
6719 } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6720 if (isset($this->message[$pos]['type'])) {
6721 $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'] : '');
6723 $parent = $this->message[$pos]['parent'];
6724 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6725 $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6727 $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6731 $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6732 // set value of simpleType (or nil complexType)
6734 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6735 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6736 $this->message[$pos]['xattrs']['!'] = null;
6737 } elseif (isset($this->message[$pos]['type'])) {
6738 $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'] : '');
6740 $parent = $this->message[$pos]['parent'];
6741 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6742 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6744 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6748 /* add value to parent's result, if parent is struct/array
6749 $parent = $this->message[$pos]['parent'];
6750 if($this->message[$parent]['type'] != 'map'){
6751 if(strtolower($this->message[$parent]['type']) == 'array'){
6752 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6754 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6762 if($this->status == 'header'){
6763 if ($this->root_header != $pos) {
6764 $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6766 } elseif($pos >= $this->root_struct){
6767 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6770 if($pos == $this->root_struct){
6771 $this->status = 'body';
6772 $this->root_struct_namespace = $this->message[$pos]['namespace'];
6773 } elseif($name == 'Body'){
6774 $this->status = 'envelope';
6775 } elseif($name == 'Header'){
6776 $this->status = 'envelope';
6777 } elseif($name == 'Envelope'){
6780 // set parent back to my parent
6781 $this->parent = $this->message[$pos]['parent'];
6785 * element content handler
6787 * @param resource $parser XML parser object
6788 * @param string $data element content
6791 function character_data($parser, $data){
6792 $pos = $this->depth_array[$this->depth];
6793 if ($this->xml_encoding=='UTF-8'){
6794 // TODO: add an option to disable this for folks who want
6795 // raw UTF-8 that, e.g., might not map to iso-8859-1
6796 // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6797 if($this->decode_utf8){
6798 $data = utf8_decode($data);
6801 $this->message[$pos]['cdata'] .= $data;
6803 if($this->status == 'header'){
6804 $this->responseHeaders .= $data;
6806 $this->document .= $data;
6811 * get the parsed message (SOAP Body)
6815 * @deprecated use get_soapbody instead
6817 function get_response(){
6818 return $this->soapresponse;
6822 * get the parsed SOAP Body (NULL if there was none)
6827 function get_soapbody(){
6828 return $this->soapresponse;
6832 * get the parsed SOAP Header (NULL if there was none)
6837 function get_soapheader(){
6838 return $this->soapheader;
6842 * get the unparsed SOAP Header
6844 * @return string XML or empty if no Header
6847 function getHeaders(){
6848 return $this->responseHeaders;
6852 * decodes simple types into PHP variables
6854 * @param string $value value to decode
6855 * @param string $type XML type to decode
6856 * @param string $typens XML type namespace to decode
6857 * @return mixed PHP value
6860 function decodeSimple($value, $type, $typens) {
6861 // TODO: use the namespace!
6862 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
6863 return (string) $value;
6865 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
6866 return (int) $value;
6868 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
6869 return (double) $value;
6871 if ($type == 'boolean') {
6872 if (strtolower($value) == 'false' || strtolower($value) == 'f') {
6875 return (boolean) $value;
6877 if ($type == 'base64' || $type == 'base64Binary') {
6878 $this->debug('Decode base64 value');
6879 return base64_decode($value);
6881 // obscure numeric types
6882 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
6883 || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
6884 || $type == 'unsignedInt'
6885 || $type == 'unsignedShort' || $type == 'unsignedByte') {
6886 return (int) $value;
6888 // bogus: parser treats array with no elements as a simple type
6889 if ($type == 'array') {
6893 return (string) $value;
6897 * builds response structures for compound values (arrays/structs)
6900 * @param integer $pos position in node tree
6901 * @return mixed PHP value
6904 function buildVal($pos){
6905 if(!isset($this->message[$pos]['type'])){
6906 $this->message[$pos]['type'] = '';
6908 $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
6909 // if there are children...
6910 if($this->message[$pos]['children'] != ''){
6911 $this->debug('in buildVal, there are children');
6912 $children = explode('|',$this->message[$pos]['children']);
6913 array_shift($children); // knock off empty
6915 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
6918 foreach($children as $child_pos){
6919 $this->debug("in buildVal, got an MD array element: $r, $c");
6920 $params[$r][] = $this->message[$child_pos]['result'];
6922 if($c == $this->message[$pos]['arrayCols']){
6928 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
6929 $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
6930 foreach($children as $child_pos){
6931 $params[] = &$this->message[$child_pos]['result'];
6933 // apache Map type: java hashtable
6934 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
6935 $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
6936 foreach($children as $child_pos){
6937 $kv = explode("|",$this->message[$child_pos]['children']);
6938 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
6940 // generic compound type
6941 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
6943 // Apache Vector type: treat as an array
6944 $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']);
6945 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
6951 foreach($children as $child_pos){
6953 $params[] = &$this->message[$child_pos]['result'];
6955 if (isset($params[$this->message[$child_pos]['name']])) {
6956 // de-serialize repeated element name into an array
6957 if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
6958 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
6960 $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
6962 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
6967 if (isset($this->message[$pos]['xattrs'])) {
6968 $this->debug('in buildVal, handling attributes');
6969 foreach ($this->message[$pos]['xattrs'] as $n => $v) {
6973 // handle simpleContent
6974 if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6975 $this->debug('in buildVal, handling simpleContent');
6976 if (isset($this->message[$pos]['type'])) {
6977 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6979 $parent = $this->message[$pos]['parent'];
6980 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6981 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6983 $params['!'] = $this->message[$pos]['cdata'];
6987 $ret = is_array($params) ? $params : array();
6988 $this->debug('in buildVal, return:');
6989 $this->appendDebug($this->varDump($ret));
6992 $this->debug('in buildVal, no children, building scalar');
6993 $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
6994 if (isset($this->message[$pos]['type'])) {
6995 $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6996 $this->debug("in buildVal, return: $ret");
6999 $parent = $this->message[$pos]['parent'];
7000 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7001 $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7002 $this->debug("in buildVal, return: $ret");
7005 $ret = $this->message[$pos]['cdata'];
7006 $this->debug("in buildVal, return: $ret");
7013 * Backward compatibility
7015 class soap_parser extends nusoap_parser {
7024 * [nu]soapclient higher level class for easy usage.
7028 * // instantiate client with server info
7029 * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
7031 * // call method, get results
7032 * echo $soapclient->call( string methodname [ ,array parameters] );
7035 * unset($soapclient);
7037 * @author Dietrich Ayala <dietrich@ganx4.com>
7038 * @author Scott Nichol <snichol@users.sourceforge.net>
7039 * @version $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
7042 class nusoap_client extends nusoap_base {
7044 var $username = ''; // Username for HTTP authentication
7045 var $password = ''; // Password for HTTP authentication
7046 var $authtype = ''; // Type of HTTP authentication
7047 var $certRequest = array(); // Certificate for HTTP SSL authentication
7048 var $requestHeaders = false; // SOAP headers in request (text)
7049 var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
7050 var $responseHeader = NULL; // SOAP Header from response (parsed)
7051 var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
7053 var $forceEndpoint = ''; // overrides WSDL endpoint
7054 var $proxyhost = '';
7055 var $proxyport = '';
7056 var $proxyusername = '';
7057 var $proxypassword = '';
7058 var $xml_encoding = ''; // character set encoding of incoming (response) messages
7059 var $http_encoding = false;
7060 var $timeout = 0; // HTTP connection timeout
7061 var $response_timeout = 30; // HTTP response timeout
7062 var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
7063 var $persistentConnection = false;
7064 var $defaultRpcParams = false; // This is no longer used
7065 var $request = ''; // HTTP request
7066 var $response = ''; // HTTP response
7067 var $responseData = ''; // SOAP payload of response
7068 var $cookies = array(); // Cookies from response or for request
7069 var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
7070 var $operations = array(); // WSDL operations, empty for WSDL initialization error
7071 var $curl_options = array(); // User-specified cURL options
7072 var $bindingType = ''; // WSDL operation binding type
7073 var $use_curl = false; // whether to always try to use cURL
7076 * fault related variables
7102 * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
7103 * @param bool $wsdl optional, set to true if using WSDL
7104 * @param int $portName optional portName in WSDL document
7105 * @param string $proxyhost
7106 * @param string $proxyport
7107 * @param string $proxyusername
7108 * @param string $proxypassword
7109 * @param integer $timeout set the connection timeout
7110 * @param integer $response_timeout set the response timeout
7113 function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
7114 parent::nusoap_base();
7115 $this->endpoint = $endpoint;
7116 $this->proxyhost = $proxyhost;
7117 $this->proxyport = $proxyport;
7118 $this->proxyusername = $proxyusername;
7119 $this->proxypassword = $proxypassword;
7120 $this->timeout = $timeout;
7121 $this->response_timeout = $response_timeout;
7123 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7124 $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7128 if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
7129 $this->wsdl = $endpoint;
7130 $this->endpoint = $this->wsdl->wsdl;
7131 $this->wsdlFile = $this->endpoint;
7132 $this->debug('existing wsdl instance created from ' . $this->endpoint);
7135 $this->wsdlFile = $this->endpoint;
7137 $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7139 $this->endpointType = 'wsdl';
7141 $this->debug("instantiate SOAP with endpoint at $endpoint");
7142 $this->endpointType = 'soap';
7147 * calls method, returns PHP native type
7149 * @param string $operation SOAP server URL or path
7150 * @param mixed $params An array, associative or simple, of the parameters
7151 * for the method call, or a string that is the XML
7152 * for the call. For rpc style, this call will
7153 * wrap the XML in a tag named after the method, as
7154 * well as the SOAP Envelope and Body. For document
7155 * style, this will only wrap with the Envelope and Body.
7156 * IMPORTANT: when using an array with document style,
7157 * in which case there
7158 * is really one parameter, the root of the fragment
7159 * used in the call, which encloses what programmers
7160 * normally think of parameters. A parameter array
7161 * *must* include the wrapper.
7162 * @param string $namespace optional method namespace (WSDL can override)
7163 * @param string $soapAction optional SOAPAction value (WSDL can override)
7164 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7165 * @param boolean $rpcParams optional (no longer used)
7166 * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7167 * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
7168 * @return mixed response from SOAP call
7171 function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
7172 $this->operation = $operation;
7173 $this->fault = false;
7174 $this->setError('');
7175 $this->request = '';
7176 $this->response = '';
7177 $this->responseData = '';
7178 $this->faultstring = '';
7179 $this->faultcode = '';
7180 $this->opData = array();
7182 $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7183 $this->appendDebug('params=' . $this->varDump($params));
7184 $this->appendDebug('headers=' . $this->varDump($headers));
7186 $this->requestHeaders = $headers;
7188 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7190 if ($this->getError())
7193 // serialize parameters
7194 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
7195 // use WSDL for operation
7196 $this->opData = $opData;
7197 $this->debug("found operation");
7198 $this->appendDebug('opData=' . $this->varDump($opData));
7199 if (isset($opData['soapAction'])) {
7200 $soapAction = $opData['soapAction'];
7202 if (! $this->forceEndpoint) {
7203 $this->endpoint = $opData['endpoint'];
7205 $this->endpoint = $this->forceEndpoint;
7207 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7208 $style = $opData['style'];
7209 $use = $opData['input']['use'];
7210 // add ns to ns array
7211 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
7212 $nsPrefix = 'ns' . rand(1000, 9999);
7213 $this->wsdl->namespaces[$nsPrefix] = $namespace;
7215 $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7216 // serialize payload
7217 if (is_string($params)) {
7218 $this->debug("serializing param string for WSDL operation $operation");
7220 } elseif (is_array($params)) {
7221 $this->debug("serializing param array for WSDL operation $operation");
7222 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType);
7224 $this->debug('params must be array or string');
7225 $this->setError('params must be array or string');
7228 $usedNamespaces = $this->wsdl->usedNamespaces;
7229 if (isset($opData['input']['encodingStyle'])) {
7230 $encodingStyle = $opData['input']['encodingStyle'];
7232 $encodingStyle = '';
7234 $this->appendDebug($this->wsdl->getDebug());
7235 $this->wsdl->clearDebug();
7236 if ($errstr = $this->wsdl->getError()) {
7237 $this->debug('got wsdl error: '.$errstr);
7238 $this->setError('wsdl error: '.$errstr);
7241 } elseif($this->endpointType == 'wsdl') {
7242 // operation not in WSDL
7243 $this->appendDebug($this->wsdl->getDebug());
7244 $this->wsdl->clearDebug();
7245 $this->setError( 'operation '.$operation.' not present.');
7246 $this->debug("operation '$operation' not present.");
7250 //$this->namespaces['ns1'] = $namespace;
7251 $nsPrefix = 'ns' . rand(1000, 9999);
7254 if (is_string($params)) {
7255 $this->debug("serializing param string for operation $operation");
7257 } elseif (is_array($params)) {
7258 $this->debug("serializing param array for operation $operation");
7259 foreach($params as $k => $v){
7260 $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
7263 $this->debug('params must be array or string');
7264 $this->setError('params must be array or string');
7267 $usedNamespaces = array();
7268 if ($use == 'encoded') {
7269 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7271 $encodingStyle = '';
7274 // wrap RPC calls with method element
7275 if ($style == 'rpc') {
7276 if ($use == 'literal') {
7277 $this->debug("wrapping RPC request with literal method element");
7279 // 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
7280 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7282 "</$nsPrefix:$operation>";
7284 $payload = "<$operation>" . $payload . "</$operation>";
7287 $this->debug("wrapping RPC request with encoded method element");
7289 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7291 "</$nsPrefix:$operation>";
7293 $payload = "<$operation>" .
7299 // serialize envelope
7300 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
7301 $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7302 $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
7304 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
7305 if($errstr = $this->getError()){
7306 $this->debug('Error: '.$errstr);
7309 $this->return = $return;
7310 $this->debug('sent message successfully and got a(n) '.gettype($return));
7311 $this->appendDebug('return=' . $this->varDump($return));
7314 if(is_array($return) && isset($return['faultcode'])){
7315 $this->debug('got fault');
7316 $this->setError($return['faultcode'].': '.$return['faultstring']);
7317 $this->fault = true;
7318 foreach($return as $k => $v){
7320 $this->debug("$k = $v<br>");
7323 } elseif ($style == 'document') {
7324 // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7325 // we are only going to return the first part here...sorry about that
7328 // array of return values
7329 if(is_array($return)){
7330 // multiple 'out' parameters, which we return wrapped up
7332 if(sizeof($return) > 1){
7335 // single 'out' parameter (normally the return value)
7336 $return = array_shift($return);
7337 $this->debug('return shifted value: ');
7338 $this->appendDebug($this->varDump($return));
7340 // nothing returned (ie, echoVoid)
7349 * check WSDL passed as an instance or pulled from an endpoint
7353 function checkWSDL() {
7354 $this->appendDebug($this->wsdl->getDebug());
7355 $this->wsdl->clearDebug();
7356 $this->debug('checkWSDL');
7358 if ($errstr = $this->wsdl->getError()) {
7359 $this->debug('got wsdl error: '.$errstr);
7360 $this->setError('wsdl error: '.$errstr);
7361 } elseif ($this->operations = $this->wsdl->getOperations('soap')) {
7362 $this->bindingType = 'soap';
7363 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7364 } elseif ($this->operations = $this->wsdl->getOperations('soap12')) {
7365 $this->bindingType = 'soap12';
7366 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7367 $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7369 $this->debug('getOperations returned false');
7370 $this->setError('no operations defined in the WSDL document!');
7375 * instantiate wsdl object and parse wsdl file
7379 function loadWSDL() {
7380 $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile);
7381 $this->wsdl =& new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl);
7382 $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7383 $this->wsdl->fetchWSDL($this->wsdlFile);
7388 * get available data pertaining to an operation
7390 * @param string $operation operation name
7391 * @return array array of data pertaining to the operation
7394 function getOperationData($operation){
7395 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7397 if ($this->getError())
7400 if(isset($this->operations[$operation])){
7401 return $this->operations[$operation];
7403 $this->debug("No data for operation: $operation");
7407 * send the SOAP message
7409 * Note: if the operation has multiple return values
7410 * the return value of this method will be an array
7413 * @param string $msg a SOAPx4 soapmsg object
7414 * @param string $soapaction SOAPAction value
7415 * @param integer $timeout set connection timeout in seconds
7416 * @param integer $response_timeout set response timeout in seconds
7417 * @return mixed native PHP types.
7420 function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
7421 $this->checkCookies();
7425 case ereg('^http',$this->endpoint):
7426 $this->debug('transporting via HTTP');
7427 if($this->persistentConnection == true && is_object($this->persistentConnection)){
7428 $http =& $this->persistentConnection;
7430 $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7431 if ($this->persistentConnection) {
7432 $http->usePersistentConnection();
7435 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7436 $http->setSOAPAction($soapaction);
7437 if($this->proxyhost && $this->proxyport){
7438 $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
7440 if($this->authtype != '') {
7441 $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
7443 if($this->http_encoding != ''){
7444 $http->setEncoding($this->http_encoding);
7446 $this->debug('sending message, length='.strlen($msg));
7447 if(ereg('^http:',$this->endpoint)){
7448 //if(strpos($this->endpoint,'http:')){
7449 $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
7450 } elseif(ereg('^https',$this->endpoint)){
7451 //} elseif(strpos($this->endpoint,'https:')){
7452 //if(phpversion() == '4.3.0-dev'){
7453 //$response = $http->send($msg,$timeout,$response_timeout);
7454 //$this->request = $http->outgoing_payload;
7455 //$this->response = $http->incoming_payload;
7457 $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
7459 $this->setError('no http/s in endpoint url');
7461 $this->request = $http->outgoing_payload;
7462 $this->response = $http->incoming_payload;
7463 $this->appendDebug($http->getDebug());
7464 $this->UpdateCookies($http->incoming_cookies);
7466 // save transport object if using persistent connections
7467 if ($this->persistentConnection) {
7468 $http->clearDebug();
7469 if (!is_object($this->persistentConnection)) {
7470 $this->persistentConnection = $http;
7474 if($err = $http->getError()){
7475 $this->setError('HTTP Error: '.$err);
7477 } elseif($this->getError()){
7480 $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
7481 return $this->parseResponse($http->incoming_headers, $this->responseData);
7485 $this->setError('no transport found, or selected transport is not yet supported!');
7492 * processes SOAP message returned from server
7494 * @param array $headers The HTTP headers
7495 * @param string $data unprocessed response data from server
7496 * @return mixed value of the message, decoded into a PHP type
7499 function parseResponse($headers, $data) {
7500 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
7501 $this->appendDebug($this->varDump($headers));
7502 if (!strstr($headers['content-type'], 'text/xml')) {
7503 $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7506 if (strpos($headers['content-type'], '=')) {
7507 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
7508 $this->debug('Got response encoding: ' . $enc);
7509 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
7510 $this->xml_encoding = strtoupper($enc);
7512 $this->xml_encoding = 'US-ASCII';
7515 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7516 $this->xml_encoding = 'ISO-8859-1';
7518 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
7519 $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
7520 // add parser debug data to our debug
7521 $this->appendDebug($parser->getDebug());
7523 if($errstr = $parser->getError()){
7524 $this->setError( $errstr);
7525 // destroy the parser object
7530 $this->responseHeaders = $parser->getHeaders();
7532 $this->responseHeader = $parser->get_soapheader();
7533 // get decoded message
7534 $return = $parser->get_soapbody();
7535 // add document for doclit support
7536 $this->document = $parser->document;
7537 // destroy the parser object
7539 // return decode message
7545 * sets user-specified cURL options
7547 * @param mixed $option The cURL option (always integer?)
7548 * @param mixed $value The cURL option value
7551 function setCurlOption($option, $value) {
7552 $this->debug("setCurlOption option=$option, value=");
7553 $this->appendDebug($this->varDump($value));
7554 $this->curl_options[$option] = $value;
7558 * sets the SOAP endpoint, which can override WSDL
7560 * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
7563 function setEndpoint($endpoint) {
7564 $this->debug("setEndpoint(\"$endpoint\")");
7565 $this->forceEndpoint = $endpoint;
7569 * set the SOAP headers
7571 * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
7574 function setHeaders($headers){
7575 $this->debug("setHeaders headers=");
7576 $this->appendDebug($this->varDump($headers));
7577 $this->requestHeaders = $headers;
7581 * get the SOAP response headers (namespace resolution incomplete)
7586 function getHeaders(){
7587 return $this->responseHeaders;
7591 * get the SOAP response Header (parsed)
7596 function getHeader(){
7597 return $this->responseHeader;
7601 * set proxy info here
7603 * @param string $proxyhost
7604 * @param string $proxyport
7605 * @param string $proxyusername
7606 * @param string $proxypassword
7609 function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
7610 $this->proxyhost = $proxyhost;
7611 $this->proxyport = $proxyport;
7612 $this->proxyusername = $proxyusername;
7613 $this->proxypassword = $proxypassword;
7617 * if authenticating, set user credentials here
7619 * @param string $username
7620 * @param string $password
7621 * @param string $authtype (basic|digest|certificate|ntlm)
7622 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
7625 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
7626 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
7627 $this->appendDebug($this->varDump($certRequest));
7628 $this->username = $username;
7629 $this->password = $password;
7630 $this->authtype = $authtype;
7631 $this->certRequest = $certRequest;
7637 * @param string $enc HTTP encoding
7640 function setHTTPEncoding($enc='gzip, deflate'){
7641 $this->debug("setHTTPEncoding(\"$enc\")");
7642 $this->http_encoding = $enc;
7646 * Set whether to try to use cURL connections if possible
7648 * @param boolean $use Whether to try to use cURL
7651 function setUseCURL($use) {
7652 $this->debug("setUseCURL($use)");
7653 $this->use_curl = $use;
7657 * use HTTP persistent connections if possible
7661 function useHTTPPersistentConnection(){
7662 $this->debug("useHTTPPersistentConnection");
7663 $this->persistentConnection = true;
7667 * gets the default RPC parameter setting.
7668 * If true, default is that call params are like RPC even for document style.
7669 * Each call() can override this value.
7671 * This is no longer used.
7677 function getDefaultRpcParams() {
7678 return $this->defaultRpcParams;
7682 * sets the default RPC parameter setting.
7683 * If true, default is that call params are like RPC even for document style
7684 * Each call() can override this value.
7686 * This is no longer used.
7688 * @param boolean $rpcParams
7692 function setDefaultRpcParams($rpcParams) {
7693 $this->defaultRpcParams = $rpcParams;
7697 * dynamically creates an instance of a proxy class,
7698 * allowing user to directly call methods from wsdl
7700 * @return object soap_proxy object
7703 function getProxy() {
7705 $evalStr = $this->_getProxyClassCode($r);
7706 //$this->debug("proxy class: $evalStr");
7707 if ($this->getError()) {
7708 $this->debug("Error from _getProxyClassCode, so return NULL");
7713 // instantiate proxy object
7714 eval("\$proxy = new nusoap_proxy_$r('');");
7715 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
7716 $proxy->endpointType = 'wsdl';
7717 $proxy->wsdlFile = $this->wsdlFile;
7718 $proxy->wsdl = $this->wsdl;
7719 $proxy->operations = $this->operations;
7720 $proxy->defaultRpcParams = $this->defaultRpcParams;
7721 // transfer other state
7722 $proxy->soap_defencoding = $this->soap_defencoding;
7723 $proxy->username = $this->username;
7724 $proxy->password = $this->password;
7725 $proxy->authtype = $this->authtype;
7726 $proxy->certRequest = $this->certRequest;
7727 $proxy->requestHeaders = $this->requestHeaders;
7728 $proxy->endpoint = $this->endpoint;
7729 $proxy->forceEndpoint = $this->forceEndpoint;
7730 $proxy->proxyhost = $this->proxyhost;
7731 $proxy->proxyport = $this->proxyport;
7732 $proxy->proxyusername = $this->proxyusername;
7733 $proxy->proxypassword = $this->proxypassword;
7734 $proxy->http_encoding = $this->http_encoding;
7735 $proxy->timeout = $this->timeout;
7736 $proxy->response_timeout = $this->response_timeout;
7737 $proxy->persistentConnection = &$this->persistentConnection;
7738 $proxy->decode_utf8 = $this->decode_utf8;
7739 $proxy->curl_options = $this->curl_options;
7740 $proxy->bindingType = $this->bindingType;
7741 $proxy->use_curl = $this->use_curl;
7746 * dynamically creates proxy class code
7748 * @return string PHP/NuSOAP code for the proxy class
7751 function _getProxyClassCode($r) {
7752 $this->debug("in getProxy endpointType=$this->endpointType");
7753 $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
7754 if ($this->endpointType != 'wsdl') {
7755 $evalStr = 'A proxy can only be created for a WSDL client';
7756 $this->setError($evalStr);
7757 $evalStr = "echo \"$evalStr\";";
7760 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7762 if ($this->getError()) {
7763 return "echo \"" . $this->getError() . "\";";
7767 foreach ($this->operations as $operation => $opData) {
7768 if ($operation != '') {
7769 // create param string and param comment string
7770 if (sizeof($opData['input']['parts']) > 0) {
7772 $paramArrayStr = '';
7773 $paramCommentStr = '';
7774 foreach ($opData['input']['parts'] as $name => $type) {
7775 $paramStr .= "\$$name, ";
7776 $paramArrayStr .= "'$name' => \$$name, ";
7777 $paramCommentStr .= "$type \$$name, ";
7779 $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
7780 $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
7781 $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
7784 $paramArrayStr = '';
7785 $paramCommentStr = 'void';
7787 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
7788 $evalStr .= "// $paramCommentStr
7789 function " . str_replace('.', '__', $operation) . "($paramStr) {
7790 \$params = array($paramArrayStr);
7791 return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
7795 unset($paramCommentStr);
7798 $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client {
7805 * dynamically creates proxy class code
7807 * @return string PHP/NuSOAP code for the proxy class
7810 function getProxyClassCode() {
7812 return $this->_getProxyClassCode($r);
7816 * gets the HTTP body for the current request.
7818 * @param string $soapmsg The SOAP payload
7819 * @return string The HTTP body, which includes the SOAP payload
7822 function getHTTPBody($soapmsg) {
7827 * gets the HTTP content type for the current request.
7829 * Note: getHTTPBody must be called before this.
7831 * @return string the HTTP content type for the current request.
7834 function getHTTPContentType() {
7839 * gets the HTTP content type charset for the current request.
7840 * returns false for non-text content types.
7842 * Note: getHTTPBody must be called before this.
7844 * @return string the HTTP content type charset for the current request.
7847 function getHTTPContentTypeCharset() {
7848 return $this->soap_defencoding;
7852 * whether or not parser should decode utf8 element content
7854 * @return always returns true
7857 function decodeUTF8($bool){
7858 $this->decode_utf8 = $bool;
7863 * adds a new Cookie into $this->cookies array
7865 * @param string $name Cookie Name
7866 * @param string $value Cookie Value
7867 * @return boolean if cookie-set was successful returns true, else false
7870 function setCookie($name, $value) {
7871 if (strlen($name) == 0) {
7874 $this->cookies[] = array('name' => $name, 'value' => $value);
7881 * @return array with all internal cookies
7884 function getCookies() {
7885 return $this->cookies;
7889 * checks all Cookies and delete those which are expired
7891 * @return boolean always return true
7894 function checkCookies() {
7895 if (sizeof($this->cookies) == 0) {
7898 $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
7899 $curr_cookies = $this->cookies;
7900 $this->cookies = array();
7901 foreach ($curr_cookies as $cookie) {
7902 if (! is_array($cookie)) {
7903 $this->debug('Remove cookie that is not an array');
7906 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
7907 if (strtotime($cookie['expires']) > time()) {
7908 $this->cookies[] = $cookie;
7910 $this->debug('Remove expired cookie ' . $cookie['name']);
7913 $this->cookies[] = $cookie;
7916 $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
7921 * updates the current cookies with a new set
7923 * @param array $cookies new cookies with which to update current ones
7924 * @return boolean always return true
7927 function UpdateCookies($cookies) {
7928 if (sizeof($this->cookies) == 0) {
7929 // no existing cookies: take whatever is new
7930 if (sizeof($cookies) > 0) {
7931 $this->debug('Setting new cookie(s)');
7932 $this->cookies = $cookies;
7936 if (sizeof($cookies) == 0) {
7937 // no new cookies: keep what we've got
7941 foreach ($cookies as $newCookie) {
7942 if (!is_array($newCookie)) {
7945 if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
7948 $newName = $newCookie['name'];
7951 for ($i = 0; $i < count($this->cookies); $i++) {
7952 $cookie = $this->cookies[$i];
7953 if (!is_array($cookie)) {
7956 if (!isset($cookie['name'])) {
7959 if ($newName != $cookie['name']) {
7962 $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
7963 $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
7964 if ($newDomain != $domain) {
7967 $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
7968 $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
7969 if ($newPath != $path) {
7972 $this->cookies[$i] = $newCookie;
7974 $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
7978 $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
7979 $this->cookies[] = $newCookie;
7986 if (!extension_loaded('soap')) {
7988 * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
7990 class soapclient extends nusoap_client {