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 If you have any questions or comments, please email:
28 http://dietrich.ganx4.com/nusoap
31 http://www.nusphere.com
38 require_once('class.soap_client.php');
39 require_once('class.soap_val.php');
40 require_once('class.soap_parser.php');
41 require_once('class.soap_fault.php');
44 require_once('class.soap_transport_http.php');
46 // optional add-on classes
47 require_once('class.xmlschema.php');
48 require_once('class.wsdl.php');
51 require_once('class.soap_server.php');*/
53 // class variable emulation
54 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
55 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel
= 9;
61 * @author Dietrich Ayala <dietrich@ganx4.com>
67 * Identification for HTTP headers.
72 var $title = 'NuSOAP';
74 * Version for HTTP headers.
79 var $version = '0.7.2';
81 * CVS revision for HTTP headers.
86 var $revision = '$Revision$';
88 * Current error string (manipulated by getError/setError)
95 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
102 * toggles automatic encoding of special characters as entities
103 * (should always be true, I think)
108 var $charencoding = true;
110 * the debug level for this instance
123 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
126 * charset encoding for outgoing messages
131 var $soap_defencoding = 'ISO-8859-1';
132 //var $soap_defencoding = 'UTF-8';
135 * namespaces in an array of prefix => uri
137 * this is "seeded" by a set of constants, but it may be altered by code
142 var $namespaces = array(
143 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
144 'xsd' => 'http://www.w3.org/2001/XMLSchema',
145 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
146 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
150 * namespaces used in the current context, e.g. during serialization
155 var $usedNamespaces = array();
158 * XML Schema types in an array of uri => (array of xml type => php type)
159 * is this legacy yet?
160 * no, this is used by the xmlschema class to verify type => namespace mappings.
164 var $typemap = array(
165 'http://www.w3.org/2001/XMLSchema' => array(
166 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
167 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
168 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
169 // abstract "any" types
170 'anyType'=>'string','anySimpleType'=>'string',
172 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
173 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
174 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
175 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
176 'http://www.w3.org/2000/10/XMLSchema' => array(
177 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
178 'float'=>'double','dateTime'=>'string',
179 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
180 'http://www.w3.org/1999/XMLSchema' => array(
181 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
182 'float'=>'double','dateTime'=>'string',
183 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
184 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
185 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
186 'http://xml.apache.org/xml-soap' => array('Map')
190 * XML entities to convert
195 * @see expandEntities
197 var $xmlEntities = array('quot' => '"','amp' => '&',
198 'lt' => '<','gt' => '>','apos' => "'");
205 function nusoap_base() {
206 $this->debugLevel
= $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel
;
210 * gets the global debug level, which applies to future instances
212 * @return integer Debug level 0-9, where 0 turns off
215 function getGlobalDebugLevel() {
216 return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel
;
220 * sets the global debug level, which applies to future instances
222 * @param int $level Debug level 0-9, where 0 turns off
225 function setGlobalDebugLevel($level) {
226 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel
= $level;
230 * gets the debug level for this instance
232 * @return int Debug level 0-9, where 0 turns off
235 function getDebugLevel() {
236 return $this->debugLevel
;
240 * sets the debug level for this instance
242 * @param int $level Debug level 0-9, where 0 turns off
245 function setDebugLevel($level) {
246 $this->debugLevel
= $level;
250 * adds debug data to the instance debug string with formatting
252 * @param string $string debug data
255 function debug($string){
256 if ($this->debugLevel
> 0) {
257 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
262 * adds debug data to the instance debug string without formatting
264 * @param string $string debug data
267 function appendDebug($string){
268 if ($this->debugLevel
> 0) {
269 // it would be nice to use a memory stream here to use
270 // memory more efficiently
271 $this->debug_str
.= $string;
276 * clears the current debug data for this instance
280 function clearDebug() {
281 // it would be nice to use a memory stream here to use
282 // memory more efficiently
283 $this->debug_str
= '';
287 * gets the current debug data for this instance
292 function &getDebug() {
293 // it would be nice to use a memory stream here to use
294 // memory more efficiently
295 return $this->debug_str
;
299 * gets the current debug data for this instance as an XML comment
300 * this may change the contents of the debug data
302 * @return debug data as an XML comment
305 function &getDebugAsXMLComment() {
306 // it would be nice to use a memory stream here to use
307 // memory more efficiently
308 while (strpos($this->debug_str
, '--')) {
309 $this->debug_str
= str_replace('--', '- -', $this->debug_str
);
311 return "<!--\n" . $this->debug_str
. "\n-->";
315 * expands entities, e.g. changes '<' to '<'.
317 * @param string $val The string in which to expand entities.
320 function expandEntities($val) {
321 if ($this->charencoding
) {
322 $val = str_replace('&', '&', $val);
323 $val = str_replace("'", ''', $val);
324 $val = str_replace('"', '"', $val);
325 $val = str_replace('<', '<', $val);
326 $val = str_replace('>', '>', $val);
332 * returns error string if present
334 * @return mixed error string or false
338 if($this->error_str
!= ''){
339 return $this->error_str
;
347 * @return boolean $string error string
350 function setError($str){
351 $this->error_str
= $str;
355 * detect if array is a simple array or a struct (associative array)
357 * @param mixed $val The PHP array
358 * @return string (arraySimple|arrayStruct)
361 function isArraySimpleOrStruct($val) {
362 $keyList = array_keys($val);
363 foreach ($keyList as $keyListValue) {
364 if (!is_int($keyListValue)) {
365 return 'arrayStruct';
368 return 'arraySimple';
372 * serializes PHP values in accordance w/ section 5. Type information is
373 * not serialized if $use == 'literal'.
375 * @param mixed $val The value to serialize
376 * @param string $name The name (local part) of the XML element
377 * @param string $type The XML schema type (local part) for the element
378 * @param string $name_ns The namespace for the name of the XML element
379 * @param string $type_ns The namespace for the type of the element
380 * @param array $attributes The attributes to serialize as name=>value pairs
381 * @param string $use The WSDL "use" (encoded|literal)
382 * @return string The serialized element, possibly with child elements
385 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
386 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
387 $this->appendDebug('value=' . $this->varDump($val));
388 $this->appendDebug('attributes=' . $this->varDump($attributes));
390 if(is_object($val) && get_class($val) == 'soapval'){
391 return $val->serialize($use);
393 // force valid name if necessary
394 if (is_numeric($name)) {
395 $name = '__numeric_' . $name;
399 // if name has ns, add ns prefix to name
402 $prefix = 'nu'.rand(1000,9999);
403 $name = $prefix.':'.$name;
404 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
406 // if type is prefixed, create type prefix
407 if($type_ns != '' && $type_ns == $this->namespaces
['xsd']){
408 // need to fix this. shouldn't default to xsd if no ns specified
409 // w/o checking against typemap
410 $type_prefix = 'xsd';
412 $type_prefix = 'ns'.rand(1000,9999);
413 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
415 // serialize attributes if present
418 foreach($attributes as $k => $v){
419 $atts .= " $k=\"".$this->expandEntities($v).'"';
422 // serialize null value
424 if ($use == 'literal') {
425 // TODO: depends on minOccurs
426 return "<$name$xmlns $atts/>";
428 if (isset($type) && isset($type_prefix)) {
429 $type_str = " xsi:type=\"$type_prefix:$type\"";
433 return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
436 // serialize if an xsd built-in primitive type
437 if($type != '' && isset($this->typemap
[$this->XMLSchemaVersion
][$type])){
439 if ($type == 'boolean') {
440 $val = $val ?
'true' : 'false';
444 } else if (is_string($val)) {
445 $val = $this->expandEntities($val);
447 if ($use == 'literal') {
448 return "<$name$xmlns $atts>$val</$name>";
450 return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
453 // detect type and serialize
456 case (is_bool($val) ||
$type == 'boolean'):
457 if ($type == 'boolean') {
458 $val = $val ?
'true' : 'false';
462 if ($use == 'literal') {
463 $xml .= "<$name$xmlns $atts>$val</$name>";
465 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
468 case (is_int($val) ||
is_long($val) ||
$type == 'int'):
469 if ($use == 'literal') {
470 $xml .= "<$name$xmlns $atts>$val</$name>";
472 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
475 case (is_float($val)||
is_double($val) ||
$type == 'float'):
476 if ($use == 'literal') {
477 $xml .= "<$name$xmlns $atts>$val</$name>";
479 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
482 case (is_string($val) ||
$type == 'string'):
483 $val = $this->expandEntities($val);
484 if ($use == 'literal') {
485 $xml .= "<$name$xmlns $atts>$val</$name>";
487 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
490 case is_object($val):
492 $name = get_class($val);
493 $this->debug("In serialize_val, used class name $name as element name");
495 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
497 foreach(get_object_vars($val) as $k => $v){
498 $pXml = isset($pXml) ?
$pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
500 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
503 case (is_array($val) ||
$type):
504 // detect if struct or array
505 $valueType = $this->isArraySimpleOrStruct($val);
506 if($valueType=='arraySimple' ||
ereg('^ArrayOf',$type)){
508 if(is_array($val) && count($val)> 0){
510 if(is_object($v) && get_class($v) == 'soapval'){
511 $tt_ns = $v->type_ns
;
513 } elseif (is_array($v)) {
514 $tt = $this->isArraySimpleOrStruct($v);
518 $array_types[$tt] = 1;
519 // TODO: for literal, the name should be $name
520 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
523 if(count($array_types) > 1){
524 $array_typename = 'xsd:anyType';
525 } elseif(isset($tt) && isset($this->typemap
[$this->XMLSchemaVersion
][$tt])) {
526 if ($tt == 'integer') {
529 $array_typename = 'xsd:'.$tt;
530 } elseif(isset($tt) && $tt == 'arraySimple'){
531 $array_typename = 'SOAP-ENC:Array';
532 } elseif(isset($tt) && $tt == 'arrayStruct'){
533 $array_typename = 'unnamed_struct_use_soapval';
535 // if type is prefixed, create type prefix
536 if ($tt_ns != '' && $tt_ns == $this->namespaces
['xsd']){
537 $array_typename = 'xsd:' . $tt;
539 $tt_prefix = 'ns' . rand(1000, 9999);
540 $array_typename = "$tt_prefix:$tt";
541 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
543 $array_typename = $tt;
547 if ($use == 'literal') {
549 } else if (isset($type) && isset($type_prefix)) {
550 $type_str = " xsi:type=\"$type_prefix:$type\"";
552 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
556 if ($use == 'literal') {
558 } else if (isset($type) && isset($type_prefix)) {
559 $type_str = " xsi:type=\"$type_prefix:$type\"";
561 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
564 // TODO: for array in literal, there is no wrapper here
565 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
568 if(isset($type) && isset($type_prefix)){
569 $type_str = " xsi:type=\"$type_prefix:$type\"";
573 if ($use == 'literal') {
574 $xml .= "<$name$xmlns $atts>";
576 $xml .= "<$name$xmlns$type_str$atts>";
578 foreach($val as $k => $v){
580 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
582 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
583 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
586 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
593 $xml .= 'not detected, got '.gettype($val).' for '.$val;
600 * serializes a message
602 * @param string $body the XML of the SOAP body
603 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
604 * @param array $namespaces optional the namespaces used in generating the body and headers
605 * @param string $style optional (rpc|document)
606 * @param string $use optional (encoded|literal)
607 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
608 * @return string the message
611 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
612 // TODO: add an option to automatically run utf8_encode on $body and $headers
613 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
614 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
616 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
617 $this->debug("headers:");
618 $this->appendDebug($this->varDump($headers));
619 $this->debug("namespaces:");
620 $this->appendDebug($this->varDump($namespaces));
622 // serialize namespaces
624 foreach(array_merge($this->namespaces
,$namespaces) as $k => $v){
625 $ns_string .= " xmlns:$k=\"$v\"";
628 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
633 if (is_array($headers)) {
635 foreach ($headers as $header) {
636 $xml .= $this->serialize_val($header, false, false, false, false, false, $use);
639 $this->debug("In serializeEnvelope, serialzied array of headers to $headers");
641 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
643 // serialize envelope
645 '<?xml version="1.0" encoding="'.$this->soap_defencoding
.'"?'.">".
646 '<SOAP-ENV:Envelope'.$ns_string.">".
651 "</SOAP-ENV:Envelope>";
655 * formats a string to be inserted into an HTML stream
657 * @param string $str The string to format
658 * @return string The formatted string
662 function formatDump($str){
663 $str = htmlspecialchars($str);
668 * contracts (changes namespace to prefix) a qualified name
670 * @param string $qname qname
671 * @return string contracted qname
674 function contractQname($qname){
675 // get element namespace
676 //$this->xdebug("Contract $qname");
677 if (strrpos($qname, ':')) {
678 // get unqualified name
679 $name = substr($qname, strrpos($qname, ':') +
1);
681 $ns = substr($qname, 0, strrpos($qname, ':'));
682 $p = $this->getPrefixFromNamespace($ns);
684 return $p . ':' . $name;
693 * expands (changes prefix to namespace) a qualified name
695 * @param string $string qname
696 * @return string expanded qname
699 function expandQname($qname){
700 // get element prefix
701 if(strpos($qname,':') && !ereg('^http://',$qname)){
702 // get unqualified name
703 $name = substr(strstr($qname,':'),1);
705 $prefix = substr($qname,0,strpos($qname,':'));
706 if(isset($this->namespaces
[$prefix])){
707 return $this->namespaces
[$prefix].':'.$name;
717 * returns the local part of a prefixed string
718 * returns the original string, if not prefixed
720 * @param string $str The prefixed string
721 * @return string The local part
724 function getLocalPart($str){
725 if($sstr = strrchr($str,':')){
726 // get unqualified name
727 return substr( $sstr, 1 );
734 * returns the prefix part of a prefixed string
735 * returns false, if not prefixed
737 * @param string $str The prefixed string
738 * @return mixed The prefix or false if there is no prefix
741 function getPrefix($str){
742 if($pos = strrpos($str,':')){
744 return substr($str,0,$pos);
750 * pass it a prefix, it returns a namespace
752 * @param string $prefix The prefix
753 * @return mixed The namespace, false if no namespace has the specified prefix
756 function getNamespaceFromPrefix($prefix){
757 if (isset($this->namespaces
[$prefix])) {
758 return $this->namespaces
[$prefix];
760 //$this->setError("No namespace registered for prefix '$prefix'");
765 * returns the prefix for a given namespace (or prefix)
766 * or false if no prefixes registered for the given namespace
768 * @param string $ns The namespace
769 * @return mixed The prefix, false if the namespace has no prefixes
772 function getPrefixFromNamespace($ns) {
773 foreach ($this->namespaces
as $p => $n) {
774 if ($ns == $n ||
$ns == $p) {
775 $this->usedNamespaces
[$p] = $n;
783 * returns the time in ODBC canonical form with microseconds
785 * @return string The time in ODBC canonical form with microseconds
788 function getmicrotime() {
789 if (function_exists('gettimeofday')) {
790 $tod = gettimeofday();
792 $usec = $tod['usec'];
797 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
801 * Returns a string with the output of var_dump
803 * @param mixed $data The variable to var_dump
804 * @return string The output of var_dump
807 function varDump($data) {
810 $ret_val = ob_get_contents();
816 // XML Schema Datatype Helper Functions
818 //xsd:dateTime helpers
821 * convert unix timestamp to ISO 8601 compliant date string
823 * @param string $timestamp Unix time stamp
826 function timestamp_to_iso8601($timestamp,$utc=true){
827 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
830 '([0-9]{4})-'. // centuries & years CCYY-
831 '([0-9]{2})-'. // months MM-
832 '([0-9]{2})'. // days DD
834 '([0-9]{2}):'. // hours hh:
835 '([0-9]{2}):'. // minutes mm:
836 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
837 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
839 if(ereg($eregStr,$datestr,$regs)){
840 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
849 * convert ISO 8601 compliant date string to unix timestamp
851 * @param string $datestr ISO 8601 compliant date string
854 function iso8601_to_timestamp($datestr){
856 '([0-9]{4})-'. // centuries & years CCYY-
857 '([0-9]{2})-'. // months MM-
858 '([0-9]{2})'. // days DD
860 '([0-9]{2}):'. // hours hh:
861 '([0-9]{2}):'. // minutes mm:
862 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
863 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
864 if(ereg($eregStr,$datestr,$regs)){
867 $op = substr($regs[8],0,1);
868 $h = substr($regs[8],1,2);
869 $m = substr($regs[8],strlen($regs[8])-2,2);
871 $regs[4] = $regs[4] +
$h;
872 $regs[5] = $regs[5] +
$m;
873 } elseif($op == '+'){
874 $regs[4] = $regs[4] - $h;
875 $regs[5] = $regs[5] - $m;
878 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
885 * sleeps some number of microseconds
887 * @param string $usec the number of microseconds to sleep
891 function usleepWindows($usec)
893 $start = gettimeofday();
897 $stop = gettimeofday();
898 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
899 +
$stop['usec'] - $start['usec'];
901 while ($timePassed < $usec);
909 * Contains information for a SOAP fault.
910 * Mainly used for returning faults from deployed functions
911 * in a server instance.
912 * @author Dietrich Ayala <dietrich@ganx4.com>
916 class soap_fault
extends nusoap_base
{
918 * The fault code (client|server)
930 * The fault string, a description of the fault
936 * The fault detail, typically a string or array of string
945 * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
946 * @param string $faultactor only used when msg routed between multiple actors
947 * @param string $faultstring human readable error message
948 * @param mixed $faultdetail detail, typically a string or array of string
950 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
951 parent
::nusoap_base();
952 $this->faultcode
= $faultcode;
953 $this->faultactor
= $faultactor;
954 $this->faultstring
= $faultstring;
955 $this->faultdetail
= $faultdetail;
961 * @return string The serialization of the fault instance.
964 function serialize(){
966 foreach($this->namespaces
as $k => $v){
967 $ns_string .= "\n xmlns:$k=\"$v\"";
970 '<?xml version="1.0" encoding="'.$this->soap_defencoding
.'"?>'.
971 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
974 $this->serialize_val($this->faultcode
, 'faultcode').
975 $this->serialize_val($this->faultactor
, 'faultactor').
976 $this->serialize_val($this->faultstring
, 'faultstring').
977 $this->serialize_val($this->faultdetail
, 'detail').
980 '</SOAP-ENV:Envelope>';
992 * parses an XML Schema, allows access to it's data, other utility methods
993 * no validation... yet.
994 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
995 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
996 * tutorials I refer to :)
998 * @author Dietrich Ayala <dietrich@ganx4.com>
1002 class XMLSchema
extends nusoap_base
{
1008 var $enclosingNamespaces;
1010 var $schemaInfo = array();
1011 var $schemaTargetNamespace = '';
1012 // types, elements, attributes defined by the schema
1013 var $attributes = array();
1014 var $complexTypes = array();
1015 var $complexTypeStack = array();
1016 var $currentComplexType = null;
1017 var $elements = array();
1018 var $elementStack = array();
1019 var $currentElement = null;
1020 var $simpleTypes = array();
1021 var $simpleTypeStack = array();
1022 var $currentSimpleType = null;
1024 var $imports = array();
1029 var $depth_array = array();
1030 var $message = array();
1031 var $defaultNamespace = array();
1036 * @param string $schema schema document URI
1037 * @param string $xml xml document URI
1038 * @param string $namespaces namespaces defined in enclosing XML
1041 function XMLSchema($schema='',$xml='',$namespaces=array()){
1042 parent
::nusoap_base();
1043 $this->debug('xmlschema class instantiated, inside constructor');
1045 $this->schema
= $schema;
1049 $this->enclosingNamespaces
= $namespaces;
1050 $this->namespaces
= array_merge($this->namespaces
, $namespaces);
1052 // parse schema file
1054 $this->debug('initial schema file: '.$schema);
1055 $this->parseFile($schema, 'schema');
1060 $this->debug('initial xml file: '.$xml);
1061 $this->parseFile($xml, 'xml');
1069 * @param string $xml, path/URL to XML file
1070 * @param string $type, (schema | xml)
1074 function parseFile($xml,$type){
1077 $xmlStr = @join
("",@file
($xml));
1079 $msg = 'Error reading XML from '.$xml;
1080 $this->setError($msg);
1084 $this->debug("parsing $xml");
1085 $this->parseString($xmlStr,$type);
1086 $this->debug("done parsing $xml");
1094 * parse an XML string
1096 * @param string $xml path or URL
1097 * @param string $type, (schema|xml)
1100 function parseString($xml,$type){
1104 // Create an XML parser.
1105 $this->parser
= xml_parser_create();
1106 // Set the options for parsing the XML data.
1107 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
1109 // Set the object for the parser.
1110 xml_set_object($this->parser
, $this);
1112 // Set the element handlers for the parser.
1113 if($type == "schema"){
1114 xml_set_element_handler($this->parser
, 'schemaStartElement','schemaEndElement');
1115 xml_set_character_data_handler($this->parser
,'schemaCharacterData');
1116 } elseif($type == "xml"){
1117 xml_set_element_handler($this->parser
, 'xmlStartElement','xmlEndElement');
1118 xml_set_character_data_handler($this->parser
,'xmlCharacterData');
1121 // Parse the XML file.
1122 if(!xml_parse($this->parser
,$xml,true)){
1123 // Display an error message.
1124 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1125 xml_get_current_line_number($this->parser
),
1126 xml_error_string(xml_get_error_code($this->parser
))
1128 $this->debug($errstr);
1129 $this->debug("XML payload:\n" . $xml);
1130 $this->setError($errstr);
1133 xml_parser_free($this->parser
);
1135 $this->debug('no xml passed to parseString()!!');
1136 $this->setError('no xml passed to parseString()!!');
1141 * start-element handler
1143 * @param string $parser XML parser object
1144 * @param string $name element name
1145 * @param string $attrs associative array of attributes
1148 function schemaStartElement($parser, $name, $attrs) {
1150 // position in the total number of elements, starting from 0
1151 $pos = $this->position++
;
1152 $depth = $this->depth++
;
1153 // set self as current value for this depth
1154 $this->depth_array
[$depth] = $pos;
1155 $this->message
[$pos] = array('cdata' => '');
1157 $this->defaultNamespace
[$pos] = $this->defaultNamespace
[$this->depth_array
[$depth - 1]];
1159 $this->defaultNamespace
[$pos] = false;
1162 // get element prefix
1163 if($prefix = $this->getPrefix($name)){
1164 // get unqualified name
1165 $name = $this->getLocalPart($name);
1170 // loop thru attributes, expanding, and registering namespace declarations
1171 if(count($attrs) > 0){
1172 foreach($attrs as $k => $v){
1173 // if ns declarations, add to class level array of valid namespaces
1174 if(ereg("^xmlns",$k)){
1175 //$this->xdebug("$k: $v");
1176 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1177 if($ns_prefix = substr(strrchr($k,':'),1)){
1178 //$this->xdebug("Add namespace[$ns_prefix] = $v");
1179 $this->namespaces
[$ns_prefix] = $v;
1181 $this->defaultNamespace
[$pos] = $v;
1182 if (! $this->getPrefixFromNamespace($v)) {
1183 $this->namespaces
['ns'.(count($this->namespaces
)+
1)] = $v;
1186 if($v == 'http://www.w3.org/2001/XMLSchema' ||
$v == 'http://www.w3.org/1999/XMLSchema' ||
$v == 'http://www.w3.org/2000/10/XMLSchema'){
1187 $this->XMLSchemaVersion
= $v;
1188 $this->namespaces
['xsi'] = $v.'-instance';
1192 foreach($attrs as $k => $v){
1193 // expand each attribute
1194 $k = strpos($k,':') ?
$this->expandQname($k) : $k;
1195 $v = strpos($v,':') ?
$this->expandQname($v) : $v;
1202 // find status, register data
1204 case 'all': // (optional) compositor content for a complexType
1208 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1209 $this->complexTypes
[$this->currentComplexType
]['compositor'] = $name;
1210 //if($name == 'all' || $name == 'sequence'){
1211 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1214 case 'attribute': // complexType attribute
1215 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1216 $this->xdebug("parsing attribute:");
1217 $this->appendDebug($this->varDump($attrs));
1218 if (!isset($attrs['form'])) {
1219 $attrs['form'] = $this->schemaInfo
['attributeFormDefault'];
1221 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1222 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1223 if (!strpos($v, ':')) {
1224 // no namespace in arrayType attribute value...
1225 if ($this->defaultNamespace
[$pos]) {
1226 // ...so use the default
1227 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace
[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1231 if(isset($attrs['name'])){
1232 $this->attributes
[$attrs['name']] = $attrs;
1233 $aname = $attrs['name'];
1234 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1235 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1236 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1240 } elseif(isset($attrs['ref'])){
1241 $aname = $attrs['ref'];
1242 $this->attributes
[$attrs['ref']] = $attrs;
1245 if($this->currentComplexType
){ // This should *always* be
1246 $this->complexTypes
[$this->currentComplexType
]['attrs'][$aname] = $attrs;
1248 // arrayType attribute
1249 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) ||
$this->getLocalPart($aname) == 'arrayType'){
1250 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1251 $prefix = $this->getPrefix($aname);
1252 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1253 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1257 if(strpos($v,'[,]')){
1258 $this->complexTypes
[$this->currentComplexType
]['multidimensional'] = true;
1260 $v = substr($v,0,strpos($v,'[')); // clip the []
1261 if(!strpos($v,':') && isset($this->typemap
[$this->XMLSchemaVersion
][$v])){
1262 $v = $this->XMLSchemaVersion
.':'.$v;
1264 $this->complexTypes
[$this->currentComplexType
]['arrayType'] = $v;
1267 case 'complexContent': // (optional) content for a complexType
1270 array_push($this->complexTypeStack
, $this->currentComplexType
);
1271 if(isset($attrs['name'])){
1272 $this->xdebug('processing named complexType '.$attrs['name']);
1273 //$this->currentElement = false;
1274 $this->currentComplexType
= $attrs['name'];
1275 $this->complexTypes
[$this->currentComplexType
] = $attrs;
1276 $this->complexTypes
[$this->currentComplexType
]['typeClass'] = 'complexType';
1277 // This is for constructs like
1278 // <complexType name="ListOfString" base="soap:Array">
1280 // <element name="string" type="xsd:string"
1281 // minOccurs="0" maxOccurs="unbounded" />
1284 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1285 $this->xdebug('complexType is unusual array');
1286 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1288 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'struct';
1291 $this->xdebug('processing unnamed complexType for element '.$this->currentElement
);
1292 $this->currentComplexType
= $this->currentElement
. '_ContainedType';
1293 //$this->currentElement = false;
1294 $this->complexTypes
[$this->currentComplexType
] = $attrs;
1295 $this->complexTypes
[$this->currentComplexType
]['typeClass'] = 'complexType';
1296 // This is for constructs like
1297 // <complexType name="ListOfString" base="soap:Array">
1299 // <element name="string" type="xsd:string"
1300 // minOccurs="0" maxOccurs="unbounded" />
1303 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1304 $this->xdebug('complexType is unusual array');
1305 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1307 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'struct';
1312 array_push($this->elementStack
, $this->currentElement
);
1313 // elements defined as part of a complex type should
1314 // not really be added to $this->elements, but for some
1316 if (!isset($attrs['form'])) {
1317 $attrs['form'] = $this->schemaInfo
['elementFormDefault'];
1319 if(isset($attrs['type'])){
1320 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1321 if (! $this->getPrefix($attrs['type'])) {
1322 if ($this->defaultNamespace
[$pos]) {
1323 $attrs['type'] = $this->defaultNamespace
[$pos] . ':' . $attrs['type'];
1324 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1327 // This is for constructs like
1328 // <complexType name="ListOfString" base="soap:Array">
1330 // <element name="string" type="xsd:string"
1331 // minOccurs="0" maxOccurs="unbounded" />
1334 if ($this->currentComplexType
&& $this->complexTypes
[$this->currentComplexType
]['phpType'] == 'array') {
1335 $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1336 $this->complexTypes
[$this->currentComplexType
]['arrayType'] = $attrs['type'];
1338 $this->currentElement
= $attrs['name'];
1339 $this->elements
[ $attrs['name'] ] = $attrs;
1340 $this->elements
[ $attrs['name'] ]['typeClass'] = 'element';
1341 $ename = $attrs['name'];
1342 } elseif(isset($attrs['ref'])){
1343 $this->xdebug("processing element as ref to ".$attrs['ref']);
1344 $this->currentElement
= "ref to ".$attrs['ref'];
1345 $ename = $this->getLocalPart($attrs['ref']);
1347 $this->xdebug("processing untyped element ".$attrs['name']);
1348 $this->currentElement
= $attrs['name'];
1349 $this->elements
[ $attrs['name'] ] = $attrs;
1350 $this->elements
[ $attrs['name'] ]['typeClass'] = 'element';
1351 $attrs['type'] = $this->schemaTargetNamespace
. ':' . $attrs['name'] . '_ContainedType';
1352 $this->elements
[ $attrs['name'] ]['type'] = $attrs['type'];
1353 $ename = $attrs['name'];
1355 if(isset($ename) && $this->currentComplexType
){
1356 $this->complexTypes
[$this->currentComplexType
]['elements'][$ename] = $attrs;
1359 case 'enumeration': // restriction value list member
1360 $this->xdebug('enumeration ' . $attrs['value']);
1361 if ($this->currentSimpleType
) {
1362 $this->simpleTypes
[$this->currentSimpleType
]['enumeration'][] = $attrs['value'];
1363 } elseif ($this->currentComplexType
) {
1364 $this->complexTypes
[$this->currentComplexType
]['enumeration'][] = $attrs['value'];
1367 case 'extension': // simpleContent or complexContent type extension
1368 $this->xdebug('extension ' . $attrs['base']);
1369 if ($this->currentComplexType
) {
1370 $this->complexTypes
[$this->currentComplexType
]['extensionBase'] = $attrs['base'];
1374 if (isset($attrs['schemaLocation'])) {
1375 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1376 $this->imports
[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1378 //$this->xdebug('import namespace ' . $attrs['namespace']);
1379 $this->imports
[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1380 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1381 $this->namespaces
['ns'.(count($this->namespaces
)+
1)] = $attrs['namespace'];
1385 case 'list': // simpleType value list
1387 case 'restriction': // simpleType, simpleContent or complexContent value restriction
1388 $this->xdebug('restriction ' . $attrs['base']);
1389 if($this->currentSimpleType
){
1390 $this->simpleTypes
[$this->currentSimpleType
]['type'] = $attrs['base'];
1391 } elseif($this->currentComplexType
){
1392 $this->complexTypes
[$this->currentComplexType
]['restrictionBase'] = $attrs['base'];
1393 if(strstr($attrs['base'],':') == ':Array'){
1394 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1399 $this->schemaInfo
= $attrs;
1400 $this->schemaInfo
['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1401 if (isset($attrs['targetNamespace'])) {
1402 $this->schemaTargetNamespace
= $attrs['targetNamespace'];
1404 if (!isset($attrs['elementFormDefault'])) {
1405 $this->schemaInfo
['elementFormDefault'] = 'unqualified';
1407 if (!isset($attrs['attributeFormDefault'])) {
1408 $this->schemaInfo
['attributeFormDefault'] = 'unqualified';
1411 case 'simpleContent': // (optional) content for a complexType
1414 array_push($this->simpleTypeStack
, $this->currentSimpleType
);
1415 if(isset($attrs['name'])){
1416 $this->xdebug("processing simpleType for name " . $attrs['name']);
1417 $this->currentSimpleType
= $attrs['name'];
1418 $this->simpleTypes
[ $attrs['name'] ] = $attrs;
1419 $this->simpleTypes
[ $attrs['name'] ]['typeClass'] = 'simpleType';
1420 $this->simpleTypes
[ $attrs['name'] ]['phpType'] = 'scalar';
1422 $this->xdebug('processing unnamed simpleType for element '.$this->currentElement
);
1423 $this->currentSimpleType
= $this->currentElement
. '_ContainedType';
1424 //$this->currentElement = false;
1425 $this->simpleTypes
[$this->currentSimpleType
] = $attrs;
1426 $this->simpleTypes
[$this->currentSimpleType
]['phpType'] = 'scalar';
1429 case 'union': // simpleType type list
1432 //$this->xdebug("do not have anything to do for element $name");
1437 * end-element handler
1439 * @param string $parser XML parser object
1440 * @param string $name element name
1443 function schemaEndElement($parser, $name) {
1444 // bring depth down a notch
1446 // position of current element is equal to the last value left in depth_array for my depth
1447 if(isset($this->depth_array
[$this->depth
])){
1448 $pos = $this->depth_array
[$this->depth
];
1450 // get element prefix
1451 if ($prefix = $this->getPrefix($name)){
1452 // get unqualified name
1453 $name = $this->getLocalPart($name);
1458 if($name == 'complexType'){
1459 $this->xdebug('done processing complexType ' . ($this->currentComplexType ?
$this->currentComplexType
: '(unknown)'));
1460 $this->currentComplexType
= array_pop($this->complexTypeStack
);
1461 //$this->currentElement = false;
1463 if($name == 'element'){
1464 $this->xdebug('done processing element ' . ($this->currentElement ?
$this->currentElement
: '(unknown)'));
1465 $this->currentElement
= array_pop($this->elementStack
);
1467 if($name == 'simpleType'){
1468 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?
$this->currentSimpleType
: '(unknown)'));
1469 $this->currentSimpleType
= array_pop($this->simpleTypeStack
);
1474 * element content handler
1476 * @param string $parser XML parser object
1477 * @param string $data element content
1480 function schemaCharacterData($parser, $data){
1481 $pos = $this->depth_array
[$this->depth
- 1];
1482 $this->message
[$pos]['cdata'] .= $data;
1486 * serialize the schema
1490 function serializeSchema(){
1492 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion
);
1495 if (sizeof($this->imports
) > 0) {
1496 foreach($this->imports
as $ns => $list) {
1497 foreach ($list as $ii) {
1498 if ($ii['location'] != '') {
1499 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1501 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1507 foreach($this->complexTypes
as $typeName => $attrs){
1509 // serialize child elements
1510 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1511 foreach($attrs['elements'] as $element => $eParts){
1512 if(isset($eParts['ref'])){
1513 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1515 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1516 foreach ($eParts as $aName => $aValue) {
1517 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1518 if ($aName != 'name' && $aName != 'type') {
1519 $contentStr .= " $aName=\"$aValue\"";
1522 $contentStr .= "/>\n";
1525 // compositor wraps elements
1526 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1527 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1531 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1532 foreach($attrs['attrs'] as $attr => $aParts){
1533 $contentStr .= " <$schemaPrefix:attribute";
1534 foreach ($aParts as $a => $v) {
1535 if ($a == 'ref' ||
$a == 'type') {
1536 $contentStr .= " $a=\"".$this->contractQName($v).'"';
1537 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1538 $this->usedNamespaces
['wsdl'] = $this->namespaces
['wsdl'];
1539 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1541 $contentStr .= " $a=\"$v\"";
1544 $contentStr .= "/>\n";
1548 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1549 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1550 // complex or simple content
1551 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) ||
(isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1552 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1555 // finalize complex type
1556 if($contentStr != ''){
1557 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1559 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1561 $xml .= $contentStr;
1564 if(isset($this->simpleTypes
) && count($this->simpleTypes
) > 0){
1565 foreach($this->simpleTypes
as $typeName => $eParts){
1566 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n";
1567 if (isset($eParts['enumeration'])) {
1568 foreach ($eParts['enumeration'] as $e) {
1569 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
1572 $xml .= " </$schemaPrefix:simpleType>";
1576 if(isset($this->elements
) && count($this->elements
) > 0){
1577 foreach($this->elements
as $element => $eParts){
1578 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1582 if(isset($this->attributes
) && count($this->attributes
) > 0){
1583 foreach($this->attributes
as $attr => $aParts){
1584 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1588 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1589 foreach (array_diff($this->usedNamespaces
, $this->enclosingNamespaces
) as $nsp => $ns) {
1590 $el .= " xmlns:$nsp=\"$ns\"";
1592 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1597 * adds debug data to the clas level debug string
1599 * @param string $string debug data
1602 function xdebug($string){
1603 $this->debug('<' . $this->schemaTargetNamespace
. '> '.$string);
1607 * get the PHP type of a user defined type in the schema
1608 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1609 * returns false if no type exists, or not w/ the given namespace
1610 * else returns a string that is either a native php type, or 'struct'
1612 * @param string $type, name of defined type
1613 * @param string $ns, namespace of type
1618 function getPHPType($type,$ns){
1619 if(isset($this->typemap
[$ns][$type])){
1620 //print "found type '$type' and ns $ns in typemap<br>";
1621 return $this->typemap
[$ns][$type];
1622 } elseif(isset($this->complexTypes
[$type])){
1623 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1624 return $this->complexTypes
[$type]['phpType'];
1630 * returns an associative array of information about a given type
1631 * returns false if no type exists by the given name
1633 * For a complexType typeDef = array(
1634 * 'restrictionBase' => '',
1636 * 'compositor' => '(sequence|all)',
1637 * 'elements' => array(), // refs to elements array
1638 * 'attrs' => array() // refs to attributes array
1639 * ... and so on (see addComplexType)
1642 * For simpleType or element, the array has different keys.
1647 * @see addComplexType
1648 * @see addSimpleType
1651 function getTypeDef($type){
1652 //$this->debug("in getTypeDef for type $type");
1653 if(isset($this->complexTypes
[$type])){
1654 $this->xdebug("in getTypeDef, found complexType $type");
1655 return $this->complexTypes
[$type];
1656 } elseif(isset($this->simpleTypes
[$type])){
1657 $this->xdebug("in getTypeDef, found simpleType $type");
1658 if (!isset($this->simpleTypes
[$type]['phpType'])) {
1659 // get info for type to tack onto the simple type
1660 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1661 $uqType = substr($this->simpleTypes
[$type]['type'], strrpos($this->simpleTypes
[$type]['type'], ':') +
1);
1662 $ns = substr($this->simpleTypes
[$type]['type'], 0, strrpos($this->simpleTypes
[$type]['type'], ':'));
1663 $etype = $this->getTypeDef($uqType);
1665 $this->xdebug("in getTypeDef, found type for simpleType $type:");
1666 $this->xdebug($this->varDump($etype));
1667 if (isset($etype['phpType'])) {
1668 $this->simpleTypes
[$type]['phpType'] = $etype['phpType'];
1670 if (isset($etype['elements'])) {
1671 $this->simpleTypes
[$type]['elements'] = $etype['elements'];
1675 return $this->simpleTypes
[$type];
1676 } elseif(isset($this->elements
[$type])){
1677 $this->xdebug("in getTypeDef, found element $type");
1678 if (!isset($this->elements
[$type]['phpType'])) {
1679 // get info for type to tack onto the element
1680 $uqType = substr($this->elements
[$type]['type'], strrpos($this->elements
[$type]['type'], ':') +
1);
1681 $ns = substr($this->elements
[$type]['type'], 0, strrpos($this->elements
[$type]['type'], ':'));
1682 $etype = $this->getTypeDef($uqType);
1684 $this->xdebug("in getTypeDef, found type for element $type:");
1685 $this->xdebug($this->varDump($etype));
1686 if (isset($etype['phpType'])) {
1687 $this->elements
[$type]['phpType'] = $etype['phpType'];
1689 if (isset($etype['elements'])) {
1690 $this->elements
[$type]['elements'] = $etype['elements'];
1692 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1693 $this->xdebug("in getTypeDef, element $type is an XSD type");
1694 $this->elements
[$type]['phpType'] = 'scalar';
1697 return $this->elements
[$type];
1698 } elseif(isset($this->attributes
[$type])){
1699 $this->xdebug("in getTypeDef, found attribute $type");
1700 return $this->attributes
[$type];
1701 } elseif (ereg('_ContainedType$', $type)) {
1702 $this->xdebug("in getTypeDef, have an untyped element $type");
1703 $typeDef['typeClass'] = 'simpleType';
1704 $typeDef['phpType'] = 'scalar';
1705 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1708 $this->xdebug("in getTypeDef, did not find $type");
1713 * returns a sample serialization of a given type, or false if no type by the given name
1715 * @param string $type, name of type
1720 function serializeTypeDef($type){
1721 //print "in sTD() for type $type<br>";
1722 if($typeDef = $this->getTypeDef($type)){
1724 if(is_array($typeDef['attrs'])){
1725 foreach($attrs as $attName => $data){
1726 $str .= " $attName=\"{type = ".$data['type']."}\"";
1729 $str .= " xmlns=\"".$this->schema
['targetNamespace']."\"";
1730 if(count($typeDef['elements']) > 0){
1732 foreach($typeDef['elements'] as $element => $eData){
1733 $str .= $this->serializeTypeDef($element);
1736 } elseif($typeDef['typeClass'] == 'element') {
1737 $str .= "></$type>";
1747 * returns HTML form elements that allow a user
1748 * to enter values for creating an instance of the given type.
1750 * @param string $name, name for type instance
1751 * @param string $type, name of type
1756 function typeToForm($name,$type){
1758 if($typeDef = $this->getTypeDef($type)){
1760 if($typeDef['phpType'] == 'struct'){
1761 $buffer .= '<table>';
1762 foreach($typeDef['elements'] as $child => $childDef){
1764 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1765 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1767 $buffer .= '</table>';
1769 } elseif($typeDef['phpType'] == 'array'){
1770 $buffer .= '<table>';
1771 for($i=0;$i < 3; $i++
){
1773 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1774 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1776 $buffer .= '</table>';
1779 $buffer .= "<input type='text' name='parameters[$name]'>";
1782 $buffer .= "<input type='text' name='parameters[$name]'>";
1788 * adds a complex type to the schema
1798 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1802 * example: PHP associative array ( SOAP Struct )
1809 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1813 * @param typeClass (complexType|simpleType|attribute)
1814 * @param phpType: currently supported are array and struct (php assoc array)
1815 * @param compositor (all|sequence|choice)
1816 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1817 * @param elements = array ( name = array(name=>'',type=>'') )
1818 * @param attrs = array(
1820 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1821 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1824 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1828 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1829 $this->complexTypes
[$name] = array(
1831 'typeClass' => $typeClass,
1832 'phpType' => $phpType,
1833 'compositor'=> $compositor,
1834 'restrictionBase' => $restrictionBase,
1835 'elements' => $elements,
1837 'arrayType' => $arrayType
1840 $this->xdebug("addComplexType $name:");
1841 $this->appendDebug($this->varDump($this->complexTypes
[$name]));
1845 * adds a simple type to the schema
1847 * @param string $name
1848 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1849 * @param string $typeClass (should always be simpleType)
1850 * @param string $phpType (should always be scalar)
1851 * @param array $enumeration array of values
1856 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1857 $this->simpleTypes
[$name] = array(
1859 'typeClass' => $typeClass,
1860 'phpType' => $phpType,
1861 'type' => $restrictionBase,
1862 'enumeration' => $enumeration
1865 $this->xdebug("addSimpleType $name:");
1866 $this->appendDebug($this->varDump($this->simpleTypes
[$name]));
1870 * adds an element to the schema
1872 * @param array $attrs attributes that must include name and type
1876 function addElement($attrs) {
1877 if (! $this->getPrefix($attrs['type'])) {
1878 $attrs['type'] = $this->schemaTargetNamespace
. ':' . $attrs['type'];
1880 $this->elements
[ $attrs['name'] ] = $attrs;
1881 $this->elements
[ $attrs['name'] ]['typeClass'] = 'element';
1883 $this->xdebug("addElement " . $attrs['name']);
1884 $this->appendDebug($this->varDump($this->elements
[ $attrs['name'] ]));
1895 * For creating serializable abstractions of native PHP types. This class
1896 * allows element name/namespace, XSD type, and XML attributes to be
1897 * associated with a value. This is extremely useful when WSDL is not
1898 * used, but is also useful when WSDL is used with polymorphic types, including
1899 * xsd:anyType and user-defined types.
1901 * @author Dietrich Ayala <dietrich@ganx4.com>
1905 class soapval
extends nusoap_base
{
1907 * The XML element name
1914 * The XML type name (string or false)
1928 * The XML element namespace (string or false)
1935 * The XML type namespace (string or false)
1942 * The XML element attributes (array or false)
1952 * @param string $name optional name
1953 * @param mixed $type optional type name
1954 * @param mixed $value optional value
1955 * @param mixed $element_ns optional namespace of value
1956 * @param mixed $type_ns optional namespace of type
1957 * @param mixed $attributes associative array of attributes to add to element serialization
1960 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1961 parent
::nusoap_base();
1962 $this->name
= $name;
1963 $this->type
= $type;
1964 $this->value
= $value;
1965 $this->element_ns
= $element_ns;
1966 $this->type_ns
= $type_ns;
1967 $this->attributes
= $attributes;
1971 * return serialized value
1973 * @param string $use The WSDL use value (encoded|literal)
1974 * @return string XML data
1977 function serialize($use='encoded') {
1978 return $this->serialize_val($this->value
,$this->name
,$this->type
,$this->element_ns
,$this->type_ns
,$this->attributes
,$use);
1982 * decodes a soapval object into a PHP native type
1988 return $this->value
;
1999 * transport class for sending/receiving data via HTTP and HTTPS
2000 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2002 * @author Dietrich Ayala <dietrich@ganx4.com>
2006 class soap_transport_http
extends nusoap_base
{
2010 var $digest_uri = '';
2015 var $request_method = 'POST';
2016 var $protocol_version = '1.0';
2018 var $outgoing_headers = array();
2019 var $incoming_headers = array();
2020 var $incoming_cookies = array();
2021 var $outgoing_payload = '';
2022 var $incoming_payload = '';
2023 var $useSOAPAction = true;
2024 var $persistentConnection = false;
2025 var $ch = false; // cURL handle
2029 var $digestRequest = array();
2030 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
2031 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2032 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2033 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2034 // passphrase: SSL key password/passphrase
2035 // verifypeer: default is 1
2036 // verifyhost: default is 1
2041 function soap_transport_http($url){
2042 parent
::nusoap_base();
2043 $this->setURL($url);
2044 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision
, $rev);
2045 $this->outgoing_headers
['User-Agent'] = $this->title
.'/'.$this->version
.' ('.$rev[1].')';
2046 $this->debug('set User-Agent: ' . $this->outgoing_headers
['User-Agent']);
2049 function setURL($url) {
2052 $u = parse_url($url);
2053 foreach($u as $k => $v){
2054 $this->debug("$k = $v");
2058 // add any GET params to path
2059 if(isset($u['query']) && $u['query'] != ''){
2060 $this->path
.= '?' . $u['query'];
2064 if(!isset($u['port'])){
2065 if($u['scheme'] == 'https'){
2072 $this->uri
= $this->path
;
2073 $this->digest_uri
= $this->uri
;
2076 if (!isset($u['port'])) {
2077 $this->outgoing_headers
['Host'] = $this->host
;
2079 $this->outgoing_headers
['Host'] = $this->host
.':'.$this->port
;
2081 $this->debug('set Host: ' . $this->outgoing_headers
['Host']);
2083 if (isset($u['user']) && $u['user'] != '') {
2084 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ?
urldecode($u['pass']) : '');
2088 function connect($connection_timeout=0,$response_timeout=30){
2089 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2090 // "regular" socket.
2091 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2092 // loaded), and until PHP5 stream_get_wrappers is not available.
2093 // if ($this->scheme == 'https') {
2094 // if (version_compare(phpversion(), '4.3.0') >= 0) {
2095 // if (extension_loaded('openssl')) {
2096 // $this->scheme = 'ssl';
2097 // $this->debug('Using SSL over OpenSSL');
2101 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2102 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
2103 // use persistent connection
2104 if($this->persistentConnection
&& isset($this->fp
) && is_resource($this->fp
)){
2105 if (!feof($this->fp
)) {
2106 $this->debug('Re-use persistent connection');
2110 $this->debug('Closed persistent connection at EOF');
2113 // munge host if using OpenSSL
2114 if ($this->scheme
== 'ssl') {
2115 $host = 'ssl://' . $this->host
;
2117 $host = $this->host
;
2119 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2122 if($connection_timeout > 0){
2123 $this->fp
= @fsockopen
( $host, $this->port
, $this->errno
, $this->error_str
, $connection_timeout);
2125 $this->fp
= @fsockopen
( $host, $this->port
, $this->errno
, $this->error_str
);
2130 $msg = 'Couldn\'t open socket connection to server ' . $this->url
;
2132 $msg .= ', Error ('.$this->errno
.'): '.$this->error_str
;
2134 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
2137 $this->setError($msg);
2141 // set response timeout
2142 $this->debug('set response timeout to ' . $response_timeout);
2143 socket_set_timeout( $this->fp
, $response_timeout);
2145 $this->debug('socket connected');
2147 } else if ($this->scheme
== 'https') {
2148 if (!extension_loaded('curl')) {
2149 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2152 $this->debug('connect using https');
2154 $this->ch
= curl_init();
2156 $hostURL = ($this->port
!= '') ?
"https://$this->host:$this->port" : "https://$this->host";
2158 $hostURL .= $this->path
;
2159 curl_setopt($this->ch
, CURLOPT_URL
, $hostURL);
2160 // follow location headers (re-directs)
2161 curl_setopt($this->ch
, CURLOPT_FOLLOWLOCATION
, 1);
2162 // ask for headers in the response output
2163 curl_setopt($this->ch
, CURLOPT_HEADER
, 1);
2164 // ask for the response output as the return value
2165 curl_setopt($this->ch
, CURLOPT_RETURNTRANSFER
, 1);
2167 // We manage this ourselves through headers and encoding
2168 // if(function_exists('gzuncompress')){
2169 // curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
2171 // persistent connection
2172 if ($this->persistentConnection
) {
2173 // The way we send data, we cannot use persistent connections, since
2174 // there will be some "junk" at the end of our request.
2175 //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
2176 $this->persistentConnection
= false;
2177 $this->outgoing_headers
['Connection'] = 'close';
2178 $this->debug('set Connection: ' . $this->outgoing_headers
['Connection']);
2181 if ($connection_timeout != 0) {
2182 curl_setopt($this->ch
, CURLOPT_TIMEOUT
, $connection_timeout);
2184 // TODO: cURL has added a connection timeout separate from the response timeout
2185 //if ($connection_timeout != 0) {
2186 // curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2188 //if ($response_timeout != 0) {
2189 // curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);
2192 // recent versions of cURL turn on peer/host checking by default,
2193 // while PHP binaries are not compiled with a default location for the
2194 // CA cert bundle, so disable peer/host checking.
2195 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2196 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYPEER
, 0);
2197 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYHOST
, 0);
2199 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2200 if ($this->authtype
== 'certificate') {
2201 if (isset($this->certRequest
['cainfofile'])) {
2202 curl_setopt($this->ch
, CURLOPT_CAINFO
, $this->certRequest
['cainfofile']);
2204 if (isset($this->certRequest
['verifypeer'])) {
2205 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYPEER
, $this->certRequest
['verifypeer']);
2207 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYPEER
, 1);
2209 if (isset($this->certRequest
['verifyhost'])) {
2210 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYHOST
, $this->certRequest
['verifyhost']);
2212 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYHOST
, 1);
2214 if (isset($this->certRequest
['sslcertfile'])) {
2215 curl_setopt($this->ch
, CURLOPT_SSLCERT
, $this->certRequest
['sslcertfile']);
2217 if (isset($this->certRequest
['sslkeyfile'])) {
2218 curl_setopt($this->ch
, CURLOPT_SSLKEY
, $this->certRequest
['sslkeyfile']);
2220 if (isset($this->certRequest
['passphrase'])) {
2221 curl_setopt($this->ch
, CURLOPT_SSLKEYPASSWD
, $this->certRequest
['passphrase']);
2224 $this->debug('cURL connection set up');
2227 $this->setError('Unknown scheme ' . $this->scheme
);
2228 $this->debug('Unknown scheme ' . $this->scheme
);
2234 * send the SOAP message via HTTP
2236 * @param string $data message data
2237 * @param integer $timeout set connection timeout in seconds
2238 * @param integer $response_timeout set response timeout in seconds
2239 * @param array $cookies cookies to send
2240 * @return string data
2243 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2245 $this->debug('entered send() with data of length: '.strlen($data));
2247 $this->tryagain
= true;
2249 while ($this->tryagain
) {
2250 $this->tryagain
= false;
2253 if (!$this->connect($timeout, $response_timeout)){
2258 if (!$this->sendRequest($data, $cookies)){
2263 $respdata = $this->getResponse();
2265 $this->setError('Too many tries to get an OK response');
2268 $this->debug('end of send()');
2274 * send the SOAP message via HTTPS 1.0 using CURL
2276 * @param string $msg message data
2277 * @param integer $timeout set connection timeout in seconds
2278 * @param integer $response_timeout set response timeout in seconds
2279 * @param array $cookies cookies to send
2280 * @return string data
2283 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2284 return $this->send($data, $timeout, $response_timeout, $cookies);
2288 * if authenticating, set user credentials here
2290 * @param string $username
2291 * @param string $password
2292 * @param string $authtype (basic, digest, certificate)
2293 * @param array $digestRequest (keys must be nonce, nc, realm, qop)
2294 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2297 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2298 $this->debug("Set credentials for authtype $authtype");
2300 if ($authtype == 'basic') {
2301 $this->outgoing_headers
['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
2302 } elseif ($authtype == 'digest') {
2303 if (isset($digestRequest['nonce'])) {
2304 $digestRequest['nc'] = isset($digestRequest['nc']) ?
$digestRequest['nc']++
: 1;
2306 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2308 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2309 $A1 = $username. ':' . (isset($digestRequest['realm']) ?
$digestRequest['realm'] : '') . ':' . $password;
2314 // A2 = Method ":" digest-uri-value
2315 $A2 = 'POST:' . $this->digest_uri
;
2320 // KD(secret, data) = H(concat(secret, ":", data))
2322 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
2324 // ":" unq(cnonce-value)
2325 // ":" unq(qop-value)
2328 // if qop is missing,
2329 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2331 $unhashedDigest = '';
2332 $nonce = isset($digestRequest['nonce']) ?
$digestRequest['nonce'] : '';
2334 if ($digestRequest['qop'] != '') {
2335 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2337 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2340 $hashedDigest = md5($unhashedDigest);
2342 $this->outgoing_headers
['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri
. '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
2344 } elseif ($authtype == 'certificate') {
2345 $this->certRequest
= $certRequest;
2347 $this->username
= $username;
2348 $this->password
= $password;
2349 $this->authtype
= $authtype;
2350 $this->digestRequest
= $digestRequest;
2352 if (isset($this->outgoing_headers
['Authorization'])) {
2353 $this->debug('set Authorization: ' . substr($this->outgoing_headers
['Authorization'], 0, 12) . '...');
2355 $this->debug('Authorization header not set');
2360 * set the soapaction value
2362 * @param string $soapaction
2365 function setSOAPAction($soapaction) {
2366 $this->outgoing_headers
['SOAPAction'] = '"' . $soapaction . '"';
2367 $this->debug('set SOAPAction: ' . $this->outgoing_headers
['SOAPAction']);
2373 * @param string $enc encoding style. supported values: gzip, deflate, or both
2376 function setEncoding($enc='gzip, deflate') {
2377 if (function_exists('gzdeflate')) {
2378 $this->protocol_version
= '1.1';
2379 $this->outgoing_headers
['Accept-Encoding'] = $enc;
2380 $this->debug('set Accept-Encoding: ' . $this->outgoing_headers
['Accept-Encoding']);
2381 if (!isset($this->outgoing_headers
['Connection'])) {
2382 $this->outgoing_headers
['Connection'] = 'close';
2383 $this->persistentConnection
= false;
2384 $this->debug('set Connection: ' . $this->outgoing_headers
['Connection']);
2386 set_magic_quotes_runtime(0);
2388 $this->encoding
= $enc;
2393 * set proxy info here
2395 * @param string $proxyhost
2396 * @param string $proxyport
2397 * @param string $proxyusername
2398 * @param string $proxypassword
2401 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
2402 $this->uri
= $this->url
;
2403 $this->host
= $proxyhost;
2404 $this->port
= $proxyport;
2405 if ($proxyusername != '' && $proxypassword != '') {
2406 $this->outgoing_headers
['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
2407 $this->debug('set Proxy-Authorization: ' . $this->outgoing_headers
['Proxy-Authorization']);
2412 * decode a string that is encoded w/ "chunked' transfer encoding
2413 * as defined in RFC2068 19.4.6
2415 * @param string $buffer
2421 function decodeChunked($buffer, $lb){
2426 // read chunk-size, chunk-extension (if any) and CRLF
2427 // get the position of the linebreak
2428 $chunkend = strpos($buffer, $lb);
2429 if ($chunkend == FALSE) {
2430 $this->debug('no linebreak found in decodeChunked');
2433 $temp = substr($buffer,0,$chunkend);
2434 $chunk_size = hexdec( trim($temp) );
2435 $chunkstart = $chunkend +
strlen($lb);
2436 // while (chunk-size > 0) {
2437 while ($chunk_size > 0) {
2438 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2439 $chunkend = strpos( $buffer, $lb, $chunkstart +
$chunk_size);
2441 // Just in case we got a broken connection
2442 if ($chunkend == FALSE) {
2443 $chunk = substr($buffer,$chunkstart);
2444 // append chunk-data to entity-body
2446 $length +
= strlen($chunk);
2450 // read chunk-data and CRLF
2451 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2452 // append chunk-data to entity-body
2454 // length := length + chunk-size
2455 $length +
= strlen($chunk);
2456 // read chunk-size and CRLF
2457 $chunkstart = $chunkend +
strlen($lb);
2459 $chunkend = strpos($buffer, $lb, $chunkstart) +
strlen($lb);
2460 if ($chunkend == FALSE) {
2461 break; //Just in case we got a broken connection
2463 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2464 $chunk_size = hexdec( trim($temp) );
2465 $chunkstart = $chunkend;
2471 * Writes payload, including HTTP headers, to $this->outgoing_payload.
2473 function buildPayload($data, $cookie_str = '') {
2474 // add content-length header
2475 $this->outgoing_headers
['Content-Length'] = strlen($data);
2476 $this->debug('set Content-Length: ' . $this->outgoing_headers
['Content-Length']);
2478 // start building outgoing payload:
2479 $req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2480 $this->debug("HTTP request: $req");
2481 $this->outgoing_payload
= "$req\r\n";
2483 // loop thru headers, serializing
2484 foreach($this->outgoing_headers
as $k => $v){
2486 $this->debug("HTTP header: $hdr");
2487 $this->outgoing_payload
.= "$hdr\r\n";
2491 if ($cookie_str != '') {
2492 $hdr = 'Cookie: '.$cookie_str;
2493 $this->debug("HTTP header: $hdr");
2494 $this->outgoing_payload
.= "$hdr\r\n";
2497 // header/body separator
2498 $this->outgoing_payload
.= "\r\n";
2501 $this->outgoing_payload
.= $data;
2504 function sendRequest($data, $cookies = NULL) {
2505 // build cookie string
2506 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme
== 'ssl') ||
($this->scheme
== 'https')));
2509 $this->buildPayload($data, $cookie_str);
2511 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
2513 if(!fputs($this->fp
, $this->outgoing_payload
, strlen($this->outgoing_payload
))) {
2514 $this->setError('couldn\'t write message data to socket');
2515 $this->debug('couldn\'t write message data to socket');
2518 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload
));
2520 } else if ($this->scheme
== 'https') {
2522 // TODO: cURL does say this should only be the verb, and in fact it
2523 // turns out that the URI and HTTP version are appended to this, which
2524 // some servers refuse to work with
2525 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2526 foreach($this->outgoing_headers
as $k => $v){
2527 $curl_headers[] = "$k: $v";
2529 if ($cookie_str != '') {
2530 $curl_headers[] = 'Cookie: ' . $cookie_str;
2532 curl_setopt($this->ch
, CURLOPT_HTTPHEADER
, $curl_headers);
2533 if ($this->request_method
== "POST") {
2534 curl_setopt($this->ch
, CURLOPT_POST
, 1);
2535 curl_setopt($this->ch
, CURLOPT_POSTFIELDS
, $data);
2538 $this->debug('set cURL payload');
2543 function getResponse(){
2544 $this->incoming_payload
= '';
2546 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
2547 // loop until headers have been retrieved
2549 while (!isset($lb)){
2551 // We might EOF during header read.
2552 if(feof($this->fp
)) {
2553 $this->incoming_payload
= $data;
2554 $this->debug('found no headers before EOF after length ' . strlen($data));
2555 $this->debug("received before EOF:\n" . $data);
2556 $this->setError('server failed to send headers');
2560 $tmp = fgets($this->fp
, 256);
2561 $tmplen = strlen($tmp);
2562 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2565 $this->incoming_payload
= $data;
2566 $this->debug('socket read of headers timed out after length ' . strlen($data));
2567 $this->debug("read before timeout: " . $data);
2568 $this->setError('socket read of headers timed out');
2573 $pos = strpos($data,"\r\n\r\n");
2577 $pos = strpos($data,"\n\n");
2582 // remove 100 header
2583 if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2588 // store header data
2589 $this->incoming_payload
.= $data;
2590 $this->debug('found end of headers after length ' . strlen($data));
2592 $header_data = trim(substr($data,0,$pos));
2593 $header_array = explode($lb,$header_data);
2594 $this->incoming_headers
= array();
2595 $this->incoming_cookies
= array();
2596 foreach($header_array as $header_line){
2597 $arr = explode(':',$header_line, 2);
2598 if(count($arr) > 1){
2599 $header_name = strtolower(trim($arr[0]));
2600 $this->incoming_headers
[$header_name] = trim($arr[1]);
2601 if ($header_name == 'set-cookie') {
2602 // TODO: allow multiple cookies from parseCookie
2603 $cookie = $this->parseCookie(trim($arr[1]));
2605 $this->incoming_cookies
[] = $cookie;
2606 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2608 $this->debug('did not find cookie in ' . trim($arr[1]));
2611 } else if (isset($header_name)) {
2612 // append continuation line to previous header
2613 $this->incoming_headers
[$header_name] .= $lb . ' ' . $header_line;
2617 // loop until msg has been received
2618 if (isset($this->incoming_headers
['transfer-encoding']) && strtolower($this->incoming_headers
['transfer-encoding']) == 'chunked') {
2619 $content_length = 2147483647; // ignore any content-length header
2621 $this->debug("want to read chunked content");
2622 } elseif (isset($this->incoming_headers
['content-length'])) {
2623 $content_length = $this->incoming_headers
['content-length'];
2625 $this->debug("want to read content of length $content_length");
2627 $content_length = 2147483647;
2629 $this->debug("want to read content to EOF");
2634 $tmp = fgets($this->fp
, 256);
2635 $tmplen = strlen($tmp);
2636 $this->debug("read chunk line of $tmplen bytes");
2638 $this->incoming_payload
= $data;
2639 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2640 $this->debug("read before timeout:\n" . $data);
2641 $this->setError('socket read of chunk length timed out');
2644 $content_length = hexdec(trim($tmp));
2645 $this->debug("chunk length $content_length");
2648 while (($strlen < $content_length) && (!feof($this->fp
))) {
2649 $readlen = min(8192, $content_length - $strlen);
2650 $tmp = fread($this->fp
, $readlen);
2651 $tmplen = strlen($tmp);
2652 $this->debug("read buffer of $tmplen bytes");
2653 if (($tmplen == 0) && (!feof($this->fp
))) {
2654 $this->incoming_payload
= $data;
2655 $this->debug('socket read of body timed out after length ' . strlen($data));
2656 $this->debug("read before timeout:\n" . $data);
2657 $this->setError('socket read of body timed out');
2663 if ($chunked && ($content_length > 0)) {
2664 $tmp = fgets($this->fp
, 256);
2665 $tmplen = strlen($tmp);
2666 $this->debug("read chunk terminator of $tmplen bytes");
2668 $this->incoming_payload
= $data;
2669 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2670 $this->debug("read before timeout:\n" . $data);
2671 $this->setError('socket read of chunk terminator timed out');
2675 } while ($chunked && ($content_length > 0) && (!feof($this->fp
)));
2676 if (feof($this->fp
)) {
2677 $this->debug('read to EOF');
2679 $this->debug('read body of length ' . strlen($data));
2680 $this->incoming_payload
.= $data;
2681 $this->debug('received a total of '.strlen($this->incoming_payload
).' bytes of data from server');
2683 // close filepointer
2685 (isset($this->incoming_headers
['connection']) && strtolower($this->incoming_headers
['connection']) == 'close') ||
2686 (! $this->persistentConnection
) ||
feof($this->fp
)){
2689 $this->debug('closed socket');
2692 // connection was closed unexpectedly
2693 if($this->incoming_payload
== ''){
2694 $this->setError('no response from server');
2698 // decode transfer-encoding
2699 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2700 // if(!$data = $this->decodeChunked($data, $lb)){
2701 // $this->setError('Decoding of chunked data failed');
2704 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2705 // set decoded payload
2706 // $this->incoming_payload = $header_data.$lb.$lb.$data;
2709 } else if ($this->scheme
== 'https') {
2711 $this->debug('send and receive with cURL');
2712 $this->incoming_payload
= curl_exec($this->ch
);
2713 $data = $this->incoming_payload
;
2715 $cErr = curl_error($this->ch
);
2717 $err = 'cURL ERROR: '.curl_errno($this->ch
).': '.$cErr.'<br>';
2718 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
2719 foreach(curl_getinfo($this->ch
) as $k => $v){
2720 $err .= "$k: $v<br>";
2723 $this->setError($err);
2724 curl_close($this->ch
);
2728 //var_dump(curl_getinfo($this->ch));
2732 $this->debug('No cURL error, closing cURL');
2733 curl_close($this->ch
);
2735 // remove 100 header(s)
2736 while (ereg('^HTTP/1.1 100',$data)) {
2737 if ($pos = strpos($data,"\r\n\r\n")) {
2738 $data = ltrim(substr($data,$pos));
2739 } elseif($pos = strpos($data,"\n\n") ) {
2740 $data = ltrim(substr($data,$pos));
2744 // separate content from HTTP headers
2745 if ($pos = strpos($data,"\r\n\r\n")) {
2747 } elseif( $pos = strpos($data,"\n\n")) {
2750 $this->debug('no proper separation of headers and document');
2751 $this->setError('no proper separation of headers and document');
2754 $header_data = trim(substr($data,0,$pos));
2755 $header_array = explode($lb,$header_data);
2756 $data = ltrim(substr($data,$pos));
2757 $this->debug('found proper separation of headers and document');
2758 $this->debug('cleaned data, stringlen: '.strlen($data));
2760 foreach ($header_array as $header_line) {
2761 $arr = explode(':',$header_line,2);
2762 if(count($arr) > 1){
2763 $header_name = strtolower(trim($arr[0]));
2764 $this->incoming_headers
[$header_name] = trim($arr[1]);
2765 if ($header_name == 'set-cookie') {
2766 // TODO: allow multiple cookies from parseCookie
2767 $cookie = $this->parseCookie(trim($arr[1]));
2769 $this->incoming_cookies
[] = $cookie;
2770 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2772 $this->debug('did not find cookie in ' . trim($arr[1]));
2775 } else if (isset($header_name)) {
2776 // append continuation line to previous header
2777 $this->incoming_headers
[$header_name] .= $lb . ' ' . $header_line;
2782 $arr = explode(' ', $header_array[0], 3);
2783 $http_version = $arr[0];
2784 $http_status = intval($arr[1]);
2785 $http_reason = count($arr) > 2 ?
$arr[2] : '';
2787 // see if we need to resend the request with http digest authentication
2788 if (isset($this->incoming_headers
['location']) && $http_status == 301) {
2789 $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers
['location']);
2790 $this->setURL($this->incoming_headers
['location']);
2791 $this->tryagain
= true;
2795 // see if we need to resend the request with http digest authentication
2796 if (isset($this->incoming_headers
['www-authenticate']) && $http_status == 401) {
2797 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers
['www-authenticate']);
2798 if (strstr($this->incoming_headers
['www-authenticate'], "Digest ")) {
2799 $this->debug('Server wants digest authentication');
2800 // remove "Digest " from our elements
2801 $digestString = str_replace('Digest ', '', $this->incoming_headers
['www-authenticate']);
2803 // parse elements into array
2804 $digestElements = explode(',', $digestString);
2805 foreach ($digestElements as $val) {
2806 $tempElement = explode('=', trim($val), 2);
2807 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2810 // should have (at least) qop, realm, nonce
2811 if (isset($digestRequest['nonce'])) {
2812 $this->setCredentials($this->username
, $this->password
, 'digest', $digestRequest);
2813 $this->tryagain
= true;
2817 $this->debug('HTTP authentication failed');
2818 $this->setError('HTTP authentication failed');
2823 ($http_status >= 300 && $http_status <= 307) ||
2824 ($http_status >= 400 && $http_status <= 417) ||
2825 ($http_status >= 501 && $http_status <= 505)
2827 $this->setError("Unsupported HTTP response status $http_status $http_reason (soap_client->response has contents of the response)");
2831 // decode content-encoding
2832 if(isset($this->incoming_headers
['content-encoding']) && $this->incoming_headers
['content-encoding'] != ''){
2833 if(strtolower($this->incoming_headers
['content-encoding']) == 'deflate' ||
strtolower($this->incoming_headers
['content-encoding']) == 'gzip'){
2834 // if decoding works, use it. else assume data wasn't gzencoded
2835 if(function_exists('gzinflate')){
2836 //$timer->setMarker('starting decoding of gzip/deflated content');
2837 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2838 // this means there are no Zlib headers, although there should be
2839 $this->debug('The gzinflate function exists');
2840 $datalen = strlen($data);
2841 if ($this->incoming_headers
['content-encoding'] == 'deflate') {
2842 if ($degzdata = @gzinflate
($data)) {
2844 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2845 if (strlen($data) < $datalen) {
2846 // test for the case that the payload has been compressed twice
2847 $this->debug('The inflated payload is smaller than the gzipped one; try again');
2848 if ($degzdata = @gzinflate
($data)) {
2850 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2854 $this->debug('Error using gzinflate to inflate the payload');
2855 $this->setError('Error using gzinflate to inflate the payload');
2857 } elseif ($this->incoming_headers
['content-encoding'] == 'gzip') {
2858 if ($degzdata = @gzinflate
(substr($data, 10))) { // do our best
2860 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2861 if (strlen($data) < $datalen) {
2862 // test for the case that the payload has been compressed twice
2863 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2864 if ($degzdata = @gzinflate
(substr($data, 10))) {
2866 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2870 $this->debug('Error using gzinflate to un-gzip the payload');
2871 $this->setError('Error using gzinflate to un-gzip the payload');
2874 //$timer->setMarker('finished decoding of gzip/deflated content');
2875 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2876 // set decoded payload
2877 $this->incoming_payload
= $header_data.$lb.$lb.$data;
2879 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2880 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2883 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers
['content-encoding']);
2884 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers
['content-encoding']);
2887 $this->debug('No Content-Encoding header');
2890 if(strlen($data) == 0){
2891 $this->debug('no data after headers!');
2892 $this->setError('no data present after HTTP headers');
2899 function setContentType($type, $charset = false) {
2900 $this->outgoing_headers
['Content-Type'] = $type . ($charset ?
'; charset=' . $charset : '');
2901 $this->debug('set Content-Type: ' . $this->outgoing_headers
['Content-Type']);
2904 function usePersistentConnection(){
2905 if (isset($this->outgoing_headers
['Accept-Encoding'])) {
2908 $this->protocol_version
= '1.1';
2909 $this->persistentConnection
= true;
2910 $this->outgoing_headers
['Connection'] = 'Keep-Alive';
2911 $this->debug('set Connection: ' . $this->outgoing_headers
['Connection']);
2916 * parse an incoming Cookie into it's parts
2918 * @param string $cookie_str content of cookie
2919 * @return array with data of that cookie
2923 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
2925 function parseCookie($cookie_str) {
2926 $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
2927 $data = split(';', $cookie_str);
2928 $value_str = $data[0];
2930 $cookie_param = 'domain=';
2931 $start = strpos($cookie_str, $cookie_param);
2933 $domain = substr($cookie_str, $start +
strlen($cookie_param));
2934 $domain = substr($domain, 0, strpos($domain, ';'));
2939 $cookie_param = 'expires=';
2940 $start = strpos($cookie_str, $cookie_param);
2942 $expires = substr($cookie_str, $start +
strlen($cookie_param));
2943 $expires = substr($expires, 0, strpos($expires, ';'));
2948 $cookie_param = 'path=';
2949 $start = strpos($cookie_str, $cookie_param);
2951 $path = substr($cookie_str, $start +
strlen($cookie_param));
2952 $path = substr($path, 0, strpos($path, ';'));
2957 $cookie_param = ';secure;';
2958 if (strpos($cookie_str, $cookie_param) !== FALSE) {
2964 $sep_pos = strpos($value_str, '=');
2967 $name = substr($value_str, 0, $sep_pos);
2968 $value = substr($value_str, $sep_pos +
1);
2969 $cookie= array( 'name' => $name,
2971 'domain' => $domain,
2973 'expires' => $expires,
2982 * sort out cookies for the current request
2984 * @param array $cookies array with all cookies
2985 * @param boolean $secure is the send-content secure or not?
2986 * @return string for Cookie-HTTP-Header
2989 function getCookiesForRequest($cookies, $secure=false) {
2991 if ((! is_null($cookies)) && (is_array($cookies))) {
2992 foreach ($cookies as $cookie) {
2993 if (! is_array($cookie)) {
2996 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
2997 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
2998 if (strtotime($cookie['expires']) <= time()) {
2999 $this->debug('cookie has expired');
3003 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3004 $domain = preg_quote($cookie['domain']);
3005 if (! preg_match("'.*$domain$'i", $this->host
)) {
3006 $this->debug('cookie has different domain');
3010 if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3011 $path = preg_quote($cookie['path']);
3012 if (! preg_match("'^$path.*'i", $this->path
)) {
3013 $this->debug('cookie is for a different path');
3017 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3018 $this->debug('cookie is secure, transport is not');
3021 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3022 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3035 * soap_server allows the user to create a SOAP server
3036 * that is capable of receiving messages and returning responses
3038 * NOTE: WSDL functionality is experimental
3040 * @author Dietrich Ayala <dietrich@ganx4.com>
3044 class soap_server
extends nusoap_base
{
3046 * HTTP headers of request
3050 var $headers = array();
3058 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3062 var $requestHeaders = '';
3064 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3070 * SOAP payload for request (text)
3074 var $requestSOAP = '';
3076 * requested method namespace URI
3080 var $methodURI = '';
3082 * name of method requested
3086 var $methodname = '';
3088 * method parameters from request
3092 var $methodparams = array();
3094 * SOAP Action from request
3098 var $SOAPAction = '';
3100 * character set encoding of incoming (request) messages
3104 var $xml_encoding = '';
3106 * toggles whether the parser decodes element content w/ utf8_decode()
3110 var $decode_utf8 = true;
3113 * HTTP headers of response
3117 var $outgoing_headers = array();
3125 * SOAP headers for response (text)
3129 var $responseHeaders = '';
3131 * SOAP payload for response (text)
3135 var $responseSOAP = '';
3137 * method return value to place in response
3141 var $methodreturn = false;
3143 * whether $methodreturn is a string of literal XML
3147 var $methodreturnisliteralxml = false;
3149 * SOAP fault for response (or false)
3155 * text indication of result (for debugging)
3159 var $result = 'successful';
3162 * assoc array of operations => opData; operations are added by the register()
3163 * method or by parsing an external WSDL definition
3167 var $operations = array();
3169 * wsdl instance (if one)
3175 * URL for WSDL (if one)
3179 var $externalWSDLURL = false;
3181 * whether to append debug to response as XML comment
3185 var $debug_flag = false;
3190 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3192 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3195 function soap_server($wsdl=false){
3196 parent
::nusoap_base();
3197 // turn on debugging?
3199 global $HTTP_SERVER_VARS;
3201 if (isset($_SERVER)) {
3202 $this->debug("_SERVER is defined:");
3203 $this->appendDebug($this->varDump($_SERVER));
3204 } elseif (isset($HTTP_SERVER_VARS)) {
3205 $this->debug("HTTP_SERVER_VARS is defined:");
3206 $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3208 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3211 if (isset($debug)) {
3212 $this->debug("In soap_server, set debug_flag=$debug based on global flag");
3213 $this->debug_flag
= $debug;
3214 } elseif (isset($_SERVER['QUERY_STRING'])) {
3215 $qs = explode('&', $_SERVER['QUERY_STRING']);
3216 foreach ($qs as $v) {
3217 if (substr($v, 0, 6) == 'debug=') {
3218 $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3219 $this->debug_flag
= substr($v, 6);
3222 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3223 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3224 foreach ($qs as $v) {
3225 if (substr($v, 0, 6) == 'debug=') {
3226 $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3227 $this->debug_flag
= substr($v, 6);
3234 $this->debug("In soap_server, WSDL is specified");
3235 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3236 $this->wsdl
= $wsdl;
3237 $this->externalWSDLURL
= $this->wsdl
->wsdl
;
3238 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL
);
3240 $this->debug('Create wsdl from ' . $wsdl);
3241 $this->wsdl
= new wsdl($wsdl);
3242 $this->externalWSDLURL
= $wsdl;
3244 $this->appendDebug($this->wsdl
->getDebug());
3245 $this->wsdl
->clearDebug();
3246 if($err = $this->wsdl
->getError()){
3247 die('WSDL ERROR: '.$err);
3253 * processes request and returns response
3255 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
3258 function service($data){
3259 global $HTTP_SERVER_VARS;
3261 if (isset($_SERVER['QUERY_STRING'])) {
3262 $qs = $_SERVER['QUERY_STRING'];
3263 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3264 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3268 $this->debug("In service, query string=$qs");
3270 if (ereg('wsdl', $qs) ){
3271 $this->debug("In service, this is a request for WSDL");
3272 if($this->externalWSDLURL
){
3273 if (strpos($this->externalWSDLURL
,"://")!==false) { // assume URL
3274 header('Location: '.$this->externalWSDLURL
);
3275 } else { // assume file
3276 header("Content-Type: text/xml\r\n");
3277 $fp = fopen($this->externalWSDLURL
, 'r');
3280 } elseif ($this->wsdl
) {
3281 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3282 print $this->wsdl
->serialize($this->debug_flag
);
3283 if ($this->debug_flag
) {
3284 $this->debug('wsdl:');
3285 $this->appendDebug($this->varDump($this->wsdl
));
3286 print $this->getDebugAsXMLComment();
3289 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3290 print "This service does not provide WSDL";
3292 } elseif ($data == '' && $this->wsdl
) {
3293 $this->debug("In service, there is no data, so return Web description");
3294 print $this->wsdl
->webDescription();
3296 $this->debug("In service, invoke the request");
3297 $this->parse_request($data);
3298 if (! $this->fault
) {
3299 $this->invoke_method();
3301 if (! $this->fault
) {
3302 $this->serialize_return();
3304 $this->send_response();
3309 * parses HTTP request headers.
3311 * The following fields are set by this function (when successful)
3320 function parse_http_headers() {
3321 global $HTTP_SERVER_VARS;
3323 $this->request
= '';
3324 $this->SOAPAction
= '';
3325 if(function_exists('getallheaders')){
3326 $this->debug("In parse_http_headers, use getallheaders");
3327 $headers = getallheaders();
3328 foreach($headers as $k=>$v){
3329 $k = strtolower($k);
3330 $this->headers
[$k] = $v;
3331 $this->request
.= "$k: $v\r\n";
3332 $this->debug("$k: $v");
3334 // get SOAPAction header
3335 if(isset($this->headers
['soapaction'])){
3336 $this->SOAPAction
= str_replace('"','',$this->headers
['soapaction']);
3338 // get the character encoding of the incoming request
3339 if(isset($this->headers
['content-type']) && strpos($this->headers
['content-type'],'=')){
3340 $enc = str_replace('"','',substr(strstr($this->headers
["content-type"],'='),1));
3341 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3342 $this->xml_encoding
= strtoupper($enc);
3344 $this->xml_encoding
= 'US-ASCII';
3347 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3348 $this->xml_encoding
= 'ISO-8859-1';
3350 } elseif(isset($_SERVER) && is_array($_SERVER)){
3351 $this->debug("In parse_http_headers, use _SERVER");
3352 foreach ($_SERVER as $k => $v) {
3353 if (substr($k, 0, 5) == 'HTTP_') {
3354 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
3356 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
3358 if ($k == 'soapaction') {
3359 // get SOAPAction header
3361 $v = str_replace('"', '', $v);
3362 $v = str_replace('\\', '', $v);
3363 $this->SOAPAction
= $v;
3364 } else if ($k == 'content-type') {
3365 // get the character encoding of the incoming request
3366 if (strpos($v, '=')) {
3367 $enc = substr(strstr($v, '='), 1);
3368 $enc = str_replace('"', '', $enc);
3369 $enc = str_replace('\\', '', $enc);
3370 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3371 $this->xml_encoding
= strtoupper($enc);
3373 $this->xml_encoding
= 'US-ASCII';
3376 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3377 $this->xml_encoding
= 'ISO-8859-1';
3380 $this->headers
[$k] = $v;
3381 $this->request
.= "$k: $v\r\n";
3382 $this->debug("$k: $v");
3384 } elseif (is_array($HTTP_SERVER_VARS)) {
3385 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3386 foreach ($HTTP_SERVER_VARS as $k => $v) {
3387 if (substr($k, 0, 5) == 'HTTP_') {
3388 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
3390 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
3392 if ($k == 'soapaction') {
3393 // get SOAPAction header
3395 $v = str_replace('"', '', $v);
3396 $v = str_replace('\\', '', $v);
3397 $this->SOAPAction
= $v;
3398 } else if ($k == 'content-type') {
3399 // get the character encoding of the incoming request
3400 if (strpos($v, '=')) {
3401 $enc = substr(strstr($v, '='), 1);
3402 $enc = str_replace('"', '', $enc);
3403 $enc = str_replace('\\', '', $enc);
3404 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3405 $this->xml_encoding
= strtoupper($enc);
3407 $this->xml_encoding
= 'US-ASCII';
3410 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3411 $this->xml_encoding
= 'ISO-8859-1';
3414 $this->headers
[$k] = $v;
3415 $this->request
.= "$k: $v\r\n";
3416 $this->debug("$k: $v");
3419 $this->debug("In parse_http_headers, HTTP headers not accessible");
3420 $this->setError("HTTP headers not accessible");
3427 * The following fields are set by this function (when successful)
3441 * This sets the fault field on error
3443 * @param string $data XML string
3446 function parse_request($data='') {
3447 $this->debug('entering parse_request()');
3448 $this->parse_http_headers();
3449 $this->debug('got character encoding: '.$this->xml_encoding
);
3450 // uncompress if necessary
3451 if (isset($this->headers
['content-encoding']) && $this->headers
['content-encoding'] != '') {
3452 $this->debug('got content encoding: ' . $this->headers
['content-encoding']);
3453 if ($this->headers
['content-encoding'] == 'deflate' ||
$this->headers
['content-encoding'] == 'gzip') {
3454 // if decoding works, use it. else assume data wasn't gzencoded
3455 if (function_exists('gzuncompress')) {
3456 if ($this->headers
['content-encoding'] == 'deflate' && $degzdata = @gzuncompress
($data)) {
3458 } elseif ($this->headers
['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3461 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3465 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3470 $this->request
.= "\r\n".$data;
3471 $data = $this->parseRequest($this->headers
, $data);
3472 $this->requestSOAP
= $data;
3473 $this->debug('leaving parse_request');
3477 * invokes a PHP function for the requested SOAP method
3479 * The following fields are set by this function (when successful)
3483 * Note that the PHP function that is called may also set the following
3484 * fields to affect the response sent to the client
3489 * This sets the fault field on error
3493 function invoke_method() {
3494 $this->debug('in invoke_method, methodname=' . $this->methodname
. ' methodURI=' . $this->methodURI
. ' SOAPAction=' . $this->SOAPAction
);
3497 if ($this->opData
= $this->wsdl
->getOperationData($this->methodname
)) {
3498 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname
);
3499 $this->appendDebug('opData=' . $this->varDump($this->opData
));
3500 } elseif ($this->opData
= $this->wsdl
->getOperationDataForSoapAction($this->SOAPAction
)) {
3501 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3502 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction
. ' for operation=' . $this->opData
['name']);
3503 $this->appendDebug('opData=' . $this->varDump($this->opData
));
3504 $this->methodname
= $this->opData
['name'];
3506 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname
);
3507 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname
. "' is not defined in the WSDL for this service");
3511 $this->debug('in invoke_method, no WSDL to validate method');
3514 // if a . is present in $this->methodname, we see if there is a class in scope,
3515 // which could be referred to. We will also distinguish between two deliminators,
3516 // to allow methods to be called a the class or an instance
3519 if (strpos($this->methodname
, '..') > 0) {
3521 } else if (strpos($this->methodname
, '.') > 0) {
3527 if (strlen($delim) > 0 && substr_count($this->methodname
, $delim) == 1 &&
3528 class_exists(substr($this->methodname
, 0, strpos($this->methodname
, $delim)))) {
3529 // get the class and method name
3530 $class = substr($this->methodname
, 0, strpos($this->methodname
, $delim));
3531 $method = substr($this->methodname
, strpos($this->methodname
, $delim) +
strlen($delim));
3532 $this->debug("in invoke_method, class=$class method=$method delim=$delim");
3535 // does method exist?
3537 if (!function_exists($this->methodname
)) {
3538 $this->debug("in invoke_method, function '$this->methodname' not found!");
3539 $this->result
= 'fault: method not found';
3540 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3544 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ?
strtolower($method) : $method;
3545 if (!in_array($method_to_compare, get_class_methods($class))) {
3546 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3547 $this->result
= 'fault: method not found';
3548 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3553 // evaluate message, getting back parameters
3554 // verify that request parameters match the method's signature
3555 if(! $this->verify_method($this->methodname
,$this->methodparams
)){
3557 $this->debug('ERROR: request not verified against method signature');
3558 $this->result
= 'fault: request failed validation against method signature';
3560 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
3564 // if there are parameters to pass
3565 $this->debug('in invoke_method, params:');
3566 $this->appendDebug($this->varDump($this->methodparams
));
3567 $this->debug("in invoke_method, calling '$this->methodname'");
3568 if (!function_exists('call_user_func_array')) {
3570 $this->debug('in invoke_method, calling function using eval()');
3571 $funcCall = "\$this->methodreturn = $this->methodname(";
3573 if ($delim == '..') {
3574 $this->debug('in invoke_method, calling class method using eval()');
3575 $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3577 $this->debug('in invoke_method, calling instance method using eval()');
3578 // generate unique instance name
3579 $instname = "\$inst_".time();
3580 $funcCall = $instname." = new ".$class."(); ";
3581 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3584 if ($this->methodparams
) {
3585 foreach ($this->methodparams
as $param) {
3586 if (is_array($param)) {
3587 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3590 $funcCall .= "\"$param\",";
3592 $funcCall = substr($funcCall, 0, -1);
3595 $this->debug('in invoke_method, function call: '.$funcCall);
3599 $this->debug('in invoke_method, calling function using call_user_func_array()');
3600 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
3601 } elseif ($delim == '..') {
3602 $this->debug('in invoke_method, calling class method using call_user_func_array()');
3603 $call_arg = array ($class, $method);
3605 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
3606 $instance = new $class ();
3607 $call_arg = array(&$instance, $method);
3609 $this->methodreturn
= call_user_func_array($call_arg, array_values($this->methodparams
));
3611 $this->debug('in invoke_method, methodreturn:');
3612 $this->appendDebug($this->varDump($this->methodreturn
));
3613 $this->debug("in invoke_method, called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn
));
3617 * serializes the return value from a PHP function into a full SOAP Envelope
3619 * The following fields are set by this function (when successful)
3623 * This sets the fault field on error
3627 function serialize_return() {
3628 $this->debug('Entering serialize_return methodname: ' . $this->methodname
. ' methodURI: ' . $this->methodURI
);
3630 if (isset($this->methodreturn
) && (get_class($this->methodreturn
) == 'soap_fault')) {
3631 $this->debug('got a fault object from method');
3632 $this->fault
= $this->methodreturn
;
3634 } elseif ($this->methodreturnisliteralxml
) {
3635 $return_val = $this->methodreturn
;
3636 // returned value(s)
3638 $this->debug('got a(n) '.gettype($this->methodreturn
).' from method');
3639 $this->debug('serializing return value');
3641 // weak attempt at supporting multiple output params
3642 if(sizeof($this->opData
['output']['parts']) > 1){
3643 $opParams = $this->methodreturn
;
3645 // TODO: is this really necessary?
3646 $opParams = array($this->methodreturn
);
3648 $return_val = $this->wsdl
->serializeRPCParameters($this->methodname
,'output',$opParams);
3649 $this->appendDebug($this->wsdl
->getDebug());
3650 $this->wsdl
->clearDebug();
3651 if($errstr = $this->wsdl
->getError()){
3652 $this->debug('got wsdl error: '.$errstr);
3653 $this->fault('SOAP-ENV:Server', 'unable to serialize result');
3657 if (isset($this->methodreturn
)) {
3658 $return_val = $this->serialize_val($this->methodreturn
, 'return');
3661 $this->debug('in absence of WSDL, assume void return for backward compatibility');
3665 $this->debug('return value:');
3666 $this->appendDebug($this->varDump($return_val));
3668 $this->debug('serializing response');
3670 $this->debug('have WSDL for serialization: style is ' . $this->opData
['style']);
3671 if ($this->opData
['style'] == 'rpc') {
3672 $this->debug('style is rpc for serialization: use is ' . $this->opData
['output']['use']);
3673 if ($this->opData
['output']['use'] == 'literal') {
3674 $payload = '<'.$this->methodname
.'Response xmlns="'.$this->methodURI
.'">'.$return_val.'</'.$this->methodname
."Response>";
3676 $payload = '<ns1:'.$this->methodname
.'Response xmlns:ns1="'.$this->methodURI
.'">'.$return_val.'</ns1:'.$this->methodname
."Response>";
3679 $this->debug('style is not rpc for serialization: assume document');
3680 $payload = $return_val;
3683 $this->debug('do not have WSDL for serialization: assume rpc/encoded');
3684 $payload = '<ns1:'.$this->methodname
.'Response xmlns:ns1="'.$this->methodURI
.'">'.$return_val.'</ns1:'.$this->methodname
."Response>";
3686 $this->result
= 'successful';
3688 //if($this->debug_flag){
3689 $this->appendDebug($this->wsdl
->getDebug());
3691 if (isset($opData['output']['encodingStyle'])) {
3692 $encodingStyle = $opData['output']['encodingStyle'];
3694 $encodingStyle = '';
3696 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
3697 $this->responseSOAP
= $this->serializeEnvelope($payload,$this->responseHeaders
,$this->wsdl
->usedNamespaces
,$this->opData
['style'],$encodingStyle);
3699 $this->responseSOAP
= $this->serializeEnvelope($payload,$this->responseHeaders
);
3701 $this->debug("Leaving serialize_return");
3705 * sends an HTTP response
3707 * The following fields are set by this function (when successful)
3714 function send_response() {
3715 $this->debug('Enter send_response');
3717 $payload = $this->fault
->serialize();
3718 $this->outgoing_headers
[] = "HTTP/1.0 500 Internal Server Error";
3719 $this->outgoing_headers
[] = "Status: 500 Internal Server Error";
3721 $payload = $this->responseSOAP
;
3722 // Some combinations of PHP+Web server allow the Status
3723 // to come through as a header. Since OK is the default
3725 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
3726 // $this->outgoing_headers[] = "Status: 200 OK";
3728 // add debug data if in debug mode
3729 if(isset($this->debug_flag
) && $this->debug_flag
){
3730 $payload .= $this->getDebugAsXMLComment();
3732 $this->outgoing_headers
[] = "Server: $this->title Server v$this->version";
3733 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision
, $rev);
3734 $this->outgoing_headers
[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
3735 // Let the Web server decide about this
3736 //$this->outgoing_headers[] = "Connection: Close\r\n";
3737 $payload = $this->getHTTPBody($payload);
3738 $type = $this->getHTTPContentType();
3739 $charset = $this->getHTTPContentTypeCharset();
3740 $this->outgoing_headers
[] = "Content-Type: $type" . ($charset ?
'; charset=' . $charset : '');
3741 //begin code to compress payload - by John
3742 // NOTE: there is no way to know whether the Web server will also compress
3744 if (strlen($payload) > 1024 && isset($this->headers
) && isset($this->headers
['accept-encoding'])) {
3745 if (strstr($this->headers
['accept-encoding'], 'gzip')) {
3746 if (function_exists('gzencode')) {
3747 if (isset($this->debug_flag
) && $this->debug_flag
) {
3748 $payload .= "<!-- Content being gzipped -->";
3750 $this->outgoing_headers
[] = "Content-Encoding: gzip";
3751 $payload = gzencode($payload);
3753 if (isset($this->debug_flag
) && $this->debug_flag
) {
3754 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
3757 } elseif (strstr($this->headers
['accept-encoding'], 'deflate')) {
3758 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
3759 // instead of gzcompress output,
3760 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
3761 if (function_exists('gzdeflate')) {
3762 if (isset($this->debug_flag
) && $this->debug_flag
) {
3763 $payload .= "<!-- Content being deflated -->";
3765 $this->outgoing_headers
[] = "Content-Encoding: deflate";
3766 $payload = gzdeflate($payload);
3768 if (isset($this->debug_flag
) && $this->debug_flag
) {
3769 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
3775 $this->outgoing_headers
[] = "Content-Length: ".strlen($payload);
3776 reset($this->outgoing_headers
);
3777 foreach($this->outgoing_headers
as $hdr){
3778 header($hdr, false);
3781 $this->response
= join("\r\n",$this->outgoing_headers
)."\r\n\r\n".$payload;
3785 * takes the value that was created by parsing the request
3786 * and compares to the method's signature, if available.
3788 * @param string $operation The operation to be invoked
3789 * @param array $request The array of parameter values
3790 * @return boolean Whether the operation was found
3793 function verify_method($operation,$request){
3794 if(isset($this->wsdl
) && is_object($this->wsdl
)){
3795 if($this->wsdl
->getOperationData($operation)){
3798 } elseif(isset($this->operations
[$operation])){
3805 * processes SOAP message received from client
3807 * @param array $headers The HTTP headers
3808 * @param string $data unprocessed request data from client
3809 * @return mixed value of the message, decoded into a PHP type
3812 function parseRequest($headers, $data) {
3813 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
3814 if (!strstr($headers['content-type'], 'text/xml')) {
3815 $this->setError('Request not of type text/xml');
3818 if (strpos($headers['content-type'], '=')) {
3819 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
3820 $this->debug('Got response encoding: ' . $enc);
3821 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3822 $this->xml_encoding
= strtoupper($enc);
3824 $this->xml_encoding
= 'US-ASCII';
3827 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3828 $this->xml_encoding
= 'ISO-8859-1';
3830 $this->debug('Use encoding: ' . $this->xml_encoding
. ' when creating soap_parser');
3831 // parse response, get soap parser obj
3832 $parser = new soap_parser($data,$this->xml_encoding
,'',$this->decode_utf8
);
3834 $this->debug("parser debug: \n".$parser->getDebug());
3835 // if fault occurred during message parsing
3836 if($err = $parser->getError()){
3837 $this->result
= 'fault: error in msg parsing: '.$err;
3838 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
3839 // else successfully parsed request into soapval object
3841 // get/set methodname
3842 $this->methodURI
= $parser->root_struct_namespace
;
3843 $this->methodname
= $parser->root_struct_name
;
3844 $this->debug('methodname: '.$this->methodname
.' methodURI: '.$this->methodURI
);
3845 $this->debug('calling parser->get_response()');
3846 $this->methodparams
= $parser->get_response();
3848 $this->requestHeaders
= $parser->getHeaders();
3849 // add document for doclit support
3850 $this->document
= $parser->document
;
3855 * gets the HTTP body for the current response.
3857 * @param string $soapmsg The SOAP payload
3858 * @return string The HTTP body, which includes the SOAP payload
3861 function getHTTPBody($soapmsg) {
3866 * gets the HTTP content type for the current response.
3868 * Note: getHTTPBody must be called before this.
3870 * @return string the HTTP content type for the current response.
3873 function getHTTPContentType() {
3878 * gets the HTTP content type charset for the current response.
3879 * returns false for non-text content types.
3881 * Note: getHTTPBody must be called before this.
3883 * @return string the HTTP content type charset for the current response.
3886 function getHTTPContentTypeCharset() {
3887 return $this->soap_defencoding
;
3891 * add a method to the dispatch map (this has been replaced by the register method)
3893 * @param string $methodname
3894 * @param string $in array of input values
3895 * @param string $out array of output values
3899 function add_to_map($methodname,$in,$out){
3900 $this->operations
[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
3904 * register a service function with the server
3906 * @param string $name the name of the PHP function, class.method or class..method
3907 * @param array $in assoc array of input values: key = param name, value = param type
3908 * @param array $out assoc array of output values: key = param name, value = param type
3909 * @param mixed $namespace the element namespace for the method or false
3910 * @param mixed $soapaction the soapaction for the method or false
3911 * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
3912 * @param mixed $use optional (encoded|literal) or false
3913 * @param string $documentation optional Description to include in WSDL
3914 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
3917 function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
3918 global $HTTP_SERVER_VARS;
3920 if($this->externalWSDLURL
){
3921 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
3924 die('You must specify a name when you register an operation');
3926 if (!is_array($in)) {
3927 die('You must provide an array for operation inputs');
3929 if (!is_array($out)) {
3930 die('You must provide an array for operation outputs');
3932 if(false == $namespace) {
3934 if(false == $soapaction) {
3935 if (isset($_SERVER)) {
3936 $SERVER_NAME = $_SERVER['SERVER_NAME'];
3937 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ?
$_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
3938 } elseif (isset($HTTP_SERVER_VARS)) {
3939 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
3940 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ?
$HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
3942 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
3944 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
3946 if(false == $style) {
3952 if ($use == 'encoded' && $encodingStyle = '') {
3953 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3956 $this->operations
[$name] = array(
3960 'namespace' => $namespace,
3961 'soapaction' => $soapaction,
3964 $this->wsdl
->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
3970 * Specify a fault to be returned to the client.
3971 * This also acts as a flag to the server that a fault has occured.
3973 * @param string $faultcode
3974 * @param string $faultstring
3975 * @param string $faultactor
3976 * @param string $faultdetail
3979 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
3980 if ($faultdetail == '' && $this->debug_flag
) {
3981 $faultdetail = $this->getDebug();
3983 $this->fault
= new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
3984 $this->fault
->soap_defencoding
= $this->soap_defencoding
;
3988 * Sets up wsdl object.
3989 * Acts as a flag to enable internal WSDL generation
3991 * @param string $serviceName, name of the service
3992 * @param mixed $namespace optional 'tns' service namespace or false
3993 * @param mixed $endpoint optional URL of service endpoint or false
3994 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
3995 * @param string $transport optional SOAP transport
3996 * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
3998 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4000 global $HTTP_SERVER_VARS;
4002 if (isset($_SERVER)) {
4003 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4004 $SERVER_PORT = $_SERVER['SERVER_PORT'];
4005 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ?
$_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4006 $HTTPS = isset($_SERVER['HTTPS']) ?
$_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ?
$HTTP_SERVER_VARS['HTTPS'] : 'off');
4007 } elseif (isset($HTTP_SERVER_VARS)) {
4008 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4009 $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4010 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ?
$HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4011 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ?
$HTTP_SERVER_VARS['HTTPS'] : 'off';
4013 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4015 if ($SERVER_PORT == 80) {
4018 $SERVER_PORT = ':' . $SERVER_PORT;
4020 if(false == $namespace) {
4021 $namespace = "http://$SERVER_NAME/soap/$serviceName";
4024 if(false == $endpoint) {
4025 if ($HTTPS == '1' ||
$HTTPS == 'on') {
4030 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4033 if(false == $schemaTargetNamespace) {
4034 $schemaTargetNamespace = $namespace;
4037 $this->wsdl
= new wsdl
;
4038 $this->wsdl
->serviceName
= $serviceName;
4039 $this->wsdl
->endpoint
= $endpoint;
4040 $this->wsdl
->namespaces
['tns'] = $namespace;
4041 $this->wsdl
->namespaces
['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4042 $this->wsdl
->namespaces
['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4043 if ($schemaTargetNamespace != $namespace) {
4044 $this->wsdl
->namespaces
['types'] = $schemaTargetNamespace;
4046 $this->wsdl
->schemas
[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl
->namespaces
);
4047 $this->wsdl
->schemas
[$schemaTargetNamespace][0]->schemaTargetNamespace
= $schemaTargetNamespace;
4048 $this->wsdl
->schemas
[$schemaTargetNamespace][0]->imports
['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4049 $this->wsdl
->schemas
[$schemaTargetNamespace][0]->imports
['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4050 $this->wsdl
->bindings
[$serviceName.'Binding'] = array(
4051 'name'=>$serviceName.'Binding',
4053 'transport'=>$transport,
4054 'portType'=>$serviceName.'PortType');
4055 $this->wsdl
->ports
[$serviceName.'Port'] = array(
4056 'binding'=>$serviceName.'Binding',
4057 'location'=>$endpoint,
4058 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4069 * parses a WSDL file, allows access to it's data, other utility methods
4071 * @author Dietrich Ayala <dietrich@ganx4.com>
4075 class wsdl
extends nusoap_base
{
4076 // URL or filename of the root of this WSDL
4078 // define internal arrays of bindings, ports, operations, messages, etc.
4079 var $schemas = array();
4081 var $message = array();
4082 var $complexTypes = array();
4083 var $messages = array();
4084 var $currentMessage;
4085 var $currentOperation;
4086 var $portTypes = array();
4087 var $currentPortType;
4088 var $bindings = array();
4089 var $currentBinding;
4090 var $ports = array();
4092 var $opData = array();
4094 var $documentation = false;
4096 // array of wsdl docs to import
4097 var $import = array();
4102 var $depth_array = array();
4104 var $proxyhost = '';
4105 var $proxyport = '';
4106 var $proxyusername = '';
4107 var $proxypassword = '';
4109 var $response_timeout = 30;
4114 * @param string $wsdl WSDL document URL
4115 * @param string $proxyhost
4116 * @param string $proxyport
4117 * @param string $proxyusername
4118 * @param string $proxypassword
4119 * @param integer $timeout set the connection timeout
4120 * @param integer $response_timeout set the response timeout
4123 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
4124 parent
::nusoap_base();
4125 $this->wsdl
= $wsdl;
4126 $this->proxyhost
= $proxyhost;
4127 $this->proxyport
= $proxyport;
4128 $this->proxyusername
= $proxyusername;
4129 $this->proxypassword
= $proxypassword;
4130 $this->timeout
= $timeout;
4131 $this->response_timeout
= $response_timeout;
4135 $this->debug('initial wsdl URL: ' . $wsdl);
4136 $this->parseWSDL($wsdl);
4139 // TODO: handle imports more properly, grabbing them in-line and nesting them
4140 $imported_urls = array();
4142 while ($imported > 0) {
4145 foreach ($this->schemas
as $ns => $list) {
4146 foreach ($list as $xs) {
4147 $wsdlparts = parse_url($this->wsdl
); // this is bogusly simple!
4148 foreach ($xs->imports
as $ns2 => $list2) {
4149 for ($ii = 0; $ii < count($list2); $ii++
) {
4150 if (! $list2[$ii]['loaded']) {
4151 $this->schemas
[$ns]->imports
[$ns2][$ii]['loaded'] = true;
4152 $url = $list2[$ii]['location'];
4154 $urlparts = parse_url($url);
4155 if (!isset($urlparts['host'])) {
4156 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ?
':' .$wsdlparts['port'] : '') .
4157 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') +
1) .$urlparts['path'];
4159 if (! in_array($url, $imported_urls)) {
4160 $this->parseWSDL($url);
4162 $imported_urls[] = $url;
4165 $this->debug("Unexpected scenario: empty URL for unloaded import");
4173 $wsdlparts = parse_url($this->wsdl
); // this is bogusly simple!
4174 foreach ($this->import
as $ns => $list) {
4175 for ($ii = 0; $ii < count($list); $ii++
) {
4176 if (! $list[$ii]['loaded']) {
4177 $this->import
[$ns][$ii]['loaded'] = true;
4178 $url = $list[$ii]['location'];
4180 $urlparts = parse_url($url);
4181 if (!isset($urlparts['host'])) {
4182 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ?
':' . $wsdlparts['port'] : '') .
4183 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') +
1) .$urlparts['path'];
4185 if (! in_array($url, $imported_urls)) {
4186 $this->parseWSDL($url);
4188 $imported_urls[] = $url;
4191 $this->debug("Unexpected scenario: empty URL for unloaded import");
4197 // add new data to operation data
4198 foreach($this->bindings
as $binding => $bindingData) {
4199 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4200 foreach($bindingData['operations'] as $operation => $data) {
4201 $this->debug('post-parse data gathering for ' . $operation);
4202 $this->bindings
[$binding]['operations'][$operation]['input'] =
4203 isset($this->bindings
[$binding]['operations'][$operation]['input']) ?
4204 array_merge($this->bindings
[$binding]['operations'][$operation]['input'], $this->portTypes
[ $bindingData['portType'] ][$operation]['input']) :
4205 $this->portTypes
[ $bindingData['portType'] ][$operation]['input'];
4206 $this->bindings
[$binding]['operations'][$operation]['output'] =
4207 isset($this->bindings
[$binding]['operations'][$operation]['output']) ?
4208 array_merge($this->bindings
[$binding]['operations'][$operation]['output'], $this->portTypes
[ $bindingData['portType'] ][$operation]['output']) :
4209 $this->portTypes
[ $bindingData['portType'] ][$operation]['output'];
4210 if(isset($this->messages
[ $this->bindings
[$binding]['operations'][$operation]['input']['message'] ])){
4211 $this->bindings
[$binding]['operations'][$operation]['input']['parts'] = $this->messages
[ $this->bindings
[$binding]['operations'][$operation]['input']['message'] ];
4213 if(isset($this->messages
[ $this->bindings
[$binding]['operations'][$operation]['output']['message'] ])){
4214 $this->bindings
[$binding]['operations'][$operation]['output']['parts'] = $this->messages
[ $this->bindings
[$binding]['operations'][$operation]['output']['message'] ];
4216 if (isset($bindingData['style'])) {
4217 $this->bindings
[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4219 $this->bindings
[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ?
$bindingData['transport'] : '';
4220 $this->bindings
[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes
[ $bindingData['portType'] ][$operation]['documentation']) ?
$this->portTypes
[ $bindingData['portType'] ][$operation]['documentation'] : '';
4221 $this->bindings
[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ?
$bindingData['endpoint'] : '';
4228 * parses the wsdl document
4230 * @param string $wsdl path or URL
4233 function parseWSDL($wsdl = '')
4236 $this->debug('no wsdl passed to parseWSDL()!!');
4237 $this->setError('no wsdl passed to parseWSDL()!!');
4241 // parse $wsdl for url format
4242 $wsdl_props = parse_url($wsdl);
4244 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' ||
$wsdl_props['scheme'] == 'https')) {
4245 $this->debug('getting WSDL http(s) URL ' . $wsdl);
4247 $tr = new soap_transport_http($wsdl);
4248 $tr->request_method
= 'GET';
4249 $tr->useSOAPAction
= false;
4250 if($this->proxyhost
&& $this->proxyport
){
4251 $tr->setProxy($this->proxyhost
,$this->proxyport
,$this->proxyusername
,$this->proxypassword
);
4253 $tr->setEncoding('gzip, deflate');
4254 $wsdl_string = $tr->send('', $this->timeout
, $this->response_timeout
);
4255 //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4256 //$this->debug("WSDL response\n" . $tr->incoming_payload);
4257 $this->appendDebug($tr->getDebug());
4259 if($err = $tr->getError() ){
4260 $errstr = 'HTTP ERROR: '.$err;
4261 $this->debug($errstr);
4262 $this->setError($errstr);
4267 $this->debug("got WSDL URL");
4269 // $wsdl is not http(s), so treat it as a file URL or plain file path
4270 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4271 $path = isset($wsdl_props['host']) ?
($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4275 $this->debug('getting WSDL file ' . $path);
4276 if ($fp = @fopen
($path, 'r')) {
4278 while ($data = fread($fp, 32768)) {
4279 $wsdl_string .= $data;
4283 $errstr = "Bad path to WSDL file $path";
4284 $this->debug($errstr);
4285 $this->setError($errstr);
4289 $this->debug('Parse WSDL');
4290 // end new code added
4291 // Create an XML parser.
4292 $this->parser
= xml_parser_create();
4293 // Set the options for parsing the XML data.
4294 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4295 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
4296 // Set the object for the parser.
4297 xml_set_object($this->parser
, $this);
4298 // Set the element handlers for the parser.
4299 xml_set_element_handler($this->parser
, 'start_element', 'end_element');
4300 xml_set_character_data_handler($this->parser
, 'character_data');
4301 // Parse the XML file.
4302 if (!xml_parse($this->parser
, $wsdl_string, true)) {
4303 // Display an error message.
4305 'XML error parsing WSDL from %s on line %d: %s',
4307 xml_get_current_line_number($this->parser
),
4308 xml_error_string(xml_get_error_code($this->parser
))
4310 $this->debug($errstr);
4311 $this->debug("XML payload:\n" . $wsdl_string);
4312 $this->setError($errstr);
4316 xml_parser_free($this->parser
);
4317 $this->debug('Parsing WSDL done');
4318 // catch wsdl parse errors
4319 if($this->getError()){
4326 * start-element handler
4328 * @param string $parser XML parser object
4329 * @param string $name element name
4330 * @param string $attrs associative array of attributes
4333 function start_element($parser, $name, $attrs)
4335 if ($this->status
== 'schema') {
4336 $this->currentSchema
->schemaStartElement($parser, $name, $attrs);
4337 $this->appendDebug($this->currentSchema
->getDebug());
4338 $this->currentSchema
->clearDebug();
4339 } elseif (ereg('schema$', $name)) {
4340 $this->debug('Parsing WSDL schema');
4341 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4342 $this->status
= 'schema';
4343 $this->currentSchema
= new xmlschema('', '', $this->namespaces
);
4344 $this->currentSchema
->schemaStartElement($parser, $name, $attrs);
4345 $this->appendDebug($this->currentSchema
->getDebug());
4346 $this->currentSchema
->clearDebug();
4348 // position in the total number of elements, starting from 0
4349 $pos = $this->position++
;
4350 $depth = $this->depth++
;
4351 // set self as current value for this depth
4352 $this->depth_array
[$depth] = $pos;
4353 $this->message
[$pos] = array('cdata' => '');
4354 // process attributes
4355 if (count($attrs) > 0) {
4356 // register namespace declarations
4357 foreach($attrs as $k => $v) {
4358 if (ereg("^xmlns", $k)) {
4359 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4360 $this->namespaces
[$ns_prefix] = $v;
4362 $this->namespaces
['ns' . (count($this->namespaces
) +
1)] = $v;
4364 if ($v == 'http://www.w3.org/2001/XMLSchema' ||
$v == 'http://www.w3.org/1999/XMLSchema' ||
$v == 'http://www.w3.org/2000/10/XMLSchema') {
4365 $this->XMLSchemaVersion
= $v;
4366 $this->namespaces
['xsi'] = $v . '-instance';
4370 // expand each attribute prefix to its namespace
4371 foreach($attrs as $k => $v) {
4372 $k = strpos($k, ':') ?
$this->expandQname($k) : $k;
4373 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4374 $v = strpos($v, ':') ?
$this->expandQname($v) : $v;
4382 // get element prefix, namespace and name
4383 if (ereg(':', $name)) {
4385 $prefix = substr($name, 0, strpos($name, ':'));
4387 $namespace = isset($this->namespaces
[$prefix]) ?
$this->namespaces
[$prefix] : '';
4388 // get unqualified name
4389 $name = substr(strstr($name, ':'), 1);
4391 // process attributes, expanding any prefixes to namespaces
4392 // find status, register data
4393 switch ($this->status
) {
4395 if ($name == 'part') {
4396 if (isset($attrs['type'])) {
4397 $this->debug("msg " . $this->currentMessage
. ": found part $attrs[name]: " . implode(',', $attrs));
4398 $this->messages
[$this->currentMessage
][$attrs['name']] = $attrs['type'];
4400 if (isset($attrs['element'])) {
4401 $this->debug("msg " . $this->currentMessage
. ": found part $attrs[name]: " . implode(',', $attrs));
4402 $this->messages
[$this->currentMessage
][$attrs['name']] = $attrs['element'];
4409 $this->currentPortOperation
= $attrs['name'];
4410 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4411 if (isset($attrs['parameterOrder'])) {
4412 $this->portTypes
[$this->currentPortType
][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4415 case 'documentation':
4416 $this->documentation
= true;
4418 // merge input/output data
4420 $m = isset($attrs['message']) ?
$this->getLocalPart($attrs['message']) : '';
4421 $this->portTypes
[$this->currentPortType
][$this->currentPortOperation
][$name]['message'] = $m;
4429 if (isset($attrs['style'])) {
4430 $this->bindings
[$this->currentBinding
]['prefix'] = $prefix;
4432 $this->bindings
[$this->currentBinding
] = array_merge($this->bindings
[$this->currentBinding
], $attrs);
4435 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
][$this->opStatus
]['headers'][] = $attrs;
4438 if (isset($attrs['soapAction'])) {
4439 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
]['soapAction'] = $attrs['soapAction'];
4441 if (isset($attrs['style'])) {
4442 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
]['style'] = $attrs['style'];
4444 if (isset($attrs['name'])) {
4445 $this->currentOperation
= $attrs['name'];
4446 $this->debug("current binding operation: $this->currentOperation");
4447 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
]['name'] = $attrs['name'];
4448 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
]['binding'] = $this->currentBinding
;
4449 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
]['endpoint'] = isset($this->bindings
[$this->currentBinding
]['endpoint']) ?
$this->bindings
[$this->currentBinding
]['endpoint'] : '';
4453 $this->opStatus
= 'input';
4456 $this->opStatus
= 'output';
4459 if (isset($this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
][$this->opStatus
])) {
4460 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
][$this->opStatus
] = array_merge($this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
][$this->opStatus
], $attrs);
4462 $this->bindings
[$this->currentBinding
]['operations'][$this->currentOperation
][$this->opStatus
] = $attrs;
4470 $this->currentPort
= $attrs['name'];
4471 $this->debug('current port: ' . $this->currentPort
);
4472 $this->ports
[$this->currentPort
]['binding'] = $this->getLocalPart($attrs['binding']);
4476 $this->ports
[$this->currentPort
]['location'] = $attrs['location'];
4477 $this->ports
[$this->currentPort
]['bindingType'] = $namespace;
4478 $this->bindings
[ $this->ports
[$this->currentPort
]['binding'] ]['bindingType'] = $namespace;
4479 $this->bindings
[ $this->ports
[$this->currentPort
]['binding'] ]['endpoint'] = $attrs['location'];
4487 if (isset($attrs['location'])) {
4488 $this->import
[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
4489 $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import
[$attrs['namespace']]).')');
4491 $this->import
[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
4492 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
4493 $this->namespaces
['ns'.(count($this->namespaces
)+
1)] = $attrs['namespace'];
4495 $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import
[$attrs['namespace']]).')');
4500 // $this->status = 'schema';
4503 $this->status
= 'message';
4504 $this->messages
[$attrs['name']] = array();
4505 $this->currentMessage
= $attrs['name'];
4508 $this->status
= 'portType';
4509 $this->portTypes
[$attrs['name']] = array();
4510 $this->currentPortType
= $attrs['name'];
4513 if (isset($attrs['name'])) {
4515 if (strpos($attrs['name'], ':')) {
4516 $this->currentBinding
= $this->getLocalPart($attrs['name']);
4518 $this->currentBinding
= $attrs['name'];
4520 $this->status
= 'binding';
4521 $this->bindings
[$this->currentBinding
]['portType'] = $this->getLocalPart($attrs['type']);
4522 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
4526 $this->serviceName
= $attrs['name'];
4527 $this->status
= 'service';
4528 $this->debug('current service: ' . $this->serviceName
);
4531 foreach ($attrs as $name => $value) {
4532 $this->wsdl_info
[$name] = $value;
4540 * end-element handler
4542 * @param string $parser XML parser object
4543 * @param string $name element name
4546 function end_element($parser, $name){
4547 // unset schema status
4548 if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
4550 $this->appendDebug($this->currentSchema
->getDebug());
4551 $this->currentSchema
->clearDebug();
4552 $this->schemas
[$this->currentSchema
->schemaTargetNamespace
][] = $this->currentSchema
;
4553 $this->debug('Parsing WSDL schema done');
4555 if ($this->status
== 'schema') {
4556 $this->currentSchema
->schemaEndElement($parser, $name);
4558 // bring depth down a notch
4561 // end documentation
4562 if ($this->documentation
) {
4563 //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
4564 //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
4565 $this->documentation
= false;
4570 * element content handler
4572 * @param string $parser XML parser object
4573 * @param string $data element content
4576 function character_data($parser, $data)
4578 $pos = isset($this->depth_array
[$this->depth
]) ?
$this->depth_array
[$this->depth
] : 0;
4579 if (isset($this->message
[$pos]['cdata'])) {
4580 $this->message
[$pos]['cdata'] .= $data;
4582 if ($this->documentation
) {
4583 $this->documentation
.= $data;
4587 function getBindingData($binding)
4589 if (is_array($this->bindings
[$binding])) {
4590 return $this->bindings
[$binding];
4595 * returns an assoc array of operation names => operation data
4597 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
4601 function getOperations($bindingType = 'soap')
4604 if ($bindingType == 'soap') {
4605 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4608 foreach($this->ports
as $port => $portData) {
4609 // binding type of port matches parameter
4610 if ($portData['bindingType'] == $bindingType) {
4611 //$this->debug("getOperations for port $port");
4612 //$this->debug("port data: " . $this->varDump($portData));
4613 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
4615 if (isset($this->bindings
[ $portData['binding'] ]['operations'])) {
4616 $ops = array_merge ($ops, $this->bindings
[ $portData['binding'] ]['operations']);
4624 * returns an associative array of data necessary for calling an operation
4626 * @param string $operation , name of operation
4627 * @param string $bindingType , type of binding eg: soap
4631 function getOperationData($operation, $bindingType = 'soap')
4633 if ($bindingType == 'soap') {
4634 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4637 foreach($this->ports
as $port => $portData) {
4638 // binding type of port matches parameter
4639 if ($portData['bindingType'] == $bindingType) {
4641 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4642 foreach(array_keys($this->bindings
[ $portData['binding'] ]['operations']) as $bOperation) {
4643 // note that we could/should also check the namespace here
4644 if ($operation == $bOperation) {
4645 $opData = $this->bindings
[ $portData['binding'] ]['operations'][$operation];
4654 * returns an associative array of data necessary for calling an operation
4656 * @param string $soapAction soapAction for operation
4657 * @param string $bindingType type of binding eg: soap
4661 function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
4662 if ($bindingType == 'soap') {
4663 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4666 foreach($this->ports
as $port => $portData) {
4667 // binding type of port matches parameter
4668 if ($portData['bindingType'] == $bindingType) {
4669 // loop through operations for the binding
4670 foreach ($this->bindings
[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4671 if ($opData['soapAction'] == $soapAction) {
4680 * returns an array of information about a given type
4681 * returns false if no type exists by the given name
4684 * 'elements' => array(), // refs to elements array
4685 * 'restrictionBase' => '',
4687 * 'order' => '(sequence|all)',
4688 * 'attrs' => array() // refs to attributes array
4691 * @param $type string the type
4692 * @param $ns string namespace (not prefix) of the type
4697 function getTypeDef($type, $ns) {
4698 $this->debug("in getTypeDef: type=$type, ns=$ns");
4699 if ((! $ns) && isset($this->namespaces
['tns'])) {
4700 $ns = $this->namespaces
['tns'];
4701 $this->debug("in getTypeDef: type namespace forced to $ns");
4703 if (isset($this->schemas
[$ns])) {
4704 $this->debug("in getTypeDef: have schema for namespace $ns");
4705 for ($i = 0; $i < count($this->schemas
[$ns]); $i++
) {
4706 $xs = &$this->schemas
[$ns][$i];
4707 $t = $xs->getTypeDef($type);
4708 $this->appendDebug($xs->getDebug());
4711 if (!isset($t['phpType'])) {
4712 // get info for type to tack onto the element
4713 $uqType = substr($t['type'], strrpos($t['type'], ':') +
1);
4714 $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
4715 $etype = $this->getTypeDef($uqType, $ns);
4717 $this->debug("found type for [element] $type:");
4718 $this->debug($this->varDump($etype));
4719 if (isset($etype['phpType'])) {
4720 $t['phpType'] = $etype['phpType'];
4722 if (isset($etype['elements'])) {
4723 $t['elements'] = $etype['elements'];
4725 if (isset($etype['attrs'])) {
4726 $t['attrs'] = $etype['attrs'];
4734 $this->debug("in getTypeDef: do not have schema for namespace $ns");
4740 * prints html description of services
4744 function webDescription(){
4745 global $HTTP_SERVER_VARS;
4747 if (isset($_SERVER)) {
4748 $PHP_SELF = $_SERVER['PHP_SELF'];
4749 } elseif (isset($HTTP_SERVER_VARS)) {
4750 $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
4752 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4756 <html><head><title>NuSOAP: '.$this->serviceName
.'</title>
4757 <style type="text/css">
4758 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
4759 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
4760 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
4761 ul { margin-top: 10px; margin-left: 20px; }
4762 li { list-style-type: none; margin-top: 10px; color: #000000; }
4764 margin-left: 0px; padding-bottom: 2em; }
4766 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
4767 margin-top: 10px; margin-left: 0px; color: #000000;
4768 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
4770 font-family: arial; font-size: 26px; color: #ffffff;
4771 background-color: #999999; width: 105%; margin-left: 0px;
4772 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
4774 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
4775 font-family: arial; overflow: hidden; width: 600;
4776 padding: 20px; font-size: 10px; background-color: #999999;
4777 layer-background-color:#FFFFFF; }
4778 a,a:active { color: charcoal; font-weight: bold; }
4779 a:visited { color: #666666; font-weight: bold; }
4780 a:hover { color: cc3300; font-weight: bold; }
4782 <script type="text/javascript">
4784 // POP-UP CAPTIONS...
4785 function lib_bwcheck(){ //Browsercheck (needed)
4786 this.ver=navigator.appVersion
4787 this.agent=navigator.userAgent
4788 this.dom=document.getElementById?1:0
4789 this.opera5=this.agent.indexOf("Opera 5")>-1
4790 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
4791 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
4792 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
4793 this.ie=this.ie4||this.ie5||this.ie6
4794 this.mac=this.agent.indexOf("Mac")>-1
4795 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
4796 this.ns4=(document.layers && !this.dom)?1:0;
4797 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
4800 var bw = new lib_bwcheck()
4801 //Makes crossbrowser object.
4802 function makeObj(obj){
4803 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
4804 if(!this.evnt) return false
4805 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
4806 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
4807 this.writeIt=b_writeIt;
4810 // A unit of measure that will be added when setting the position of a layer.
4811 //var px = bw.ns4||window.opera?"":"px";
4812 function b_writeIt(text){
4813 if (bw.ns4){this.wref.write(text);this.wref.close()}
4814 else this.wref.innerHTML = text
4816 //Shows the messages
4818 function popup(divid){
4819 if(oDesc = new makeObj(divid)){
4820 oDesc.css.visibility = "visible"
4823 function popout(){ // Hides message
4824 if(oDesc) oDesc.css.visibility = "hidden"
4832 <div class=title>'.$this->serviceName
.'</div>
4834 <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
4835 Click on an operation name to view it's details.</p>
4837 foreach($this->getOperations() as $op => $data){
4838 $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
4839 // create hidden div
4840 $b .= "<div id='$op' class='hidden'>
4841 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
4842 foreach($data as $donnie => $marie){ // loop through opdata
4843 if($donnie == 'input' ||
$donnie == 'output'){ // show input/output data
4844 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
4845 foreach($marie as $captain => $tenille){ // loop through data
4846 if($captain == 'parts'){ // loop thru parts
4847 $b .= " $captain:<br>";
4848 //if(is_array($tenille)){
4849 foreach($tenille as $joanie => $chachi){
4850 $b .= " $joanie: $chachi<br>";
4854 $b .= " $captain: $tenille<br>";
4858 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
4866 </div></body></html>';
4871 * serialize the parsed wsdl
4873 * @param mixed $debug whether to put debug=1 in endpoint URL
4874 * @return string serialization of WSDL
4877 function serialize($debug = 0)
4879 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
4880 $xml .= "\n<definitions";
4881 foreach($this->namespaces
as $k => $v) {
4882 $xml .= " xmlns:$k=\"$v\"";
4884 // 10.9.02 - add poulter fix for wsdl and tns declarations
4885 if (isset($this->namespaces
['wsdl'])) {
4886 $xml .= " xmlns=\"" . $this->namespaces
['wsdl'] . "\"";
4888 if (isset($this->namespaces
['tns'])) {
4889 $xml .= " targetNamespace=\"" . $this->namespaces
['tns'] . "\"";
4893 if (sizeof($this->import
) > 0) {
4894 foreach($this->import
as $ns => $list) {
4895 foreach ($list as $ii) {
4896 if ($ii['location'] != '') {
4897 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
4899 $xml .= '<import namespace="' . $ns . '" />';
4905 if (count($this->schemas
)>=1) {
4906 $xml .= "\n<types>\n";
4907 foreach ($this->schemas
as $ns => $list) {
4908 foreach ($list as $xs) {
4909 $xml .= $xs->serializeSchema();
4915 if (count($this->messages
) >= 1) {
4916 foreach($this->messages
as $msgName => $msgParts) {
4917 $xml .= "\n<message name=\"" . $msgName . '">';
4918 if(is_array($msgParts)){
4919 foreach($msgParts as $partName => $partType) {
4920 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
4921 if (strpos($partType, ':')) {
4922 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
4923 } elseif (isset($this->typemap
[$this->namespaces
['xsd']][$partType])) {
4924 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
4925 $typePrefix = 'xsd';
4927 foreach($this->typemap
as $ns => $types) {
4928 if (isset($types[$partType])) {
4929 $typePrefix = $this->getPrefixFromNamespace($ns);
4932 if (!isset($typePrefix)) {
4933 die("$partType has no namespace!");
4936 $ns = $this->getNamespaceFromPrefix($typePrefix);
4937 $typeDef = $this->getTypeDef($this->getLocalPart($partType), $ns);
4938 if ($typeDef['typeClass'] == 'element') {
4939 $elementortype = 'element';
4941 $elementortype = 'type';
4943 $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
4946 $xml .= '</message>';
4949 // bindings & porttypes
4950 if (count($this->bindings
) >= 1) {
4953 foreach($this->bindings
as $bindingName => $attrs) {
4954 $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
4955 $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
4956 $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
4957 foreach($attrs['operations'] as $opName => $opParts) {
4958 $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
4959 $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
4960 if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
4961 $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
4965 $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
4966 if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
4967 $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
4971 $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
4972 $binding_xml .= "\n" . ' </operation>';
4973 $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
4974 if (isset($opParts['parameterOrder'])) {
4975 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
4977 $portType_xml .= '>';
4978 if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
4979 $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
4981 $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
4982 $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
4983 $portType_xml .= "\n" . ' </operation>';
4985 $portType_xml .= "\n" . '</portType>';
4986 $binding_xml .= "\n" . '</binding>';
4988 $xml .= $portType_xml . $binding_xml;
4991 $xml .= "\n<service name=\"" . $this->serviceName
. '">';
4992 if (count($this->ports
) >= 1) {
4993 foreach($this->ports
as $pName => $attrs) {
4994 $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
4995 $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ?
'?debug=1' : '') . '"/>';
4996 $xml .= "\n" . ' </port>';
4999 $xml .= "\n" . '</service>';
5000 return $xml . "\n</definitions>";
5004 * serialize PHP values according to a WSDL message definition
5007 * - multi-ref serialization
5008 * - validate PHP values against type definitions, return errors if invalid
5010 * @param string $operation operation name
5011 * @param string $direction (input|output)
5012 * @param mixed $parameters parameter value(s)
5013 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5016 function serializeRPCParameters($operation, $direction, $parameters)
5018 $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5019 $this->appendDebug('parameters=' . $this->varDump($parameters));
5021 if ($direction != 'input' && $direction != 'output') {
5022 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5023 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5026 if (!$opData = $this->getOperationData($operation)) {
5027 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5028 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5031 $this->debug('opData:');
5032 $this->appendDebug($this->varDump($opData));
5034 // Get encoding style for output and set to current
5035 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5036 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5037 $encodingStyle = $opData['output']['encodingStyle'];
5038 $enc_style = $encodingStyle;
5043 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5045 $use = $opData[$direction]['use'];
5046 $this->debug('have ' . count($opData[$direction]['parts']) . ' part(s) to serialize');
5047 if (is_array($parameters)) {
5048 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5049 $this->debug('have ' . count($parameters) . ' parameter(s) provided as ' . $parametersArrayType . ' to serialize');
5050 foreach($opData[$direction]['parts'] as $name => $type) {
5051 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5052 // Track encoding style
5053 if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5054 $encodingStyle = $opData[$direction]['encodingStyle'];
5055 $enc_style = $encodingStyle;
5059 // NOTE: add error handling here
5060 // if serializeType returns false, then catch global error and fault
5061 if ($parametersArrayType == 'arraySimple') {
5062 $p = array_shift($parameters);
5063 $this->debug('calling serializeType w/indexed param');
5064 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5065 } elseif (isset($parameters[$name])) {
5066 $this->debug('calling serializeType w/named param');
5067 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5069 // TODO: only send nillable
5070 $this->debug('calling serializeType w/null param');
5071 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5075 $this->debug('no parameters passed.');
5078 $this->debug("serializeRPCParameters returning: $xml");
5083 * serialize a PHP value according to a WSDL message definition
5086 * - multi-ref serialization
5087 * - validate PHP values against type definitions, return errors if invalid
5089 * @param string $ type name
5090 * @param mixed $ param value
5091 * @return mixed new param or false if initial value didn't validate
5095 function serializeParameters($operation, $direction, $parameters)
5097 $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5098 $this->appendDebug('parameters=' . $this->varDump($parameters));
5100 if ($direction != 'input' && $direction != 'output') {
5101 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5102 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5105 if (!$opData = $this->getOperationData($operation)) {
5106 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5107 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5110 $this->debug('opData:');
5111 $this->appendDebug($this->varDump($opData));
5113 // Get encoding style for output and set to current
5114 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5115 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5116 $encodingStyle = $opData['output']['encodingStyle'];
5117 $enc_style = $encodingStyle;
5122 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5124 $use = $opData[$direction]['use'];
5125 $this->debug("use=$use");
5126 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5127 if (is_array($parameters)) {
5128 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5129 $this->debug('have ' . $parametersArrayType . ' parameters');
5130 foreach($opData[$direction]['parts'] as $name => $type) {
5131 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5132 // Track encoding style
5133 if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5134 $encodingStyle = $opData[$direction]['encodingStyle'];
5135 $enc_style = $encodingStyle;
5139 // NOTE: add error handling here
5140 // if serializeType returns false, then catch global error and fault
5141 if ($parametersArrayType == 'arraySimple') {
5142 $p = array_shift($parameters);
5143 $this->debug('calling serializeType w/indexed param');
5144 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5145 } elseif (isset($parameters[$name])) {
5146 $this->debug('calling serializeType w/named param');
5147 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5149 // TODO: only send nillable
5150 $this->debug('calling serializeType w/null param');
5151 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5155 $this->debug('no parameters passed.');
5158 $this->debug("serializeParameters returning: $xml");
5163 * serializes a PHP value according a given type definition
5165 * @param string $name name of value (part or element)
5166 * @param string $type XML schema type of value (type or element)
5167 * @param mixed $value a native PHP value (parameter value)
5168 * @param string $use use for part (encoded|literal)
5169 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5170 * @param boolean $unqualified a kludge for what should be XML namespace form handling
5171 * @return string value serialized as an XML string
5174 function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5176 $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ?
"unqualified" : "qualified"));
5177 $this->appendDebug("value=" . $this->varDump($value));
5178 if($use == 'encoded' && $encodingStyle) {
5179 $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5182 // if a soapval has been supplied, let its type override the WSDL
5183 if (is_object($value) && get_class($value) == 'soapval') {
5184 if ($value->type_ns
) {
5185 $type = $value->type_ns
. ':' . $value->type
;
5187 $this->debug("in serializeType: soapval overrides type to $type");
5188 } elseif ($value->type
) {
5189 $type = $value->type
;
5191 $this->debug("in serializeType: soapval overrides type to $type");
5194 $this->debug("in serializeType: soapval does not override type");
5196 $attrs = $value->attributes
;
5197 $value = $value->value
;
5198 $this->debug("in serializeType: soapval overrides value to $value");
5200 if (!is_array($value)) {
5201 $value['!'] = $value;
5203 foreach ($attrs as $n => $v) {
5204 $value['!' . $n] = $v;
5206 $this->debug("in serializeType: soapval provides attributes");
5213 if (strpos($type, ':')) {
5214 $uqType = substr($type, strrpos($type, ':') +
1);
5215 $ns = substr($type, 0, strrpos($type, ':'));
5216 $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5217 if ($this->getNamespaceFromPrefix($ns)) {
5218 $ns = $this->getNamespaceFromPrefix($ns);
5219 $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5222 if($ns == $this->XMLSchemaVersion ||
$ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5223 $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5224 if ($unqualified && $use == 'literal') {
5225 $elementNS = " xmlns=\"\"";
5229 if (is_null($value)) {
5230 if ($use == 'literal') {
5231 // TODO: depends on minOccurs
5232 $xml = "<$name$elementNS/>";
5234 // TODO: depends on nillable, which should be checked before calling this method
5235 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5237 $this->debug("in serializeType: returning: $xml");
5240 if ($uqType == 'Array') {
5241 // JBoss/Axis does this sometimes
5242 return $this->serialize_val($value, $name, false, false, false, false, $use);
5244 if ($uqType == 'boolean') {
5245 if ((is_string($value) && $value == 'false') ||
(! $value)) {
5251 if ($uqType == 'string' && gettype($value) == 'string') {
5252 $value = $this->expandEntities($value);
5254 if (($uqType == 'long' ||
$uqType == 'unsignedLong') && gettype($value) == 'double') {
5255 $value = sprintf("%.0lf", $value);
5258 // TODO: what about null/nil values?
5259 // check type isn't a custom type extending xmlschema namespace
5260 if (!$this->getTypeDef($uqType, $ns)) {
5261 if ($use == 'literal') {
5263 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5265 $xml = "<$name$elementNS>$value</$name>";
5268 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5270 $this->debug("in serializeType: returning: $xml");
5273 $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5274 } else if ($ns == 'http://xml.apache.org/xml-soap') {
5275 $this->debug('in serializeType: appears to be Apache SOAP type');
5276 if ($uqType == 'Map') {
5277 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5279 $this->debug('in serializeType: Add namespace for Apache SOAP type');
5280 $tt_prefix = 'ns' . rand(1000, 9999);
5281 $this->namespaces
[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5282 // force this to be added to usedNamespaces
5283 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5286 foreach($value as $k => $v) {
5287 $this->debug("serializing map element: key $k, value $v");
5288 $contents .= '<item>';
5289 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5290 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5291 $contents .= '</item>';
5293 if ($use == 'literal') {
5295 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5297 $xml = "<$name>$contents</$name>";
5300 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
5302 $this->debug("in serializeType: returning: $xml");
5305 $this->debug('in serializeType: Apache SOAP type, but only support Map');
5308 // TODO: should the type be compared to types in XSD, and the namespace
5309 // set to XSD if the type matches?
5310 $this->debug("in serializeType: No namespace for type $type");
5314 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
5315 $this->setError("$type ($uqType) is not a supported type.");
5316 $this->debug("in serializeType: $type ($uqType) is not a supported type.");
5319 $this->debug("in serializeType: found typeDef");
5320 $this->appendDebug('typeDef=' . $this->varDump($typeDef));
5322 $phpType = $typeDef['phpType'];
5323 $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ?
$typeDef['arrayType'] : '') );
5324 // if php type == struct, map value to the <all> element names
5325 if ($phpType == 'struct') {
5326 if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
5327 $elementName = $uqType;
5328 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5329 $elementNS = " xmlns=\"$ns\"";
5331 $elementNS = " xmlns=\"\"";
5334 $elementName = $name;
5336 $elementNS = " xmlns=\"\"";
5341 if (is_null($value)) {
5342 if ($use == 'literal') {
5343 // TODO: depends on minOccurs
5344 $xml = "<$elementName$elementNS/>";
5346 $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5348 $this->debug("in serializeType: returning: $xml");
5351 if (is_object($value)) {
5352 $value = get_object_vars($value);
5354 if (is_array($value)) {
5355 $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5356 if ($use == 'literal') {
5358 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
5360 $xml = "<$elementName$elementNS$elementAttrs>";
5363 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
5366 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5367 $xml .= "</$elementName>";
5369 $this->debug("in serializeType: phpType is struct, but value is not an array");
5370 $this->setError("phpType is struct, but value is not an array: see debug output for details");
5373 } elseif ($phpType == 'array') {
5374 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5375 $elementNS = " xmlns=\"$ns\"";
5378 $elementNS = " xmlns=\"\"";
5383 if (is_null($value)) {
5384 if ($use == 'literal') {
5385 // TODO: depends on minOccurs
5386 $xml = "<$name$elementNS/>";
5388 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
5389 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5391 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5393 $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
5395 $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
5397 $this->debug("in serializeType: returning: $xml");
5400 if (isset($typeDef['multidimensional'])) {
5402 foreach($value as $v) {
5403 $cols = ',' . sizeof($v);
5404 $nv = array_merge($nv, $v);
5410 if (is_array($value) && sizeof($value) >= 1) {
5411 $rows = sizeof($value);
5413 foreach($value as $k => $v) {
5414 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
5415 //if (strpos($typeDef['arrayType'], ':') ) {
5416 if (!in_array($typeDef['arrayType'],$this->typemap
['http://www.w3.org/2001/XMLSchema'])) {
5417 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
5419 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion
, false, $use);
5426 // TODO: for now, an empty value will be serialized as a zero element
5427 // array. Revisit this when coding the handling of null/nil values.
5428 if ($use == 'literal') {
5429 $xml = "<$name$elementNS>"
5433 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
5434 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
5436 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
5437 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
5441 } elseif ($phpType == 'scalar') {
5442 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5443 $elementNS = " xmlns=\"$ns\"";
5446 $elementNS = " xmlns=\"\"";
5451 if ($use == 'literal') {
5453 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5455 $xml = "<$name$elementNS>$value</$name>";
5458 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5461 $this->debug("in serializeType: returning: $xml");
5466 * serializes the attributes for a complexType
5468 * @param array $typeDef our internal representation of an XML schema type (or element)
5469 * @param mixed $value a native PHP value (parameter value)
5470 * @param string $ns the namespace of the type
5471 * @param string $uqType the local part of the type
5472 * @return string value serialized as an XML string
5475 function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
5477 if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
5478 $this->debug("serialize attributes for XML Schema type $ns:$uqType");
5479 if (is_array($value)) {
5481 } elseif (is_object($value)) {
5482 $xvalue = get_object_vars($value);
5484 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5487 foreach ($typeDef['attrs'] as $aName => $attrs) {
5488 if (isset($xvalue['!' . $aName])) {
5489 $xname = '!' . $aName;
5490 $this->debug("value provided for attribute $aName with key $xname");
5491 } elseif (isset($xvalue[$aName])) {
5493 $this->debug("value provided for attribute $aName with key $xname");
5494 } elseif (isset($attrs['default'])) {
5495 $xname = '!' . $aName;
5496 $xvalue[$xname] = $attrs['default'];
5497 $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
5500 $this->debug("no value provided for attribute $aName");
5503 $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
5507 $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
5509 if (isset($typeDef['extensionBase'])) {
5510 $ns = $this->getPrefix($typeDef['extensionBase']);
5511 $uqType = $this->getLocalPart($typeDef['extensionBase']);
5512 if ($this->getNamespaceFromPrefix($ns)) {
5513 $ns = $this->getNamespaceFromPrefix($ns);
5515 if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5516 $this->debug("serialize attributes for extension base $ns:$uqType");
5517 $xml .= $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5519 $this->debug("extension base $ns:$uqType is not a supported type");
5526 * serializes the elements for a complexType
5528 * @param array $typeDef our internal representation of an XML schema type (or element)
5529 * @param mixed $value a native PHP value (parameter value)
5530 * @param string $ns the namespace of the type
5531 * @param string $uqType the local part of the type
5532 * @param string $use use for part (encoded|literal)
5533 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5534 * @return string value serialized as an XML string
5537 function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
5539 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5540 $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
5541 if (is_array($value)) {
5543 } elseif (is_object($value)) {
5544 $xvalue = get_object_vars($value);
5546 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5549 // toggle whether all elements are present - ideally should validate against schema
5550 if (count($typeDef['elements']) != count($xvalue)){
5553 foreach ($typeDef['elements'] as $eName => $attrs) {
5554 if (!isset($xvalue[$eName])) {
5555 if (isset($attrs['default'])) {
5556 $xvalue[$eName] = $attrs['default'];
5557 $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
5560 // if user took advantage of a minOccurs=0, then only serialize named parameters
5561 if (isset($optionals)
5562 && (!isset($xvalue[$eName]))
5563 && ( (!isset($attrs['nillable'])) ||
$attrs['nillable'] != 'true')
5565 if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
5566 $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
5569 $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
5572 if (isset($xvalue[$eName])) {
5573 $v = $xvalue[$eName];
5577 if (isset($attrs['form'])) {
5578 $unqualified = ($attrs['form'] == 'unqualified');
5580 $unqualified = false;
5582 if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' ||
$attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
5584 foreach ($vv as $k => $v) {
5585 if (isset($attrs['type']) ||
isset($attrs['ref'])) {
5586 // serialize schema-defined type
5587 $xml .= $this->serializeType($eName, isset($attrs['type']) ?
$attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5589 // serialize generic type (can this ever really happen?)
5590 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5591 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5595 if (isset($attrs['type']) ||
isset($attrs['ref'])) {
5596 // serialize schema-defined type
5597 $xml .= $this->serializeType($eName, isset($attrs['type']) ?
$attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5599 // serialize generic type (can this ever really happen?)
5600 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5601 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5607 $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
5609 if (isset($typeDef['extensionBase'])) {
5610 $ns = $this->getPrefix($typeDef['extensionBase']);
5611 $uqType = $this->getLocalPart($typeDef['extensionBase']);
5612 if ($this->getNamespaceFromPrefix($ns)) {
5613 $ns = $this->getNamespaceFromPrefix($ns);
5615 if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5616 $this->debug("serialize elements for extension base $ns:$uqType");
5617 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5619 $this->debug("extension base $ns:$uqType is not a supported type");
5626 * adds an XML Schema complex type to the WSDL types
5628 * @param string name
5629 * @param string typeClass (complexType|simpleType|attribute)
5630 * @param string phpType: currently supported are array and struct (php assoc array)
5631 * @param string compositor (all|sequence|choice)
5632 * @param string restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5633 * @param array elements = array ( name => array(name=>'',type=>'') )
5634 * @param array attrs = array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
5635 * @param string arrayType: namespace:name (xsd:string)
5639 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
5640 if (count($elements) > 0) {
5641 foreach($elements as $n => $e){
5642 // expand each element
5643 foreach ($e as $k => $v) {
5644 $k = strpos($k,':') ?
$this->expandQname($k) : $k;
5645 $v = strpos($v,':') ?
$this->expandQname($v) : $v;
5648 $eElements[$n] = $ee;
5650 $elements = $eElements;
5653 if (count($attrs) > 0) {
5654 foreach($attrs as $n => $a){
5655 // expand each attribute
5656 foreach ($a as $k => $v) {
5657 $k = strpos($k,':') ?
$this->expandQname($k) : $k;
5658 $v = strpos($v,':') ?
$this->expandQname($v) : $v;
5666 $restrictionBase = strpos($restrictionBase,':') ?
$this->expandQname($restrictionBase) : $restrictionBase;
5667 $arrayType = strpos($arrayType,':') ?
$this->expandQname($arrayType) : $arrayType;
5669 $typens = isset($this->namespaces
['types']) ?
$this->namespaces
['types'] : $this->namespaces
['tns'];
5670 $this->schemas
[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
5674 * adds an XML Schema simple type to the WSDL types
5676 * @param string $name
5677 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5678 * @param string $typeClass (should always be simpleType)
5679 * @param string $phpType (should always be scalar)
5680 * @param array $enumeration array of values
5684 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
5685 $restrictionBase = strpos($restrictionBase,':') ?
$this->expandQname($restrictionBase) : $restrictionBase;
5687 $typens = isset($this->namespaces
['types']) ?
$this->namespaces
['types'] : $this->namespaces
['tns'];
5688 $this->schemas
[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
5692 * adds an element to the WSDL types
5694 * @param array $attrs attributes that must include name and type
5698 function addElement($attrs) {
5699 $typens = isset($this->namespaces
['types']) ?
$this->namespaces
['types'] : $this->namespaces
['tns'];
5700 $this->schemas
[$typens][0]->addElement($attrs);
5704 * register an operation with the server
5706 * @param string $name operation (method) name
5707 * @param array $in assoc array of input values: key = param name, value = param type
5708 * @param array $out assoc array of output values: key = param name, value = param type
5709 * @param string $namespace optional The namespace for the operation
5710 * @param string $soapaction optional The soapaction for the operation
5711 * @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
5712 * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
5713 * @param string $documentation optional The description to include in the WSDL
5714 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
5717 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
5718 if ($use == 'encoded' && $encodingStyle == '') {
5719 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5722 if ($style == 'document') {
5723 $elements = array();
5724 foreach ($in as $n => $t) {
5725 $elements[$n] = array('name' => $n, 'type' => $t);
5727 $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
5728 $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
5729 $in = array('parameters' => 'tns:' . $name);
5731 $elements = array();
5732 foreach ($out as $n => $t) {
5733 $elements[$n] = array('name' => $n, 'type' => $t);
5735 $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
5736 $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType'));
5737 $out = array('parameters' => 'tns:' . $name . 'Response');
5741 $this->bindings
[ $this->serviceName
. 'Binding' ]['operations'][$name] =
5744 'binding' => $this->serviceName
. 'Binding',
5745 'endpoint' => $this->endpoint
,
5746 'soapAction' => $soapaction,
5750 'namespace' => $namespace,
5751 'encodingStyle' => $encodingStyle,
5752 'message' => $name . 'Request',
5756 'namespace' => $namespace,
5757 'encodingStyle' => $encodingStyle,
5758 'message' => $name . 'Response',
5760 'namespace' => $namespace,
5761 'transport' => 'http://schemas.xmlsoap.org/soap/http',
5762 'documentation' => $documentation);
5767 foreach($in as $pName => $pType)
5769 if(strpos($pType,':')) {
5770 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5772 $this->messages
[$name.'Request'][$pName] = $pType;
5775 $this->messages
[$name.'Request']= '0';
5779 foreach($out as $pName => $pType)
5781 if(strpos($pType,':')) {
5782 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5784 $this->messages
[$name.'Response'][$pName] = $pType;
5787 $this->messages
[$name.'Response']= '0';
5798 * soap_parser class parses SOAP XML messages into native PHP values
5800 * @author Dietrich Ayala <dietrich@ganx4.com>
5804 class soap_parser
extends nusoap_base
{
5807 var $xml_encoding = '';
5809 var $root_struct = '';
5810 var $root_struct_name = '';
5811 var $root_struct_namespace = '';
5812 var $root_header = '';
5813 var $document = ''; // incoming SOAP body (text)
5814 // determines where in the message we are (envelope,header,body,method)
5818 var $default_namespace = '';
5819 var $namespaces = array();
5820 var $message = array();
5823 var $fault_code = '';
5824 var $fault_str = '';
5825 var $fault_detail = '';
5826 var $depth_array = array();
5827 var $debug_flag = true;
5828 var $soapresponse = NULL;
5829 var $responseHeaders = ''; // incoming SOAP headers (text)
5830 var $body_position = 0;
5831 // for multiref parsing:
5832 // array of id => pos
5834 // array of id => hrefs => pos
5835 var $multirefs = array();
5836 // toggle for auto-decoding element content
5837 var $decode_utf8 = true;
5840 * constructor that actually does the parsing
5842 * @param string $xml SOAP message
5843 * @param string $encoding character encoding scheme of message
5844 * @param string $method method for which XML is parsed (unused?)
5845 * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
5848 function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
5849 parent
::nusoap_base();
5851 $this->xml_encoding
= $encoding;
5852 $this->method
= $method;
5853 $this->decode_utf8
= $decode_utf8;
5855 // Check whether content has been read.
5857 // Check XML encoding
5858 $pos_xml = strpos($xml, '<?xml');
5859 if ($pos_xml !== FALSE) {
5860 $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml +
2) - $pos_xml +
1);
5861 if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
5862 $xml_encoding = $res[1];
5863 if (strtoupper($xml_encoding) != $encoding) {
5864 $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
5866 if ($encoding != 'ISO-8859-1' ||
strtoupper($xml_encoding) != 'UTF-8') {
5867 $this->setError($err);
5870 // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
5872 $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
5875 $this->debug('No encoding specified in XML declaration');
5878 $this->debug('No XML declaration');
5880 $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
5881 // Create an XML parser - why not xml_parser_create_ns?
5882 $this->parser
= xml_parser_create($this->xml_encoding
);
5883 // Set the options for parsing the XML data.
5884 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
5885 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
5886 xml_parser_set_option($this->parser
, XML_OPTION_TARGET_ENCODING
, $this->xml_encoding
);
5887 // Set the object for the parser.
5888 xml_set_object($this->parser
, $this);
5889 // Set the element handlers for the parser.
5890 xml_set_element_handler($this->parser
, 'start_element','end_element');
5891 xml_set_character_data_handler($this->parser
,'character_data');
5893 // Parse the XML file.
5894 if(!xml_parse($this->parser
,$xml,true)){
5895 // Display an error message.
5896 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
5897 xml_get_current_line_number($this->parser
),
5898 xml_error_string(xml_get_error_code($this->parser
)));
5900 $this->debug("XML payload:\n" . $xml);
5901 $this->setError($err);
5903 $this->debug('parsed successfully, found root struct: '.$this->root_struct
.' of name '.$this->root_struct_name
);
5905 $this->soapresponse
= $this->message
[$this->root_struct
]['result'];
5906 // get header value: no, because this is documented as XML string
5907 // if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
5908 // $this->responseHeaders = $this->message[$this->root_header]['result'];
5910 // resolve hrefs/ids
5911 if(sizeof($this->multirefs
) > 0){
5912 foreach($this->multirefs
as $id => $hrefs){
5913 $this->debug('resolving multirefs for id: '.$id);
5914 $idVal = $this->buildVal($this->ids
[$id]);
5915 if (is_array($idVal) && isset($idVal['!id'])) {
5916 unset($idVal['!id']);
5918 foreach($hrefs as $refPos => $ref){
5919 $this->debug('resolving href at pos '.$refPos);
5920 $this->multirefs
[$id][$refPos] = $idVal;
5925 xml_parser_free($this->parser
);
5927 $this->debug('xml was empty, didn\'t parse!');
5928 $this->setError('xml was empty, didn\'t parse!');
5933 * start-element handler
5935 * @param resource $parser XML parser object
5936 * @param string $name element name
5937 * @param array $attrs associative array of attributes
5940 function start_element($parser, $name, $attrs) {
5941 // position in a total number of elements, starting from 0
5942 // update class level pos
5943 $pos = $this->position++
;
5945 $this->message
[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
5946 // depth = how many levels removed from root?
5947 // set mine as current global depth and increment global depth value
5948 $this->message
[$pos]['depth'] = $this->depth++
;
5950 // else add self as child to whoever the current parent is
5952 $this->message
[$this->parent
]['children'] .= '|'.$pos;
5955 $this->message
[$pos]['parent'] = $this->parent
;
5956 // set self as current parent
5957 $this->parent
= $pos;
5958 // set self as current value for this depth
5959 $this->depth_array
[$this->depth
] = $pos;
5960 // get element prefix
5961 if(strpos($name,':')){
5963 $prefix = substr($name,0,strpos($name,':'));
5964 // get unqualified name
5965 $name = substr(strstr($name,':'),1);
5968 if($name == 'Envelope'){
5969 $this->status
= 'envelope';
5970 } elseif($name == 'Header'){
5971 $this->root_header
= $pos;
5972 $this->status
= 'header';
5973 } elseif($name == 'Body'){
5974 $this->status
= 'body';
5975 $this->body_position
= $pos;
5977 } elseif($this->status
== 'body' && $pos == ($this->body_position+
1)){
5978 $this->status
= 'method';
5979 $this->root_struct_name
= $name;
5980 $this->root_struct
= $pos;
5981 $this->message
[$pos]['type'] = 'struct';
5982 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
5985 $this->message
[$pos]['status'] = $this->status
;
5987 $this->message
[$pos]['name'] = htmlspecialchars($name);
5989 $this->message
[$pos]['attrs'] = $attrs;
5991 // loop through atts, logging ns and type declarations
5993 foreach($attrs as $key => $value){
5994 $key_prefix = $this->getPrefix($key);
5995 $key_localpart = $this->getLocalPart($key);
5996 // if ns declarations, add to class level array of valid namespaces
5997 if($key_prefix == 'xmlns'){
5998 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
5999 $this->XMLSchemaVersion
= $value;
6000 $this->namespaces
['xsd'] = $this->XMLSchemaVersion
;
6001 $this->namespaces
['xsi'] = $this->XMLSchemaVersion
.'-instance';
6003 $this->namespaces
[$key_localpart] = $value;
6004 // set method namespace
6005 if($name == $this->root_struct_name
){
6006 $this->methodNamespace
= $value;
6008 // if it's a type declaration, set type
6009 } elseif($key_localpart == 'type'){
6010 if (isset($this->message
[$pos]['type']) && $this->message
[$pos]['type'] == 'array') {
6011 // do nothing: already processed arrayType
6013 $value_prefix = $this->getPrefix($value);
6014 $value_localpart = $this->getLocalPart($value);
6015 $this->message
[$pos]['type'] = $value_localpart;
6016 $this->message
[$pos]['typePrefix'] = $value_prefix;
6017 if(isset($this->namespaces
[$value_prefix])){
6018 $this->message
[$pos]['type_namespace'] = $this->namespaces
[$value_prefix];
6019 } else if(isset($attrs['xmlns:'.$value_prefix])) {
6020 $this->message
[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6022 // should do something here with the namespace of specified type?
6024 } elseif($key_localpart == 'arrayType'){
6025 $this->message
[$pos]['type'] = 'array';
6026 /* do arrayType ereg here
6027 [1] arrayTypeValue ::= atype asize
6028 [2] atype ::= QName rank*
6029 [3] rank ::= '[' (',')* ']'
6030 [4] asize ::= '[' length~ ']'
6031 [5] length ::= nextDimension* Digit+
6032 [6] nextDimension ::= Digit+ ','
6034 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
6035 if(ereg($expr,$value,$regs)){
6036 $this->message
[$pos]['typePrefix'] = $regs[1];
6037 $this->message
[$pos]['arrayTypePrefix'] = $regs[1];
6038 if (isset($this->namespaces
[$regs[1]])) {
6039 $this->message
[$pos]['arrayTypeNamespace'] = $this->namespaces
[$regs[1]];
6040 } else if (isset($attrs['xmlns:'.$regs[1]])) {
6041 $this->message
[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6043 $this->message
[$pos]['arrayType'] = $regs[2];
6044 $this->message
[$pos]['arraySize'] = $regs[3];
6045 $this->message
[$pos]['arrayCols'] = $regs[4];
6047 // specifies nil value (or not)
6048 } elseif ($key_localpart == 'nil'){
6049 $this->message
[$pos]['nil'] = ($value == 'true' ||
$value == '1');
6050 // some other attribute
6051 } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6052 $this->message
[$pos]['xattrs']['!' . $key] = $value;
6055 if ($key == 'xmlns') {
6056 $this->default_namespace
= $value;
6060 $this->ids
[$value] = $pos;
6063 if($key_localpart == 'root' && $value == 1){
6064 $this->status
= 'method';
6065 $this->root_struct_name
= $name;
6066 $this->root_struct
= $pos;
6067 $this->debug("found root struct $this->root_struct_name, pos $pos");
6070 $attstr .= " $key=\"$value\"";
6072 // get namespace - must be done after namespace atts are processed
6074 $this->message
[$pos]['namespace'] = $this->namespaces
[$prefix];
6075 $this->default_namespace
= $this->namespaces
[$prefix];
6077 $this->message
[$pos]['namespace'] = $this->default_namespace
;
6079 if($this->status
== 'header'){
6080 if ($this->root_header
!= $pos) {
6081 $this->responseHeaders
.= "<" . (isset($prefix) ?
$prefix . ':' : '') . "$name$attstr>";
6083 } elseif($this->root_struct_name
!= ''){
6084 $this->document
.= "<" . (isset($prefix) ?
$prefix . ':' : '') . "$name$attstr>";
6089 * end-element handler
6091 * @param resource $parser XML parser object
6092 * @param string $name element name
6095 function end_element($parser, $name) {
6096 // position of current element is equal to the last value left in depth_array for my depth
6097 $pos = $this->depth_array
[$this->depth
--];
6099 // get element prefix
6100 if(strpos($name,':')){
6102 $prefix = substr($name,0,strpos($name,':'));
6103 // get unqualified name
6104 $name = substr(strstr($name,':'),1);
6107 // build to native type
6108 if(isset($this->body_position
) && $pos > $this->body_position
){
6109 // deal w/ multirefs
6110 if(isset($this->message
[$pos]['attrs']['href'])){
6112 $id = substr($this->message
[$pos]['attrs']['href'],1);
6113 // add placeholder to href array
6114 $this->multirefs
[$id][$pos] = 'placeholder';
6115 // add set a reference to it as the result value
6116 $this->message
[$pos]['result'] =& $this->multirefs
[$id][$pos];
6117 // build complexType values
6118 } elseif($this->message
[$pos]['children'] != ''){
6119 // if result has already been generated (struct/array)
6120 if(!isset($this->message
[$pos]['result'])){
6121 $this->message
[$pos]['result'] = $this->buildVal($pos);
6123 // build complexType values of attributes and possibly simpleContent
6124 } elseif (isset($this->message
[$pos]['xattrs'])) {
6125 if (isset($this->message
[$pos]['nil']) && $this->message
[$pos]['nil']) {
6126 $this->message
[$pos]['xattrs']['!'] = null;
6127 } elseif (isset($this->message
[$pos]['cdata']) && trim($this->message
[$pos]['cdata']) != '') {
6128 if (isset($this->message
[$pos]['type'])) {
6129 $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'] : '');
6131 $parent = $this->message
[$pos]['parent'];
6132 if (isset($this->message
[$parent]['type']) && ($this->message
[$parent]['type'] == 'array') && isset($this->message
[$parent]['arrayType'])) {
6133 $this->message
[$pos]['xattrs']['!'] = $this->decodeSimple($this->message
[$pos]['cdata'], $this->message
[$parent]['arrayType'], isset($this->message
[$parent]['arrayTypeNamespace']) ?
$this->message
[$parent]['arrayTypeNamespace'] : '');
6135 $this->message
[$pos]['xattrs']['!'] = $this->message
[$pos]['cdata'];
6139 $this->message
[$pos]['result'] = $this->message
[$pos]['xattrs'];
6140 // set value of simpleType (or nil complexType)
6142 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6143 if (isset($this->message
[$pos]['nil']) && $this->message
[$pos]['nil']) {
6144 $this->message
[$pos]['xattrs']['!'] = null;
6145 } elseif (isset($this->message
[$pos]['type'])) {
6146 $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'] : '');
6148 $parent = $this->message
[$pos]['parent'];
6149 if (isset($this->message
[$parent]['type']) && ($this->message
[$parent]['type'] == 'array') && isset($this->message
[$parent]['arrayType'])) {
6150 $this->message
[$pos]['result'] = $this->decodeSimple($this->message
[$pos]['cdata'], $this->message
[$parent]['arrayType'], isset($this->message
[$parent]['arrayTypeNamespace']) ?
$this->message
[$parent]['arrayTypeNamespace'] : '');
6152 $this->message
[$pos]['result'] = $this->message
[$pos]['cdata'];
6156 /* add value to parent's result, if parent is struct/array
6157 $parent = $this->message[$pos]['parent'];
6158 if($this->message[$parent]['type'] != 'map'){
6159 if(strtolower($this->message[$parent]['type']) == 'array'){
6160 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6162 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6170 if($this->status
== 'header'){
6171 if ($this->root_header
!= $pos) {
6172 $this->responseHeaders
.= "</" . (isset($prefix) ?
$prefix . ':' : '') . "$name>";
6174 } elseif($pos >= $this->root_struct
){
6175 $this->document
.= "</" . (isset($prefix) ?
$prefix . ':' : '') . "$name>";
6178 if($pos == $this->root_struct
){
6179 $this->status
= 'body';
6180 $this->root_struct_namespace
= $this->message
[$pos]['namespace'];
6181 } elseif($name == 'Body'){
6182 $this->status
= 'envelope';
6183 } elseif($name == 'Header'){
6184 $this->status
= 'envelope';
6185 } elseif($name == 'Envelope'){
6188 // set parent back to my parent
6189 $this->parent
= $this->message
[$pos]['parent'];
6193 * element content handler
6195 * @param resource $parser XML parser object
6196 * @param string $data element content
6199 function character_data($parser, $data){
6200 $pos = $this->depth_array
[$this->depth
];
6201 if ($this->xml_encoding
=='UTF-8'){
6202 // TODO: add an option to disable this for folks who want
6203 // raw UTF-8 that, e.g., might not map to iso-8859-1
6204 // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6205 if($this->decode_utf8
){
6206 $data = utf8_decode($data);
6209 $this->message
[$pos]['cdata'] .= $data;
6211 if($this->status
== 'header'){
6212 $this->responseHeaders
.= $data;
6214 $this->document
.= $data;
6219 * get the parsed message
6224 function get_response(){
6225 return $this->soapresponse
;
6229 * get the parsed headers
6231 * @return string XML or empty if no headers
6234 function getHeaders(){
6235 return $this->responseHeaders
;
6239 * decodes simple types into PHP variables
6241 * @param string $value value to decode
6242 * @param string $type XML type to decode
6243 * @param string $typens XML type namespace to decode
6244 * @return mixed PHP value
6247 function decodeSimple($value, $type, $typens) {
6248 // TODO: use the namespace!
6249 if ((!isset($type)) ||
$type == 'string' ||
$type == 'long' ||
$type == 'unsignedLong') {
6250 return (string) $value;
6252 if ($type == 'int' ||
$type == 'integer' ||
$type == 'short' ||
$type == 'byte') {
6253 return (int) $value;
6255 if ($type == 'float' ||
$type == 'double' ||
$type == 'decimal') {
6256 return (double) $value;
6258 if ($type == 'boolean') {
6259 if (strtolower($value) == 'false' ||
strtolower($value) == 'f') {
6262 return (boolean
) $value;
6264 if ($type == 'base64' ||
$type == 'base64Binary') {
6265 $this->debug('Decode base64 value');
6266 return base64_decode($value);
6268 // obscure numeric types
6269 if ($type == 'nonPositiveInteger' ||
$type == 'negativeInteger'
6270 ||
$type == 'nonNegativeInteger' ||
$type == 'positiveInteger'
6271 ||
$type == 'unsignedInt'
6272 ||
$type == 'unsignedShort' ||
$type == 'unsignedByte') {
6273 return (int) $value;
6275 // bogus: parser treats array with no elements as a simple type
6276 if ($type == 'array') {
6280 return (string) $value;
6284 * builds response structures for compound values (arrays/structs)
6287 * @param integer $pos position in node tree
6288 * @return mixed PHP value
6291 function buildVal($pos){
6292 if(!isset($this->message
[$pos]['type'])){
6293 $this->message
[$pos]['type'] = '';
6295 $this->debug('in buildVal() for '.$this->message
[$pos]['name']."(pos $pos) of type ".$this->message
[$pos]['type']);
6296 // if there are children...
6297 if($this->message
[$pos]['children'] != ''){
6298 $this->debug('in buildVal, there are children');
6299 $children = explode('|',$this->message
[$pos]['children']);
6300 array_shift($children); // knock off empty
6302 if(isset($this->message
[$pos]['arrayCols']) && $this->message
[$pos]['arrayCols'] != ''){
6305 foreach($children as $child_pos){
6306 $this->debug("in buildVal, got an MD array element: $r, $c");
6307 $params[$r][] = $this->message
[$child_pos]['result'];
6309 if($c == $this->message
[$pos]['arrayCols']){
6315 } elseif($this->message
[$pos]['type'] == 'array' ||
$this->message
[$pos]['type'] == 'Array'){
6316 $this->debug('in buildVal, adding array '.$this->message
[$pos]['name']);
6317 foreach($children as $child_pos){
6318 $params[] = &$this->message
[$child_pos]['result'];
6320 // apache Map type: java hashtable
6321 } elseif($this->message
[$pos]['type'] == 'Map' && $this->message
[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
6322 $this->debug('in buildVal, Java Map '.$this->message
[$pos]['name']);
6323 foreach($children as $child_pos){
6324 $kv = explode("|",$this->message
[$child_pos]['children']);
6325 $params[$this->message
[$kv[1]]['result']] = &$this->message
[$kv[2]]['result'];
6327 // generic compound type
6328 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
6330 // Apache Vector type: treat as an array
6331 $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message
[$pos]['name']);
6332 if ($this->message
[$pos]['type'] == 'Vector' && $this->message
[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
6338 foreach($children as $child_pos){
6340 $params[] = &$this->message
[$child_pos]['result'];
6342 if (isset($params[$this->message
[$child_pos]['name']])) {
6343 // de-serialize repeated element name into an array
6344 if ((!is_array($params[$this->message
[$child_pos]['name']])) ||
(!isset($params[$this->message
[$child_pos]['name']][0]))) {
6345 $params[$this->message
[$child_pos]['name']] = array($params[$this->message
[$child_pos]['name']]);
6347 $params[$this->message
[$child_pos]['name']][] = &$this->message
[$child_pos]['result'];
6349 $params[$this->message
[$child_pos]['name']] = &$this->message
[$child_pos]['result'];
6354 if (isset($this->message
[$pos]['xattrs'])) {
6355 $this->debug('in buildVal, handling attributes');
6356 foreach ($this->message
[$pos]['xattrs'] as $n => $v) {
6360 // handle simpleContent
6361 if (isset($this->message
[$pos]['cdata']) && trim($this->message
[$pos]['cdata']) != '') {
6362 $this->debug('in buildVal, handling simpleContent');
6363 if (isset($this->message
[$pos]['type'])) {
6364 $params['!'] = $this->decodeSimple($this->message
[$pos]['cdata'], $this->message
[$pos]['type'], isset($this->message
[$pos]['type_namespace']) ?
$this->message
[$pos]['type_namespace'] : '');
6366 $parent = $this->message
[$pos]['parent'];
6367 if (isset($this->message
[$parent]['type']) && ($this->message
[$parent]['type'] == 'array') && isset($this->message
[$parent]['arrayType'])) {
6368 $params['!'] = $this->decodeSimple($this->message
[$pos]['cdata'], $this->message
[$parent]['arrayType'], isset($this->message
[$parent]['arrayTypeNamespace']) ?
$this->message
[$parent]['arrayTypeNamespace'] : '');
6370 $params['!'] = $this->message
[$pos]['cdata'];
6374 $ret = is_array($params) ?
$params : array();
6375 $this->debug('in buildVal, return:');
6376 $this->appendDebug($this->varDump($ret));
6379 $this->debug('in buildVal, no children, building scalar');
6380 $cdata = isset($this->message
[$pos]['cdata']) ?
$this->message
[$pos]['cdata'] : '';
6381 if (isset($this->message
[$pos]['type'])) {
6382 $ret = $this->decodeSimple($cdata, $this->message
[$pos]['type'], isset($this->message
[$pos]['type_namespace']) ?
$this->message
[$pos]['type_namespace'] : '');
6383 $this->debug("in buildVal, return: $ret");
6386 $parent = $this->message
[$pos]['parent'];
6387 if (isset($this->message
[$parent]['type']) && ($this->message
[$parent]['type'] == 'array') && isset($this->message
[$parent]['arrayType'])) {
6388 $ret = $this->decodeSimple($cdata, $this->message
[$parent]['arrayType'], isset($this->message
[$parent]['arrayTypeNamespace']) ?
$this->message
[$parent]['arrayTypeNamespace'] : '');
6389 $this->debug("in buildVal, return: $ret");
6392 $ret = $this->message
[$pos]['cdata'];
6393 $this->debug("in buildVal, return: $ret");
6407 * soap_client higher level class for easy usage.
6411 * // instantiate client with server info
6412 * $soap_client = new soap_client( string path [ ,boolean wsdl] );
6414 * // call method, get results
6415 * echo $soap_client->call( string methodname [ ,array parameters] );
6418 * unset($soap_client);
6420 * @author Dietrich Ayala <dietrich@ganx4.com>
6424 class soap_client
extends nusoap_base
{
6429 var $certRequest = array();
6430 var $requestHeaders = false; // SOAP headers in request (text)
6431 var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
6432 var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
6434 var $forceEndpoint = ''; // overrides WSDL endpoint
6435 var $proxyhost = '';
6436 var $proxyport = '';
6437 var $proxyusername = '';
6438 var $proxypassword = '';
6439 var $xml_encoding = ''; // character set encoding of incoming (response) messages
6440 var $http_encoding = false;
6441 var $timeout = 0; // HTTP connection timeout
6442 var $response_timeout = 30; // HTTP response timeout
6443 var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
6444 var $persistentConnection = false;
6445 var $defaultRpcParams = false; // This is no longer used
6446 var $request = ''; // HTTP request
6447 var $response = ''; // HTTP response
6448 var $responseData = ''; // SOAP payload of response
6449 var $cookies = array(); // Cookies from response or for request
6450 var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
6451 var $operations = array(); // WSDL operations, empty for WSDL initialization error
6454 * fault related variables
6480 * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
6481 * @param bool $wsdl optional, set to true if using WSDL
6482 * @param int $portName optional portName in WSDL document
6483 * @param string $proxyhost
6484 * @param string $proxyport
6485 * @param string $proxyusername
6486 * @param string $proxypassword
6487 * @param integer $timeout set the connection timeout
6488 * @param integer $response_timeout set the response timeout
6491 function soap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
6492 parent
::nusoap_base();
6493 $this->endpoint
= $endpoint;
6494 $this->proxyhost
= $proxyhost;
6495 $this->proxyport
= $proxyport;
6496 $this->proxyusername
= $proxyusername;
6497 $this->proxypassword
= $proxypassword;
6498 $this->timeout
= $timeout;
6499 $this->response_timeout
= $response_timeout;
6503 if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
6504 $this->wsdl
= $endpoint;
6505 $this->endpoint
= $this->wsdl
->wsdl
;
6506 $this->wsdlFile
= $this->endpoint
;
6507 $this->debug('existing wsdl instance created from ' . $this->endpoint
);
6509 $this->wsdlFile
= $this->endpoint
;
6511 // instantiate wsdl object and parse wsdl file
6512 $this->debug('instantiating wsdl class with doc: '.$endpoint);
6513 $this->wsdl
=& new wsdl($this->wsdlFile
,$this->proxyhost
,$this->proxyport
,$this->proxyusername
,$this->proxypassword
,$this->timeout
,$this->response_timeout
);
6515 $this->appendDebug($this->wsdl
->getDebug());
6516 $this->wsdl
->clearDebug();
6518 if($errstr = $this->wsdl
->getError()){
6519 $this->debug('got wsdl error: '.$errstr);
6520 $this->setError('wsdl error: '.$errstr);
6521 } elseif($this->operations
= $this->wsdl
->getOperations()){
6522 $this->debug( 'got '.count($this->operations
).' operations from wsdl '.$this->wsdlFile
);
6523 $this->endpointType
= 'wsdl';
6525 $this->debug( 'getOperations returned false');
6526 $this->setError('no operations defined in the WSDL document!');
6529 $this->debug("instantiate SOAP with endpoint at $endpoint");
6530 $this->endpointType
= 'soap';
6535 * calls method, returns PHP native type
6537 * @param string $method SOAP server URL or path
6538 * @param mixed $params An array, associative or simple, of the parameters
6539 * for the method call, or a string that is the XML
6540 * for the call. For rpc style, this call will
6541 * wrap the XML in a tag named after the method, as
6542 * well as the SOAP Envelope and Body. For document
6543 * style, this will only wrap with the Envelope and Body.
6544 * IMPORTANT: when using an array with document style,
6545 * in which case there
6546 * is really one parameter, the root of the fragment
6547 * used in the call, which encloses what programmers
6548 * normally think of parameters. A parameter array
6549 * *must* include the wrapper.
6550 * @param string $namespace optional method namespace (WSDL can override)
6551 * @param string $soapAction optional SOAPAction value (WSDL can override)
6552 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
6553 * @param boolean $rpcParams optional (no longer used)
6554 * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
6555 * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
6556 * @return mixed response from SOAP call
6559 function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
6560 $this->operation
= $operation;
6561 $this->fault
= false;
6562 $this->setError('');
6563 $this->request
= '';
6564 $this->response
= '';
6565 $this->responseData
= '';
6566 $this->faultstring
= '';
6567 $this->faultcode
= '';
6568 $this->opData
= array();
6570 $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
6571 $this->appendDebug('params=' . $this->varDump($params));
6572 $this->appendDebug('headers=' . $this->varDump($headers));
6574 $this->requestHeaders
= $headers;
6576 // serialize parameters
6577 if($this->endpointType
== 'wsdl' && $opData = $this->getOperationData($operation)){
6578 // use WSDL for operation
6579 $this->opData
= $opData;
6580 $this->debug("found operation");
6581 $this->appendDebug('opData=' . $this->varDump($opData));
6582 if (isset($opData['soapAction'])) {
6583 $soapAction = $opData['soapAction'];
6585 if (! $this->forceEndpoint
) {
6586 $this->endpoint
= $opData['endpoint'];
6588 $this->endpoint
= $this->forceEndpoint
;
6590 $namespace = isset($opData['input']['namespace']) ?
$opData['input']['namespace'] : $namespace;
6591 $style = $opData['style'];
6592 $use = $opData['input']['use'];
6593 // add ns to ns array
6594 if($namespace != '' && !isset($this->wsdl
->namespaces
[$namespace])){
6595 $nsPrefix = 'ns' . rand(1000, 9999);
6596 $this->wsdl
->namespaces
[$nsPrefix] = $namespace;
6598 $nsPrefix = $this->wsdl
->getPrefixFromNamespace($namespace);
6599 // serialize payload
6600 if (is_string($params)) {
6601 $this->debug("serializing param string for WSDL operation $operation");
6603 } elseif (is_array($params)) {
6604 $this->debug("serializing param array for WSDL operation $operation");
6605 $payload = $this->wsdl
->serializeRPCParameters($operation,'input',$params);
6607 $this->debug('params must be array or string');
6608 $this->setError('params must be array or string');
6611 $usedNamespaces = $this->wsdl
->usedNamespaces
;
6612 if (isset($opData['input']['encodingStyle'])) {
6613 $encodingStyle = $opData['input']['encodingStyle'];
6615 $encodingStyle = '';
6617 $this->appendDebug($this->wsdl
->getDebug());
6618 $this->wsdl
->clearDebug();
6619 if ($errstr = $this->wsdl
->getError()) {
6620 $this->debug('got wsdl error: '.$errstr);
6621 $this->setError('wsdl error: '.$errstr);
6624 } elseif($this->endpointType
== 'wsdl') {
6625 // operation not in WSDL
6626 $this->appendDebug($this->wsdl
->getDebug());
6627 $this->wsdl
->clearDebug();
6628 $this->setError( 'operation '.$operation.' not present.');
6629 $this->debug("operation '$operation' not present.");
6633 //$this->namespaces['ns1'] = $namespace;
6634 $nsPrefix = 'ns' . rand(1000, 9999);
6637 if (is_string($params)) {
6638 $this->debug("serializing param string for operation $operation");
6640 } elseif (is_array($params)) {
6641 $this->debug("serializing param array for operation $operation");
6642 foreach($params as $k => $v){
6643 $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
6646 $this->debug('params must be array or string');
6647 $this->setError('params must be array or string');
6650 $usedNamespaces = array();
6651 if ($use == 'encoded') {
6652 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6654 $encodingStyle = '';
6657 // wrap RPC calls with method element
6658 if ($style == 'rpc') {
6659 if ($use == 'literal') {
6660 $this->debug("wrapping RPC request with literal method element");
6662 $payload = "<$operation xmlns=\"$namespace\">" . $payload . "</$operation>";
6664 $payload = "<$operation>" . $payload . "</$operation>";
6667 $this->debug("wrapping RPC request with encoded method element");
6669 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
6671 "</$nsPrefix:$operation>";
6673 $payload = "<$operation>" .
6679 // serialize envelope
6680 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders
,$usedNamespaces,$style,$use,$encodingStyle);
6681 $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
6682 $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
6684 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout
,$this->response_timeout
);
6685 if($errstr = $this->getError()){
6686 $this->debug('Error: '.$errstr);
6689 $this->return = $return;
6690 $this->debug('sent message successfully and got a(n) '.gettype($return));
6691 $this->appendDebug('return=' . $this->varDump($return));
6694 if(is_array($return) && isset($return['faultcode'])){
6695 $this->debug('got fault');
6696 $this->setError($return['faultcode'].': '.$return['faultstring']);
6697 $this->fault
= true;
6698 foreach($return as $k => $v){
6700 $this->debug("$k = $v<br>");
6703 } elseif ($style == 'document') {
6704 // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
6705 // we are only going to return the first part here...sorry about that
6708 // array of return values
6709 if(is_array($return)){
6710 // multiple 'out' parameters, which we return wrapped up
6712 if(sizeof($return) > 1){
6715 // single 'out' parameter (normally the return value)
6716 $return = array_shift($return);
6717 $this->debug('return shifted value: ');
6718 $this->appendDebug($this->varDump($return));
6720 // nothing returned (ie, echoVoid)
6729 * get available data pertaining to an operation
6731 * @param string $operation operation name
6732 * @return array array of data pertaining to the operation
6735 function getOperationData($operation){
6736 if(isset($this->operations
[$operation])){
6737 return $this->operations
[$operation];
6739 $this->debug("No data for operation: $operation");
6743 * send the SOAP message
6745 * Note: if the operation has multiple return values
6746 * the return value of this method will be an array
6749 * @param string $msg a SOAPx4 soapmsg object
6750 * @param string $soapaction SOAPAction value
6751 * @param integer $timeout set connection timeout in seconds
6752 * @param integer $response_timeout set response timeout in seconds
6753 * @return mixed native PHP types.
6756 function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
6757 $this->checkCookies();
6761 case ereg('^http',$this->endpoint
):
6762 $this->debug('transporting via HTTP');
6763 if($this->persistentConnection
== true && is_object($this->persistentConnection
)){
6764 $http =& $this->persistentConnection
;
6766 $http = new soap_transport_http($this->endpoint
);
6767 if ($this->persistentConnection
) {
6768 $http->usePersistentConnection();
6771 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
6772 $http->setSOAPAction($soapaction);
6773 if($this->proxyhost
&& $this->proxyport
){
6774 $http->setProxy($this->proxyhost
,$this->proxyport
,$this->proxyusername
,$this->proxypassword
);
6776 if($this->authtype
!= '') {
6777 $http->setCredentials($this->username
, $this->password
, $this->authtype
, array(), $this->certRequest
);
6779 if($this->http_encoding
!= ''){
6780 $http->setEncoding($this->http_encoding
);
6782 $this->debug('sending message, length='.strlen($msg));
6783 if(ereg('^http:',$this->endpoint
)){
6784 //if(strpos($this->endpoint,'http:')){
6785 $this->responseData
= $http->send($msg,$timeout,$response_timeout,$this->cookies
);
6786 } elseif(ereg('^https',$this->endpoint
)){
6787 //} elseif(strpos($this->endpoint,'https:')){
6788 //if(phpversion() == '4.3.0-dev'){
6789 //$response = $http->send($msg,$timeout,$response_timeout);
6790 //$this->request = $http->outgoing_payload;
6791 //$this->response = $http->incoming_payload;
6793 $this->responseData
= $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies
);
6795 $this->setError('no http/s in endpoint url');
6797 $this->request
= $http->outgoing_payload
;
6798 $this->response
= $http->incoming_payload
;
6799 $this->appendDebug($http->getDebug());
6800 $this->UpdateCookies($http->incoming_cookies
);
6802 // save transport object if using persistent connections
6803 if ($this->persistentConnection
) {
6804 $http->clearDebug();
6805 if (!is_object($this->persistentConnection
)) {
6806 $this->persistentConnection
= $http;
6810 if($err = $http->getError()){
6811 $this->setError('HTTP Error: '.$err);
6813 } elseif($this->getError()){
6816 $this->debug('got response, length='. strlen($this->responseData
).' type='.$http->incoming_headers
['content-type']);
6817 return $this->parseResponse($http->incoming_headers
, $this->responseData
);
6821 $this->setError('no transport found, or selected transport is not yet supported!');
6828 * processes SOAP message returned from server
6830 * @param array $headers The HTTP headers
6831 * @param string $data unprocessed response data from server
6832 * @return mixed value of the message, decoded into a PHP type
6835 function parseResponse($headers, $data) {
6836 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
6837 if (!strstr($headers['content-type'], 'text/xml')) {
6838 $this->setError('Response not of type text/xml');
6841 if (strpos($headers['content-type'], '=')) {
6842 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
6843 $this->debug('Got response encoding: ' . $enc);
6844 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
6845 $this->xml_encoding
= strtoupper($enc);
6847 $this->xml_encoding
= 'US-ASCII';
6850 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
6851 $this->xml_encoding
= 'ISO-8859-1';
6853 $this->debug('Use encoding: ' . $this->xml_encoding
. ' when creating soap_parser');
6854 $parser = new soap_parser($data,$this->xml_encoding
,$this->operation
,$this->decode_utf8
);
6855 // add parser debug data to our debug
6856 $this->appendDebug($parser->getDebug());
6858 if($errstr = $parser->getError()){
6859 $this->setError( $errstr);
6860 // destroy the parser object
6865 $this->responseHeaders
= $parser->getHeaders();
6866 // get decoded message
6867 $return = $parser->get_response();
6868 // add document for doclit support
6869 $this->document
= $parser->document
;
6870 // destroy the parser object
6872 // return decode message
6878 * sets the SOAP endpoint, which can override WSDL
6880 * @param $endpoint string The endpoint URL to use, or empty string or false to prevent override
6883 function setEndpoint($endpoint) {
6884 $this->forceEndpoint
= $endpoint;
6888 * set the SOAP headers
6890 * @param $headers mixed String of XML with SOAP header content, or array of soapval objects for SOAP headers
6893 function setHeaders($headers){
6894 $this->requestHeaders
= $headers;
6898 * get the SOAP response headers (namespace resolution incomplete)
6903 function getHeaders(){
6904 return $this->responseHeaders
;
6908 * set proxy info here
6910 * @param string $proxyhost
6911 * @param string $proxyport
6912 * @param string $proxyusername
6913 * @param string $proxypassword
6916 function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
6917 $this->proxyhost
= $proxyhost;
6918 $this->proxyport
= $proxyport;
6919 $this->proxyusername
= $proxyusername;
6920 $this->proxypassword
= $proxypassword;
6924 * if authenticating, set user credentials here
6926 * @param string $username
6927 * @param string $password
6928 * @param string $authtype (basic|digest|certificate)
6929 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
6932 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
6933 $this->username
= $username;
6934 $this->password
= $password;
6935 $this->authtype
= $authtype;
6936 $this->certRequest
= $certRequest;
6942 * @param string $enc
6945 function setHTTPEncoding($enc='gzip, deflate'){
6946 $this->http_encoding
= $enc;
6950 * use HTTP persistent connections if possible
6954 function useHTTPPersistentConnection(){
6955 $this->persistentConnection
= true;
6959 * gets the default RPC parameter setting.
6960 * If true, default is that call params are like RPC even for document style.
6961 * Each call() can override this value.
6963 * This is no longer used.
6969 function getDefaultRpcParams() {
6970 return $this->defaultRpcParams
;
6974 * sets the default RPC parameter setting.
6975 * If true, default is that call params are like RPC even for document style
6976 * Each call() can override this value.
6978 * This is no longer used.
6980 * @param boolean $rpcParams
6984 function setDefaultRpcParams($rpcParams) {
6985 $this->defaultRpcParams
= $rpcParams;
6989 * dynamically creates an instance of a proxy class,
6990 * allowing user to directly call methods from wsdl
6992 * @return object soap_proxy object
6995 function getProxy(){
6997 $evalStr = $this->_getProxyClassCode($r);
6998 //$this->debug("proxy class: $evalStr";
7001 // instantiate proxy object
7002 eval("\$proxy = new soap_proxy_$r('');");
7003 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
7004 $proxy->endpointType
= 'wsdl';
7005 $proxy->wsdlFile
= $this->wsdlFile
;
7006 $proxy->wsdl
= $this->wsdl
;
7007 $proxy->operations
= $this->operations
;
7008 $proxy->defaultRpcParams
= $this->defaultRpcParams
;
7009 // transfer other state
7010 $proxy->username
= $this->username
;
7011 $proxy->password
= $this->password
;
7012 $proxy->authtype
= $this->authtype
;
7013 $proxy->proxyhost
= $this->proxyhost
;
7014 $proxy->proxyport
= $this->proxyport
;
7015 $proxy->proxyusername
= $this->proxyusername
;
7016 $proxy->proxypassword
= $this->proxypassword
;
7017 $proxy->timeout
= $this->timeout
;
7018 $proxy->response_timeout
= $this->response_timeout
;
7019 $proxy->http_encoding
= $this->http_encoding
;
7020 $proxy->persistentConnection
= $this->persistentConnection
;
7021 $proxy->requestHeaders
= $this->requestHeaders
;
7022 $proxy->soap_defencoding
= $this->soap_defencoding
;
7023 $proxy->endpoint
= $this->endpoint
;
7024 $proxy->forceEndpoint
= $this->forceEndpoint
;
7029 * dynamically creates proxy class code
7031 * @return string PHP/NuSOAP code for the proxy class
7034 function _getProxyClassCode($r) {
7035 if ($this->endpointType
!= 'wsdl') {
7036 $evalStr = 'A proxy can only be created for a WSDL client';
7037 $this->setError($evalStr);
7041 foreach ($this->operations
as $operation => $opData) {
7042 if ($operation != '') {
7043 // create param string and param comment string
7044 if (sizeof($opData['input']['parts']) > 0) {
7046 $paramArrayStr = '';
7047 $paramCommentStr = '';
7048 foreach ($opData['input']['parts'] as $name => $type) {
7049 $paramStr .= "\$$name, ";
7050 $paramArrayStr .= "'$name' => \$$name, ";
7051 $paramCommentStr .= "$type \$$name, ";
7053 $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
7054 $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
7055 $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
7058 $paramArrayStr = '';
7059 $paramCommentStr = 'void';
7061 $opData['namespace'] = !isset($opData['namespace']) ?
'http://testuri.com' : $opData['namespace'];
7062 $evalStr .= "// $paramCommentStr
7063 function " . str_replace('.', '__', $operation) . "($paramStr) {
7064 \$params = array($paramArrayStr);
7065 return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ?
$opData['soapAction'] : '')."');
7069 unset($paramCommentStr);
7072 $evalStr = 'class soap_proxy_'.$r.' extends soap_client {
7079 * dynamically creates proxy class code
7081 * @return string PHP/NuSOAP code for the proxy class
7084 function getProxyClassCode() {
7086 return $this->_getProxyClassCode($r);
7090 * gets the HTTP body for the current request.
7092 * @param string $soapmsg The SOAP payload
7093 * @return string The HTTP body, which includes the SOAP payload
7096 function getHTTPBody($soapmsg) {
7101 * gets the HTTP content type for the current request.
7103 * Note: getHTTPBody must be called before this.
7105 * @return string the HTTP content type for the current request.
7108 function getHTTPContentType() {
7113 * gets the HTTP content type charset for the current request.
7114 * returns false for non-text content types.
7116 * Note: getHTTPBody must be called before this.
7118 * @return string the HTTP content type charset for the current request.
7121 function getHTTPContentTypeCharset() {
7122 return $this->soap_defencoding
;
7126 * whether or not parser should decode utf8 element content
7128 * @return always returns true
7131 function decodeUTF8($bool){
7132 $this->decode_utf8
= $bool;
7137 * adds a new Cookie into $this->cookies array
7139 * @param string $name Cookie Name
7140 * @param string $value Cookie Value
7141 * @return if cookie-set was successful returns true, else false
7144 function setCookie($name, $value) {
7145 if (strlen($name) == 0) {
7148 $this->cookies
[] = array('name' => $name, 'value' => $value);
7155 * @return array with all internal cookies
7158 function getCookies() {
7159 return $this->cookies
;
7163 * checks all Cookies and delete those which are expired
7165 * @return always return true
7168 function checkCookies() {
7169 if (sizeof($this->cookies
) == 0) {
7172 $this->debug('checkCookie: check ' . sizeof($this->cookies
) . ' cookies');
7173 $curr_cookies = $this->cookies
;
7174 $this->cookies
= array();
7175 foreach ($curr_cookies as $cookie) {
7176 if (! is_array($cookie)) {
7177 $this->debug('Remove cookie that is not an array');
7180 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
7181 if (strtotime($cookie['expires']) > time()) {
7182 $this->cookies
[] = $cookie;
7184 $this->debug('Remove expired cookie ' . $cookie['name']);
7187 $this->cookies
[] = $cookie;
7190 $this->debug('checkCookie: '.sizeof($this->cookies
).' cookies left in array');
7195 * updates the current cookies with a new set
7197 * @param array $cookies new cookies with which to update current ones
7198 * @return always return true
7201 function UpdateCookies($cookies) {
7202 if (sizeof($this->cookies
) == 0) {
7203 // no existing cookies: take whatever is new
7204 if (sizeof($cookies) > 0) {
7205 $this->debug('Setting new cookie(s)');
7206 $this->cookies
= $cookies;
7210 if (sizeof($cookies) == 0) {
7211 // no new cookies: keep what we've got
7215 foreach ($cookies as $newCookie) {
7216 if (!is_array($newCookie)) {
7219 if ((!isset($newCookie['name'])) ||
(!isset($newCookie['value']))) {
7222 $newName = $newCookie['name'];
7225 for ($i = 0; $i < count($this->cookies
); $i++
) {
7226 $cookie = $this->cookies
[$i];
7227 if (!is_array($cookie)) {
7230 if (!isset($cookie['name'])) {
7233 if ($newName != $cookie['name']) {
7236 $newDomain = isset($newCookie['domain']) ?
$newCookie['domain'] : 'NODOMAIN';
7237 $domain = isset($cookie['domain']) ?
$cookie['domain'] : 'NODOMAIN';
7238 if ($newDomain != $domain) {
7241 $newPath = isset($newCookie['path']) ?
$newCookie['path'] : 'NOPATH';
7242 $path = isset($cookie['path']) ?
$cookie['path'] : 'NOPATH';
7243 if ($newPath != $path) {
7246 $this->cookies
[$i] = $newCookie;
7248 $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
7252 $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
7253 $this->cookies
[] = $newCookie;