3 * NuSOAP - Web Services Toolkit for PHP
5 * @author: Dietrich Ayala
9 Copyright (c) 2002 NuSphere Corporation
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 If you have any questions or comments, please email:
29 http://dietrich.ganx4.com/nusoap
32 http://www.nusphere.com
39 require_once('class.soapclient.php');
40 require_once('class.soap_val.php');
41 require_once('class.soap_parser.php');
42 require_once('class.soap_fault.php');
45 require_once('class.soap_transport_http.php');
47 // optional add-on classes
48 require_once('class.xmlschema.php');
49 require_once('class.wsdl.php');
52 require_once('class.soap_server.php');*/
58 * @author Dietrich Ayala <dietrich@ganx4.com>
64 var $title = 'NuSOAP';
65 var $version = '0.6.3';
66 var $error_str = false;
68 // toggles automatic encoding of special characters
69 var $charencoding = true;
74 * @var XMLSchemaVersion
77 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
80 * set default encoding
82 * @var soap_defencoding
85 //var $soap_defencoding = 'UTF-8';
86 var $soap_defencoding = 'ISO-8859-1';
89 * load namespace uris into an array of uri => prefix
94 var $namespaces = array(
95 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
96 'xsd' => 'http://www.w3.org/2001/XMLSchema',
97 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
98 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
99 'si' => 'http://soapinterop.org/xsd');
101 * load types into typemap array
102 * is this legacy yet?
103 * no, this is used by the xmlschema class to verify type => namespace mappings.
109 'http://www.w3.org/2001/XMLSchema' =>
111 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
112 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
113 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
115 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
116 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
117 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
118 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
119 'http://www.w3.org/1999/XMLSchema' =>
121 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
122 'float'=>'double','dateTime'=>'string',
123 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
124 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
125 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
126 'http://xml.apache.org/xml-soap' => array('Map')
130 * entities to convert
135 var $xmlEntities = array('quot' => '"','amp' => '&',
136 'lt' => '<','gt' => '>','apos' => "'");
139 * adds debug data to the class level debug string
141 * @param string $string debug data
144 function debug($string){
145 $this->debug_str .= get_class($this).": $string\n";
149 * returns error string if present
151 * @return boolean $string error string
155 if($this->error_str != ''){
156 return $this->error_str;
164 * @return boolean $string error string
167 function setError($str){
168 $this->error_str = $str;
172 * serializes PHP values in accordance w/ section 5. Type information is
173 * not serialized if $use == 'literal'.
178 function serialize_val($val,$name=false,$type=false,$name_ns=false,
179 $type_ns=false,$attributes=false,$use='encoded') {
180 if(is_object($val) && strtolower(get_class($val)) == 'soapval'){
181 return $val->serialize($use);
183 $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
184 // if no name, use item
185 $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
186 // if name has ns, add ns prefix to name
189 $prefix = 'nu'.rand(1000,9999);
190 $name = $prefix.':'.$name;
191 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
193 // if type is prefixed, create type prefix
194 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
195 // need to fix this. shouldn't default to xsd if no ns specified
196 // w/o checking against typemap
197 $type_prefix = 'xsd';
199 $type_prefix = 'ns'.rand(1000,9999);
200 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
202 // serialize attributes if present
204 foreach($attributes as $k => $v){
205 $atts .= " $k=\"$v\"";
208 // serialize if an xsd built-in primitive type
209 if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
210 if ($use == 'literal') {
211 return "<$name$xmlns>$val</$name>";
213 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
216 // detect type and serialize
220 case ($type == '' && is_null($val)):
221 if ($use == 'literal') {
222 // TODO: depends on nillable
223 $xml .= "<$name$xmlns/>";
225 $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>";
228 case (is_bool($val) || $type == 'boolean'):
232 if ($use == 'literal') {
233 $xml .= "<$name$xmlns $atts>$val</$name>";
235 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
238 case (is_int($val) || is_long($val) || $type == 'int'):
239 if ($use == 'literal') {
240 $xml .= "<$name$xmlns $atts>$val</$name>";
242 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
245 case (is_float($val)|| is_double($val) || $type == 'float'):
246 if ($use == 'literal') {
247 $xml .= "<$name$xmlns $atts>$val</$name>";
249 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
252 case (is_string($val) || $type == 'string'):
253 if($this->charencoding){
254 $val = htmlspecialchars($val, ENT_QUOTES);
256 if ($use == 'literal') {
257 $xml .= "<$name$xmlns $atts>$val</$name>";
259 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
262 case is_object($val):
263 $name = strtolower(get_class($val));
264 foreach(get_object_vars($val) as $k => $v){
265 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
267 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
270 case (is_array($val) || $type):
271 // detect if struct or array
272 $keyList = array_keys($val);
273 $valueType = 'arraySimple';
274 foreach($keyList as $keyListValue){
275 if(!is_int($keyListValue)){
276 $valueType = 'arrayStruct';
280 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
282 if(is_array($val) && count($val)> 0){
284 if(is_object($v) && strtolower(get_class($v)) == 'soapval'){
289 $array_types[$tt] = 1;
290 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
291 if(is_array($v) && is_numeric(key($v))){
297 if(count($array_types) > 1){
298 $array_typename = 'xsd:ur-type';
299 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
300 $array_typename = 'xsd:'.$tt;
301 } elseif($tt == 'array' || $tt == 'Array'){
302 $array_typename = 'SOAP-ENC:Array';
304 $array_typename = $tt;
306 if(isset($array_types['array'])){
307 $array_type = $i.",".$i;
311 if ($use == 'literal') {
312 $xml = "<$name $atts>".$xml."</$name>";
314 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"$atts>".$xml."</$name>";
318 if ($use == 'literal') {
319 $xml = "<$name $atts>".$xml."</$name>";;
321 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>".$xml."</$name>";;
326 if(isset($type) && isset($type_prefix)){
327 $type_str = " xsi:type=\"$type_prefix:$type\"";
331 if ($use == 'literal') {
332 $xml .= "<$name$xmlns $atts>";
334 $xml .= "<$name$xmlns$type_str$atts>";
336 foreach($val as $k => $v){
337 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
343 $xml .= 'not detected, got '.gettype($val).' for '.$val;
353 * @param string headers
354 * @param array namespaces
355 * @param string style
356 * @return string message
359 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc'){
360 // serialize namespaces
362 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
363 $ns_string .= " xmlns:$k=\"$v\"";
365 if($style == 'rpc') {
366 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
371 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
373 // serialize envelope
375 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
376 '<SOAP-ENV:Envelope'.$ns_string.">".
381 "</SOAP-ENV:Envelope>";
384 function formatDump($str){
385 $str = htmlspecialchars($str);
390 * returns the local part of a prefixed string
391 * returns the original string, if not prefixed
397 function getLocalPart($str){
398 if($sstr = strrchr($str,':')){
399 // get unqualified name
400 return substr( $sstr, 1 );
407 * returns the prefix part of a prefixed string
408 * returns false, if not prefixed
414 function getPrefix($str){
415 if($pos = strrpos($str,':')){
417 return substr($str,0,$pos);
422 function varDump($data) {
425 $ret_val = ob_get_contents();
431 // XML Schema Datatype Helper Functions
433 //xsd:dateTime helpers
436 * convert unix timestamp to ISO 8601 compliant date string
438 * @param string $timestamp Unix time stamp
441 function timestamp_to_iso8601($timestamp,$utc=true){
442 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
445 '([0-9]{4})-'. // centuries & years CCYY-
446 '([0-9]{2})-'. // months MM-
447 '([0-9]{2})'. // days DD
449 '([0-9]{2}):'. // hours hh:
450 '([0-9]{2}):'. // minutes mm:
451 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
452 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
454 if(ereg($eregStr,$datestr,$regs)){
455 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
464 * convert ISO 8601 compliant date string to unix timestamp
466 * @param string $datestr ISO 8601 compliant date string
469 function iso8601_to_timestamp($datestr){
471 '([0-9]{4})-'. // centuries & years CCYY-
472 '([0-9]{2})-'. // months MM-
473 '([0-9]{2})'. // days DD
475 '([0-9]{2}):'. // hours hh:
476 '([0-9]{2}):'. // minutes mm:
477 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
478 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
479 if(ereg($eregStr,$datestr,$regs)){
482 $op = substr($regs[8],0,1);
483 $h = substr($regs[8],1,2);
484 $m = substr($regs[8],strlen($regs[8])-2,2);
486 $regs[4] = $regs[4] + $h;
487 $regs[5] = $regs[5] + $m;
488 } elseif($op == '+'){
489 $regs[4] = $regs[4] - $h;
490 $regs[5] = $regs[5] - $m;
493 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
502 * soap_fault class, allows for creation of faults
503 * mainly used for returning faults from deployed functions
504 * in a server instance.
505 * @author Dietrich Ayala <dietrich@ganx4.com>
509 class soap_fault extends nusoap_base {
519 * @param string $faultcode (client | server)
520 * @param string $faultactor only used when msg routed between multiple actors
521 * @param string $faultstring human readable error message
522 * @param string $faultdetail
524 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
525 $this->faultcode = $faultcode;
526 $this->faultactor = $faultactor;
527 $this->faultstring = $faultstring;
528 $this->faultdetail = $faultdetail;
536 function serialize(){
538 foreach($this->namespaces as $k => $v){
539 $ns_string .= "\n xmlns:$k=\"$v\"";
542 '<?xml version="1.0"?'.">\n".
543 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
546 '<faultcode>'.$this->faultcode.'</faultcode>'.
547 '<faultactor>'.$this->faultactor.'</faultactor>'.
548 '<faultstring>'.$this->faultstring.'</faultstring>'.
549 '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
552 '</SOAP-ENV:Envelope>';
560 * parses an XML Schema, allows access to it's data, other utility methods
561 * no validation... yet.
562 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
563 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
564 * tutorials I refer to :)
566 * @author Dietrich Ayala <dietrich@ganx4.com>
570 class XMLSchema extends nusoap_base {
575 // define internal arrays of bindings, ports, operations, messages, etc.
576 var $complexTypes = array();
578 var $schemaTargetNamespace = '';
583 var $depth_array = array();
588 * @param string $schema schema document URI
589 * @param string $xml xml document URI
592 function XMLSchema($schema='',$xml=''){
594 $this->debug('xmlschema class instantiated, inside constructor');
596 $this->schema = $schema;
601 $this->debug('initial schema file: '.$schema);
602 $this->parseFile($schema);
607 $this->debug('initial xml file: '.$xml);
608 $this->parseFile($xml);
616 * @param string $xml, path/URL to XML file
617 * @param string $type, (schema | xml)
621 function parseFile($xml,$type){
624 $this->debug('parsing $xml');
625 $xmlStr = @join("",@file($xml));
627 $this->setError('No file at the specified URL: '.$xml);
630 $this->parseString($xmlStr,$type);
638 * parse an XML string
640 * @param string $xml path or URL
641 * @param string $type, (schema|xml)
644 function parseString($xml,$type){
648 // Create an XML parser.
649 $this->parser = xml_parser_create();
650 // Set the options for parsing the XML data.
651 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
653 // Set the object for the parser.
654 xml_set_object($this->parser, $this);
656 // Set the element handlers for the parser.
657 if($type == "schema"){
658 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
659 xml_set_character_data_handler($this->parser,'schemaCharacterData');
660 } elseif($type == "xml"){
661 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
662 xml_set_character_data_handler($this->parser,'xmlCharacterData');
665 // Parse the XML file.
666 if(!xml_parse($this->parser,$xml,true)){
667 // Display an error message.
668 $errstr = sprintf('XML error on line %d: %s',
669 xml_get_current_line_number($this->parser),
670 xml_error_string(xml_get_error_code($this->parser))
672 $this->debug('XML parse error: '.$errstr);
673 $this->setError('Parser error: '.$errstr);
676 xml_parser_free($this->parser);
678 $this->debug('no xml passed to parseString()!!');
679 $this->setError('no xml passed to parseString()!!');
684 * start-element handler
686 * @param string $parser XML parser object
687 * @param string $name element name
688 * @param string $attrs associative array of attributes
691 function schemaStartElement($parser, $name, $attrs) {
693 // position in the total number of elements, starting from 0
694 $pos = $this->position++;
695 $depth = $this->depth++;
696 // set self as current value for this depth
697 $this->depth_array[$depth] = $pos;
699 // get element prefix
700 if($prefix = $this->getPrefix($name)){
701 // get unqualified name
702 $name = $this->getLocalPart($name);
707 // loop thru attributes, expanding, and registering namespace declarations
708 if(count($attrs) > 0){
709 foreach($attrs as $k => $v){
710 // if ns declarations, add to class level array of valid namespaces
711 if(ereg("^xmlns",$k)){
712 //$this->xdebug("$k: $v");
713 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
714 if($ns_prefix = substr(strrchr($k,':'),1)){
715 $this->namespaces[$ns_prefix] = $v;
717 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
719 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
720 $this->XMLSchemaVersion = $v;
721 $this->namespaces['xsi'] = $v.'-instance';
725 foreach($attrs as $k => $v) {
726 // expand each attribute
727 $k = strpos($k,':') ? $this->expandQname($k) : $k;
728 $v = strpos($v,':') ? $this->expandQname($v) : $v;
735 // find status, register data
737 case ('all'|'choice'|'sequence'):
738 //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
739 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
741 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
745 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
746 if(isset($attrs['name'])){
747 $this->attributes[$attrs['name']] = $attrs;
748 $aname = $attrs['name'];
749 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
750 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
751 } elseif(isset($attrs['ref'])){
752 $aname = $attrs['ref'];
753 $this->attributes[$attrs['ref']] = $attrs;
756 if(isset($this->currentComplexType)){
757 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
758 } elseif(isset($this->currentElement)){
759 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
761 // arrayType attribute
762 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
763 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
764 $prefix = $this->getPrefix($aname);
765 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
766 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
770 if(strpos($v,'[,]')){
771 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
773 $v = substr($v,0,strpos($v,'[')); // clip the []
774 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
775 $v = $this->XMLSchemaVersion.':'.$v;
777 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
781 if(isset($attrs['name'])){
782 $this->currentElement = false;
783 $this->currentComplexType = $attrs['name'];
784 $this->complexTypes[$this->currentComplexType] = $attrs;
785 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
786 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
787 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
789 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
791 $this->xdebug('processing complexType '.$attrs['name']);
795 if(isset($attrs['type'])){
796 $this->xdebug("processing element ".$attrs['name']);
797 $this->currentElement = $attrs['name'];
798 $this->elements[ $attrs['name'] ] = $attrs;
799 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
800 $ename = $attrs['name'];
801 } elseif(isset($attrs['ref'])){
802 $ename = $attrs['ref'];
804 $this->xdebug('adding complexType '.$attrs['name']);
805 $this->currentComplexType = $attrs['name'];
806 $this->complexTypes[ $attrs['name'] ] = $attrs;
807 $this->complexTypes[ $attrs['name'] ]['element'] = 1;
808 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
810 if(isset($ename) && $this->currentComplexType){
811 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
815 $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
816 if($this->currentElement){
817 $this->elements[$this->currentElement]['type'] = $attrs['base'];
818 } elseif($this->currentComplexType){
819 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
820 if(strstr($attrs['base'],':') == ':Array'){
821 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
826 $this->schema = $attrs;
827 $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
830 $this->currentElement = $attrs['name'];
831 $this->elements[ $attrs['name'] ] = $attrs;
832 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
838 * end-element handler
840 * @param string $parser XML parser object
841 * @param string $name element name
844 function schemaEndElement($parser, $name) {
845 // position of current element is equal to the last value left in depth_array for my depth
846 if(isset($this->depth_array[$this->depth])){
847 $pos = $this->depth_array[$this->depth];
849 // bring depth down a notch
852 if($name == 'complexType'){
853 $this->currentComplexType = false;
854 $this->currentElement = false;
856 if($name == 'element'){
857 $this->currentElement = false;
862 * element content handler
864 * @param string $parser XML parser object
865 * @param string $data element content
868 function schemaCharacterData($parser, $data){
869 $pos = $this->depth_array[$this->depth];
870 $this->message[$pos]['cdata'] .= $data;
874 * serialize the schema
878 function serializeSchema(){
880 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
883 foreach($this->complexTypes as $typeName => $attrs){
885 // serialize child elements
886 if(count($attrs['elements']) > 0){
887 foreach($attrs['elements'] as $element => $eParts){
888 if(isset($eParts['ref'])){
889 $contentStr .= "<element ref=\"$element\"/>";
891 $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
896 if(count($attrs['attrs']) >= 1){
897 foreach($attrs['attrs'] as $attr => $aParts){
898 $contentStr .= '<attribute ref="'.$aParts['ref'].'"';
899 if(isset($aParts['wsdl:arrayType'])){
900 $contentStr .= ' wsdl:arrayType="'.$aParts['wsdl:arrayType'].'"';
906 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
907 $contentStr = "<$schemaPrefix:restriction base=\"".$attrs['restrictionBase']."\">".$contentStr."</$schemaPrefix:restriction>";
909 // "all" compositor obviates complex/simple content
910 if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){
911 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
913 // complex or simple content
914 elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){
915 $contentStr = "<$schemaPrefix:complexContent>".$contentStr."</$schemaPrefix:complexContent>";
918 if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){
919 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
921 // finalize complex type
922 if($contentStr != ''){
923 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr."</$schemaPrefix:complexType>";
925 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
930 if(isset($this->elements) && count($this->elements) > 0){
931 foreach($this->elements as $element => $eParts){
932 $xml .= "<$schemaPrefix:element name=\"$element\" type=\"".$eParts['type']."\"/>";
936 if(isset($this->attributes) && count($this->attributes) > 0){
937 foreach($this->attributes as $attr => $aParts){
938 $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"".$aParts['type']."\"/>";
942 $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml."</$schemaPrefix:schema>";
947 * expands a qualified name
949 * @param string $string qname
950 * @return string expanded qname
953 function expandQname($qname){
954 // get element prefix
955 if(strpos($qname,':') && !ereg('^http://',$qname)){
956 // get unqualified name
957 $name = substr(strstr($qname,':'),1);
959 $prefix = substr($qname,0,strpos($qname,':'));
960 if(isset($this->namespaces[$prefix])){
961 return $this->namespaces[$prefix].':'.$name;
971 * adds debug data to the clas level debug string
973 * @param string $string debug data
976 function xdebug($string){
977 $this->debug(' xmlschema: '.$string);
981 * get the PHP type of a user defined type in the schema
982 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
983 * returns false if no type exists, or not w/ the given namespace
984 * else returns a string that is either a native php type, or 'struct'
986 * @param string $type, name of defined type
987 * @param string $ns, namespace of type
991 function getPHPType($type,$ns){
993 if(isset($typemap[$ns][$type])){
994 //print "found type '$type' and ns $ns in typemap<br>";
995 return $typemap[$ns][$type];
996 } elseif(isset($this->complexTypes[$type])){
997 //print "getting type '$type' and ns $ns from complexTypes array<br>";
998 return $this->complexTypes[$type]['phpType'];
1004 * returns the local part of a prefixed string
1005 * returns the original string, if not prefixed
1011 function getLocalPart($str){
1012 if($sstr = strrchr($str,':')){
1013 // get unqualified name
1014 return substr( $sstr, 1 );
1021 * returns the prefix part of a prefixed string
1022 * returns false, if not prefixed
1028 function getPrefix($str){
1029 if($pos = strrpos($str,':')){
1031 return substr($str,0,$pos);
1037 * pass it a prefix, it returns a namespace
1038 * returns false if no namespace registered with the given prefix
1044 function getNamespaceFromPrefix($prefix){
1045 if(isset($this->namespaces[$prefix])){
1046 return $this->namespaces[$prefix];
1048 //$this->setError("No namespace registered for prefix '$prefix'");
1053 * returns the prefix for a given namespace (or prefix)
1054 * or false if no prefixes registered for the given namespace
1060 function getPrefixFromNamespace($ns){
1061 foreach($this->namespaces as $p => $n){
1062 if($ns == $n || $ns == $p){
1063 $this->usedNamespaces[$p] = $n;
1071 * returns an array of information about a given type
1072 * returns false if no type exists by the given name
1075 * 'elements' => array(), // refs to elements array
1076 * 'restrictionBase' => '',
1078 * 'order' => '(sequence|all)',
1079 * 'attrs' => array() // refs to attributes array
1086 function getTypeDef($type){
1087 if(isset($this->complexTypes[$type])){
1088 return $this->complexTypes[$type];
1089 } elseif(isset($this->elements[$type])){
1090 return $this->elements[$type];
1091 } elseif(isset($this->attributes[$type])){
1092 return $this->attributes[$type];
1098 * returns a sample serialization of a given type, or false if no type by the given name
1100 * @param string $type, name of type
1104 function serializeTypeDef($type){
1105 //print "in sTD() for type $type<br>";
1106 if($typeDef = $this->getTypeDef($type)){
1108 if(is_array($typeDef['attrs'])){
1109 foreach($attrs as $attName => $data){
1110 $str .= " $attName=\"{type = ".$data['type']."}\"";
1113 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1114 if(count($typeDef['elements']) > 0){
1116 foreach($typeDef['elements'] as $element => $eData){
1117 $str .= $this->serializeTypeDef($element);
1120 } elseif($typeDef['typeClass'] == 'element') {
1121 $str .= "></$type>";
1131 * returns HTML form elements that allow a user
1132 * to enter values for creating an instance of the given type.
1134 * @param string $name, name for type instance
1135 * @param string $type, name of type
1139 function typeToForm($name,$type){
1141 if($typeDef = $this->getTypeDef($type)){
1143 if($typeDef['phpType'] == 'struct'){
1144 $buffer .= '<table>';
1145 foreach($typeDef['elements'] as $child => $childDef){
1146 $buffer .= "<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>".
1147 "<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1149 $buffer .= '</table>';
1151 } elseif($typeDef['phpType'] == 'array'){
1152 $buffer .= '<table>';
1153 for($i=0;$i < 3; $i++){
1154 $buffer .= "<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>".
1155 "<td><input type='text' name='parameters[".$name."][]'></td></tr>";
1157 $buffer .= '</table>';
1160 $buffer .= "<input type='text' name='parameters[$name]'>";
1163 $buffer .= "<input type='text' name='parameters[$name]'>";
1169 * adds an XML Schema complex type to the WSDL types
1179 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1183 * example: PHP associative array ( SOAP Struct )
1190 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1194 * @param typeClass (complexType|simpleType|attribute)
1195 * @param phpType: currently supported are array and struct (php assoc array)
1196 * @param compositor (all|sequence|choice)
1197 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1198 * @param elements = array ( name = array(name=>'',type=>'') )
1199 * @param attrs = array(
1201 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1202 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1205 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1208 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1209 $this->complexTypes[$name] = array(
1211 'typeClass' => $typeClass,
1212 'phpType' => $phpType,
1213 'compositor'=> $compositor,
1214 'restrictionBase' => $restrictionBase,
1215 'elements' => $elements,
1217 'arrayType' => $arrayType
1225 * for creating serializable abstractions of native PHP types
1226 * NOTE: this is only really used when WSDL is not available.
1228 * @author Dietrich Ayala <dietrich@ganx4.com>
1232 class soapval extends nusoap_base {
1236 * @param string $name optional name
1237 * @param string $type optional type name
1238 * @param mixed $value optional value
1239 * @param string $namespace optional namespace of value
1240 * @param string $type_namespace optional namespace of type
1241 * @param array $attributes associative array of attributes to add to element serialization
1244 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1245 $this->name = $name;
1246 $this->value = $value;
1247 $this->type = $type;
1248 $this->element_ns = $element_ns;
1249 $this->type_ns = $type_ns;
1250 $this->attributes = $attributes;
1254 * return serialized value
1256 * @return string XML data
1259 function serialize($use='encoded') {
1260 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1264 * decodes a soapval object into a PHP native type
1266 * @param object $soapval optional SOAPx4 soapval object, else uses self
1271 return $this->value;
1278 * transport class for sending/receiving data via HTTP and HTTPS
1279 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1281 * @author Dietrich Ayala <dietrich@ganx4.com>
1285 class soap_transport_http extends nusoap_base {
1290 var $proxyhost = '';
1291 var $proxyport = '';
1293 var $request_method = 'POST';
1294 var $protocol_version = '1.0';
1296 var $outgoing_headers = array();
1297 var $incoming_headers = array();
1298 var $outgoing_payload = '';
1299 var $incoming_payload = '';
1300 var $useSOAPAction = true;
1305 function soap_transport_http($url){
1307 $u = parse_url($url);
1308 foreach($u as $k => $v){
1309 $this->debug("$k = $v");
1312 if(isset($u['query']) && $u['query'] != ''){
1313 $this->path .= '?' . $u['query'];
1315 if(!isset($u['port']) && $u['scheme'] == 'http'){
1320 function connect($timeout){
1323 if($this->proxyhost != '' && $this->proxyport != ''){
1324 $host = $this->proxyhost;
1325 $port = $this->proxyport;
1326 $this->debug("using http proxy: $host, $port");
1328 $host = $this->host;
1329 $port = $this->port;
1332 if($this->scheme == 'https'){
1333 $host = 'ssl://'.$host;
1337 $this->debug("connection params: $host, $port");
1340 $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
1342 $fp = fsockopen($host, $port, $this->errno, $this->error_str);
1347 $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error: '.$this->error_str);
1348 $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error: '.$this->error_str);
1355 * send the SOAP message via HTTP
1357 * @param string $data message data
1358 * @param integer $timeout set timeout in seconds
1359 * @return string data
1362 function send($data, $timeout=0) {
1363 $this->debug('entered send() with data of length: '.strlen($data));
1365 if(!$fp = $this->connect($timeout)){
1368 $this->debug('socket connected');
1370 // start building outgoing payload:
1371 // swap url for path if going through a proxy
1372 if($this->proxyhost != '' && $this->proxyport != ''){
1373 $this->outgoing_payload = "$this->request_method $this->url ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1375 $this->outgoing_payload = "$this->request_method $this->path ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1378 $this->outgoing_payload .=
1379 "User-Agent: $this->title/$this->version\r\n".
1380 "Host: ".$this->host."\r\n";
1383 if($this->username != '') {
1384 $this->debug('setting http auth credentials');
1385 $this->outgoing_payload .= 'Authorization: Basic '.base64_encode("$this->username:$this->password")."\r\n";
1388 $this->outgoing_payload .= 'Content-Type: text/xml; charset='.$this->soap_defencoding."\r\nContent-Length: ".strlen($data)."\r\n";
1390 if($this->encoding != '' && function_exists('gzdeflate')){
1391 $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n".
1392 "Connection: close\r\n";
1393 if (!check_php_version(5,3)) {
1394 set_magic_quotes_runtime(0);
1398 if($this->useSOAPAction){
1399 $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n";
1401 $this->outgoing_payload .= "\r\n";
1403 $this->outgoing_payload .= $data;
1406 if(!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1407 $this->setError('couldn\'t write message data to socket');
1408 $this->debug('Write error');
1410 $this->debug('wrote data to socket');
1413 $this->incoming_payload = '';
1415 while( $data = fread($fp, 32768) ){
1416 $this->incoming_payload .= $data;
1417 //$strlen += strlen($data);
1419 $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server');
1421 // close filepointer
1423 $this->debug('closed socket');
1425 // connection was closed unexpectedly
1426 if($this->incoming_payload == ''){
1427 $this->setError('no response from server');
1431 $this->debug('received incoming payload: '.strlen($this->incoming_payload));
1432 $data = $this->incoming_payload."\r\n\r\n\r\n\r\n";
1434 // remove 100 header
1435 if(ereg('^HTTP/1.1 100',$data)){
1436 if($pos = strpos($data,"\r\n\r\n") ){
1437 $data = ltrim(substr($data,$pos));
1438 } elseif($pos = strpos($data,"\n\n") ){
1439 $data = ltrim(substr($data,$pos));
1443 // separate content from HTTP headers
1444 if( $pos = strpos($data,"\r\n\r\n") ){
1446 } elseif( $pos = strpos($data,"\n\n") ){
1449 $this->setError('no proper separation of headers and document');
1452 $header_data = trim(substr($data,0,$pos));
1453 $header_array = explode($lb,$header_data);
1454 $data = ltrim(substr($data,$pos));
1455 $this->debug('found proper separation of headers and document');
1456 $this->debug('cleaned data, stringlen: '.strlen($data));
1458 foreach($header_array as $header_line){
1459 $arr = explode(':',$header_line);
1460 if(count($arr) >= 2){
1461 $headers[trim($arr[0])] = trim($arr[1]);
1464 //print "headers: <pre>$header_data</pre><br>";
1465 //print "data: <pre>$data</pre><br>";
1467 // decode transfer-encoding
1468 if(isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked'){
1469 //$timer->setMarker('starting to decode chunked content');
1470 if(!$data = $this->decodeChunked($data)){
1471 $this->setError('Decoding of chunked data failed');
1474 //$timer->setMarker('finished decoding of chunked content');
1475 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1478 // decode content-encoding
1479 if(isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != ''){
1480 if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1481 // if decoding works, use it. else assume data wasn't gzencoded
1482 if(function_exists('gzinflate')){
1483 //$timer->setMarker('starting decoding of gzip/deflated content');
1484 if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1486 } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1489 $this->setError('Errors occurred when trying to decode the data');
1491 //$timer->setMarker('finished decoding of gzip/deflated content');
1492 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
1494 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1499 if(strlen($data) == 0){
1500 $this->debug('no data after headers!');
1501 $this->setError('no data present after HTTP headers');
1504 $this->debug('end of send()');
1510 * send the SOAP message via HTTPS 1.0 using CURL
1512 * @param string $msg message data
1513 * @param integer $timeout set timeout in seconds
1514 * @return string data
1517 function sendHTTPS($data, $timeout=0) {
1519 //$t->setMarker('inside sendHTTPS()');
1520 $this->debug('entered sendHTTPS() with data of length: '.strlen($data));
1523 //$t->setMarker('got curl handle');
1525 if($this->proxyhost && $this->proxyport){
1526 $host = $this->proxyhost;
1527 $port = $this->proxyport;
1529 $host = $this->host;
1530 $port = $this->port;
1533 $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
1535 $hostURL .= $this->path;
1536 curl_setopt($ch, CURLOPT_URL, $hostURL);
1537 // set other options
1538 curl_setopt($ch, CURLOPT_HEADER, 1);
1539 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1541 if(function_exists('gzinflate')){
1542 curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
1544 // persistent connection
1545 //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1549 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1553 if($this->username != '') {
1554 $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n';
1557 if($this->encoding != ''){
1558 if(function_exists('gzdeflate')){
1559 $encoding_headers = "Accept-Encoding: $this->encoding\r\n".
1560 "Connection: close\r\n";
1561 if (!check_php_version(5,3)) {
1562 set_magic_quotes_runtime(0);
1567 if($this->proxyhost && $this->proxyport){
1568 $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
1570 $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
1573 $this->outgoing_payload .=
1574 "User-Agent: $this->title v$this->version\r\n".
1575 "Host: ".$this->host."\r\n".
1578 "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n".
1579 "Content-Length: ".strlen($data)."\r\n".
1580 "SOAPAction: \"$this->soapaction\""."\r\n\r\n".
1584 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1585 //$t->setMarker('set curl options, executing...');
1587 $this->incoming_payload = curl_exec($ch);
1588 //$t->setMarker('executed transfer');
1589 $data = $this->incoming_payload;
1591 $cErr = curl_error($ch);
1594 $err = 'cURL ERROR: '.curl_errno($ch).': '.$cErr.'<br>';
1595 foreach(curl_getinfo($ch) as $k => $v){
1596 $err .= "$k: $v<br>";
1598 $this->setError($err);
1603 //var_dump(curl_getinfo($ch));
1608 //$t->setMarker('closed curl');
1610 // remove 100 header
1611 if(ereg('^HTTP/1.1 100',$data)){
1612 if($pos = strpos($data,"\r\n\r\n") ){
1613 $data = ltrim(substr($data,$pos));
1614 } elseif($pos = strpos($data,"\n\n") ){
1615 $data = ltrim(substr($data,$pos));
1619 // separate content from HTTP headers
1620 if( $pos = strpos($data,"\r\n\r\n") ){
1622 } elseif( $pos = strpos($data,"\n\n") ){
1625 $this->setError('no proper separation of headers and document');
1628 $header_data = trim(substr($data,0,$pos));
1629 $header_array = explode($lb,$header_data);
1630 $data = ltrim(substr($data,$pos));
1631 $this->debug('found proper separation of headers and document');
1632 $this->debug('cleaned data, stringlen: '.strlen($data));
1634 foreach($header_array as $header_line){
1635 $arr = explode(':',$header_line);
1636 $headers[trim($arr[0])] = trim($arr[1]);
1638 if(strlen($data) == 0){
1639 $this->debug('no data after headers!');
1640 $this->setError('no data present after HTTP headers.');
1644 // decode transfer-encoding
1645 if($headers['Transfer-Encoding'] == 'chunked'){
1646 if(!$data = $this->decodeChunked($data)){
1647 $this->setError('Decoding of chunked data failed');
1651 // decode content-encoding
1652 if($headers['Content-Encoding'] != ''){
1653 if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1654 // if decoding works, use it. else assume data wasn't gzencoded
1655 if(function_exists('gzinflate')){
1656 if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1658 } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1661 $this->setError('Errors occurred when trying to decode the data');
1664 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1668 // set decoded payload
1669 $this->incoming_payload = $header_data."\r\n\r\n".$data;
1674 * if authenticating, set user credentials here
1676 * @param string $user
1677 * @param string $pass
1680 function setCredentials($username, $password) {
1681 $this->username = $username;
1682 $this->password = $password;
1686 * set the soapaction value
1688 * @param string $soapaction
1691 function setSOAPAction($soapaction) {
1692 $this->soapaction = $soapaction;
1698 * @param string $enc encoding style. supported values: gzip, deflate, or both
1701 function setEncoding($enc='gzip, deflate'){
1702 $this->encoding = $enc;
1703 $this->protocol_version = '1.1';
1707 * set proxy info here
1709 * @param string $proxyhost
1710 * @param string $proxyport
1713 function setProxy($proxyhost, $proxyport) {
1714 $this->proxyhost = $proxyhost;
1715 $this->proxyport = $proxyport;
1719 * decode a string that is encoded w/ "chunked' transfer encoding
1720 * as defined in RFC2068 19.4.6
1722 * @param string $buffer
1726 function decodeChunked($buffer){
1731 // read chunk-size, chunk-extension (if any) and CRLF
1732 // get the position of the linebreak
1733 $chunkend = strpos($buffer,"\r\n") + 2;
1734 $temp = substr($buffer,0,$chunkend);
1735 $chunk_size = hexdec( trim($temp) );
1736 $chunkstart = $chunkend;
1737 // while (chunk-size > 0) {
1738 while ($chunk_size > 0) {
1740 $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size);
1742 // Just in case we got a broken connection
1743 if ($chunkend == FALSE) {
1744 $chunk = substr($buffer,$chunkstart);
1745 // append chunk-data to entity-body
1747 $length += strlen($chunk);
1751 // read chunk-data and CRLF
1752 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1753 // append chunk-data to entity-body
1755 // length := length + chunk-size
1756 $length += strlen($chunk);
1757 // read chunk-size and CRLF
1758 $chunkstart = $chunkend + 2;
1760 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
1761 if ($chunkend == FALSE) {
1762 break; //Just in case we got a broken connection
1764 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1765 $chunk_size = hexdec( trim($temp) );
1766 $chunkstart = $chunkend;
1769 //$this->Header['content-length'] = $length;
1770 //unset($this->Header['transfer-encoding']);
1780 * soap_server allows the user to create a SOAP server
1781 * that is capable of receiving messages and returning responses
1783 * NOTE: WSDL functionality is experimental
1785 * @author Dietrich Ayala <dietrich@ganx4.com>
1789 class soap_server extends nusoap_base {
1791 var $service = ''; // service name
1792 var $operations = array(); // assoc array of operations => opData
1793 var $responseHeaders = false;
1796 var $charset_encoding = 'UTF-8';
1798 var $result = 'successful';
1800 var $externalWSDLURL = false;
1801 var $debug_flag = true;
1805 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1807 * @param string $wsdl path or URL to a WSDL file
1810 function soap_server($wsdl=false){
1812 // turn on debugging?
1815 $this->debug_flag = 1;
1820 $this->wsdl = new wsdl($wsdl);
1821 $this->externalWSDLURL = $wsdl;
1822 if($err = $this->wsdl->getError()){
1823 die('WSDL ERROR: '.$err);
1829 * processes request and returns response
1831 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
1834 function service($data){
1836 global $QUERY_STRING, $HTTP_SERVER_VARS;
1837 if(isset($HTTP_SERVER_VARS['QUERY_STRING'])){
1838 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
1839 } elseif(isset($GLOBALS['QUERY_STRING'])){
1840 $qs = $GLOBALS['QUERY_STRING'];
1841 } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
1842 $qs = $QUERY_STRING;
1845 if(isset($qs) && ereg('wsdl', $qs) ){
1846 if($this->externalWSDLURL){
1847 header('Location: '.$this->externalWSDLURL);
1850 header("Content-Type: text/xml\r\n");
1851 print $this->wsdl->serialize();
1856 // print web interface
1857 if($data == '' && $this->wsdl){
1858 print $this->webDescription();
1861 // $response is the serialized response message
1862 $response = $this->parse_request($data);
1863 $this->debug('server sending...');
1864 $payload = $response;
1865 // add debug data if in debug mode
1866 if(isset($this->debug_flag) && $this->debug_flag == 1){
1867 $payload .= "<!--\n".str_replace('--','- -',$this->debug_str)."\n-->";
1871 $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
1872 $header[] = "Status: 500 Internal Server Error\r\n";
1874 $header[] = "Status: 200 OK\r\n";
1876 $header[] = "Server: $this->title Server v$this->version\r\n";
1877 $header[] = "Connection: Close\r\n";
1878 $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
1879 $header[] = "Content-Length: ".strlen($payload)."\r\n\r\n";
1881 foreach($header as $hdr){
1884 $this->response = join("\r\n",$header).$payload;
1890 * parses request and posts response
1892 * @param string $data XML string
1893 * @return string XML response msg
1896 function parse_request($data='') {
1897 if (!isset($_SERVER))
1898 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
1899 $this->debug('entering parseRequest() on '.date('H:i Y-m-d'));
1902 if(function_exists('getallheaders')){
1903 $this->headers = getallheaders();
1904 foreach($this->headers as $k=>$v){
1905 $dump .= "$k: $v\r\n";
1906 $this->debug("$k: $v");
1908 // get SOAPAction header
1909 if(isset($this->headers['SOAPAction'])){
1910 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
1912 // get the character encoding of the incoming request
1913 if(strpos($this->headers['Content-Type'],'=')){
1914 $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
1915 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
1916 $this->xml_encoding = $enc;
1918 $this->xml_encoding = 'us-ascii';
1921 $this->debug('got encoding: '.$this->charset_encoding);
1922 } elseif(is_array($_SERVER)){
1923 $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
1924 $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
1926 $this->request = $dump."\r\n\r\n".$data;
1927 // parse response, get soap parser obj
1928 $parser = new soap_parser($data,$this->charset_encoding);
1929 // if fault occurred during message parsing
1930 if($err = $parser->getError()){
1932 $this->debug("parser debug: \n".$parser->debug_str);
1933 $this->result = 'fault: error in msg parsing: '.$err;
1934 $this->fault('Server',"error in msg parsing:\n".$err);
1936 return $this->fault->serialize();
1937 // else successfully parsed request into soapval object
1939 // get/set methodname
1940 $this->methodname = $parser->root_struct_name;
1941 $this->debug('method name: '.$this->methodname);
1942 // does method exist?
1943 if(!function_exists($this->methodname)){
1944 // "method not found" fault here
1945 $this->debug("method '$this->methodname' not found!");
1946 $this->debug("parser debug: \n".$parser->debug_str);
1947 $this->result = 'fault: method not found';
1948 $this->fault('Server',"method '$this->methodname' not defined in service '$this->service'");
1949 return $this->fault->serialize();
1952 if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
1954 $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
1955 return $this->fault->serialize();
1958 $this->debug("method '$this->methodname' exists");
1959 // evaluate message, getting back parameters
1960 $this->debug('calling parser->get_response()');
1961 $request_data = $parser->get_response();
1963 $this->debug("parser debug: \n".$parser->debug_str);
1964 // verify that request parameters match the method's signature
1965 if($this->verify_method($this->methodname,$request_data)){
1966 // if there are parameters to pass
1967 $this->debug('params var dump '.$this->varDump($request_data));
1969 $this->debug("calling '$this->methodname' with params");
1970 if (! function_exists('call_user_func_array')) {
1971 $this->debug('calling method using eval()');
1972 $funcCall = $this->methodname.'(';
1973 foreach($request_data as $param) {
1974 $funcCall .= "\"$param\",";
1976 $funcCall = substr($funcCall, 0, -1).')';
1977 $this->debug('function call:<br>'.$funcCall);
1978 @eval("\$method_response = $funcCall;");
1980 $this->debug('calling method using call_user_func_array()');
1981 $method_response = call_user_func_array("$this->methodname",$request_data);
1983 $this->debug('response var dump'.$this->varDump($method_response));
1985 // call method w/ no parameters
1986 $this->debug("calling $this->methodname w/ no params");
1987 $m = $this->methodname;
1988 $method_response = @$m();
1990 $this->debug("done calling method: $this->methodname, received $method_response of type".gettype($method_response));
1991 // if we got nothing back. this might be ok (echoVoid)
1992 if(isset($method_response) && $method_response != '' || is_bool($method_response)) {
1994 if(strtolower(get_class($method_response)) == 'soap_fault'){
1995 $this->debug('got a fault object from method');
1996 $this->fault = $method_response;
1997 return $method_response->serialize();
1998 // if return val is soapval object
1999 } elseif(strtolower(get_class($method_response)) == 'soapval'){
2000 $this->debug('got a soapval object from method');
2001 $return_val = $method_response->serialize();
2004 $this->debug('got a(n) '.gettype($method_response).' from method');
2005 $this->debug('serializing return value');
2007 // weak attempt at supporting multiple output params
2008 if(sizeof($this->opData['output']['parts']) > 1){
2009 $opParams = $method_response;
2011 $opParams = array($method_response);
2013 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2015 $return_val = $this->serialize_val($method_response);
2018 $this->debug('return val:'.$this->varDump($return_val));
2021 $this->debug('got no response from method');
2023 $this->debug('serializing response');
2024 $payload = '<'.$this->methodname."Response>".$return_val.'</'.$this->methodname."Response>";
2025 $this->result = 'successful';
2027 //if($this->debug_flag){
2028 $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
2030 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2031 return $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
2033 return $this->serializeEnvelope($payload,$this->responseHeaders);
2037 $this->debug('ERROR: request not verified against method signature');
2038 $this->result = 'fault: request failed validation against method signature';
2040 $this->fault('Server',"Operation '$this->methodname' not defined in service.");
2041 return $this->fault->serialize();
2047 * takes the value that was created by parsing the request
2048 * and compares to the method's signature, if available.
2054 function verify_method($operation,$request){
2055 if(isset($this->wsdl) && is_object($this->wsdl)){
2056 if($this->wsdl->getOperationData($operation)){
2059 } elseif(isset($this->operations[$operation])){
2066 * add a method to the dispatch map
2068 * @param string $methodname
2069 * @param string $in array of input values
2070 * @param string $out array of output values
2073 function add_to_map($methodname,$in,$out){
2074 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2078 * register a service with the server
2080 * @param string $methodname
2081 * @param string $in assoc array of input values: key = param name, value = param type
2082 * @param string $out assoc array of output values: key = param name, value = param type
2083 * @param string $namespace
2084 * @param string $soapaction
2085 * @param string $style (rpc|literal)
2088 function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false){
2089 if($this->externalWSDLURL){
2090 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2096 if(false == $namespace) {
2098 if(false == $soapaction) {
2099 global $SERVER_NAME, $SCRIPT_NAME;
2100 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2102 if(false == $style) {
2109 $this->operations[] = array($name => array());
2110 $this->operations[$name] = array(
2114 'namespace' => $namespace,
2115 'soapaction' => $soapaction,
2118 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use);
2124 * create a fault. this also acts as a flag to the server that a fault has occured.
2126 * @param string faultcode
2127 * @param string faultactor
2128 * @param string faultstring
2129 * @param string faultdetail
2132 function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){
2133 $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2137 * prints html description of services
2141 function webDescription(){
2143 <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
2144 <style type="text/css">
2145 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2146 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2147 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2148 ul { margin-top: 10px; margin-left: 20px; }
2149 li { list-style-type: none; margin-top: 10px; color: #000000; }
2151 margin-left: 0px; padding-bottom: 2em; }
2153 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2154 margin-top: 10px; margin-left: 0px; color: #000000;
2155 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2157 font-family: arial; font-size: 26px; color: #ffffff;
2158 background-color: #999999; width: 105%; margin-left: 0px;
2159 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2161 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2162 font-family: arial; overflow: hidden; width: 600;
2163 padding: 20px; font-size: 10px; background-color: #999999;
2164 layer-background-color:#FFFFFF; }
2165 a,a:active { color: charcoal; font-weight: bold; }
2166 a:visited { color: #666666; font-weight: bold; }
2167 a:hover { color: cc3300; font-weight: bold; }
2169 <script type="text/javascript">
2171 // POP-UP CAPTIONS...
2172 function lib_bwcheck(){ //Browsercheck (needed)
2173 this.ver=navigator.appVersion
2174 this.agent=navigator.userAgent
2175 this.dom=document.getElementById?1:0
2176 this.opera5=this.agent.indexOf("Opera 5")>-1
2177 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2178 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2179 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2180 this.ie=this.ie4||this.ie5||this.ie6
2181 this.mac=this.agent.indexOf("Mac")>-1
2182 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2183 this.ns4=(document.layers && !this.dom)?1:0;
2184 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2187 var bw = new lib_bwcheck()
2188 //Makes crossbrowser object.
2189 function makeObj(obj){
2190 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2191 if(!this.evnt) return false
2192 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2193 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2194 this.writeIt=b_writeIt;
2197 // A unit of measure that will be added when setting the position of a layer.
2198 //var px = bw.ns4||window.opera?"":"px";
2199 function b_writeIt(text){
2200 if (bw.ns4){this.wref.write(text);this.wref.close()}
2201 else this.wref.innerHTML = text
2203 //Shows the messages
2205 function popup(divid){
2206 if(oDesc = new makeObj(divid)){
2207 oDesc.css.visibility = "visible"
2210 function popout(){ // Hides message
2211 if(oDesc) oDesc.css.visibility = "hidden"
2219 <div class=title>'.$this->wsdl->serviceName.'</div>
2221 <p>View the <a href="'.$GLOBALS['PHP_SELF'].'?wsdl">WSDL</a> for the service.
2222 Click on an operation name to view it's details.</p>
2224 foreach($this->wsdl->getOperations() as $op => $data){
2225 $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2226 // create hidden div
2227 $b .= "<div id='$op' class='hidden'>
2228 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2229 foreach($data as $donnie => $marie){ // loop through opdata
2230 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
2231 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
2232 foreach($marie as $captain => $tenille){ // loop through data
2233 if($captain == 'parts'){ // loop thru parts
2234 $b .= " $captain:<br>";
2235 //if(is_array($tenille)){
2236 foreach($tenille as $joanie => $chachi){
2237 $b .= " $joanie: $chachi<br>";
2241 $b .= " $captain: $tenille<br>";
2245 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
2253 </div></body></html>';
2258 * sets up wsdl object
2259 * this acts as a flag to enable internal WSDL generation
2260 * NOTE: NOT FUNCTIONAL
2262 * @param string $serviceName, name of the service
2263 * @param string $namespace, tns namespace
2265 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http') {
2266 if (!isset($_SERVER))
2267 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
2268 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2269 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2270 if(false == $namespace) {
2271 $namespace = "http://$SERVER_NAME/soap/$serviceName";
2274 if(false == $endpoint) {
2275 $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2278 $this->wsdl = new wsdl;
2279 $this->wsdl->serviceName = $serviceName;
2280 $this->wsdl->endpoint = $endpoint;
2281 $this->wsdl->namespaces['tns'] = $namespace;
2282 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
2283 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
2284 $this->wsdl->bindings[$serviceName.'Binding'] = array(
2285 'name'=>$serviceName.'Binding',
2287 'transport'=>$transport,
2288 'portType'=>$serviceName.'PortType');
2289 $this->wsdl->ports[$serviceName.'Port'] = array(
2290 'binding'=>$serviceName.'Binding',
2291 'location'=>$endpoint,
2292 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
2299 * parses a WSDL file, allows access to it's data, other utility methods
2301 * @author Dietrich Ayala <dietrich@ganx4.com>
2305 class wsdl extends XMLSchema {
2307 // define internal arrays of bindings, ports, operations, messages, etc.
2308 var $message = array();
2309 var $complexTypes = array();
2310 var $messages = array();
2311 var $currentMessage;
2312 var $currentOperation;
2313 var $portTypes = array();
2314 var $currentPortType;
2315 var $bindings = array();
2316 var $currentBinding;
2317 var $ports = array();
2319 var $opData = array();
2321 var $documentation = false;
2323 // array of wsdl docs to import
2324 var $import = array();
2329 var $depth_array = array();
2330 var $usedNamespaces = array();
2332 var $proxyhost = '';
2333 var $proxyport = '';
2338 * @param string $wsdl WSDL document URL
2341 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){
2342 $this->wsdl = $wsdl;
2343 $this->proxyhost = $proxyhost;
2344 $this->proxyport = $proxyport;
2348 $this->debug('initial wsdl file: ' . $wsdl);
2349 $this->parseWSDL($wsdl);
2352 if (sizeof($this->import) > 0) {
2353 foreach($this->import as $ns => $url) {
2354 $this->debug('importing wsdl from ' . $url);
2355 $this->parseWSDL($url);
2361 * parses the wsdl document
2363 * @param string $wsdl path or URL
2366 function parseWSDL($wsdl = '')
2369 $this->debug('no wsdl passed to parseWSDL()!!');
2370 $this->setError('no wsdl passed to parseWSDL()!!');
2374 $this->debug('getting ' . $wsdl);
2376 // parse $wsdl for url format
2377 $wsdl_props = parse_url($wsdl);
2379 if (isset($wsdl_props['host'])) {
2382 $tr = new soap_transport_http($wsdl);
2383 $tr->request_method = 'GET';
2384 $tr->useSOAPAction = false;
2385 if($this->proxyhost && $this->proxyport){
2386 $tr->setProxy($this->proxyhost,$this->proxyport);
2388 if (isset($wsdl_props['user'])) {
2389 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
2391 $wsdl_string = $tr->send('');
2393 if($err = $tr->getError() ){
2394 $this->debug('HTTP ERROR: '.$err);
2395 $this->setError('HTTP ERROR: '.$err);
2399 /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
2400 $fsockopen_timeout = 30;
2401 // check if a port value is supplied in url
2402 if (isset($wsdl_props['port'])) {
2404 $wsdl_url_port = $wsdl_props['port'];
2406 // no, assign port number, based on url protocol (scheme)
2407 switch ($wsdl_props['scheme']) {
2411 $wsdl_url_port = 443;
2415 $wsdl_url_port = 80;
2418 // FIXME: should implement SSL/TLS support here if CURL is available
2419 if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
2420 // perform HTTP GET for WSDL file
2421 // 10.9.02 - added poulter fix for doing this properly
2422 $sHeader = "GET " . $wsdl_props['path'];
2423 if (isset($wsdl_props['query'])) {
2424 $sHeader .= "?" . $wsdl_props['query'];
2426 $sHeader .= " HTTP/1.0\r\n";
2428 if (isset($wsdl_props['user'])) {
2429 $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2430 $sHeader .= "Authorization: Basic $base64auth\r\n";
2432 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2433 fputs($fp, $sHeader);
2435 while (fgets($fp, 1024) != "\r\n") {
2436 // do nothing, just read/skip past HTTP headers
2437 // FIXME: should actually detect HTTP response code, and act accordingly if error
2438 // HTTP headers end with extra CRLF before content body
2440 // read in WSDL just like regular fopen()
2442 while ($data = fread($fp, 32768)) {
2443 $wsdl_string .= $data;
2447 $this->setError('bad path to WSDL file.');
2452 // $wsdl seems to be a non-url file path, do the regular fopen
2453 if ($fp = @fopen($wsdl, 'r')) {
2455 while ($data = fread($fp, 32768)) {
2456 $wsdl_string .= $data;
2460 $this->setError('bad path to WSDL file.');
2464 // end new code added
2465 // Create an XML parser.
2466 $this->parser = xml_parser_create();
2467 // Set the options for parsing the XML data.
2468 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2469 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
2470 // Set the object for the parser.
2471 xml_set_object($this->parser, $this);
2472 // Set the element handlers for the parser.
2473 xml_set_element_handler($this->parser, 'start_element', 'end_element');
2474 xml_set_character_data_handler($this->parser, 'character_data');
2475 // Parse the XML file.
2476 if (!xml_parse($this->parser, $wsdl_string, true)) {
2477 // Display an error message.
2479 'XML error on line %d: %s',
2480 xml_get_current_line_number($this->parser),
2481 xml_error_string(xml_get_error_code($this->parser))
2483 $this->debug('XML parse error: ' . $errstr);
2484 $this->setError('Parser error: ' . $errstr);
2488 xml_parser_free($this->parser);
2489 // catch wsdl parse errors
2490 if($this->getError()){
2493 // add new data to operation data
2494 foreach($this->bindings as $binding => $bindingData) {
2495 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
2496 foreach($bindingData['operations'] as $operation => $data) {
2497 $this->debug('post-parse data gathering for ' . $operation);
2498 $this->bindings[$binding]['operations'][$operation]['input'] =
2499 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
2500 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
2501 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
2502 $this->bindings[$binding]['operations'][$operation]['output'] =
2503 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
2504 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
2505 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
2506 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
2507 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
2509 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
2510 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
2512 if (isset($bindingData['style'])) {
2513 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2515 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
2516 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
2517 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
2525 * start-element handler
2527 * @param string $parser XML parser object
2528 * @param string $name element name
2529 * @param string $attrs associative array of attributes
2532 function start_element($parser, $name, $attrs)
2534 if ($this->status == 'schema' || ereg('schema$', $name)) {
2535 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
2536 $this->status = 'schema';
2537 $this->schemaStartElement($parser, $name, $attrs);
2539 // position in the total number of elements, starting from 0
2540 $pos = $this->position++;
2541 $depth = $this->depth++;
2542 // set self as current value for this depth
2543 $this->depth_array[$depth] = $pos;
2544 $this->message[$pos] = array('cdata' => '');
2545 // get element prefix
2546 if (ereg(':', $name)) {
2548 $prefix = substr($name, 0, strpos($name, ':'));
2550 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
2551 // get unqualified name
2552 $name = substr(strstr($name, ':'), 1);
2555 if (count($attrs) > 0) {
2556 foreach($attrs as $k => $v) {
2557 // if ns declarations, add to class level array of valid namespaces
2558 if (ereg("^xmlns", $k)) {
2559 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
2560 $this->namespaces[$ns_prefix] = $v;
2562 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
2564 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
2565 $this->XMLSchemaVersion = $v;
2566 $this->namespaces['xsi'] = $v . '-instance';
2569 // expand each attribute
2570 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
2571 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
2572 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
2580 // find status, register data
2581 switch ($this->status) {
2583 if ($name == 'part') {
2584 if (isset($attrs['type'])) {
2585 $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
2586 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
2588 if (isset($attrs['element'])) {
2589 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2596 $this->currentPortOperation = $attrs['name'];
2597 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
2598 if (isset($attrs['parameterOrder'])) {
2599 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
2602 case 'documentation':
2603 $this->documentation = true;
2605 // merge input/output data
2607 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
2608 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
2616 if (isset($attrs['style'])) {
2617 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
2619 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
2622 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
2625 if (isset($attrs['soapAction'])) {
2626 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
2628 if (isset($attrs['style'])) {
2629 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2631 if (isset($attrs['name'])) {
2632 $this->currentOperation = $attrs['name'];
2633 $this->debug("current binding operation: $this->currentOperation");
2634 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
2635 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
2636 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
2640 $this->opStatus = 'input';
2643 $this->opStatus = 'output';
2646 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
2647 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
2649 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
2657 $this->currentPort = $attrs['name'];
2658 $this->debug('current port: ' . $this->currentPort);
2659 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
2663 $this->ports[$this->currentPort]['location'] = $attrs['location'];
2664 $this->ports[$this->currentPort]['bindingType'] = $namespace;
2665 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
2666 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
2674 if (isset($attrs['location'])) {
2675 $this->import[$attrs['namespace']] = $attrs['location'];
2679 $this->status = 'schema';
2682 $this->status = 'message';
2683 $this->messages[$attrs['name']] = array();
2684 $this->currentMessage = $attrs['name'];
2687 $this->status = 'portType';
2688 $this->portTypes[$attrs['name']] = array();
2689 $this->currentPortType = $attrs['name'];
2692 if (isset($attrs['name'])) {
2694 if (strpos($attrs['name'], ':')) {
2695 $this->currentBinding = $this->getLocalPart($attrs['name']);
2697 $this->currentBinding = $attrs['name'];
2699 $this->status = 'binding';
2700 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
2701 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
2705 $this->serviceName = $attrs['name'];
2706 $this->status = 'service';
2707 $this->debug('current service: ' . $this->serviceName);
2710 foreach ($attrs as $name => $value) {
2711 $this->wsdl_info[$name] = $value;
2719 * end-element handler
2721 * @param string $parser XML parser object
2722 * @param string $name element name
2725 function end_element($parser, $name){
2726 // unset schema status
2727 if (ereg('types$', $name) || ereg('schema$', $name)) {
2730 if ($this->status == 'schema') {
2731 $this->schemaEndElement($parser, $name);
2733 // bring depth down a notch
2736 // end documentation
2737 if ($this->documentation) {
2738 $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2739 $this->documentation = false;
2744 * element content handler
2746 * @param string $parser XML parser object
2747 * @param string $data element content
2750 function character_data($parser, $data)
2752 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
2753 if (isset($this->message[$pos]['cdata'])) {
2754 $this->message[$pos]['cdata'] .= $data;
2756 if ($this->documentation) {
2757 $this->documentation .= $data;
2761 function getBindingData($binding)
2763 if (is_array($this->bindings[$binding])) {
2764 return $this->bindings[$binding];
2769 * returns an assoc array of operation names => operation data
2770 * NOTE: currently only supports multiple services of differing binding types
2771 * This method needs some work
2773 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2777 function getOperations($bindingType = 'soap')
2779 if ($bindingType == 'soap') {
2780 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2783 foreach($this->ports as $port => $portData) {
2784 // binding type of port matches parameter
2785 if ($portData['bindingType'] == $bindingType) {
2787 return $this->bindings[ $portData['binding'] ]['operations'];
2794 * returns an associative array of data necessary for calling an operation
2796 * @param string $operation , name of operation
2797 * @param string $bindingType , type of binding eg: soap
2801 function getOperationData($operation, $bindingType = 'soap')
2803 if ($bindingType == 'soap') {
2804 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2807 foreach($this->ports as $port => $portData) {
2808 // binding type of port matches parameter
2809 if ($portData['bindingType'] == $bindingType) {
2811 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
2812 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
2813 if ($operation == $bOperation) {
2814 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
2823 * serialize the parsed wsdl
2825 * @return string , serialization of WSDL
2828 function serialize()
2830 $xml = '<?xml version="1.0"?><definitions';
2831 foreach($this->namespaces as $k => $v) {
2832 $xml .= " xmlns:$k=\"$v\"";
2834 // 10.9.02 - add poulter fix for wsdl and tns declarations
2835 if (isset($this->namespaces['wsdl'])) {
2836 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2838 if (isset($this->namespaces['tns'])) {
2839 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2843 if (sizeof($this->import) > 0) {
2844 foreach($this->import as $ns => $url) {
2845 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2849 if (count($this->complexTypes)>=1) {
2851 $xml .= $this->serializeSchema();
2855 if (count($this->messages) >= 1) {
2856 foreach($this->messages as $msgName => $msgParts) {
2857 $xml .= '<message name="' . $msgName . '">';
2858 foreach($msgParts as $partName => $partType) {
2859 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
2860 if (strpos($partType, ':')) {
2861 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
2862 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
2863 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
2864 $typePrefix = 'xsd';
2866 foreach($this->typemap as $ns => $types) {
2867 if (isset($types[$partType])) {
2868 $typePrefix = $this->getPrefixFromNamespace($ns);
2871 if (!isset($typePrefix)) {
2872 die("$partType has no namespace!");
2875 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2877 $xml .= '</message>';
2880 // bindings & porttypes
2881 if (count($this->bindings) >= 1) {
2884 foreach($this->bindings as $bindingName => $attrs) {
2885 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
2886 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
2887 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
2888 foreach($attrs['operations'] as $opName => $opParts) {
2889 $binding_xml .= '<operation name="' . $opName . '">';
2890 $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
2891 $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
2892 $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
2893 $binding_xml .= '</operation>';
2894 $portType_xml .= '<operation name="' . $opParts['name'] . '"';
2895 if (isset($opParts['parameterOrder'])) {
2896 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
2898 $portType_xml .= '>';
2899 $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
2900 $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
2901 $portType_xml .= '</operation>';
2903 $portType_xml .= '</portType>';
2904 $binding_xml .= '</binding>';
2906 $xml .= $portType_xml . $binding_xml;
2909 $xml .= '<service name="' . $this->serviceName . '">';
2910 if (count($this->ports) >= 1) {
2911 foreach($this->ports as $pName => $attrs) {
2912 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
2913 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
2917 $xml .= '</service>';
2918 return $xml . '</definitions>';
2922 * serialize a PHP value according to a WSDL message definition
2925 * - multi-ref serialization
2926 * - validate PHP values against type definitions, return errors if invalid
2928 * @param string $ type name
2929 * @param mixed $ param value
2930 * @return mixed new param or false if initial value didn't validate
2932 function serializeRPCParameters($operation, $direction, $parameters)
2934 $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
2936 if ($direction != 'input' && $direction != 'output') {
2937 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
2938 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
2941 if (!$opData = $this->getOperationData($operation)) {
2942 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
2943 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
2946 $this->debug($this->varDump($opData));
2949 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
2951 $use = $opData[$direction]['use'];
2952 $this->debug("use=$use");
2953 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
2954 foreach($opData[$direction]['parts'] as $name => $type) {
2955 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
2956 // NOTE: add error handling here
2957 // if serializeType returns false, then catch global error and fault
2958 if (isset($parameters[$name])) {
2959 $this->debug('calling serializeType w/ named param');
2960 $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
2961 } elseif(is_array($parameters)) {
2962 $this->debug('calling serializeType w/ unnamed param');
2963 $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
2965 $this->debug('no parameters passed.');
2973 * serializes a PHP value according a given type definition
2975 * @param string $name , name of type (part)
2976 * @param string $type , type of type, heh (type or element)
2977 * @param mixed $value , a native PHP value (parameter value)
2978 * @param string $use , use for part (encoded|literal)
2979 * @return string serialization
2982 function serializeType($name, $type, $value, $use='encoded')
2984 $this->debug("in serializeType: $name, $type, $value, $use");
2986 if (strpos($type, ':')) {
2987 $uqType = substr($type, strrpos($type, ':') + 1);
2988 $ns = substr($type, 0, strrpos($type, ':'));
2989 $this->debug("got a prefixed type: $uqType, $ns");
2991 if($ns == $this->XMLSchemaVersion ||
2992 ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){
2994 if ($uqType == 'boolean' && !$value) {
2996 } elseif ($uqType == 'boolean') {
2999 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
3000 $value = htmlspecialchars($value);
3003 if ($use == 'literal') {
3004 return "<$name>$value</$name>";
3006 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3012 if(!$typeDef = $this->getTypeDef($uqType)){
3013 $this->setError("$uqType is not a supported type.");
3016 //foreach($typeDef as $k => $v) {
3017 //$this->debug("typedef, $k: $v");
3020 $phpType = $typeDef['phpType'];
3021 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
3022 // if php type == struct, map value to the <all> element names
3023 if ($phpType == 'struct') {
3024 if (isset($typeDef['element']) && $typeDef['element']) {
3025 $elementName = $uqType;
3026 // TODO: use elementFormDefault="qualified|unqualified" to determine
3027 // how to scope the namespace
3028 $elementNS = " xmlns=\"$ns\"";
3030 $elementName = $name;
3033 if ($use == 'literal') {
3034 $xml = "<$elementName$elementNS>";
3036 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3039 if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3041 //if (is_array($this->complexTypes[$uqType]['elements'])) {
3042 // toggle whether all elements are present - ideally should validate against schema
3043 if(count($this->complexTypes[$uqType]['elements']) != count($value)){
3046 foreach($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
3047 // if user took advantage of a minOccurs=0, then only serialize named parameters
3048 if(isset($optionals) && !isset($value[$eName])){
3052 if (isset($value[$eName])) {
3053 $v = $value[$eName];
3054 } elseif (is_array($value)) {
3055 $v = array_shift($value);
3057 // serialize schema-defined type
3058 if (!isset($attrs['type'])) {
3059 $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
3060 // serialize generic type
3062 $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
3063 $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
3068 $xml .= "</$elementName>";
3069 } elseif ($phpType == 'array') {
3070 $rows = sizeof($value);
3071 if (isset($typeDef['multidimensional'])) {
3073 foreach($value as $v) {
3074 $cols = ',' . sizeof($v);
3075 $nv = array_merge($nv, $v);
3081 if (is_array($value) && sizeof($value) >= 1) {
3083 foreach($value as $k => $v) {
3084 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
3085 //if (strpos($typeDef['arrayType'], ':') ) {
3086 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
3087 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
3089 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
3092 $this->debug('contents: '.$this->varDump($contents));
3096 if ($use == 'literal') {
3101 $xml = "<$name xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
3102 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
3104 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
3105 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
3110 $this->debug('returning: '.$this->varDump($xml));
3115 * register a service with the server
3117 * @param string $methodname
3118 * @param string $in assoc array of input values: key = param name, value = param type
3119 * @param string $out assoc array of output values: key = param name, value = param type
3120 * @param string $namespace
3121 * @param string $soapaction
3122 * @param string $style (rpc|literal)
3125 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3127 if ($style == 'rpc' && $use == 'encoded') {
3128 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3130 $encodingStyle = '';
3133 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
3136 'binding' => $this->serviceName . 'Binding',
3137 'endpoint' => $this->endpoint,
3138 'soapAction' => $soapaction,
3142 'namespace' => $namespace,
3143 'encodingStyle' => $encodingStyle,
3144 'message' => $name . 'Request',
3148 'namespace' => $namespace,
3149 'encodingStyle' => $encodingStyle,
3150 'message' => $name . 'Response',
3152 'namespace' => $namespace,
3153 'transport' => 'http://schemas.xmlsoap.org/soap/http',
3154 'documentation' => $documentation);
3159 foreach($in as $pName => $pType)
3161 if(strpos($pType,':')) {
3162 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3164 $this->messages[$name.'Request'][$pName] = $pType;
3170 foreach($out as $pName => $pType)
3172 if(strpos($pType,':')) {
3173 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3175 $this->messages[$name.'Response'][$pName] = $pType;
3186 * soap_parser class parses SOAP XML messages into native PHP values
3188 * @author Dietrich Ayala <dietrich@ganx4.com>
3192 class soap_parser extends nusoap_base {
3195 var $xml_encoding = '';
3197 var $root_struct = '';
3198 var $root_struct_name = '';
3199 var $root_header = '';
3201 // determines where in the message we are (envelope,header,body,method)
3205 var $default_namespace = '';
3206 var $namespaces = array();
3207 var $message = array();
3210 var $fault_code = '';
3211 var $fault_str = '';
3212 var $fault_detail = '';
3213 var $depth_array = array();
3214 var $debug_flag = true;
3215 var $soapresponse = NULL;
3216 var $responseHeaders = '';
3217 var $body_position = 0;
3218 // for multiref parsing:
3219 // array of id => pos
3221 // array of id => hrefs => pos
3222 var $multirefs = array();
3227 * @param string $xml SOAP message
3228 * @param string $encoding character encoding scheme of message
3231 function soap_parser($xml,$encoding='UTF-8',$method=''){
3233 $this->xml_encoding = $encoding;
3234 $this->method = $method;
3236 // Check whether content has been read.
3238 $this->debug('Entering soap_parser()');
3239 // Create an XML parser.
3240 $this->parser = xml_parser_create($this->xml_encoding);
3241 // Set the options for parsing the XML data.
3242 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3243 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3244 // Set the object for the parser.
3245 xml_set_object($this->parser, $this);
3246 // Set the element handlers for the parser.
3247 xml_set_element_handler($this->parser, 'start_element','end_element');
3248 xml_set_character_data_handler($this->parser,'character_data');
3250 // Parse the XML file.
3251 if(!xml_parse($this->parser,$xml,true)){
3252 // Display an error message.
3253 $err = sprintf('XML error on line %d: %s',
3254 xml_get_current_line_number($this->parser),
3255 xml_error_string(xml_get_error_code($this->parser)));
3256 $this->debug('parse error: '.$err);
3257 $this->errstr = $err;
3259 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
3261 $this->soapresponse = $this->message[$this->root_struct]['result'];
3263 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
3264 $this->responseHeaders = $this->message[$this->root_header]['result'];
3266 // resolve hrefs/ids
3267 if(sizeof($this->multirefs) > 0){
3268 foreach($this->multirefs as $id => $hrefs){
3269 $this->debug('resolving multirefs for id: '.$id);
3270 $idVal = $this->buildVal($this->ids[$id]);
3271 foreach($hrefs as $refPos => $ref){
3272 $this->debug('resolving href at pos '.$refPos);
3273 $this->multirefs[$id][$refPos] = $idVal;
3278 xml_parser_free($this->parser);
3280 $this->debug('xml was empty, didn\'t parse!');
3281 $this->errstr = 'xml was empty, didn\'t parse!';
3286 * start-element handler
3288 * @param string $parser XML parser object
3289 * @param string $name element name
3290 * @param string $attrs associative array of attributes
3293 function start_element($parser, $name, $attrs) {
3294 // position in a total number of elements, starting from 0
3295 // update class level pos
3296 $pos = $this->position++;
3298 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
3299 // depth = how many levels removed from root?
3300 // set mine as current global depth and increment global depth value
3301 $this->message[$pos]['depth'] = $this->depth++;
3303 // else add self as child to whoever the current parent is
3305 $this->message[$this->parent]['children'] .= '|'.$pos;
3308 $this->message[$pos]['parent'] = $this->parent;
3309 // set self as current parent
3310 $this->parent = $pos;
3311 // set self as current value for this depth
3312 $this->depth_array[$this->depth] = $pos;
3313 // get element prefix
3314 if(strpos($name,':')){
3316 $prefix = substr($name,0,strpos($name,':'));
3317 // get unqualified name
3318 $name = substr(strstr($name,':'),1);
3321 if($name == 'Envelope'){
3322 $this->status = 'envelope';
3323 } elseif($name == 'Header'){
3324 $this->root_header = $pos;
3325 $this->status = 'header';
3326 } elseif($name == 'Body'){
3327 $this->status = 'body';
3328 $this->body_position = $pos;
3330 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
3331 $this->status = 'method';
3332 $this->root_struct_name = $name;
3333 $this->root_struct = $pos;
3334 $this->message[$pos]['type'] = 'struct';
3335 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
3338 $this->message[$pos]['status'] = $this->status;
3340 $this->message[$pos]['name'] = htmlspecialchars($name);
3342 $this->message[$pos]['attrs'] = $attrs;
3344 // loop through atts, logging ns and type declarations
3346 foreach($attrs as $key => $value){
3347 $key_prefix = $this->getPrefix($key);
3348 $key_localpart = $this->getLocalPart($key);
3349 // if ns declarations, add to class level array of valid namespaces
3350 if($key_prefix == 'xmlns'){
3351 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
3352 $this->XMLSchemaVersion = $value;
3353 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
3354 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
3356 $this->namespaces[$key_localpart] = $value;
3357 // set method namespace
3358 if($name == $this->root_struct_name){
3359 $this->methodNamespace = $value;
3361 // if it's a type declaration, set type
3362 } elseif($key_localpart == 'type'){
3363 $value_prefix = $this->getPrefix($value);
3364 $value_localpart = $this->getLocalPart($value);
3365 $this->message[$pos]['type'] = $value_localpart;
3366 $this->message[$pos]['typePrefix'] = $value_prefix;
3367 if(isset($this->namespaces[$value_prefix])){
3368 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
3369 } else if(isset($attrs['xmlns:'.$value_prefix])) {
3370 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
3372 // should do something here with the namespace of specified type?
3373 } elseif($key_localpart == 'arrayType'){
3374 $this->message[$pos]['type'] = 'array';
3375 /* do arrayType ereg here
3376 [1] arrayTypeValue ::= atype asize
3377 [2] atype ::= QName rank*
3378 [3] rank ::= '[' (',')* ']'
3379 [4] asize ::= '[' length~ ']'
3380 [5] length ::= nextDimension* Digit+
3381 [6] nextDimension ::= Digit+ ','
3383 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
3384 if(ereg($expr,$value,$regs)){
3385 $this->message[$pos]['typePrefix'] = $regs[1];
3386 $this->message[$pos]['arraySize'] = $regs[3];
3387 $this->message[$pos]['arrayCols'] = $regs[4];
3392 $this->ids[$value] = $pos;
3395 if($key_localpart == 'root' && $value == 1){
3396 $this->status = 'method';
3397 $this->root_struct_name = $name;
3398 $this->root_struct = $pos;
3399 $this->debug("found root struct $this->root_struct_name, pos $pos");
3402 $attstr .= " $key=\"$value\"";
3404 // get namespace - must be done after namespace atts are processed
3406 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
3407 $this->default_namespace = $this->namespaces[$prefix];
3409 $this->message[$pos]['namespace'] = $this->default_namespace;
3411 if($this->status == 'header'){
3412 $this->responseHeaders .= "<$name$attstr>";
3413 } elseif($this->root_struct_name != ''){
3414 $this->document .= "<$name$attstr>";
3419 * end-element handler
3421 * @param string $parser XML parser object
3422 * @param string $name element name
3425 function end_element($parser, $name) {
3426 // position of current element is equal to the last value left in depth_array for my depth
3427 $pos = $this->depth_array[$this->depth--];
3429 // get element prefix
3430 if(strpos($name,':')){
3432 $prefix = substr($name,0,strpos($name,':'));
3433 // get unqualified name
3434 $name = substr(strstr($name,':'),1);
3437 // build to native type
3438 if(isset($this->body_position) && $pos > $this->body_position){
3439 // deal w/ multirefs
3440 if(isset($this->message[$pos]['attrs']['href'])){
3442 $id = substr($this->message[$pos]['attrs']['href'],1);
3443 // add placeholder to href array
3444 $this->multirefs[$id][$pos] = "placeholder";
3445 // add set a reference to it as the result value
3446 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
3447 // build complex values
3448 } elseif($this->message[$pos]['children'] != ""){
3449 $this->message[$pos]['result'] = $this->buildVal($pos);
3451 $this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
3452 if(is_numeric($this->message[$pos]['cdata']) ){
3453 if( strpos($this->message[$pos]['cdata'],'.') ){
3454 $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
3456 $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
3459 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3465 if($pos == $this->root_struct){
3466 $this->status = 'body';
3467 } elseif($name == 'Body'){
3468 $this->status = 'header';
3469 } elseif($name == 'Header'){
3470 $this->status = 'envelope';
3471 } elseif($name == 'Envelope'){
3474 // set parent back to my parent
3475 $this->parent = $this->message[$pos]['parent'];
3477 if($this->status == 'header'){
3478 $this->responseHeaders .= "</$name>";
3479 } elseif($pos >= $this->root_struct){
3480 $this->document .= "</$name>";
3485 * element content handler
3487 * @param string $parser XML parser object
3488 * @param string $data element content
3491 function character_data($parser, $data){
3492 $pos = $this->depth_array[$this->depth];
3493 if ($this->xml_encoding=='UTF-8'){
3494 $data = utf8_decode($data);
3496 $this->message[$pos]['cdata'] .= $data;
3498 if($this->status == 'header'){
3499 $this->responseHeaders .= $data;
3501 $this->document .= $data;
3506 * get the parsed message
3511 function get_response(){
3512 return $this->soapresponse;
3516 * get the parsed headers
3518 * @return string XML or empty if no headers
3521 function getHeaders(){
3522 return $this->responseHeaders;
3528 * @param string $text string to translate
3531 function decode_entities($text){
3532 foreach($this->entities as $entity => $encoded){
3533 $text = str_replace($encoded,$entity,$text);
3539 * builds response structures for compound values (arrays/structs)
3541 * @param string $pos position in node tree
3544 function buildVal($pos){
3545 if(!isset($this->message[$pos]['type'])){
3546 $this->message[$pos]['type'] = '';
3548 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
3549 // if there are children...
3550 if($this->message[$pos]['children'] != ''){
3551 $children = explode('|',$this->message[$pos]['children']);
3552 array_shift($children); // knock off empty
3554 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
3557 foreach($children as $child_pos){
3558 $this->debug("got an MD array element: $r, $c");
3559 $params[$r][] = $this->message[$child_pos]['result'];
3561 if($c == $this->message[$pos]['arrayCols']){
3567 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
3568 $this->debug('adding array '.$this->message[$pos]['name']);
3569 foreach($children as $child_pos){
3570 $params[] = &$this->message[$child_pos]['result'];
3572 // apache Map type: java hashtable
3573 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
3574 foreach($children as $child_pos){
3575 $kv = explode("|",$this->message[$child_pos]['children']);
3576 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
3578 // generic compound type
3579 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
3581 // is array or struct? better way to do this probably
3582 foreach($children as $child_pos){
3583 if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
3587 $keys[$this->message[$child_pos]['name']] = 1;
3590 foreach($children as $child_pos){
3592 $params[] = &$this->message[$child_pos]['result'];
3594 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
3598 return is_array($params) ? $params : array();
3600 $this->debug('no children');
3601 if(strpos($this->message[$pos]['cdata'],'&')){
3602 return strtr($this->message[$pos]['cdata'],array_flip($this->entities));
3604 return $this->message[$pos]['cdata'];
3618 * soapclient higher level class for easy usage.
3622 * // instantiate client with server info
3623 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
3625 * // call method, get results
3626 * echo $soapclient->call( string methodname [ ,array parameters] );
3629 * unset($soapclient);
3631 * @author Dietrich Ayala <dietrich@ganx4.com>
3635 class pwsoapclient extends nusoap_base {
3639 var $requestHeaders = false;
3640 var $responseHeaders;
3642 var $error_str = false;
3643 var $proxyhost = '';
3644 var $proxyport = '';
3645 var $xml_encoding = '';
3646 var $http_encoding = false;
3648 var $endpointType = '';
3649 var $persistentConnection = false;
3650 var $defaultRpcParams = false;
3653 * fault related variables
3661 var $fault, $faultcode, $faultstring, $faultdetail;
3666 * @param string $endpoint SOAP server or WSDL URL
3667 * @param bool $wsdl optional, set to true if using WSDL
3668 * @param int $portName optional portName in WSDL document
3671 function soapclient($endpoint,$wsdl = false){
3672 $this->endpoint = $endpoint;
3676 $this->endpointType = 'wsdl';
3677 $this->wsdlFile = $this->endpoint;
3679 // instantiate wsdl object and parse wsdl file
3680 $this->debug('instantiating wsdl class with doc: '.$endpoint);
3681 $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport);
3682 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3683 $this->wsdl->debug_str = '';
3685 if($errstr = $this->wsdl->getError()){
3686 $this->debug('got wsdl error: '.$errstr);
3687 $this->setError('wsdl error: '.$errstr);
3688 } elseif($this->operations = $this->wsdl->getOperations()){
3689 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
3691 $this->debug( 'getOperations returned false');
3692 $this->setError('no operations defined in the WSDL document!');
3698 * calls method, returns PHP native type
3700 * @param string $method SOAP server URL or path
3701 * @param array $params array of parameters, can be associative or not
3702 * @param string $namespace optional method namespace
3703 * @param string $soapAction optional SOAPAction value
3704 * @param boolean $headers optional array of soapval objects for headers
3705 * @param boolean $rpcParams optional treat params as RPC for use="literal"
3706 * This can be used on a per-call basis to overrider defaultRpcParams.
3710 function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null){
3711 $this->operation = $operation;
3712 $this->fault = false;
3713 $this->error_str = '';
3714 $this->request = '';
3715 $this->response = '';
3716 $this->faultstring = '';
3717 $this->faultcode = '';
3718 $this->opData = array();
3720 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
3721 $this->debug("endpointType: $this->endpointType");
3722 // if wsdl, get operation data and process parameters
3723 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
3725 $this->opData = $opData;
3726 foreach($opData as $key => $value){
3727 $this->debug("$key -> $value");
3729 $soapAction = $opData['soapAction'];
3730 $this->endpoint = $opData['endpoint'];
3731 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
3732 $style = $opData['style'];
3733 // add ns to ns array
3734 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
3735 $this->wsdl->namespaces['nu'] = $namespace;
3737 // serialize payload
3739 if($opData['input']['use'] == 'literal') {
3740 if (is_null($rpcParams)) {
3741 $rpcParams = $this->defaultRpcParams;
3744 $this->debug("serializing literal params for operation $operation");
3745 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
3746 $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
3748 $this->debug("serializing literal document for operation $operation");
3749 $payload = is_array($params) ? array_shift($params) : $params;
3752 $this->debug("serializing encoded params for operation $operation");
3753 $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation>".
3754 $this->wsdl->serializeRPCParameters($operation,'input',$params).
3755 '</'.$this->wsdl->getPrefixFromNamespace($namespace).":$operation>";
3757 $this->debug('payload size: '.strlen($payload));
3758 // serialize envelope
3759 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style);
3760 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3761 $this->wsdl->debug_str = '';
3762 } elseif($this->endpointType == 'wsdl') {
3763 $this->setError( 'operation '.$operation.' not present.');
3764 $this->debug("operation '$operation' not present.");
3765 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3773 if($namespace == ''){
3774 $namespace = 'http://testuri.org';
3775 $this->wsdl->namespaces['ns1'] = $namespace;
3777 // serialize envelope
3779 foreach($params as $k => $v){
3780 $payload .= $this->serialize_val($v,$k);
3782 $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n".$payload."</ns1:$operation>\n";
3783 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders);
3785 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
3787 $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'...");
3788 $return = $this->send($soapmsg,$soapAction,$this->timeout);
3789 if($errstr = $this->getError()){
3790 $this->debug('Error: '.$errstr);
3793 $this->return = $return;
3794 $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
3797 if(is_array($return) && isset($return['faultcode'])){
3798 $this->debug('got fault');
3799 $this->setError($return['faultcode'].': '.$return['faultstring']);
3800 $this->fault = true;
3801 foreach($return as $k => $v){
3803 $this->debug("$k = $v<br>");
3807 // array of return values
3808 if(is_array($return)){
3809 // multiple 'out' parameters
3810 if(sizeof($return) > 1){
3813 // single 'out' parameter
3814 return array_shift($return);
3815 // nothing returned (ie, echoVoid)
3824 * get available data pertaining to an operation
3826 * @param string $operation operation name
3827 * @return array array of data pertaining to the operation
3830 function getOperationData($operation){
3831 if(isset($this->operations[$operation])){
3832 return $this->operations[$operation];
3834 $this->debug("No data for operation: $operation");
3838 * send the SOAP message
3840 * Note: if the operation has multiple return values
3841 * the return value of this method will be an array
3844 * @param string $msg a SOAPx4 soapmsg object
3845 * @param string $soapaction SOAPAction value
3846 * @param integer $timeout set timeout in seconds
3847 * @return mixed native PHP types.
3850 function send($msg, $soapaction = '', $timeout=0) {
3854 case ereg('^http',$this->endpoint):
3855 $this->debug('transporting via HTTP');
3856 if($this->persistentConnection && is_object($this->persistentConnection)){
3857 $http =& $this->persistentConnection;
3859 $http = new soap_transport_http($this->endpoint);
3860 // pass encoding into transport layer, so appropriate http headers are sent
3861 $http->soap_defencoding = $this->soap_defencoding;
3863 $http->setSOAPAction($soapaction);
3864 if($this->proxyhost && $this->proxyport){
3865 $http->setProxy($this->proxyhost,$this->proxyport);
3867 if($this->username != '' && $this->password != '') {
3868 $http->setCredentials($this->username,$this->password);
3870 if($this->http_encoding != ''){
3871 $http->setEncoding($this->http_encoding);
3873 $this->debug('sending message, length: '.strlen($msg));
3874 if(ereg('^http:',$this->endpoint)){
3875 //if(strpos($this->endpoint,'http:')){
3876 $response = $http->send($msg,$timeout);
3877 } elseif(ereg('^https',$this->endpoint)){
3878 //} elseif(strpos($this->endpoint,'https:')){
3879 //if(phpversion() == '4.3.0-dev'){
3880 //$response = $http->send($msg,$timeout);
3881 //$this->request = $http->outgoing_payload;
3882 //$this->response = $http->incoming_payload;
3884 if (extension_loaded('curl')) {
3885 $response = $http->sendHTTPS($msg,$timeout);
3887 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
3890 $this->setError('no http/s in endpoint url');
3892 $this->request = $http->outgoing_payload;
3893 $this->response = $http->incoming_payload;
3894 $this->debug("transport debug data...\n".$http->debug_str);
3895 // save transport object if using persistent connections
3896 if($this->persistentConnection && !is_object($this->persistentConnection)){
3897 $this->persistentConnection = $http;
3899 if($err = $http->getError()){
3900 $this->setError('HTTP Error: '.$err);
3902 } elseif($this->getError()){
3905 $this->debug('got response, length: '.strlen($response));
3906 return $this->parseResponse($response);
3910 $this->setError('no transport found, or selected transport is not yet supported!');
3917 * processes SOAP message returned from server
3919 * @param string unprocessed response data from server
3920 * @return mixed value of the message, decoded into a PHP type
3923 function parseResponse($data) {
3924 $this->debug('Entering parseResponse(), about to create soap_parser instance');
3925 $parser = new soap_parser($data,$this->xml_encoding,$this->operation);
3927 if($errstr = $parser->getError()){
3928 $this->setError( $errstr);
3929 // destroy the parser object
3934 $this->responseHeaders = $parser->getHeaders();
3935 // get decoded message
3936 $return = $parser->get_response();
3937 // add parser debug data to our debug
3938 $this->debug($parser->debug_str);
3939 // add document for doclit support
3940 $this->document = $parser->document;
3941 // destroy the parser object
3943 // return decode message
3949 * set the SOAP headers
3951 * @param $headers string XML
3954 function setHeaders($headers){
3955 $this->requestHeaders = $headers;
3959 * get the response headers
3961 * @return mixed object SOAPx4 soapval object or empty if no headers
3964 function getHeaders(){
3965 if($this->responseHeaders != '') {
3966 return $this->responseHeaders;
3971 * set proxy info here
3973 * @param string $proxyhost
3974 * @param string $proxyport
3977 function setHTTPProxy($proxyhost, $proxyport) {
3978 $this->proxyhost = $proxyhost;
3979 $this->proxyport = $proxyport;
3983 * if authenticating, set user credentials here
3985 * @param string $username
3986 * @param string $password
3989 function setCredentials($username, $password) {
3990 $this->username = $username;
3991 $this->password = $password;
3997 * @param string $enc
4000 function setHTTPEncoding($enc='gzip, deflate'){
4001 $this->http_encoding = $enc;
4005 * use HTTP persistent connections if possible
4009 function useHTTPPersistentConnection(){
4010 $this->persistentConnection = true;
4014 * gets the default RPC parameter setting.
4015 * If true, default is that call params are like RPC even for document style.
4016 * Each call() can override this value.
4020 function getDefaultRpcParams() {
4021 return $this->defaultRpcParams;
4025 * sets the default RPC parameter setting.
4026 * If true, default is that call params are like RPC even for document style
4027 * Each call() can override this value.
4029 * @param boolean $rpcParams
4032 function setDefaultRpcParams($rpcParams) {
4033 $this->defaultRpcParams = $rpcParams;
4037 * dynamically creates proxy class, allowing user to directly call methods from wsdl
4039 * @return object soap_proxy object
4042 function getProxy(){
4044 foreach($this->operations as $operation => $opData){
4045 if($operation != ''){
4046 // create param string
4048 if(sizeof($opData['input']['parts']) > 0){
4049 foreach($opData['input']['parts'] as $name => $type){
4050 $paramStr .= "\$$name,";
4052 $paramStr = substr($paramStr,0,strlen($paramStr)-1);
4054 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
4055 $evalStr .= "function $operation ($paramStr){
4056 // load params into array
4057 \$params = array($paramStr);
4058 return \$this->call('$operation',\$params,'".$opData['namespace']."','".$opData['soapAction']."');
4064 $evalStr = 'class soap_proxy_'.$r.' extends soapclient {
4067 //print "proxy class:<pre>$evalStr</pre>";
4070 // instantiate proxy object
4071 eval("\$proxy = new soap_proxy_$r('');");
4072 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
4073 $proxy->endpointType = 'wsdl';
4074 $proxy->wsdlFile = $this->wsdlFile;
4075 $proxy->wsdl = $this->wsdl;
4076 $proxy->operations = $this->operations;
4077 $proxy->defaultRpcParams = $this->defaultRpcParams;
4085 // c-basic-offset: 4
4086 // c-hanging-comment-ender-p: nil
4087 // indent-tabs-mode: nil