Support for larger size codes (such as SNOMED US Extension codes)
[openemr.git] / gacl / soap / nusoap.php
blob3b6b74f34706f491fa1d992f1fdf6f032bf31181
1 <?php
3 /*
5 NuSOAP - Web Services Toolkit for PHP
7 Copyright (c) 2002 NuSphere Corporation
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 If you have any questions or comments, please email:
25 Dietrich Ayala
26 dietrich@ganx4.com
27 http://dietrich.ganx4.com/nusoap
29 NuSphere Corporation
30 http://www.nusphere.com
34 /* load classes
36 // necessary classes
37 require_once('class.soapclient.php');
38 require_once('class.soap_val.php');
39 require_once('class.soap_parser.php');
40 require_once('class.soap_fault.php');
42 // transport classes
43 require_once('class.soap_transport_http.php');
45 // optional add-on classes
46 require_once('class.xmlschema.php');
47 require_once('class.wsdl.php');
49 // server class
50 require_once('class.soap_server.php');*/
52 /**
54 * nusoap_base
56 * @author Dietrich Ayala <dietrich@ganx4.com>
57 * @version v 0.6.3
58 * @access public
60 class nusoap_base {
62 var $title = 'NuSOAP';
63 var $version = '0.6.3';
64 var $error_str = false;
65 var $debug_str = '';
66 // toggles automatic encoding of special characters
67 var $charencoding = true;
69 /**
70 * set schema version
72 * @var XMLSchemaVersion
73 * @access public
75 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
77 /**
78 * set default encoding
80 * @var soap_defencoding
81 * @access public
83 //var $soap_defencoding = 'UTF-8';
84 var $soap_defencoding = 'ISO-8859-1';
86 /**
87 * load namespace uris into an array of uri => prefix
89 * @var namespaces
90 * @access public
92 var $namespaces = array(
93 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
94 'xsd' => 'http://www.w3.org/2001/XMLSchema',
95 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
96 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
97 'si' => 'http://soapinterop.org/xsd');
98 /**
99 * load types into typemap array
100 * is this legacy yet?
101 * no, this is used by the xmlschema class to verify type => namespace mappings.
102 * @var typemap
103 * @access public
105 var $typemap = array(
106 'http://www.w3.org/2001/XMLSchema' => array(
107 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
108 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
109 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
110 // derived datatypes
111 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
112 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
113 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
114 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
115 'http://www.w3.org/1999/XMLSchema' => array(
116 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
117 'float'=>'double','dateTime'=>'string',
118 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
119 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
120 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
121 'http://xml.apache.org/xml-soap' => array('Map')
125 * entities to convert
127 * @var xmlEntities
128 * @access public
130 var $xmlEntities = array('quot' => '"','amp' => '&',
131 'lt' => '<','gt' => '>','apos' => "'");
134 * adds debug data to the class level debug string
136 * @param string $string debug data
137 * @access private
139 function debug($string){
140 $this->debug_str .= get_class($this).": $string\n";
144 * returns error string if present
146 * @return boolean $string error string
147 * @access public
149 function getError(){
150 if($this->error_str != ''){
151 return $this->error_str;
153 return false;
157 * sets error string
159 * @return boolean $string error string
160 * @access private
162 function setError($str){
163 $this->error_str = $str;
167 * serializes PHP values in accordance w/ section 5. Type information is
168 * not serialized if $use == 'literal'.
170 * @return string
171 * @access public
173 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
174 if(is_object($val) && get_class($val) == 'soapval'){
175 return $val->serialize($use);
177 $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
178 // if no name, use item
179 $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
180 // if name has ns, add ns prefix to name
181 $xmlns = '';
182 if($name_ns){
183 $prefix = 'nu'.rand(1000,9999);
184 $name = $prefix.':'.$name;
185 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
187 // if type is prefixed, create type prefix
188 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
189 // need to fix this. shouldn't default to xsd if no ns specified
190 // w/o checking against typemap
191 $type_prefix = 'xsd';
192 } elseif($type_ns){
193 $type_prefix = 'ns'.rand(1000,9999);
194 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
196 // serialize attributes if present
197 if($attributes){
198 foreach($attributes as $k => $v){
199 $atts .= " $k=\"$v\"";
202 // serialize if an xsd built-in primitive type
203 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
204 if ($use == 'literal') {
205 return "<$name$xmlns>$val</$name>";
206 } else {
207 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
210 // detect type and serialize
211 $xml = '';
212 $atts = '';
213 switch(true) {
214 case ($type == '' && is_null($val)):
215 if ($use == 'literal') {
216 // TODO: depends on nillable
217 $xml .= "<$name$xmlns/>";
218 } else {
219 $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>";
221 break;
222 case (is_bool($val) || $type == 'boolean'):
223 if(!$val){
224 $val = 0;
226 if ($use == 'literal') {
227 $xml .= "<$name$xmlns $atts>$val</$name>";
228 } else {
229 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
231 break;
232 case (is_int($val) || is_long($val) || $type == 'int'):
233 if ($use == 'literal') {
234 $xml .= "<$name$xmlns $atts>$val</$name>";
235 } else {
236 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
238 break;
239 case (is_float($val)|| is_double($val) || $type == 'float'):
240 if ($use == 'literal') {
241 $xml .= "<$name$xmlns $atts>$val</$name>";
242 } else {
243 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
245 break;
246 case (is_string($val) || $type == 'string'):
247 if($this->charencoding){
248 $val = htmlspecialchars($val, ENT_QUOTES);
250 if ($use == 'literal') {
251 $xml .= "<$name$xmlns $atts>$val</$name>";
252 } else {
253 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
255 break;
256 case is_object($val):
257 $name = get_class($val);
258 foreach(get_object_vars($val) as $k => $v){
259 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
261 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
262 break;
263 break;
264 case (is_array($val) || $type):
265 // detect if struct or array
266 $keyList = array_keys($val);
267 $valueType = 'arraySimple';
268 foreach($keyList as $keyListValue){
269 if(!is_int($keyListValue)){
270 $valueType = 'arrayStruct';
271 break;
274 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
275 $i = 0;
276 if(is_array($val) && count($val)> 0){
277 foreach($val as $v){
278 if(is_object($v) && get_class($v) == 'soapval'){
279 $tt = $v->type;
280 } else {
281 $tt = gettype($v);
283 $array_types[$tt] = 1;
284 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
285 if(is_array($v) && is_numeric(key($v))){
286 $i += sizeof($v);
287 } else {
288 ++$i;
291 if(count($array_types) > 1){
292 $array_typename = 'xsd:ur-type';
293 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
294 $array_typename = 'xsd:'.$tt;
295 } elseif($tt == 'array' || $tt == 'Array'){
296 $array_typename = 'SOAP-ENC:Array';
297 } else {
298 $array_typename = $tt;
300 if(isset($array_types['array'])){
301 $array_type = $i.",".$i;
302 } else {
303 $array_type = $i;
305 if ($use == 'literal') {
306 $xml = "<$name $atts>".$xml."</$name>";
307 } else {
308 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"$atts>".$xml."</$name>";
310 // empty array
311 } else {
312 if ($use == 'literal') {
313 $xml = "<$name $atts>".$xml."</$name>";;
314 } else {
315 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>".$xml."</$name>";;
318 } else {
319 // got a struct
320 if(isset($type) && isset($type_prefix)){
321 $type_str = " xsi:type=\"$type_prefix:$type\"";
322 } else {
323 $type_str = '';
325 if ($use == 'literal') {
326 $xml .= "<$name$xmlns $atts>";
327 } else {
328 $xml .= "<$name$xmlns$type_str$atts>";
330 foreach($val as $k => $v){
331 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
333 $xml .= "</$name>";
335 break;
336 default:
337 $xml .= 'not detected, got '.gettype($val).' for '.$val;
338 break;
340 return $xml;
344 * serialize message
346 * @param string body
347 * @param string headers
348 * @param array namespaces
349 * @param string style
350 * @return string message
351 * @access public
353 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc'){
354 // serialize namespaces
355 $ns_string = '';
356 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
357 $ns_string .= " xmlns:$k=\"$v\"";
359 if($style == 'rpc') {
360 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
363 // serialize headers
364 if($headers){
365 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
367 // serialize envelope
368 return
369 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
370 '<SOAP-ENV:Envelope'.$ns_string.">".
371 $headers.
372 "<SOAP-ENV:Body>".
373 $body.
374 "</SOAP-ENV:Body>".
375 "</SOAP-ENV:Envelope>";
378 function formatDump($str){
379 $str = htmlspecialchars($str);
380 return nl2br($str);
384 * returns the local part of a prefixed string
385 * returns the original string, if not prefixed
387 * @param string
388 * @return string
389 * @access public
391 function getLocalPart($str){
392 if($sstr = strrchr($str,':')){
393 // get unqualified name
394 return substr( $sstr, 1 );
395 } else {
396 return $str;
401 * returns the prefix part of a prefixed string
402 * returns false, if not prefixed
404 * @param string
405 * @return mixed
406 * @access public
408 function getPrefix($str){
409 if($pos = strrpos($str,':')){
410 // get prefix
411 return substr($str,0,$pos);
413 return false;
416 function varDump($data) {
417 ob_start();
418 var_dump($data);
419 $ret_val = ob_get_contents();
420 ob_end_clean();
421 return $ret_val;
425 // XML Schema Datatype Helper Functions
427 //xsd:dateTime helpers
430 * convert unix timestamp to ISO 8601 compliant date string
432 * @param string $timestamp Unix time stamp
433 * @access public
435 function timestamp_to_iso8601($timestamp,$utc=true){
436 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
437 if($utc){
438 $eregStr =
439 '([0-9]{4})-'. // centuries & years CCYY-
440 '([0-9]{2})-'. // months MM-
441 '([0-9]{2})'. // days DD
442 'T'. // separator T
443 '([0-9]{2}):'. // hours hh:
444 '([0-9]{2}):'. // minutes mm:
445 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
446 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
448 if(ereg($eregStr,$datestr,$regs)){
449 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
451 return false;
452 } else {
453 return $datestr;
458 * convert ISO 8601 compliant date string to unix timestamp
460 * @param string $datestr ISO 8601 compliant date string
461 * @access public
463 function iso8601_to_timestamp($datestr){
464 $eregStr =
465 '([0-9]{4})-'. // centuries & years CCYY-
466 '([0-9]{2})-'. // months MM-
467 '([0-9]{2})'. // days DD
468 'T'. // separator T
469 '([0-9]{2}):'. // hours hh:
470 '([0-9]{2}):'. // minutes mm:
471 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
472 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
473 if(ereg($eregStr,$datestr,$regs)){
474 // not utc
475 if($regs[8] != 'Z'){
476 $op = substr($regs[8],0,1);
477 $h = substr($regs[8],1,2);
478 $m = substr($regs[8],strlen($regs[8])-2,2);
479 if($op == '-'){
480 $regs[4] = $regs[4] + $h;
481 $regs[5] = $regs[5] + $m;
482 } elseif($op == '+'){
483 $regs[4] = $regs[4] - $h;
484 $regs[5] = $regs[5] - $m;
487 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
488 } else {
489 return false;
495 ?><?php
500 * soap_fault class, allows for creation of faults
501 * mainly used for returning faults from deployed functions
502 * in a server instance.
503 * @author Dietrich Ayala <dietrich@ganx4.com>
504 * @version v 0.6.3
505 * @access public
507 class soap_fault extends nusoap_base {
509 var $faultcode;
510 var $faultactor;
511 var $faultstring;
512 var $faultdetail;
515 * constructor
517 * @param string $faultcode (client | server)
518 * @param string $faultactor only used when msg routed between multiple actors
519 * @param string $faultstring human readable error message
520 * @param string $faultdetail
522 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
523 $this->faultcode = $faultcode;
524 $this->faultactor = $faultactor;
525 $this->faultstring = $faultstring;
526 $this->faultdetail = $faultdetail;
530 * serialize a fault
532 * @access public
534 function serialize(){
535 $ns_string = '';
536 foreach($this->namespaces as $k => $v){
537 $ns_string .= "\n xmlns:$k=\"$v\"";
539 $return_msg =
540 '<?xml version="1.0"?'.">\n".
541 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
542 '<SOAP-ENV:Body>'.
543 '<SOAP-ENV:Fault>'.
544 '<faultcode>'.$this->faultcode.'</faultcode>'.
545 '<faultactor>'.$this->faultactor.'</faultactor>'.
546 '<faultstring>'.$this->faultstring.'</faultstring>'.
547 '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
548 '</SOAP-ENV:Fault>'.
549 '</SOAP-ENV:Body>'.
550 '</SOAP-ENV:Envelope>';
551 return $return_msg;
557 ?><?php
562 * parses an XML Schema, allows access to it's data, other utility methods
563 * no validation... yet.
564 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
565 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
566 * tutorials I refer to :)
568 * @author Dietrich Ayala <dietrich@ganx4.com>
569 * @version v 0.6.3
570 * @access public
572 class XMLSchema extends nusoap_base {
574 // files
575 var $schema = '';
576 var $xml = '';
577 // define internal arrays of bindings, ports, operations, messages, etc.
578 var $complexTypes = array();
579 // target namespace
580 var $schemaTargetNamespace = '';
581 // parser vars
582 var $parser;
583 var $position;
584 var $depth = 0;
585 var $depth_array = array();
588 * constructor
590 * @param string $schema schema document URI
591 * @param string $xml xml document URI
592 * @access public
594 function XMLSchema($schema='',$xml=''){
596 $this->debug('xmlschema class instantiated, inside constructor');
597 // files
598 $this->schema = $schema;
599 $this->xml = $xml;
601 // parse schema file
602 if($schema != ''){
603 $this->debug('initial schema file: '.$schema);
604 $this->parseFile($schema);
607 // parse xml file
608 if($xml != ''){
609 $this->debug('initial xml file: '.$xml);
610 $this->parseFile($xml);
616 * parse an XML file
618 * @param string $xml, path/URL to XML file
619 * @param string $type, (schema | xml)
620 * @return boolean
621 * @access public
623 function parseFile($xml,$type){
624 // parse xml file
625 if($xml != ""){
626 $this->debug('parsing $xml');
627 $xmlStr = @join("",@file($xml));
628 if($xmlStr == ""){
629 $this->setError('No file at the specified URL: '.$xml);
630 return false;
631 } else {
632 $this->parseString($xmlStr,$type);
633 return true;
636 return false;
640 * parse an XML string
642 * @param string $xml path or URL
643 * @param string $type, (schema|xml)
644 * @access private
646 function parseString($xml,$type){
647 // parse xml string
648 if($xml != ""){
650 // Create an XML parser.
651 $this->parser = xml_parser_create();
652 // Set the options for parsing the XML data.
653 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
655 // Set the object for the parser.
656 xml_set_object($this->parser, $this);
658 // Set the element handlers for the parser.
659 if($type == "schema"){
660 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
661 xml_set_character_data_handler($this->parser,'schemaCharacterData');
662 } elseif($type == "xml"){
663 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
664 xml_set_character_data_handler($this->parser,'xmlCharacterData');
667 // Parse the XML file.
668 if(!xml_parse($this->parser,$xml,true)){
669 // Display an error message.
670 $errstr = sprintf('XML error on line %d: %s',
671 xml_get_current_line_number($this->parser),
672 xml_error_string(xml_get_error_code($this->parser))
674 $this->debug('XML parse error: '.$errstr);
675 $this->setError('Parser error: '.$errstr);
678 xml_parser_free($this->parser);
679 } else{
680 $this->debug('no xml passed to parseString()!!');
681 $this->setError('no xml passed to parseString()!!');
686 * start-element handler
688 * @param string $parser XML parser object
689 * @param string $name element name
690 * @param string $attrs associative array of attributes
691 * @access private
693 function schemaStartElement($parser, $name, $attrs) {
695 // position in the total number of elements, starting from 0
696 $pos = $this->position++;
697 $depth = $this->depth++;
698 // set self as current value for this depth
699 $this->depth_array[$depth] = $pos;
701 // get element prefix
702 if($prefix = $this->getPrefix($name)){
703 // get unqualified name
704 $name = $this->getLocalPart($name);
705 } else {
706 $prefix = '';
709 // loop thru attributes, expanding, and registering namespace declarations
710 if(count($attrs) > 0){
711 foreach($attrs as $k => $v){
712 // if ns declarations, add to class level array of valid namespaces
713 if(ereg("^xmlns",$k)){
714 //$this->xdebug("$k: $v");
715 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
716 if($ns_prefix = substr(strrchr($k,':'),1)){
717 $this->namespaces[$ns_prefix] = $v;
718 } else {
719 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
721 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
722 $this->XMLSchemaVersion = $v;
723 $this->namespaces['xsi'] = $v.'-instance';
727 foreach($attrs as $k => $v){
728 // expand each attribute
729 $k = strpos($k,':') ? $this->expandQname($k) : $k;
730 $v = strpos($v,':') ? $this->expandQname($v) : $v;
731 $eAttrs[$k] = $v;
733 $attrs = $eAttrs;
734 } else {
735 $attrs = array();
737 // find status, register data
738 switch($name){
739 case ('all'|'choice'|'sequence'):
740 //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
741 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
742 if($name == 'all'){
743 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
745 break;
746 case 'attribute':
747 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
748 if(isset($attrs['name'])){
749 $this->attributes[$attrs['name']] = $attrs;
750 $aname = $attrs['name'];
751 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
752 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
753 } elseif(isset($attrs['ref'])){
754 $aname = $attrs['ref'];
755 $this->attributes[$attrs['ref']] = $attrs;
758 if(isset($this->currentComplexType)){
759 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
760 } elseif(isset($this->currentElement)){
761 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
763 // arrayType attribute
764 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
765 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
766 $prefix = $this->getPrefix($aname);
767 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
768 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
769 } else {
770 $v = '';
772 if(strpos($v,'[,]')){
773 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
775 $v = substr($v,0,strpos($v,'[')); // clip the []
776 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
777 $v = $this->XMLSchemaVersion.':'.$v;
779 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
781 break;
782 case 'complexType':
783 if(isset($attrs['name'])){
784 $this->currentElement = false;
785 $this->currentComplexType = $attrs['name'];
786 $this->complexTypes[$this->currentComplexType] = $attrs;
787 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
788 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
789 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
790 } else {
791 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
793 $this->xdebug('processing complexType '.$attrs['name']);
795 break;
796 case 'element':
797 if(isset($attrs['type'])){
798 $this->xdebug("processing element ".$attrs['name']);
799 $this->currentElement = $attrs['name'];
800 $this->elements[ $attrs['name'] ] = $attrs;
801 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
802 $ename = $attrs['name'];
803 } elseif(isset($attrs['ref'])){
804 $ename = $attrs['ref'];
805 } else {
806 $this->xdebug('adding complexType '.$attrs['name']);
807 $this->currentComplexType = $attrs['name'];
808 $this->complexTypes[ $attrs['name'] ] = $attrs;
809 $this->complexTypes[ $attrs['name'] ]['element'] = 1;
810 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
812 if(isset($ename) && $this->currentComplexType){
813 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
815 break;
816 case 'restriction':
817 $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
818 if($this->currentElement){
819 $this->elements[$this->currentElement]['type'] = $attrs['base'];
820 } elseif($this->currentComplexType){
821 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
822 if(strstr($attrs['base'],':') == ':Array'){
823 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
826 break;
827 case 'schema':
828 $this->schema = $attrs;
829 $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
830 break;
831 case 'simpleType':
832 $this->currentElement = $attrs['name'];
833 $this->elements[ $attrs['name'] ] = $attrs;
834 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
835 break;
840 * end-element handler
842 * @param string $parser XML parser object
843 * @param string $name element name
844 * @access private
846 function schemaEndElement($parser, $name) {
847 // position of current element is equal to the last value left in depth_array for my depth
848 if(isset($this->depth_array[$this->depth])){
849 $pos = $this->depth_array[$this->depth];
851 // bring depth down a notch
852 $this->depth--;
853 // move on...
854 if($name == 'complexType'){
855 $this->currentComplexType = false;
856 $this->currentElement = false;
858 if($name == 'element'){
859 $this->currentElement = false;
864 * element content handler
866 * @param string $parser XML parser object
867 * @param string $data element content
868 * @access private
870 function schemaCharacterData($parser, $data){
871 $pos = $this->depth_array[$this->depth];
872 $this->message[$pos]['cdata'] .= $data;
876 * serialize the schema
878 * @access public
880 function serializeSchema(){
882 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
883 $xml = '';
884 // complex types
885 foreach($this->complexTypes as $typeName => $attrs){
886 $contentStr = '';
887 // serialize child elements
888 if(count($attrs['elements']) > 0){
889 foreach($attrs['elements'] as $element => $eParts){
890 if(isset($eParts['ref'])){
891 $contentStr .= "<element ref=\"$element\"/>";
892 } else {
893 $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
897 // attributes
898 if(count($attrs['attrs']) >= 1){
899 foreach($attrs['attrs'] as $attr => $aParts){
900 $contentStr .= '<attribute ref="'.$aParts['ref'].'"';
901 if(isset($aParts['wsdl:arrayType'])){
902 $contentStr .= ' wsdl:arrayType="'.$aParts['wsdl:arrayType'].'"';
904 $contentStr .= '/>';
907 // if restriction
908 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
909 $contentStr = "<$schemaPrefix:restriction base=\"".$attrs['restrictionBase']."\">".$contentStr."</$schemaPrefix:restriction>";
911 // "all" compositor obviates complex/simple content
912 if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){
913 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
915 // complex or simple content
916 elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){
917 $contentStr = "<$schemaPrefix:complexContent>".$contentStr."</$schemaPrefix:complexContent>";
919 // compositors
920 if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){
921 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
923 // finalize complex type
924 if($contentStr != ''){
925 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr."</$schemaPrefix:complexType>";
926 } else {
927 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
929 $xml .= $contentStr;
931 // elements
932 if(isset($this->elements) && count($this->elements) > 0){
933 foreach($this->elements as $element => $eParts){
934 $xml .= "<$schemaPrefix:element name=\"$element\" type=\"".$eParts['type']."\"/>";
937 // attributes
938 if(isset($this->attributes) && count($this->attributes) > 0){
939 foreach($this->attributes as $attr => $aParts){
940 $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"".$aParts['type']."\"/>";
943 // finish 'er up
944 $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml."</$schemaPrefix:schema>";
945 return $xml;
949 * expands a qualified name
951 * @param string $string qname
952 * @return string expanded qname
953 * @access private
955 function expandQname($qname){
956 // get element prefix
957 if(strpos($qname,':') && !ereg('^http://',$qname)){
958 // get unqualified name
959 $name = substr(strstr($qname,':'),1);
960 // get ns prefix
961 $prefix = substr($qname,0,strpos($qname,':'));
962 if(isset($this->namespaces[$prefix])){
963 return $this->namespaces[$prefix].':'.$name;
964 } else {
965 return $qname;
967 } else {
968 return $qname;
973 * adds debug data to the clas level debug string
975 * @param string $string debug data
976 * @access private
978 function xdebug($string){
979 $this->debug(' xmlschema: '.$string);
983 * get the PHP type of a user defined type in the schema
984 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
985 * returns false if no type exists, or not w/ the given namespace
986 * else returns a string that is either a native php type, or 'struct'
988 * @param string $type, name of defined type
989 * @param string $ns, namespace of type
990 * @return mixed
991 * @access public
993 function getPHPType($type,$ns){
994 global $typemap;
995 if(isset($typemap[$ns][$type])){
996 //print "found type '$type' and ns $ns in typemap<br>";
997 return $typemap[$ns][$type];
998 } elseif(isset($this->complexTypes[$type])){
999 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1000 return $this->complexTypes[$type]['phpType'];
1002 return false;
1006 * returns the local part of a prefixed string
1007 * returns the original string, if not prefixed
1009 * @param string
1010 * @return string
1011 * @access public
1013 function getLocalPart($str){
1014 if($sstr = strrchr($str,':')){
1015 // get unqualified name
1016 return substr( $sstr, 1 );
1017 } else {
1018 return $str;
1023 * returns the prefix part of a prefixed string
1024 * returns false, if not prefixed
1026 * @param string
1027 * @return mixed
1028 * @access public
1030 function getPrefix($str){
1031 if($pos = strrpos($str,':')){
1032 // get prefix
1033 return substr($str,0,$pos);
1035 return false;
1039 * pass it a prefix, it returns a namespace
1040 * returns false if no namespace registered with the given prefix
1042 * @param string
1043 * @return mixed
1044 * @access public
1046 function getNamespaceFromPrefix($prefix){
1047 if(isset($this->namespaces[$prefix])){
1048 return $this->namespaces[$prefix];
1050 //$this->setError("No namespace registered for prefix '$prefix'");
1051 return false;
1055 * returns the prefix for a given namespace (or prefix)
1056 * or false if no prefixes registered for the given namespace
1058 * @param string
1059 * @return mixed
1060 * @access public
1062 function getPrefixFromNamespace($ns){
1063 foreach($this->namespaces as $p => $n){
1064 if($ns == $n || $ns == $p){
1065 $this->usedNamespaces[$p] = $n;
1066 return $p;
1069 return false;
1073 * returns an array of information about a given type
1074 * returns false if no type exists by the given name
1076 * typeDef = array(
1077 * 'elements' => array(), // refs to elements array
1078 * 'restrictionBase' => '',
1079 * 'phpType' => '',
1080 * 'order' => '(sequence|all)',
1081 * 'attrs' => array() // refs to attributes array
1084 * @param string
1085 * @return mixed
1086 * @access public
1088 function getTypeDef($type){
1089 if(isset($this->complexTypes[$type])){
1090 return $this->complexTypes[$type];
1091 } elseif(isset($this->elements[$type])){
1092 return $this->elements[$type];
1093 } elseif(isset($this->attributes[$type])){
1094 return $this->attributes[$type];
1096 return false;
1100 * returns a sample serialization of a given type, or false if no type by the given name
1102 * @param string $type, name of type
1103 * @return mixed
1104 * @access public
1106 function serializeTypeDef($type){
1107 //print "in sTD() for type $type<br>";
1108 if($typeDef = $this->getTypeDef($type)){
1109 $str .= '<'.$type;
1110 if(is_array($typeDef['attrs'])){
1111 foreach($attrs as $attName => $data){
1112 $str .= " $attName=\"{type = ".$data['type']."}\"";
1115 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1116 if(count($typeDef['elements']) > 0){
1117 $str .= ">";
1118 foreach($typeDef['elements'] as $element => $eData){
1119 $str .= $this->serializeTypeDef($element);
1121 $str .= "</$type>";
1122 } elseif($typeDef['typeClass'] == 'element') {
1123 $str .= "></$type>";
1124 } else {
1125 $str .= "/>";
1127 return $str;
1129 return false;
1133 * returns HTML form elements that allow a user
1134 * to enter values for creating an instance of the given type.
1136 * @param string $name, name for type instance
1137 * @param string $type, name of type
1138 * @return string
1139 * @access public
1141 function typeToForm($name,$type){
1142 // get typedef
1143 if($typeDef = $this->getTypeDef($type)){
1144 // if struct
1145 if($typeDef['phpType'] == 'struct'){
1146 $buffer .= '<table>';
1147 foreach($typeDef['elements'] as $child => $childDef){
1148 $buffer .= "
1149 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1150 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1152 $buffer .= '</table>';
1153 // if array
1154 } elseif($typeDef['phpType'] == 'array'){
1155 $buffer .= '<table>';
1156 for($i=0;$i < 3; $i++){
1157 $buffer .= "
1158 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1159 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1161 $buffer .= '</table>';
1162 // if scalar
1163 } else {
1164 $buffer .= "<input type='text' name='parameters[$name]'>";
1166 } else {
1167 $buffer .= "<input type='text' name='parameters[$name]'>";
1169 return $buffer;
1173 * adds an XML Schema complex type to the WSDL types
1175 * example: array
1177 * addType(
1178 * 'ArrayOfstring',
1179 * 'complexType',
1180 * 'array',
1181 * '',
1182 * 'SOAP-ENC:Array',
1183 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1184 * 'xsd:string'
1185 * );
1187 * example: PHP associative array ( SOAP Struct )
1189 * addType(
1190 * 'SOAPStruct',
1191 * 'complexType',
1192 * 'struct',
1193 * 'all',
1194 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1195 * );
1197 * @param name
1198 * @param typeClass (complexType|simpleType|attribute)
1199 * @param phpType: currently supported are array and struct (php assoc array)
1200 * @param compositor (all|sequence|choice)
1201 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1202 * @param elements = array ( name = array(name=>'',type=>'') )
1203 * @param attrs = array(
1204 * array(
1205 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1206 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1209 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1212 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1213 $this->complexTypes[$name] = array(
1214 'name' => $name,
1215 'typeClass' => $typeClass,
1216 'phpType' => $phpType,
1217 'compositor'=> $compositor,
1218 'restrictionBase' => $restrictionBase,
1219 'elements' => $elements,
1220 'attrs' => $attrs,
1221 'arrayType' => $arrayType
1228 ?><?php
1233 * for creating serializable abstractions of native PHP types
1234 * NOTE: this is only really used when WSDL is not available.
1236 * @author Dietrich Ayala <dietrich@ganx4.com>
1237 * @version v 0.6.3
1238 * @access public
1240 class soapval extends nusoap_base {
1242 * constructor
1244 * @param string $name optional name
1245 * @param string $type optional type name
1246 * @param mixed $value optional value
1247 * @param string $namespace optional namespace of value
1248 * @param string $type_namespace optional namespace of type
1249 * @param array $attributes associative array of attributes to add to element serialization
1250 * @access public
1252 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1253 $this->name = $name;
1254 $this->value = $value;
1255 $this->type = $type;
1256 $this->element_ns = $element_ns;
1257 $this->type_ns = $type_ns;
1258 $this->attributes = $attributes;
1262 * return serialized value
1264 * @return string XML data
1265 * @access private
1267 function serialize($use='encoded') {
1268 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1272 * decodes a soapval object into a PHP native type
1274 * @param object $soapval optional SOAPx4 soapval object, else uses self
1275 * @return mixed
1276 * @access public
1278 function decode(){
1279 return $this->value;
1285 ?><?php
1290 * transport class for sending/receiving data via HTTP and HTTPS
1291 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1293 * @author Dietrich Ayala <dietrich@ganx4.com>
1294 * @version v 0.6.3
1295 * @access public
1297 class soap_transport_http extends nusoap_base {
1299 var $username = '';
1300 var $password = '';
1301 var $url = '';
1302 var $proxyhost = '';
1303 var $proxyport = '';
1304 var $scheme = '';
1305 var $request_method = 'POST';
1306 var $protocol_version = '1.0';
1307 var $encoding = '';
1308 var $outgoing_headers = array();
1309 var $incoming_headers = array();
1310 var $outgoing_payload = '';
1311 var $incoming_payload = '';
1312 var $useSOAPAction = true;
1315 * constructor
1317 function soap_transport_http($url){
1318 $this->url = $url;
1319 $u = parse_url($url);
1320 foreach($u as $k => $v){
1321 $this->debug("$k = $v");
1322 $this->$k = $v;
1324 if(isset($u['query']) && $u['query'] != ''){
1325 $this->path .= '?' . $u['query'];
1327 if(!isset($u['port']) && $u['scheme'] == 'http'){
1328 $this->port = 80;
1332 function connect($timeout){
1334 // proxy
1335 if($this->proxyhost != '' && $this->proxyport != ''){
1336 $host = $this->proxyhost;
1337 $port = $this->proxyport;
1338 $this->debug("using http proxy: $host, $port");
1339 } else {
1340 $host = $this->host;
1341 $port = $this->port;
1343 // ssl
1344 if($this->scheme == 'https'){
1345 $host = 'ssl://'.$host;
1346 $port = 443;
1349 $this->debug("connection params: $host, $port");
1350 // timeout
1351 if($timeout > 0){
1352 $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
1353 } else {
1354 $fp = fsockopen($host, $port, $this->errno, $this->error_str);
1357 // test pointer
1358 if(!$fp) {
1359 $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error: '.$this->error_str);
1360 $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error: '.$this->error_str);
1361 return false;
1363 return $fp;
1367 * send the SOAP message via HTTP
1369 * @param string $data message data
1370 * @param integer $timeout set timeout in seconds
1371 * @return string data
1372 * @access public
1374 function send($data, $timeout=0) {
1375 $this->debug('entered send() with data of length: '.strlen($data));
1376 // get connnection
1377 if(!$fp = $this->connect($timeout)){
1378 return false;
1380 $this->debug('socket connected');
1382 // start building outgoing payload:
1383 // swap url for path if going through a proxy
1384 if($this->proxyhost != '' && $this->proxyport != ''){
1385 $this->outgoing_payload = "$this->request_method $this->url ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1386 } else {
1387 $this->outgoing_payload = "$this->request_method $this->path ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1389 // make payload
1390 $this->outgoing_payload .=
1391 "User-Agent: $this->title/$this->version\r\n".
1392 "Host: ".$this->host."\r\n";
1393 // http auth
1394 $credentials = '';
1395 if($this->username != '') {
1396 $this->debug('setting http auth credentials');
1397 $this->outgoing_payload .= 'Authorization: Basic '.base64_encode("$this->username:$this->password")."\r\n";
1399 // set content type
1400 $this->outgoing_payload .= 'Content-Type: text/xml; charset='.$this->soap_defencoding."\r\nContent-Length: ".strlen($data)."\r\n";
1401 // http encoding
1402 if($this->encoding != '' && function_exists('gzdeflate')){
1403 $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n".
1404 "Connection: close\r\n";
1405 set_magic_quotes_runtime(0);
1407 // set soapaction
1408 if($this->useSOAPAction){
1409 $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n";
1411 $this->outgoing_payload .= "\r\n";
1412 // add data
1413 $this->outgoing_payload .= $data;
1415 // send payload
1416 if(!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1417 $this->setError('couldn\'t write message data to socket');
1418 $this->debug('Write error');
1420 $this->debug('wrote data to socket');
1422 // get response
1423 $this->incoming_payload = '';
1424 //$strlen = 0;
1425 while( $data = fread($fp, 32768) ){
1426 $this->incoming_payload .= $data;
1427 //$strlen += strlen($data);
1429 $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server');
1431 // close filepointer
1432 fclose($fp);
1433 $this->debug('closed socket');
1435 // connection was closed unexpectedly
1436 if($this->incoming_payload == ''){
1437 $this->setError('no response from server');
1438 return false;
1441 $this->debug('received incoming payload: '.strlen($this->incoming_payload));
1442 $data = $this->incoming_payload."\r\n\r\n\r\n\r\n";
1444 // remove 100 header
1445 if(ereg('^HTTP/1.1 100',$data)){
1446 if($pos = strpos($data,"\r\n\r\n") ){
1447 $data = ltrim(substr($data,$pos));
1448 } elseif($pos = strpos($data,"\n\n") ){
1449 $data = ltrim(substr($data,$pos));
1453 // separate content from HTTP headers
1454 if( $pos = strpos($data,"\r\n\r\n") ){
1455 $lb = "\r\n";
1456 } elseif( $pos = strpos($data,"\n\n") ){
1457 $lb = "\n";
1458 } else {
1459 $this->setError('no proper separation of headers and document');
1460 return false;
1462 $header_data = trim(substr($data,0,$pos));
1463 $header_array = explode($lb,$header_data);
1464 $data = ltrim(substr($data,$pos));
1465 $this->debug('found proper separation of headers and document');
1466 $this->debug('cleaned data, stringlen: '.strlen($data));
1467 // clean headers
1468 foreach($header_array as $header_line){
1469 $arr = explode(':',$header_line);
1470 if(count($arr) >= 2){
1471 $headers[trim($arr[0])] = trim($arr[1]);
1474 //print "headers: <pre>$header_data</pre><br>";
1475 //print "data: <pre>$data</pre><br>";
1477 // decode transfer-encoding
1478 if(isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked'){
1479 //$timer->setMarker('starting to decode chunked content');
1480 if(!$data = $this->decodeChunked($data)){
1481 $this->setError('Decoding of chunked data failed');
1482 return false;
1484 //$timer->setMarker('finished decoding of chunked content');
1485 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1488 // decode content-encoding
1489 if(isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != ''){
1490 if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1491 // if decoding works, use it. else assume data wasn't gzencoded
1492 if(function_exists('gzinflate')){
1493 //$timer->setMarker('starting decoding of gzip/deflated content');
1494 if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1495 $data = $degzdata;
1496 } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1497 $data = $degzdata;
1498 } else {
1499 $this->setError('Errors occurred when trying to decode the data');
1501 //$timer->setMarker('finished decoding of gzip/deflated content');
1502 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
1503 } else {
1504 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1509 if(strlen($data) == 0){
1510 $this->debug('no data after headers!');
1511 $this->setError('no data present after HTTP headers');
1512 return false;
1514 $this->debug('end of send()');
1515 return $data;
1520 * send the SOAP message via HTTPS 1.0 using CURL
1522 * @param string $msg message data
1523 * @param integer $timeout set timeout in seconds
1524 * @return string data
1525 * @access public
1527 function sendHTTPS($data, $timeout=0) {
1528 //global $t;
1529 //$t->setMarker('inside sendHTTPS()');
1530 $this->debug('entered sendHTTPS() with data of length: '.strlen($data));
1531 // init CURL
1532 $ch = curl_init();
1533 //$t->setMarker('got curl handle');
1534 // set proxy
1535 if($this->proxyhost && $this->proxyport){
1536 $host = $this->proxyhost;
1537 $port = $this->proxyport;
1538 } else {
1539 $host = $this->host;
1540 $port = $this->port;
1542 // set url
1543 $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
1544 // add path
1545 $hostURL .= $this->path;
1546 curl_setopt($ch, CURLOPT_URL, $hostURL);
1547 // set other options
1548 curl_setopt($ch, CURLOPT_HEADER, 1);
1549 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1550 // encode
1551 if(function_exists('gzinflate')){
1552 curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
1554 // persistent connection
1555 //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1557 // set timeout
1558 if($timeout != 0){
1559 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1562 $credentials = '';
1563 if($this->username != '') {
1564 $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n';
1567 if($this->encoding != ''){
1568 if(function_exists('gzdeflate')){
1569 $encoding_headers = "Accept-Encoding: $this->encoding\r\n".
1570 "Connection: close\r\n";
1571 set_magic_quotes_runtime(0);
1575 if($this->proxyhost && $this->proxyport){
1576 $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
1577 } else {
1578 $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
1581 $this->outgoing_payload .=
1582 "User-Agent: $this->title v$this->version\r\n".
1583 "Host: ".$this->host."\r\n".
1584 $encoding_headers.
1585 $credentials.
1586 "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n".
1587 "Content-Length: ".strlen($data)."\r\n".
1588 "SOAPAction: \"$this->soapaction\""."\r\n\r\n".
1589 $data;
1591 // set payload
1592 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1593 //$t->setMarker('set curl options, executing...');
1594 // send and receive
1595 $this->incoming_payload = curl_exec($ch);
1596 //$t->setMarker('executed transfer');
1597 $data = $this->incoming_payload;
1599 $cErr = curl_error($ch);
1601 if($cErr != ''){
1602 $err = 'cURL ERROR: '.curl_errno($ch).': '.$cErr.'<br>';
1603 foreach(curl_getinfo($ch) as $k => $v){
1604 $err .= "$k: $v<br>";
1606 $this->setError($err);
1607 curl_close($ch);
1608 return false;
1609 } else {
1610 //echo '<pre>';
1611 //var_dump(curl_getinfo($ch));
1612 //echo '</pre>';
1614 // close curl
1615 curl_close($ch);
1616 //$t->setMarker('closed curl');
1618 // remove 100 header
1619 if(ereg('^HTTP/1.1 100',$data)){
1620 if($pos = strpos($data,"\r\n\r\n") ){
1621 $data = ltrim(substr($data,$pos));
1622 } elseif($pos = strpos($data,"\n\n") ){
1623 $data = ltrim(substr($data,$pos));
1627 // separate content from HTTP headers
1628 if( $pos = strpos($data,"\r\n\r\n") ){
1629 $lb = "\r\n";
1630 } elseif( $pos = strpos($data,"\n\n") ){
1631 $lb = "\n";
1632 } else {
1633 $this->setError('no proper separation of headers and document');
1634 return false;
1636 $header_data = trim(substr($data,0,$pos));
1637 $header_array = explode($lb,$header_data);
1638 $data = ltrim(substr($data,$pos));
1639 $this->debug('found proper separation of headers and document');
1640 $this->debug('cleaned data, stringlen: '.strlen($data));
1641 // clean headers
1642 foreach($header_array as $header_line){
1643 $arr = explode(':',$header_line);
1644 $headers[trim($arr[0])] = trim($arr[1]);
1646 if(strlen($data) == 0){
1647 $this->debug('no data after headers!');
1648 $this->setError('no data present after HTTP headers.');
1649 return false;
1652 // decode transfer-encoding
1653 if($headers['Transfer-Encoding'] == 'chunked'){
1654 if(!$data = $this->decodeChunked($data)){
1655 $this->setError('Decoding of chunked data failed');
1656 return false;
1659 // decode content-encoding
1660 if($headers['Content-Encoding'] != ''){
1661 if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1662 // if decoding works, use it. else assume data wasn't gzencoded
1663 if(function_exists('gzinflate')){
1664 if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1665 $data = $degzdata;
1666 } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1667 $data = $degzdata;
1668 } else {
1669 $this->setError('Errors occurred when trying to decode the data');
1671 } else {
1672 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1676 // set decoded payload
1677 $this->incoming_payload = $header_data."\r\n\r\n".$data;
1678 return $data;
1682 * if authenticating, set user credentials here
1684 * @param string $user
1685 * @param string $pass
1686 * @access public
1688 function setCredentials($username, $password) {
1689 $this->username = $username;
1690 $this->password = $password;
1694 * set the soapaction value
1696 * @param string $soapaction
1697 * @access public
1699 function setSOAPAction($soapaction) {
1700 $this->soapaction = $soapaction;
1704 * use http encoding
1706 * @param string $enc encoding style. supported values: gzip, deflate, or both
1707 * @access public
1709 function setEncoding($enc='gzip, deflate'){
1710 $this->encoding = $enc;
1711 $this->protocol_version = '1.1';
1715 * set proxy info here
1717 * @param string $proxyhost
1718 * @param string $proxyport
1719 * @access public
1721 function setProxy($proxyhost, $proxyport) {
1722 $this->proxyhost = $proxyhost;
1723 $this->proxyport = $proxyport;
1727 * decode a string that is encoded w/ "chunked' transfer encoding
1728 * as defined in RFC2068 19.4.6
1730 * @param string $buffer
1731 * @returns string
1732 * @access public
1734 function decodeChunked($buffer){
1735 // length := 0
1736 $length = 0;
1737 $new = '';
1739 // read chunk-size, chunk-extension (if any) and CRLF
1740 // get the position of the linebreak
1741 $chunkend = strpos($buffer,"\r\n") + 2;
1742 $temp = substr($buffer,0,$chunkend);
1743 $chunk_size = hexdec( trim($temp) );
1744 $chunkstart = $chunkend;
1745 // while (chunk-size > 0) {
1746 while ($chunk_size > 0) {
1748 $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size);
1750 // Just in case we got a broken connection
1751 if ($chunkend == FALSE) {
1752 $chunk = substr($buffer,$chunkstart);
1753 // append chunk-data to entity-body
1754 $new .= $chunk;
1755 $length += strlen($chunk);
1756 break;
1759 // read chunk-data and CRLF
1760 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1761 // append chunk-data to entity-body
1762 $new .= $chunk;
1763 // length := length + chunk-size
1764 $length += strlen($chunk);
1765 // read chunk-size and CRLF
1766 $chunkstart = $chunkend + 2;
1768 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
1769 if ($chunkend == FALSE) {
1770 break; //Just in case we got a broken connection
1772 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1773 $chunk_size = hexdec( trim($temp) );
1774 $chunkstart = $chunkend;
1776 // Update headers
1777 //$this->Header['content-length'] = $length;
1778 //unset($this->Header['transfer-encoding']);
1779 return $new;
1786 ?><?php
1792 * soap_server allows the user to create a SOAP server
1793 * that is capable of receiving messages and returning responses
1795 * NOTE: WSDL functionality is experimental
1797 * @author Dietrich Ayala <dietrich@ganx4.com>
1798 * @version v 0.6.3
1799 * @access public
1801 class soap_server extends nusoap_base {
1803 var $service = ''; // service name
1804 var $operations = array(); // assoc array of operations => opData
1805 var $responseHeaders = false;
1806 var $headers = '';
1807 var $request = '';
1808 var $charset_encoding = 'UTF-8';
1809 var $fault = false;
1810 var $result = 'successful';
1811 var $wsdl = false;
1812 var $externalWSDLURL = false;
1813 var $debug_flag = true;
1816 * constructor
1817 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1819 * @param string $wsdl path or URL to a WSDL file
1820 * @access public
1822 function soap_server($wsdl=false){
1824 // turn on debugging?
1825 global $debug;
1826 if(isset($debug)){
1827 $this->debug_flag = 1;
1830 // wsdl
1831 if($wsdl){
1832 $this->wsdl = new wsdl($wsdl);
1833 $this->externalWSDLURL = $wsdl;
1834 if($err = $this->wsdl->getError()){
1835 die('WSDL ERROR: '.$err);
1841 * processes request and returns response
1843 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
1844 * @access public
1846 function service($data){
1847 // print wsdl
1848 global $QUERY_STRING;
1849 if(isset($_SERVER['QUERY_STRING'])){
1850 $qs = $_SERVER['QUERY_STRING'];
1851 } elseif(isset($GLOBALS['QUERY_STRING'])){
1852 $qs = $GLOBALS['QUERY_STRING'];
1853 } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
1854 $qs = $QUERY_STRING;
1856 // gen wsdl
1857 if(isset($qs) && ereg('wsdl', $qs) ){
1858 if($this->externalWSDLURL){
1859 header('Location: '.$this->externalWSDLURL);
1860 exit();
1861 } else {
1862 header("Content-Type: text/xml\r\n");
1863 print $this->wsdl->serialize();
1864 exit();
1868 // print web interface
1869 if($data == '' && $this->wsdl){
1870 print $this->webDescription();
1871 } else {
1873 // $response is the serialized response message
1874 $response = $this->parse_request($data);
1875 $this->debug('server sending...');
1876 $payload = $response;
1877 // add debug data if in debug mode
1878 if(isset($this->debug_flag) && $this->debug_flag == 1){
1879 $payload .= "<!--\n".str_replace('--','- -',$this->debug_str)."\n-->";
1881 // print headers
1882 if($this->fault){
1883 $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
1884 $header[] = "Status: 500 Internal Server Error\r\n";
1885 } else {
1886 $header[] = "Status: 200 OK\r\n";
1888 $header[] = "Server: $this->title Server v$this->version\r\n";
1889 $header[] = "Connection: Close\r\n";
1890 $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
1891 $header[] = "Content-Length: ".strlen($payload)."\r\n\r\n";
1892 reset($header);
1893 foreach($header as $hdr){
1894 header($hdr);
1896 $this->response = join("\r\n",$header).$payload;
1897 print $payload;
1902 * parses request and posts response
1904 * @param string $data XML string
1905 * @return string XML response msg
1906 * @access private
1908 function parse_request($data='') {
1909 $this->debug('entering parseRequest() on '.date('H:i Y-m-d'));
1910 $dump = '';
1911 // get headers
1912 if(function_exists('getallheaders')){
1913 $this->headers = getallheaders();
1914 foreach($this->headers as $k=>$v){
1915 $dump .= "$k: $v\r\n";
1916 $this->debug("$k: $v");
1918 // get SOAPAction header
1919 if(isset($this->headers['SOAPAction'])){
1920 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
1922 // get the character encoding of the incoming request
1923 if(strpos($this->headers['Content-Type'],'=')){
1924 $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
1925 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
1926 $this->xml_encoding = $enc;
1927 } else {
1928 $this->xml_encoding = 'us-ascii';
1931 $this->debug('got encoding: '.$this->charset_encoding);
1932 } elseif(is_array($_SERVER)){
1933 $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
1934 $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
1936 $this->request = $dump."\r\n\r\n".$data;
1937 // parse response, get soap parser obj
1938 $parser = new soap_parser($data,$this->charset_encoding);
1939 // if fault occurred during message parsing
1940 if($err = $parser->getError()){
1941 // parser debug
1942 $this->debug("parser debug: \n".$parser->debug_str);
1943 $this->result = 'fault: error in msg parsing: '.$err;
1944 $this->fault('Server',"error in msg parsing:\n".$err);
1945 // return soapresp
1946 return $this->fault->serialize();
1947 // else successfully parsed request into soapval object
1948 } else {
1949 // get/set methodname
1950 $this->methodname = $parser->root_struct_name;
1951 $this->debug('method name: '.$this->methodname);
1952 // does method exist?
1953 if(!function_exists($this->methodname)){
1954 // "method not found" fault here
1955 $this->debug("method '$this->methodname' not found!");
1956 $this->debug("parser debug: \n".$parser->debug_str);
1957 $this->result = 'fault: method not found';
1958 $this->fault('Server',"method '$this->methodname' not defined in service '$this->service'");
1959 return $this->fault->serialize();
1961 if($this->wsdl){
1962 if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
1963 //if(
1964 $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
1965 return $this->fault->serialize();
1968 $this->debug("method '$this->methodname' exists");
1969 // evaluate message, getting back parameters
1970 $this->debug('calling parser->get_response()');
1971 $request_data = $parser->get_response();
1972 // parser debug
1973 $this->debug("parser debug: \n".$parser->debug_str);
1974 // verify that request parameters match the method's signature
1975 if($this->verify_method($this->methodname,$request_data)){
1976 // if there are parameters to pass
1977 $this->debug('params var dump '.$this->varDump($request_data));
1978 if($request_data){
1979 $this->debug("calling '$this->methodname' with params");
1980 if (! function_exists('call_user_func_array')) {
1981 $this->debug('calling method using eval()');
1982 $funcCall = $this->methodname.'(';
1983 foreach($request_data as $param) {
1984 $funcCall .= "\"$param\",";
1986 $funcCall = substr($funcCall, 0, -1).')';
1987 $this->debug('function call:<br>'.$funcCall);
1988 @eval("\$method_response = $funcCall;");
1989 } else {
1990 $this->debug('calling method using call_user_func_array()');
1991 $method_response = call_user_func_array("$this->methodname",$request_data);
1993 $this->debug('response var dump'.$this->varDump($method_response));
1994 } else {
1995 // call method w/ no parameters
1996 $this->debug("calling $this->methodname w/ no params");
1997 $m = $this->methodname;
1998 $method_response = @$m();
2000 $this->debug("done calling method: $this->methodname, received $method_response of type".gettype($method_response));
2001 // if we got nothing back. this might be ok (echoVoid)
2002 if(isset($method_response) && $method_response != '' || is_bool($method_response)) {
2003 // if fault
2004 if(get_class($method_response) == 'soap_fault'){
2005 $this->debug('got a fault object from method');
2006 $this->fault = $method_response;
2007 return $method_response->serialize();
2008 // if return val is soapval object
2009 } elseif(get_class($method_response) == 'soapval'){
2010 $this->debug('got a soapval object from method');
2011 $return_val = $method_response->serialize();
2012 // returned other
2013 } else {
2014 $this->debug('got a(n) '.gettype($method_response).' from method');
2015 $this->debug('serializing return value');
2016 if($this->wsdl){
2017 // weak attempt at supporting multiple output params
2018 if(sizeof($this->opData['output']['parts']) > 1){
2019 $opParams = $method_response;
2020 } else {
2021 $opParams = array($method_response);
2023 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2024 } else {
2025 $return_val = $this->serialize_val($method_response);
2028 $this->debug('return val:'.$this->varDump($return_val));
2029 } else {
2030 $return_val = '';
2031 $this->debug('got no response from method');
2033 $this->debug('serializing response');
2034 $payload = '<'.$this->methodname."Response>".$return_val.'</'.$this->methodname."Response>";
2035 $this->result = 'successful';
2036 if($this->wsdl){
2037 //if($this->debug_flag){
2038 $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
2039 // }
2040 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2041 return $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
2042 } else {
2043 return $this->serializeEnvelope($payload,$this->responseHeaders);
2045 } else {
2046 // debug
2047 $this->debug('ERROR: request not verified against method signature');
2048 $this->result = 'fault: request failed validation against method signature';
2049 // return fault
2050 $this->fault('Server',"Operation '$this->methodname' not defined in service.");
2051 return $this->fault->serialize();
2057 * takes the value that was created by parsing the request
2058 * and compares to the method's signature, if available.
2060 * @param mixed
2061 * @return boolean
2062 * @access private
2064 function verify_method($operation,$request){
2065 if(isset($this->wsdl) && is_object($this->wsdl)){
2066 if($this->wsdl->getOperationData($operation)){
2067 return true;
2069 } elseif(isset($this->operations[$operation])){
2070 return true;
2072 return false;
2076 * add a method to the dispatch map
2078 * @param string $methodname
2079 * @param string $in array of input values
2080 * @param string $out array of output values
2081 * @access public
2083 function add_to_map($methodname,$in,$out){
2084 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2088 * register a service with the server
2090 * @param string $methodname
2091 * @param string $in assoc array of input values: key = param name, value = param type
2092 * @param string $out assoc array of output values: key = param name, value = param type
2093 * @param string $namespace
2094 * @param string $soapaction
2095 * @param string $style (rpc|literal)
2096 * @access public
2098 function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false){
2099 if($this->externalWSDLURL){
2100 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2102 if(false == $in) {
2104 if(false == $out) {
2106 if(false == $namespace) {
2108 if(false == $soapaction) {
2109 global $SERVER_NAME, $SCRIPT_NAME;
2110 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2112 if(false == $style) {
2113 $style = "rpc";
2115 if(false == $use) {
2116 $use = "encoded";
2119 $this->operations[$name] = array(
2120 'name' => $name,
2121 'in' => $in,
2122 'out' => $out,
2123 'namespace' => $namespace,
2124 'soapaction' => $soapaction,
2125 'style' => $style);
2126 if($this->wsdl){
2127 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use);
2129 return true;
2133 * create a fault. this also acts as a flag to the server that a fault has occured.
2135 * @param string faultcode
2136 * @param string faultactor
2137 * @param string faultstring
2138 * @param string faultdetail
2139 * @access public
2141 function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){
2142 $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2146 * prints html description of services
2148 * @access private
2150 function webDescription(){
2151 $b = '
2152 <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
2153 <style type="text/css">
2154 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2155 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2156 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2157 ul { margin-top: 10px; margin-left: 20px; }
2158 li { list-style-type: none; margin-top: 10px; color: #000000; }
2159 .content{
2160 margin-left: 0px; padding-bottom: 2em; }
2161 .nav {
2162 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2163 margin-top: 10px; margin-left: 0px; color: #000000;
2164 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2165 .title {
2166 font-family: arial; font-size: 26px; color: #ffffff;
2167 background-color: #999999; width: 105%; margin-left: 0px;
2168 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2169 .hidden {
2170 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2171 font-family: arial; overflow: hidden; width: 600;
2172 padding: 20px; font-size: 10px; background-color: #999999;
2173 layer-background-color:#FFFFFF; }
2174 a,a:active { color: charcoal; font-weight: bold; }
2175 a:visited { color: #666666; font-weight: bold; }
2176 a:hover { color: cc3300; font-weight: bold; }
2177 </style>
2178 <script language="JavaScript" type="text/javascript">
2179 <!--
2180 // POP-UP CAPTIONS...
2181 function lib_bwcheck(){ //Browsercheck (needed)
2182 this.ver=navigator.appVersion
2183 this.agent=navigator.userAgent
2184 this.dom=document.getElementById?1:0
2185 this.opera5=this.agent.indexOf("Opera 5")>-1
2186 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2187 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2188 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2189 this.ie=this.ie4||this.ie5||this.ie6
2190 this.mac=this.agent.indexOf("Mac")>-1
2191 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2192 this.ns4=(document.layers && !this.dom)?1:0;
2193 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2194 return this
2196 var bw = new lib_bwcheck()
2197 //Makes crossbrowser object.
2198 function makeObj(obj){
2199 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2200 if(!this.evnt) return false
2201 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2202 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2203 this.writeIt=b_writeIt;
2204 return this
2206 // A unit of measure that will be added when setting the position of a layer.
2207 //var px = bw.ns4||window.opera?"":"px";
2208 function b_writeIt(text){
2209 if (bw.ns4){this.wref.write(text);this.wref.close()}
2210 else this.wref.innerHTML = text
2212 //Shows the messages
2213 var oDesc;
2214 function popup(divid){
2215 if(oDesc = new makeObj(divid)){
2216 oDesc.css.visibility = "visible"
2219 function popout(){ // Hides message
2220 if(oDesc) oDesc.css.visibility = "hidden"
2222 //-->
2223 </script>
2224 </head>
2225 <body>
2226 <div class=content>
2227 <br><br>
2228 <div class=title>'.$this->wsdl->serviceName.'</div>
2229 <div class=nav>
2230 <p>View the <a href="'.$GLOBALS['PHP_SELF'].'?wsdl">WSDL</a> for the service.
2231 Click on an operation name to view it&apos;s details.</p>
2232 <ul>';
2233 foreach($this->wsdl->getOperations() as $op => $data){
2234 $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2235 // create hidden div
2236 $b .= "<div id='$op' class='hidden'>
2237 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2238 foreach($data as $donnie => $marie){ // loop through opdata
2239 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
2240 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
2241 foreach($marie as $captain => $tenille){ // loop through data
2242 if($captain == 'parts'){ // loop thru parts
2243 $b .= "&nbsp;&nbsp;$captain:<br>";
2244 //if(is_array($tenille)){
2245 foreach($tenille as $joanie => $chachi){
2246 $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
2249 } else {
2250 $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
2253 } else {
2254 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
2257 $b .= '</div>';
2259 $b .= '
2260 <ul>
2261 </div>
2262 </div></body></html>';
2263 return $b;
2267 * sets up wsdl object
2268 * this acts as a flag to enable internal WSDL generation
2269 * NOTE: NOT FUNCTIONAL
2271 * @param string $serviceName, name of the service
2272 * @param string $namespace, tns namespace
2274 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http')
2276 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2277 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2278 if(false == $namespace) {
2279 $namespace = "http://$SERVER_NAME/soap/$serviceName";
2282 if(false == $endpoint) {
2283 $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2286 $this->wsdl = new wsdl;
2287 $this->wsdl->serviceName = $serviceName;
2288 $this->wsdl->endpoint = $endpoint;
2289 $this->wsdl->namespaces['tns'] = $namespace;
2290 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
2291 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
2292 $this->wsdl->bindings[$serviceName.'Binding'] = array(
2293 'name'=>$serviceName.'Binding',
2294 'style'=>$style,
2295 'transport'=>$transport,
2296 'portType'=>$serviceName.'PortType');
2297 $this->wsdl->ports[$serviceName.'Port'] = array(
2298 'binding'=>$serviceName.'Binding',
2299 'location'=>$endpoint,
2300 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
2306 ?><?php
2311 * parses a WSDL file, allows access to it's data, other utility methods
2313 * @author Dietrich Ayala <dietrich@ganx4.com>
2314 * @version v 0.6.3
2315 * @access public
2317 class wsdl extends XMLSchema {
2318 var $wsdl;
2319 // define internal arrays of bindings, ports, operations, messages, etc.
2320 var $message = array();
2321 var $complexTypes = array();
2322 var $messages = array();
2323 var $currentMessage;
2324 var $currentOperation;
2325 var $portTypes = array();
2326 var $currentPortType;
2327 var $bindings = array();
2328 var $currentBinding;
2329 var $ports = array();
2330 var $currentPort;
2331 var $opData = array();
2332 var $status = '';
2333 var $documentation = false;
2334 var $endpoint = '';
2335 // array of wsdl docs to import
2336 var $import = array();
2337 // parser vars
2338 var $parser;
2339 var $position = 0;
2340 var $depth = 0;
2341 var $depth_array = array();
2342 var $usedNamespaces = array();
2343 // for getting wsdl
2344 var $proxyhost = '';
2345 var $proxyport = '';
2348 * constructor
2350 * @param string $wsdl WSDL document URL
2351 * @access public
2353 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){
2354 $this->wsdl = $wsdl;
2355 $this->proxyhost = $proxyhost;
2356 $this->proxyport = $proxyport;
2358 // parse wsdl file
2359 if ($wsdl != "") {
2360 $this->debug('initial wsdl file: ' . $wsdl);
2361 $this->parseWSDL($wsdl);
2363 // imports
2364 if (sizeof($this->import) > 0) {
2365 foreach($this->import as $ns => $url) {
2366 $this->debug('importing wsdl from ' . $url);
2367 $this->parseWSDL($url);
2373 * parses the wsdl document
2375 * @param string $wsdl path or URL
2376 * @access private
2378 function parseWSDL($wsdl = '')
2380 if ($wsdl == '') {
2381 $this->debug('no wsdl passed to parseWSDL()!!');
2382 $this->setError('no wsdl passed to parseWSDL()!!');
2383 return false;
2386 $this->debug('getting ' . $wsdl);
2388 // parse $wsdl for url format
2389 $wsdl_props = parse_url($wsdl);
2391 if (isset($wsdl_props['host'])) {
2393 // get wsdl
2394 $tr = new soap_transport_http($wsdl);
2395 $tr->request_method = 'GET';
2396 $tr->useSOAPAction = false;
2397 if($this->proxyhost && $this->proxyport){
2398 $tr->setProxy($this->proxyhost,$this->proxyport);
2400 if (isset($wsdl_props['user'])) {
2401 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
2403 $wsdl_string = $tr->send('');
2404 // catch errors
2405 if($err = $tr->getError() ){
2406 $this->debug('HTTP ERROR: '.$err);
2407 $this->setError('HTTP ERROR: '.$err);
2408 return false;
2410 unset($tr);
2411 /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
2412 $fsockopen_timeout = 30;
2413 // check if a port value is supplied in url
2414 if (isset($wsdl_props['port'])) {
2415 // yes
2416 $wsdl_url_port = $wsdl_props['port'];
2417 } else {
2418 // no, assign port number, based on url protocol (scheme)
2419 switch ($wsdl_props['scheme']) {
2420 case ('https') :
2421 case ('ssl') :
2422 case ('tls') :
2423 $wsdl_url_port = 443;
2424 break;
2425 case ('http') :
2426 default :
2427 $wsdl_url_port = 80;
2430 // FIXME: should implement SSL/TLS support here if CURL is available
2431 if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
2432 // perform HTTP GET for WSDL file
2433 // 10.9.02 - added poulter fix for doing this properly
2434 $sHeader = "GET " . $wsdl_props['path'];
2435 if (isset($wsdl_props['query'])) {
2436 $sHeader .= "?" . $wsdl_props['query'];
2438 $sHeader .= " HTTP/1.0\r\n";
2440 if (isset($wsdl_props['user'])) {
2441 $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2442 $sHeader .= "Authorization: Basic $base64auth\r\n";
2444 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2445 fputs($fp, $sHeader);
2447 while (fgets($fp, 1024) != "\r\n") {
2448 // do nothing, just read/skip past HTTP headers
2449 // FIXME: should actually detect HTTP response code, and act accordingly if error
2450 // HTTP headers end with extra CRLF before content body
2452 // read in WSDL just like regular fopen()
2453 $wsdl_string = '';
2454 while ($data = fread($fp, 32768)) {
2455 $wsdl_string .= $data;
2457 fclose($fp);
2458 } else {
2459 $this->setError('bad path to WSDL file.');
2460 return false;
2463 } else {
2464 // $wsdl seems to be a non-url file path, do the regular fopen
2465 if ($fp = @fopen($wsdl, 'r')) {
2466 $wsdl_string = '';
2467 while ($data = fread($fp, 32768)) {
2468 $wsdl_string .= $data;
2470 fclose($fp);
2471 } else {
2472 $this->setError('bad path to WSDL file.');
2473 return false;
2476 // end new code added
2477 // Create an XML parser.
2478 $this->parser = xml_parser_create();
2479 // Set the options for parsing the XML data.
2480 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2481 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
2482 // Set the object for the parser.
2483 xml_set_object($this->parser, $this);
2484 // Set the element handlers for the parser.
2485 xml_set_element_handler($this->parser, 'start_element', 'end_element');
2486 xml_set_character_data_handler($this->parser, 'character_data');
2487 // Parse the XML file.
2488 if (!xml_parse($this->parser, $wsdl_string, true)) {
2489 // Display an error message.
2490 $errstr = sprintf(
2491 'XML error on line %d: %s',
2492 xml_get_current_line_number($this->parser),
2493 xml_error_string(xml_get_error_code($this->parser))
2495 $this->debug('XML parse error: ' . $errstr);
2496 $this->setError('Parser error: ' . $errstr);
2497 return false;
2499 // free the parser
2500 xml_parser_free($this->parser);
2501 // catch wsdl parse errors
2502 if($this->getError()){
2503 return false;
2505 // add new data to operation data
2506 foreach($this->bindings as $binding => $bindingData) {
2507 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
2508 foreach($bindingData['operations'] as $operation => $data) {
2509 $this->debug('post-parse data gathering for ' . $operation);
2510 $this->bindings[$binding]['operations'][$operation]['input'] =
2511 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
2512 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
2513 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
2514 $this->bindings[$binding]['operations'][$operation]['output'] =
2515 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
2516 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
2517 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
2518 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
2519 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
2521 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
2522 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
2524 if (isset($bindingData['style'])) {
2525 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2527 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
2528 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
2529 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
2533 return true;
2537 * start-element handler
2539 * @param string $parser XML parser object
2540 * @param string $name element name
2541 * @param string $attrs associative array of attributes
2542 * @access private
2544 function start_element($parser, $name, $attrs)
2546 if ($this->status == 'schema' || ereg('schema$', $name)) {
2547 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
2548 $this->status = 'schema';
2549 $this->schemaStartElement($parser, $name, $attrs);
2550 } else {
2551 // position in the total number of elements, starting from 0
2552 $pos = $this->position++;
2553 $depth = $this->depth++;
2554 // set self as current value for this depth
2555 $this->depth_array[$depth] = $pos;
2556 $this->message[$pos] = array('cdata' => '');
2557 // get element prefix
2558 if (ereg(':', $name)) {
2559 // get ns prefix
2560 $prefix = substr($name, 0, strpos($name, ':'));
2561 // get ns
2562 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
2563 // get unqualified name
2564 $name = substr(strstr($name, ':'), 1);
2567 if (count($attrs) > 0) {
2568 foreach($attrs as $k => $v) {
2569 // if ns declarations, add to class level array of valid namespaces
2570 if (ereg("^xmlns", $k)) {
2571 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
2572 $this->namespaces[$ns_prefix] = $v;
2573 } else {
2574 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
2576 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
2577 $this->XMLSchemaVersion = $v;
2578 $this->namespaces['xsi'] = $v . '-instance';
2580 } //
2581 // expand each attribute
2582 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
2583 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
2584 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
2586 $eAttrs[$k] = $v;
2588 $attrs = $eAttrs;
2589 } else {
2590 $attrs = array();
2592 // find status, register data
2593 switch ($this->status) {
2594 case 'message':
2595 if ($name == 'part') {
2596 if (isset($attrs['type'])) {
2597 $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
2598 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
2600 if (isset($attrs['element'])) {
2601 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2604 break;
2605 case 'portType':
2606 switch ($name) {
2607 case 'operation':
2608 $this->currentPortOperation = $attrs['name'];
2609 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
2610 if (isset($attrs['parameterOrder'])) {
2611 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
2613 break;
2614 case 'documentation':
2615 $this->documentation = true;
2616 break;
2617 // merge input/output data
2618 default:
2619 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
2620 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
2621 break;
2623 break;
2624 case 'binding':
2625 switch ($name) {
2626 case 'binding':
2627 // get ns prefix
2628 if (isset($attrs['style'])) {
2629 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
2631 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
2632 break;
2633 case 'header':
2634 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
2635 break;
2636 case 'operation':
2637 if (isset($attrs['soapAction'])) {
2638 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
2640 if (isset($attrs['style'])) {
2641 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2643 if (isset($attrs['name'])) {
2644 $this->currentOperation = $attrs['name'];
2645 $this->debug("current binding operation: $this->currentOperation");
2646 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
2647 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
2648 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
2650 break;
2651 case 'input':
2652 $this->opStatus = 'input';
2653 break;
2654 case 'output':
2655 $this->opStatus = 'output';
2656 break;
2657 case 'body':
2658 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
2659 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
2660 } else {
2661 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
2663 break;
2665 break;
2666 case 'service':
2667 switch ($name) {
2668 case 'port':
2669 $this->currentPort = $attrs['name'];
2670 $this->debug('current port: ' . $this->currentPort);
2671 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
2673 break;
2674 case 'address':
2675 $this->ports[$this->currentPort]['location'] = $attrs['location'];
2676 $this->ports[$this->currentPort]['bindingType'] = $namespace;
2677 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
2678 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
2679 break;
2681 break;
2683 // set status
2684 switch ($name) {
2685 case "import":
2686 if (isset($attrs['location'])) {
2687 $this->import[$attrs['namespace']] = $attrs['location'];
2689 break;
2690 case 'types':
2691 $this->status = 'schema';
2692 break;
2693 case 'message':
2694 $this->status = 'message';
2695 $this->messages[$attrs['name']] = array();
2696 $this->currentMessage = $attrs['name'];
2697 break;
2698 case 'portType':
2699 $this->status = 'portType';
2700 $this->portTypes[$attrs['name']] = array();
2701 $this->currentPortType = $attrs['name'];
2702 break;
2703 case "binding":
2704 if (isset($attrs['name'])) {
2705 // get binding name
2706 if (strpos($attrs['name'], ':')) {
2707 $this->currentBinding = $this->getLocalPart($attrs['name']);
2708 } else {
2709 $this->currentBinding = $attrs['name'];
2711 $this->status = 'binding';
2712 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
2713 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
2715 break;
2716 case 'service':
2717 $this->serviceName = $attrs['name'];
2718 $this->status = 'service';
2719 $this->debug('current service: ' . $this->serviceName);
2720 break;
2721 case 'definitions':
2722 foreach ($attrs as $name => $value) {
2723 $this->wsdl_info[$name] = $value;
2725 break;
2731 * end-element handler
2733 * @param string $parser XML parser object
2734 * @param string $name element name
2735 * @access private
2737 function end_element($parser, $name){
2738 // unset schema status
2739 if (ereg('types$', $name) || ereg('schema$', $name)) {
2740 $this->status = "";
2742 if ($this->status == 'schema') {
2743 $this->schemaEndElement($parser, $name);
2744 } else {
2745 // bring depth down a notch
2746 $this->depth--;
2748 // end documentation
2749 if ($this->documentation) {
2750 $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2751 $this->documentation = false;
2756 * element content handler
2758 * @param string $parser XML parser object
2759 * @param string $data element content
2760 * @access private
2762 function character_data($parser, $data)
2764 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
2765 if (isset($this->message[$pos]['cdata'])) {
2766 $this->message[$pos]['cdata'] .= $data;
2768 if ($this->documentation) {
2769 $this->documentation .= $data;
2773 function getBindingData($binding)
2775 if (is_array($this->bindings[$binding])) {
2776 return $this->bindings[$binding];
2781 * returns an assoc array of operation names => operation data
2782 * NOTE: currently only supports multiple services of differing binding types
2783 * This method needs some work
2785 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2786 * @return array
2787 * @access public
2789 function getOperations($bindingType = 'soap')
2791 if ($bindingType == 'soap') {
2792 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2794 // loop thru ports
2795 foreach($this->ports as $port => $portData) {
2796 // binding type of port matches parameter
2797 if ($portData['bindingType'] == $bindingType) {
2798 // get binding
2799 return $this->bindings[ $portData['binding'] ]['operations'];
2802 return array();
2806 * returns an associative array of data necessary for calling an operation
2808 * @param string $operation , name of operation
2809 * @param string $bindingType , type of binding eg: soap
2810 * @return array
2811 * @access public
2813 function getOperationData($operation, $bindingType = 'soap')
2815 if ($bindingType == 'soap') {
2816 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2818 // loop thru ports
2819 foreach($this->ports as $port => $portData) {
2820 // binding type of port matches parameter
2821 if ($portData['bindingType'] == $bindingType) {
2822 // get binding
2823 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
2824 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
2825 if ($operation == $bOperation) {
2826 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
2827 return $opData;
2835 * serialize the parsed wsdl
2837 * @return string , serialization of WSDL
2838 * @access public
2840 function serialize()
2842 $xml = '<?xml version="1.0"?><definitions';
2843 foreach($this->namespaces as $k => $v) {
2844 $xml .= " xmlns:$k=\"$v\"";
2846 // 10.9.02 - add poulter fix for wsdl and tns declarations
2847 if (isset($this->namespaces['wsdl'])) {
2848 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2850 if (isset($this->namespaces['tns'])) {
2851 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2853 $xml .= '>';
2854 // imports
2855 if (sizeof($this->import) > 0) {
2856 foreach($this->import as $ns => $url) {
2857 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2860 // types
2861 if (count($this->complexTypes)>=1) {
2862 $xml .= '<types>';
2863 $xml .= $this->serializeSchema();
2864 $xml .= '</types>';
2866 // messages
2867 if (count($this->messages) >= 1) {
2868 foreach($this->messages as $msgName => $msgParts) {
2869 $xml .= '<message name="' . $msgName . '">';
2870 foreach($msgParts as $partName => $partType) {
2871 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
2872 if (strpos($partType, ':')) {
2873 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
2874 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
2875 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
2876 $typePrefix = 'xsd';
2877 } else {
2878 foreach($this->typemap as $ns => $types) {
2879 if (isset($types[$partType])) {
2880 $typePrefix = $this->getPrefixFromNamespace($ns);
2883 if (!isset($typePrefix)) {
2884 die("$partType has no namespace!");
2887 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2889 $xml .= '</message>';
2892 // bindings & porttypes
2893 if (count($this->bindings) >= 1) {
2894 $binding_xml = '';
2895 $portType_xml = '';
2896 foreach($this->bindings as $bindingName => $attrs) {
2897 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
2898 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
2899 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
2900 foreach($attrs['operations'] as $opName => $opParts) {
2901 $binding_xml .= '<operation name="' . $opName . '">';
2902 $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
2903 $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
2904 $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
2905 $binding_xml .= '</operation>';
2906 $portType_xml .= '<operation name="' . $opParts['name'] . '"';
2907 if (isset($opParts['parameterOrder'])) {
2908 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
2910 $portType_xml .= '>';
2911 $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
2912 $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
2913 $portType_xml .= '</operation>';
2915 $portType_xml .= '</portType>';
2916 $binding_xml .= '</binding>';
2918 $xml .= $portType_xml . $binding_xml;
2920 // services
2921 $xml .= '<service name="' . $this->serviceName . '">';
2922 if (count($this->ports) >= 1) {
2923 foreach($this->ports as $pName => $attrs) {
2924 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
2925 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
2926 $xml .= '</port>';
2929 $xml .= '</service>';
2930 return $xml . '</definitions>';
2934 * serialize a PHP value according to a WSDL message definition
2936 * TODO
2937 * - multi-ref serialization
2938 * - validate PHP values against type definitions, return errors if invalid
2940 * @param string $ type name
2941 * @param mixed $ param value
2942 * @return mixed new param or false if initial value didn't validate
2944 function serializeRPCParameters($operation, $direction, $parameters)
2946 $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
2948 if ($direction != 'input' && $direction != 'output') {
2949 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
2950 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
2951 return false;
2953 if (!$opData = $this->getOperationData($operation)) {
2954 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
2955 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
2956 return false;
2958 $this->debug($this->varDump($opData));
2959 // set input params
2960 $xml = '';
2961 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
2963 $use = $opData[$direction]['use'];
2964 $this->debug("use=$use");
2965 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
2966 foreach($opData[$direction]['parts'] as $name => $type) {
2967 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
2968 // NOTE: add error handling here
2969 // if serializeType returns false, then catch global error and fault
2970 if (isset($parameters[$name])) {
2971 $this->debug('calling serializeType w/ named param');
2972 $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
2973 } elseif(is_array($parameters)) {
2974 $this->debug('calling serializeType w/ unnamed param');
2975 $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
2976 } else {
2977 $this->debug('no parameters passed.');
2981 return $xml;
2985 * serializes a PHP value according a given type definition
2987 * @param string $name , name of type (part)
2988 * @param string $type , type of type, heh (type or element)
2989 * @param mixed $value , a native PHP value (parameter value)
2990 * @param string $use , use for part (encoded|literal)
2991 * @return string serialization
2992 * @access public
2994 function serializeType($name, $type, $value, $use='encoded')
2996 $this->debug("in serializeType: $name, $type, $value, $use");
2997 $xml = '';
2998 if (strpos($type, ':')) {
2999 $uqType = substr($type, strrpos($type, ':') + 1);
3000 $ns = substr($type, 0, strrpos($type, ':'));
3001 $this->debug("got a prefixed type: $uqType, $ns");
3003 if($ns == $this->XMLSchemaVersion ||
3004 ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){
3006 if ($uqType == 'boolean' && !$value) {
3007 $value = 0;
3008 } elseif ($uqType == 'boolean') {
3009 $value = 1;
3011 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
3012 $value = htmlspecialchars($value);
3014 // it's a scalar
3015 if ($use == 'literal') {
3016 return "<$name>$value</$name>";
3017 } else {
3018 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3021 } else {
3022 $uqType = $type;
3024 if(!$typeDef = $this->getTypeDef($uqType)){
3025 $this->setError("$uqType is not a supported type.");
3026 return false;
3027 } else {
3028 //foreach($typeDef as $k => $v) {
3029 //$this->debug("typedef, $k: $v");
3032 $phpType = $typeDef['phpType'];
3033 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
3034 // if php type == struct, map value to the <all> element names
3035 if ($phpType == 'struct') {
3036 if (isset($typeDef['element']) && $typeDef['element']) {
3037 $elementName = $uqType;
3038 // TODO: use elementFormDefault="qualified|unqualified" to determine
3039 // how to scope the namespace
3040 $elementNS = " xmlns=\"$ns\"";
3041 } else {
3042 $elementName = $name;
3043 $elementNS = '';
3045 if ($use == 'literal') {
3046 $xml = "<$elementName$elementNS>";
3047 } else {
3048 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3051 if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3053 //if (is_array($this->complexTypes[$uqType]['elements'])) {
3054 // toggle whether all elements are present - ideally should validate against schema
3055 if(count($this->complexTypes[$uqType]['elements']) != count($value)){
3056 $optionals = true;
3058 foreach($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
3059 // if user took advantage of a minOccurs=0, then only serialize named parameters
3060 if(isset($optionals) && !isset($value[$eName])){
3061 // do nothing
3062 } else {
3063 // get value
3064 if (isset($value[$eName])) {
3065 $v = $value[$eName];
3066 } elseif (is_array($value)) {
3067 $v = array_shift($value);
3069 // serialize schema-defined type
3070 if (!isset($attrs['type'])) {
3071 $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
3072 // serialize generic type
3073 } else {
3074 $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
3075 $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
3080 $xml .= "</$elementName>";
3081 } elseif ($phpType == 'array') {
3082 $rows = sizeof($value);
3083 if (isset($typeDef['multidimensional'])) {
3084 $nv = array();
3085 foreach($value as $v) {
3086 $cols = ',' . sizeof($v);
3087 $nv = array_merge($nv, $v);
3089 $value = $nv;
3090 } else {
3091 $cols = '';
3093 if (is_array($value) && sizeof($value) >= 1) {
3094 $contents = '';
3095 foreach($value as $k => $v) {
3096 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
3097 //if (strpos($typeDef['arrayType'], ':') ) {
3098 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
3099 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
3100 } else {
3101 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
3104 $this->debug('contents: '.$this->varDump($contents));
3105 } else {
3106 $contents = null;
3108 if ($use == 'literal') {
3109 $xml = "<$name>"
3110 .$contents
3111 ."</$name>";
3112 } else {
3113 $xml = "<$name xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
3114 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
3115 .':arrayType="'
3116 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
3117 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
3118 .$contents
3119 ."</$name>";
3122 $this->debug('returning: '.$this->varDump($xml));
3123 return $xml;
3127 * register a service with the server
3129 * @param string $methodname
3130 * @param string $in assoc array of input values: key = param name, value = param type
3131 * @param string $out assoc array of output values: key = param name, value = param type
3132 * @param string $namespace
3133 * @param string $soapaction
3134 * @param string $style (rpc|literal)
3135 * @access public
3137 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3139 if ($style == 'rpc' && $use == 'encoded') {
3140 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3141 } else {
3142 $encodingStyle = '';
3144 // get binding
3145 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
3146 array(
3147 'name' => $name,
3148 'binding' => $this->serviceName . 'Binding',
3149 'endpoint' => $this->endpoint,
3150 'soapAction' => $soapaction,
3151 'style' => $style,
3152 'input' => array(
3153 'use' => $use,
3154 'namespace' => $namespace,
3155 'encodingStyle' => $encodingStyle,
3156 'message' => $name . 'Request',
3157 'parts' => $in),
3158 'output' => array(
3159 'use' => $use,
3160 'namespace' => $namespace,
3161 'encodingStyle' => $encodingStyle,
3162 'message' => $name . 'Response',
3163 'parts' => $out),
3164 'namespace' => $namespace,
3165 'transport' => 'http://schemas.xmlsoap.org/soap/http',
3166 'documentation' => $documentation);
3167 // add portTypes
3168 // add messages
3169 if($in)
3171 foreach($in as $pName => $pType)
3173 if(strpos($pType,':')) {
3174 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3176 $this->messages[$name.'Request'][$pName] = $pType;
3180 if($out)
3182 foreach($out as $pName => $pType)
3184 if(strpos($pType,':')) {
3185 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3187 $this->messages[$name.'Response'][$pName] = $pType;
3190 return true;
3196 ?><?php
3202 * soap_parser class parses SOAP XML messages into native PHP values
3204 * @author Dietrich Ayala <dietrich@ganx4.com>
3205 * @version v 0.6.3
3206 * @access public
3208 class soap_parser extends nusoap_base {
3210 var $xml = '';
3211 var $xml_encoding = '';
3212 var $method = '';
3213 var $root_struct = '';
3214 var $root_struct_name = '';
3215 var $root_header = '';
3216 var $document = '';
3217 // determines where in the message we are (envelope,header,body,method)
3218 var $status = '';
3219 var $position = 0;
3220 var $depth = 0;
3221 var $default_namespace = '';
3222 var $namespaces = array();
3223 var $message = array();
3224 var $parent = '';
3225 var $fault = false;
3226 var $fault_code = '';
3227 var $fault_str = '';
3228 var $fault_detail = '';
3229 var $depth_array = array();
3230 var $debug_flag = true;
3231 var $soapresponse = NULL;
3232 var $responseHeaders = '';
3233 var $body_position = 0;
3234 // for multiref parsing:
3235 // array of id => pos
3236 var $ids = array();
3237 // array of id => hrefs => pos
3238 var $multirefs = array();
3241 * constructor
3243 * @param string $xml SOAP message
3244 * @param string $encoding character encoding scheme of message
3245 * @access public
3247 function soap_parser($xml,$encoding='UTF-8',$method=''){
3248 $this->xml = $xml;
3249 $this->xml_encoding = $encoding;
3250 $this->method = $method;
3252 // Check whether content has been read.
3253 if(!empty($xml)){
3254 $this->debug('Entering soap_parser()');
3255 // Create an XML parser.
3256 $this->parser = xml_parser_create($this->xml_encoding);
3257 // Set the options for parsing the XML data.
3258 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3259 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3260 // Set the object for the parser.
3261 xml_set_object($this->parser, $this);
3262 // Set the element handlers for the parser.
3263 xml_set_element_handler($this->parser, 'start_element','end_element');
3264 xml_set_character_data_handler($this->parser,'character_data');
3266 // Parse the XML file.
3267 if(!xml_parse($this->parser,$xml,true)){
3268 // Display an error message.
3269 $err = sprintf('XML error on line %d: %s',
3270 xml_get_current_line_number($this->parser),
3271 xml_error_string(xml_get_error_code($this->parser)));
3272 $this->debug('parse error: '.$err);
3273 $this->errstr = $err;
3274 } else {
3275 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
3276 // get final value
3277 $this->soapresponse = $this->message[$this->root_struct]['result'];
3278 // get header value
3279 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
3280 $this->responseHeaders = $this->message[$this->root_header]['result'];
3282 // resolve hrefs/ids
3283 if(sizeof($this->multirefs) > 0){
3284 foreach($this->multirefs as $id => $hrefs){
3285 $this->debug('resolving multirefs for id: '.$id);
3286 $idVal = $this->buildVal($this->ids[$id]);
3287 foreach($hrefs as $refPos => $ref){
3288 $this->debug('resolving href at pos '.$refPos);
3289 $this->multirefs[$id][$refPos] = $idVal;
3294 xml_parser_free($this->parser);
3295 } else {
3296 $this->debug('xml was empty, didn\'t parse!');
3297 $this->errstr = 'xml was empty, didn\'t parse!';
3302 * start-element handler
3304 * @param string $parser XML parser object
3305 * @param string $name element name
3306 * @param string $attrs associative array of attributes
3307 * @access private
3309 function start_element($parser, $name, $attrs) {
3310 // position in a total number of elements, starting from 0
3311 // update class level pos
3312 $pos = $this->position++;
3313 // and set mine
3314 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
3315 // depth = how many levels removed from root?
3316 // set mine as current global depth and increment global depth value
3317 $this->message[$pos]['depth'] = $this->depth++;
3319 // else add self as child to whoever the current parent is
3320 if($pos != 0){
3321 $this->message[$this->parent]['children'] .= '|'.$pos;
3323 // set my parent
3324 $this->message[$pos]['parent'] = $this->parent;
3325 // set self as current parent
3326 $this->parent = $pos;
3327 // set self as current value for this depth
3328 $this->depth_array[$this->depth] = $pos;
3329 // get element prefix
3330 if(strpos($name,':')){
3331 // get ns prefix
3332 $prefix = substr($name,0,strpos($name,':'));
3333 // get unqualified name
3334 $name = substr(strstr($name,':'),1);
3336 // set status
3337 if($name == 'Envelope'){
3338 $this->status = 'envelope';
3339 } elseif($name == 'Header'){
3340 $this->root_header = $pos;
3341 $this->status = 'header';
3342 } elseif($name == 'Body'){
3343 $this->status = 'body';
3344 $this->body_position = $pos;
3345 // set method
3346 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
3347 $this->status = 'method';
3348 $this->root_struct_name = $name;
3349 $this->root_struct = $pos;
3350 $this->message[$pos]['type'] = 'struct';
3351 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
3353 // set my status
3354 $this->message[$pos]['status'] = $this->status;
3355 // set name
3356 $this->message[$pos]['name'] = htmlspecialchars($name);
3357 // set attrs
3358 $this->message[$pos]['attrs'] = $attrs;
3360 // loop through atts, logging ns and type declarations
3361 $attstr = '';
3362 foreach($attrs as $key => $value){
3363 $key_prefix = $this->getPrefix($key);
3364 $key_localpart = $this->getLocalPart($key);
3365 // if ns declarations, add to class level array of valid namespaces
3366 if($key_prefix == 'xmlns'){
3367 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
3368 $this->XMLSchemaVersion = $value;
3369 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
3370 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
3372 $this->namespaces[$key_localpart] = $value;
3373 // set method namespace
3374 if($name == $this->root_struct_name){
3375 $this->methodNamespace = $value;
3377 // if it's a type declaration, set type
3378 } elseif($key_localpart == 'type'){
3379 $value_prefix = $this->getPrefix($value);
3380 $value_localpart = $this->getLocalPart($value);
3381 $this->message[$pos]['type'] = $value_localpart;
3382 $this->message[$pos]['typePrefix'] = $value_prefix;
3383 if(isset($this->namespaces[$value_prefix])){
3384 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
3385 } else if(isset($attrs['xmlns:'.$value_prefix])) {
3386 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
3388 // should do something here with the namespace of specified type?
3389 } elseif($key_localpart == 'arrayType'){
3390 $this->message[$pos]['type'] = 'array';
3391 /* do arrayType ereg here
3392 [1] arrayTypeValue ::= atype asize
3393 [2] atype ::= QName rank*
3394 [3] rank ::= '[' (',')* ']'
3395 [4] asize ::= '[' length~ ']'
3396 [5] length ::= nextDimension* Digit+
3397 [6] nextDimension ::= Digit+ ','
3399 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
3400 if(ereg($expr,$value,$regs)){
3401 $this->message[$pos]['typePrefix'] = $regs[1];
3402 $this->message[$pos]['arraySize'] = $regs[3];
3403 $this->message[$pos]['arrayCols'] = $regs[4];
3406 // log id
3407 if($key == 'id'){
3408 $this->ids[$value] = $pos;
3410 // root
3411 if($key_localpart == 'root' && $value == 1){
3412 $this->status = 'method';
3413 $this->root_struct_name = $name;
3414 $this->root_struct = $pos;
3415 $this->debug("found root struct $this->root_struct_name, pos $pos");
3417 // for doclit
3418 $attstr .= " $key=\"$value\"";
3420 // get namespace - must be done after namespace atts are processed
3421 if(isset($prefix)){
3422 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
3423 $this->default_namespace = $this->namespaces[$prefix];
3424 } else {
3425 $this->message[$pos]['namespace'] = $this->default_namespace;
3427 if($this->status == 'header'){
3428 $this->responseHeaders .= "<$name$attstr>";
3429 } elseif($this->root_struct_name != ''){
3430 $this->document .= "<$name$attstr>";
3435 * end-element handler
3437 * @param string $parser XML parser object
3438 * @param string $name element name
3439 * @access private
3441 function end_element($parser, $name) {
3442 // position of current element is equal to the last value left in depth_array for my depth
3443 $pos = $this->depth_array[$this->depth--];
3445 // get element prefix
3446 if(strpos($name,':')){
3447 // get ns prefix
3448 $prefix = substr($name,0,strpos($name,':'));
3449 // get unqualified name
3450 $name = substr(strstr($name,':'),1);
3453 // build to native type
3454 if(isset($this->body_position) && $pos > $this->body_position){
3455 // deal w/ multirefs
3456 if(isset($this->message[$pos]['attrs']['href'])){
3457 // get id
3458 $id = substr($this->message[$pos]['attrs']['href'],1);
3459 // add placeholder to href array
3460 $this->multirefs[$id][$pos] = "placeholder";
3461 // add set a reference to it as the result value
3462 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
3463 // build complex values
3464 } elseif($this->message[$pos]['children'] != ""){
3465 $this->message[$pos]['result'] = $this->buildVal($pos);
3466 } else {
3467 $this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
3468 if(is_numeric($this->message[$pos]['cdata']) ){
3469 if( strpos($this->message[$pos]['cdata'],'.') ){
3470 $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
3471 } else {
3472 $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
3474 } else {
3475 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3480 // switch status
3481 if($pos == $this->root_struct){
3482 $this->status = 'body';
3483 } elseif($name == 'Body'){
3484 $this->status = 'header';
3485 } elseif($name == 'Header'){
3486 $this->status = 'envelope';
3487 } elseif($name == 'Envelope'){
3490 // set parent back to my parent
3491 $this->parent = $this->message[$pos]['parent'];
3492 // for doclit
3493 if($this->status == 'header'){
3494 $this->responseHeaders .= "</$name>";
3495 } elseif($pos >= $this->root_struct){
3496 $this->document .= "</$name>";
3501 * element content handler
3503 * @param string $parser XML parser object
3504 * @param string $data element content
3505 * @access private
3507 function character_data($parser, $data){
3508 $pos = $this->depth_array[$this->depth];
3509 if ($this->xml_encoding=='UTF-8'){
3510 $data = utf8_decode($data);
3512 $this->message[$pos]['cdata'] .= $data;
3513 // for doclit
3514 if($this->status == 'header'){
3515 $this->responseHeaders .= $data;
3516 } else {
3517 $this->document .= $data;
3522 * get the parsed message
3524 * @return mixed
3525 * @access public
3527 function get_response(){
3528 return $this->soapresponse;
3532 * get the parsed headers
3534 * @return string XML or empty if no headers
3535 * @access public
3537 function getHeaders(){
3538 return $this->responseHeaders;
3542 * decodes entities
3544 * @param string $text string to translate
3545 * @access private
3547 function decode_entities($text){
3548 foreach($this->entities as $entity => $encoded){
3549 $text = str_replace($encoded,$entity,$text);
3551 return $text;
3555 * builds response structures for compound values (arrays/structs)
3557 * @param string $pos position in node tree
3558 * @access private
3560 function buildVal($pos){
3561 if(!isset($this->message[$pos]['type'])){
3562 $this->message[$pos]['type'] = '';
3564 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
3565 // if there are children...
3566 if($this->message[$pos]['children'] != ''){
3567 $children = explode('|',$this->message[$pos]['children']);
3568 array_shift($children); // knock off empty
3569 // md array
3570 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
3571 $r=0; // rowcount
3572 $c=0; // colcount
3573 foreach($children as $child_pos){
3574 $this->debug("got an MD array element: $r, $c");
3575 $params[$r][] = $this->message[$child_pos]['result'];
3576 $c++;
3577 if($c == $this->message[$pos]['arrayCols']){
3578 $c = 0;
3579 $r++;
3582 // array
3583 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
3584 $this->debug('adding array '.$this->message[$pos]['name']);
3585 foreach($children as $child_pos){
3586 $params[] = &$this->message[$child_pos]['result'];
3588 // apache Map type: java hashtable
3589 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
3590 foreach($children as $child_pos){
3591 $kv = explode("|",$this->message[$child_pos]['children']);
3592 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
3594 // generic compound type
3595 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
3596 } else {
3597 // is array or struct? better way to do this probably
3598 foreach($children as $child_pos){
3599 if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
3600 $struct = 1;
3601 break;
3603 $keys[$this->message[$child_pos]['name']] = 1;
3606 foreach($children as $child_pos){
3607 if(isset($struct)){
3608 $params[] = &$this->message[$child_pos]['result'];
3609 } else {
3610 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
3614 return is_array($params) ? $params : array();
3615 } else {
3616 $this->debug('no children');
3617 if(strpos($this->message[$pos]['cdata'],'&')){
3618 return strtr($this->message[$pos]['cdata'],array_flip($this->entities));
3619 } else {
3620 return $this->message[$pos]['cdata'];
3628 ?><?php
3634 * soapclient higher level class for easy usage.
3636 * usage:
3638 * // instantiate client with server info
3639 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
3641 * // call method, get results
3642 * echo $soapclient->call( string methodname [ ,array parameters] );
3644 * // bye bye client
3645 * unset($soapclient);
3647 * @author Dietrich Ayala <dietrich@ganx4.com>
3648 * @version v 0.6.3
3649 * @access public
3651 class nusoap_client extends nusoap_base {
3653 var $username = '';
3654 var $password = '';
3655 var $requestHeaders = false;
3656 var $responseHeaders;
3657 var $endpoint;
3658 var $error_str = false;
3659 var $proxyhost = '';
3660 var $proxyport = '';
3661 var $xml_encoding = '';
3662 var $http_encoding = false;
3663 var $timeout = 0;
3664 var $endpointType = '';
3665 var $persistentConnection = false;
3666 var $defaultRpcParams = false;
3669 * fault related variables
3671 * @var fault
3672 * @var faultcode
3673 * @var faultstring
3674 * @var faultdetail
3675 * @access public
3677 var $fault, $faultcode, $faultstring, $faultdetail;
3680 * constructor
3682 * @param string $endpoint SOAP server or WSDL URL
3683 * @param bool $wsdl optional, set to true if using WSDL
3684 * @param int $portName optional portName in WSDL document
3685 * @access public
3687 function nusoap_client($endpoint,$wsdl = false) {
3688 $this->endpoint = $endpoint;
3690 // make values
3691 if($wsdl){
3692 $this->endpointType = 'wsdl';
3693 $this->wsdlFile = $this->endpoint;
3695 // instantiate wsdl object and parse wsdl file
3696 $this->debug('instantiating wsdl class with doc: '.$endpoint);
3697 $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport);
3698 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3699 $this->wsdl->debug_str = '';
3700 // catch errors
3701 if($errstr = $this->wsdl->getError()){
3702 $this->debug('got wsdl error: '.$errstr);
3703 $this->setError('wsdl error: '.$errstr);
3704 } elseif($this->operations = $this->wsdl->getOperations()){
3705 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
3706 } else {
3707 $this->debug( 'getOperations returned false');
3708 $this->setError('no operations defined in the WSDL document!');
3714 * calls method, returns PHP native type
3716 * @param string $method SOAP server URL or path
3717 * @param array $params array of parameters, can be associative or not
3718 * @param string $namespace optional method namespace
3719 * @param string $soapAction optional SOAPAction value
3720 * @param boolean $headers optional array of soapval objects for headers
3721 * @param boolean $rpcParams optional treat params as RPC for use="literal"
3722 * This can be used on a per-call basis to overrider defaultRpcParams.
3723 * @return mixed
3724 * @access public
3726 function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null){
3727 $this->operation = $operation;
3728 $this->fault = false;
3729 $this->error_str = '';
3730 $this->request = '';
3731 $this->response = '';
3732 $this->faultstring = '';
3733 $this->faultcode = '';
3734 $this->opData = array();
3736 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
3737 $this->debug("endpointType: $this->endpointType");
3738 // if wsdl, get operation data and process parameters
3739 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
3741 $this->opData = $opData;
3742 foreach($opData as $key => $value){
3743 $this->debug("$key -> $value");
3745 $soapAction = $opData['soapAction'];
3746 $this->endpoint = $opData['endpoint'];
3747 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
3748 $style = $opData['style'];
3749 // add ns to ns array
3750 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
3751 $this->wsdl->namespaces['nu'] = $namespace;
3753 // serialize payload
3755 if($opData['input']['use'] == 'literal') {
3756 if (is_null($rpcParams)) {
3757 $rpcParams = $this->defaultRpcParams;
3759 if ($rpcParams) {
3760 $this->debug("serializing literal params for operation $operation");
3761 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
3762 $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
3763 } else {
3764 $this->debug("serializing literal document for operation $operation");
3765 $payload = is_array($params) ? array_shift($params) : $params;
3767 } else {
3768 $this->debug("serializing encoded params for operation $operation");
3769 $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation>".
3770 $this->wsdl->serializeRPCParameters($operation,'input',$params).
3771 '</'.$this->wsdl->getPrefixFromNamespace($namespace).":$operation>";
3773 $this->debug('payload size: '.strlen($payload));
3774 // serialize envelope
3775 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style);
3776 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3777 $this->wsdl->debug_str = '';
3778 } elseif($this->endpointType == 'wsdl') {
3779 $this->setError( 'operation '.$operation.' not present.');
3780 $this->debug("operation '$operation' not present.");
3781 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3782 return false;
3783 // no wsdl
3784 } else {
3785 // make message
3786 if(!isset($style)){
3787 $style = 'rpc';
3789 if($namespace == ''){
3790 $namespace = 'http://testuri.org';
3791 $this->wsdl->namespaces['ns1'] = $namespace;
3793 // serialize envelope
3794 $payload = '';
3795 foreach($params as $k => $v){
3796 $payload .= $this->serialize_val($v,$k);
3798 $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n".$payload."</ns1:$operation>\n";
3799 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders);
3801 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
3802 // send
3803 $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'...");
3804 $return = $this->send($soapmsg,$soapAction,$this->timeout);
3805 if($errstr = $this->getError()){
3806 $this->debug('Error: '.$errstr);
3807 return false;
3808 } else {
3809 $this->return = $return;
3810 $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
3812 // fault?
3813 if(is_array($return) && isset($return['faultcode'])){
3814 $this->debug('got fault');
3815 $this->setError($return['faultcode'].': '.$return['faultstring']);
3816 $this->fault = true;
3817 foreach($return as $k => $v){
3818 $this->$k = $v;
3819 $this->debug("$k = $v<br>");
3821 return $return;
3822 } else {
3823 // array of return values
3824 if(is_array($return)){
3825 // multiple 'out' parameters
3826 if(sizeof($return) > 1){
3827 return $return;
3829 // single 'out' parameter
3830 return array_shift($return);
3831 // nothing returned (ie, echoVoid)
3832 } else {
3833 return "";
3840 * get available data pertaining to an operation
3842 * @param string $operation operation name
3843 * @return array array of data pertaining to the operation
3844 * @access public
3846 function getOperationData($operation){
3847 if(isset($this->operations[$operation])){
3848 return $this->operations[$operation];
3850 $this->debug("No data for operation: $operation");
3854 * send the SOAP message
3856 * Note: if the operation has multiple return values
3857 * the return value of this method will be an array
3858 * of those values.
3860 * @param string $msg a SOAPx4 soapmsg object
3861 * @param string $soapaction SOAPAction value
3862 * @param integer $timeout set timeout in seconds
3863 * @return mixed native PHP types.
3864 * @access private
3866 function send($msg, $soapaction = '', $timeout=0) {
3867 // detect transport
3868 switch(true){
3869 // http(s)
3870 case ereg('^http',$this->endpoint):
3871 $this->debug('transporting via HTTP');
3872 if($this->persistentConnection && is_object($this->persistentConnection)){
3873 $http =& $this->persistentConnection;
3874 } else {
3875 $http = new soap_transport_http($this->endpoint);
3876 // pass encoding into transport layer, so appropriate http headers are sent
3877 $http->soap_defencoding = $this->soap_defencoding;
3879 $http->setSOAPAction($soapaction);
3880 if($this->proxyhost && $this->proxyport){
3881 $http->setProxy($this->proxyhost,$this->proxyport);
3883 if($this->username != '' && $this->password != '') {
3884 $http->setCredentials($this->username,$this->password);
3886 if($this->http_encoding != ''){
3887 $http->setEncoding($this->http_encoding);
3889 $this->debug('sending message, length: '.strlen($msg));
3890 if(ereg('^http:',$this->endpoint)){
3891 //if(strpos($this->endpoint,'http:')){
3892 $response = $http->send($msg,$timeout);
3893 } elseif(ereg('^https',$this->endpoint)){
3894 //} elseif(strpos($this->endpoint,'https:')){
3895 //if(phpversion() == '4.3.0-dev'){
3896 //$response = $http->send($msg,$timeout);
3897 //$this->request = $http->outgoing_payload;
3898 //$this->response = $http->incoming_payload;
3899 //} else
3900 if (extension_loaded('curl')) {
3901 $response = $http->sendHTTPS($msg,$timeout);
3902 } else {
3903 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
3905 } else {
3906 $this->setError('no http/s in endpoint url');
3908 $this->request = $http->outgoing_payload;
3909 $this->response = $http->incoming_payload;
3910 $this->debug("transport debug data...\n".$http->debug_str);
3911 // save transport object if using persistent connections
3912 if($this->persistentConnection && !is_object($this->persistentConnection)){
3913 $this->persistentConnection = $http;
3915 if($err = $http->getError()){
3916 $this->setError('HTTP Error: '.$err);
3917 return false;
3918 } elseif($this->getError()){
3919 return false;
3920 } else {
3921 $this->debug('got response, length: '.strlen($response));
3922 return $this->parseResponse($response);
3924 break;
3925 default:
3926 $this->setError('no transport found, or selected transport is not yet supported!');
3927 return false;
3928 break;
3933 * processes SOAP message returned from server
3935 * @param string unprocessed response data from server
3936 * @return mixed value of the message, decoded into a PHP type
3937 * @access private
3939 function parseResponse($data) {
3940 $this->debug('Entering parseResponse(), about to create soap_parser instance');
3941 $parser = new soap_parser($data,$this->xml_encoding,$this->operation);
3942 // if parse errors
3943 if($errstr = $parser->getError()){
3944 $this->setError( $errstr);
3945 // destroy the parser object
3946 unset($parser);
3947 return false;
3948 } else {
3949 // get SOAP headers
3950 $this->responseHeaders = $parser->getHeaders();
3951 // get decoded message
3952 $return = $parser->get_response();
3953 // add parser debug data to our debug
3954 $this->debug($parser->debug_str);
3955 // add document for doclit support
3956 $this->document = $parser->document;
3957 // destroy the parser object
3958 unset($parser);
3959 // return decode message
3960 return $return;
3965 * set the SOAP headers
3967 * @param $headers string XML
3968 * @access public
3970 function setHeaders($headers){
3971 $this->requestHeaders = $headers;
3975 * get the response headers
3977 * @return mixed object SOAPx4 soapval object or empty if no headers
3978 * @access public
3980 function getHeaders(){
3981 if($this->responseHeaders != '') {
3982 return $this->responseHeaders;
3987 * set proxy info here
3989 * @param string $proxyhost
3990 * @param string $proxyport
3991 * @access public
3993 function setHTTPProxy($proxyhost, $proxyport) {
3994 $this->proxyhost = $proxyhost;
3995 $this->proxyport = $proxyport;
3999 * if authenticating, set user credentials here
4001 * @param string $username
4002 * @param string $password
4003 * @access public
4005 function setCredentials($username, $password) {
4006 $this->username = $username;
4007 $this->password = $password;
4011 * use HTTP encoding
4013 * @param string $enc
4014 * @access public
4016 function setHTTPEncoding($enc='gzip, deflate'){
4017 $this->http_encoding = $enc;
4021 * use HTTP persistent connections if possible
4023 * @access public
4025 function useHTTPPersistentConnection(){
4026 $this->persistentConnection = true;
4030 * gets the default RPC parameter setting.
4031 * If true, default is that call params are like RPC even for document style.
4032 * Each call() can override this value.
4034 * @access public
4036 function getDefaultRpcParams() {
4037 return $this->defaultRpcParams;
4041 * sets the default RPC parameter setting.
4042 * If true, default is that call params are like RPC even for document style
4043 * Each call() can override this value.
4045 * @param boolean $rpcParams
4046 * @access public
4048 function setDefaultRpcParams($rpcParams) {
4049 $this->defaultRpcParams = $rpcParams;
4053 * dynamically creates proxy class, allowing user to directly call methods from wsdl
4055 * @return object soap_proxy object
4056 * @access public
4058 function getProxy(){
4059 $evalStr = '';
4060 foreach($this->operations as $operation => $opData){
4061 if($operation != ''){
4062 // create param string
4063 $paramStr = '';
4064 if(sizeof($opData['input']['parts']) > 0){
4065 foreach($opData['input']['parts'] as $name => $type){
4066 $paramStr .= "\$$name,";
4068 $paramStr = substr($paramStr,0,strlen($paramStr)-1);
4070 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
4071 $evalStr .= "function $operation ($paramStr){
4072 // load params into array
4073 \$params = array($paramStr);
4074 return \$this->call('$operation',\$params,'".$opData['namespace']."','".$opData['soapAction']."');
4076 unset($paramStr);
4079 $r = rand();
4080 $evalStr = 'class soap_proxy_'.$r.' extends nusoap_client {
4081 '.$evalStr.'
4083 //print "proxy class:<pre>$evalStr</pre>";
4084 // eval the class
4085 eval($evalStr);
4086 // instantiate proxy object
4087 eval("\$proxy = new soap_proxy_$r('');");
4088 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
4089 $proxy->endpointType = 'wsdl';
4090 $proxy->wsdlFile = $this->wsdlFile;
4091 $proxy->wsdl = $this->wsdl;
4092 $proxy->operations = $this->operations;
4093 $proxy->defaultRpcParams = $this->defaultRpcParams;
4094 return $proxy;