· 10 years ago · Apr 12, 2016, 09:12 PM
1<?php
2
3/*
4
5NuSOAP - Web Services Toolkit for PHP
6
7Copyright (c) 2002 NuSphere Corporation
8
9This library is free software; you can redistribute it and/or
10modify it under the terms of the GNU Lesser General Public
11License as published by the Free Software Foundation; either
12version 2.1 of the License, or (at your option) any later version.
13
14This library is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public
20License along with this library; if not, write to the Free Software
21Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23If you have any questions or comments, please email:
24
25Dietrich Ayala
26dietrich@ganx4.com
27http://dietrich.ganx4.com/nusoap
28
29NuSphere Corporation
30http://www.nusphere.com
31
32*/
33
34/* load classes
35
36// necessary classes
37require_once('class.soapclient.php');
38require_once('class.soap_val.php');
39require_once('class.soap_parser.php');
40require_once('class.soap_fault.php');
41
42// transport classes
43require_once('class.soap_transport_http.php');
44
45// optional add-on classes
46require_once('class.xmlschema.php');
47require_once('class.wsdl.php');
48
49// server class
50require_once('class.soap_server.php');*/
51
52/**
53*
54* nusoap_base
55*
56* @author Dietrich Ayala <dietrich@ganx4.com>
57* @version v 0.6.3
58* @access public
59*/
60class nusoap_base {
61
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;
68
69 /**
70 * set schema version
71 *
72 * @var XMLSchemaVersion
73 * @access public
74 */
75 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
76
77 /**
78 * set default encoding
79 *
80 * @var soap_defencoding
81 * @access public
82 */
83 //var $soap_defencoding = 'UTF-8';
84 var $soap_defencoding = 'ISO-8859-1';
85
86 /**
87 * load namespace uris into an array of uri => prefix
88 *
89 * @var namespaces
90 * @access public
91 */
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
104 */
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')
122 );
123
124 /**
125 * entities to convert
126 *
127 * @var xmlEntities
128 * @access public
129 */
130 var $xmlEntities = array('quot' => '"','amp' => '&',
131 'lt' => '<','gt' => '>','apos' => "'");
132
133 /**
134 * adds debug data to the class level debug string
135 *
136 * @param string $string debug data
137 * @access private
138 */
139 function debug($string){
140 $this->debug_str .= get_class($this).": $string\n";
141 }
142
143 /**
144 * returns error string if present
145 *
146 * @return boolean $string error string
147 * @access public
148 */
149 function getError(){
150 if($this->error_str != ''){
151 return $this->error_str;
152 }
153 return false;
154 }
155
156 /**
157 * sets error string
158 *
159 * @return boolean $string error string
160 * @access private
161 */
162 function setError($str){
163 $this->error_str = $str;
164 }
165
166 /**
167 * serializes PHP values in accordance w/ section 5. Type information is
168 * not serialized if $use == 'literal'.
169 *
170 * @return string
171 * @access public
172 */
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);
176 }
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\"";
186 }
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\"";
195 }
196 // serialize attributes if present
197 if($attributes){
198 foreach($attributes as $k => $v){
199 $atts .= " $k=\"$v\"";
200 }
201 }
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>";
208 }
209 }
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\"/>";
220 }
221 break;
222 case (is_bool($val) || $type == 'boolean'):
223 if(!$val){
224 $val = 0;
225 }
226 if ($use == 'literal') {
227 $xml .= "<$name$xmlns $atts>$val</$name>";
228 } else {
229 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
230 }
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>";
237 }
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>";
244 }
245 break;
246 case (is_string($val) || $type == 'string'):
247 if($this->charencoding){
248 $val = htmlspecialchars($val, ENT_QUOTES);
249 }
250 if ($use == 'literal') {
251 $xml .= "<$name$xmlns $atts>$val</$name>";
252 } else {
253 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
254 }
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);
260 }
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;
272 }
273 }
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);
282 }
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;
289 }
290 }
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;
299 }
300 if(isset($array_types['array'])){
301 $array_type = $i.",".$i;
302 } else {
303 $array_type = $i;
304 }
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>";
309 }
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>";;
316 }
317 }
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 = '';
324 }
325 if ($use == 'literal') {
326 $xml .= "<$name$xmlns $atts>";
327 } else {
328 $xml .= "<$name$xmlns$type_str$atts>";
329 }
330 foreach($val as $k => $v){
331 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
332 }
333 $xml .= "</$name>";
334 }
335 break;
336 default:
337 $xml .= 'not detected, got '.gettype($val).' for '.$val;
338 break;
339 }
340 return $xml;
341 }
342
343 /**
344 * serialize message
345 *
346 * @param string body
347 * @param string headers
348 * @param array namespaces
349 * @param string style
350 * @return string message
351 * @access public
352 */
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\"";
358 }
359 if($style == 'rpc') {
360 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
361 }
362
363 // serialize headers
364 if($headers){
365 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
366 }
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>";
376 }
377
378 function formatDump($str){
379 $str = htmlspecialchars($str);
380 return nl2br($str);
381 }
382
383 /**
384 * returns the local part of a prefixed string
385 * returns the original string, if not prefixed
386 *
387 * @param string
388 * @return string
389 * @access public
390 */
391 function getLocalPart($str){
392 if($sstr = strrchr($str,':')){
393 // get unqualified name
394 return substr( $sstr, 1 );
395 } else {
396 return $str;
397 }
398 }
399
400 /**
401 * returns the prefix part of a prefixed string
402 * returns false, if not prefixed
403 *
404 * @param string
405 * @return mixed
406 * @access public
407 */
408 function getPrefix($str){
409 if($pos = strrpos($str,':')){
410 // get prefix
411 return substr($str,0,$pos);
412 }
413 return false;
414 }
415
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;
422 }
423}
424
425// XML Schema Datatype Helper Functions
426
427//xsd:dateTime helpers
428
429/**
430* convert unix timestamp to ISO 8601 compliant date string
431*
432* @param string $timestamp Unix time stamp
433* @access public
434*/
435function 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
447
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]);
450 }
451 return false;
452 } else {
453 return $datestr;
454 }
455}
456
457/**
458* convert ISO 8601 compliant date string to unix timestamp
459*
460* @param string $datestr ISO 8601 compliant date string
461* @access public
462*/
463function 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;
485 }
486 }
487 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
488 } else {
489 return false;
490 }
491}
492
493
494
495?><?php
496
497
498
499/**
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
506*/
507class soap_fault extends nusoap_base {
508
509 var $faultcode;
510 var $faultactor;
511 var $faultstring;
512 var $faultdetail;
513
514 /**
515 * constructor
516 *
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
521 */
522 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
523 $this->faultcode = $faultcode;
524 $this->faultactor = $faultactor;
525 $this->faultstring = $faultstring;
526 $this->faultdetail = $faultdetail;
527 }
528
529 /**
530 * serialize a fault
531 *
532 * @access public
533 */
534 function serialize(){
535 $ns_string = '';
536 foreach($this->namespaces as $k => $v){
537 $ns_string .= "\n xmlns:$k=\"$v\"";
538 }
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;
552 }
553}
554
555
556
557?><?php
558
559
560
561/**
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 :)
567*
568* @author Dietrich Ayala <dietrich@ganx4.com>
569* @version v 0.6.3
570* @access public
571*/
572class XMLSchema extends nusoap_base {
573
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();
586
587 /**
588 * constructor
589 *
590 * @param string $schema schema document URI
591 * @param string $xml xml document URI
592 * @access public
593 */
594 function XMLSchema($schema='',$xml=''){
595
596 $this->debug('xmlschema class instantiated, inside constructor');
597 // files
598 $this->schema = $schema;
599 $this->xml = $xml;
600
601 // parse schema file
602 if($schema != ''){
603 $this->debug('initial schema file: '.$schema);
604 $this->parseFile($schema);
605 }
606
607 // parse xml file
608 if($xml != ''){
609 $this->debug('initial xml file: '.$xml);
610 $this->parseFile($xml);
611 }
612
613 }
614
615 /**
616 * parse an XML file
617 *
618 * @param string $xml, path/URL to XML file
619 * @param string $type, (schema | xml)
620 * @return boolean
621 * @access public
622 */
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;
634 }
635 }
636 return false;
637 }
638
639 /**
640 * parse an XML string
641 *
642 * @param string $xml path or URL
643 * @param string $type, (schema|xml)
644 * @access private
645 */
646 function parseString($xml,$type){
647 // parse xml string
648 if($xml != ""){
649
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);
654
655 // Set the object for the parser.
656 xml_set_object($this->parser, $this);
657
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');
665 }
666
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))
673 );
674 $this->debug('XML parse error: '.$errstr);
675 $this->setError('Parser error: '.$errstr);
676 }
677
678 xml_parser_free($this->parser);
679 } else{
680 $this->debug('no xml passed to parseString()!!');
681 $this->setError('no xml passed to parseString()!!');
682 }
683 }
684
685 /**
686 * start-element handler
687 *
688 * @param string $parser XML parser object
689 * @param string $name element name
690 * @param string $attrs associative array of attributes
691 * @access private
692 */
693 function schemaStartElement($parser, $name, $attrs) {
694
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;
700
701 // get element prefix
702 if($prefix = $this->getPrefix($name)){
703 // get unqualified name
704 $name = $this->getLocalPart($name);
705 } else {
706 $prefix = '';
707 }
708
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;
720 }
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';
724 }
725 }
726 }
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;
732 }
733 $attrs = $eAttrs;
734 } else {
735 $attrs = array();
736 }
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';
744 }
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;
756 }
757
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;
762 }
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 = '';
771 }
772 if(strpos($v,'[,]')){
773 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
774 }
775 $v = substr($v,0,strpos($v,'[')); // clip the []
776 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
777 $v = $this->XMLSchemaVersion.':'.$v;
778 }
779 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
780 }
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';
792 }
793 $this->xdebug('processing complexType '.$attrs['name']);
794 }
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';
811 }
812 if(isset($ename) && $this->currentComplexType){
813 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
814 }
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';
824 }
825 }
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;
836 }
837 }
838
839 /**
840 * end-element handler
841 *
842 * @param string $parser XML parser object
843 * @param string $name element name
844 * @access private
845 */
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];
850 }
851 // bring depth down a notch
852 $this->depth--;
853 // move on...
854 if($name == 'complexType'){
855 $this->currentComplexType = false;
856 $this->currentElement = false;
857 }
858 if($name == 'element'){
859 $this->currentElement = false;
860 }
861 }
862
863 /**
864 * element content handler
865 *
866 * @param string $parser XML parser object
867 * @param string $data element content
868 * @access private
869 */
870 function schemaCharacterData($parser, $data){
871 $pos = $this->depth_array[$this->depth];
872 $this->message[$pos]['cdata'] .= $data;
873 }
874
875 /**
876 * serialize the schema
877 *
878 * @access public
879 */
880 function serializeSchema(){
881
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]\"/>";
894 }
895 }
896 }
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'].'"';
903 }
904 $contentStr .= '/>';
905 }
906 }
907 // if restriction
908 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
909 $contentStr = "<$schemaPrefix:restriction base=\"".$attrs['restrictionBase']."\">".$contentStr."</$schemaPrefix:restriction>";
910 }
911 // "all" compositor obviates complex/simple content
912 if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){
913 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
914 }
915 // complex or simple content
916 elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){
917 $contentStr = "<$schemaPrefix:complexContent>".$contentStr."</$schemaPrefix:complexContent>";
918 }
919 // compositors
920 if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){
921 $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
922 }
923 // finalize complex type
924 if($contentStr != ''){
925 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr."</$schemaPrefix:complexType>";
926 } else {
927 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
928 }
929 $xml .= $contentStr;
930 }
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']."\"/>";
935 }
936 }
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']."\"/>";
941 }
942 }
943 // finish 'er up
944 $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml."</$schemaPrefix:schema>";
945 return $xml;
946 }
947
948 /**
949 * expands a qualified name
950 *
951 * @param string $string qname
952 * @return string expanded qname
953 * @access private
954 */
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;
966 }
967 } else {
968 return $qname;
969 }
970 }
971
972 /**
973 * adds debug data to the clas level debug string
974 *
975 * @param string $string debug data
976 * @access private
977 */
978 function xdebug($string){
979 $this->debug(' xmlschema: '.$string);
980 }
981
982 /**
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'
987 *
988 * @param string $type, name of defined type
989 * @param string $ns, namespace of type
990 * @return mixed
991 * @access public
992 */
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'];
1001 }
1002 return false;
1003 }
1004
1005 /**
1006 * returns the local part of a prefixed string
1007 * returns the original string, if not prefixed
1008 *
1009 * @param string
1010 * @return string
1011 * @access public
1012 */
1013 function getLocalPart($str){
1014 if($sstr = strrchr($str,':')){
1015 // get unqualified name
1016 return substr( $sstr, 1 );
1017 } else {
1018 return $str;
1019 }
1020 }
1021
1022 /**
1023 * returns the prefix part of a prefixed string
1024 * returns false, if not prefixed
1025 *
1026 * @param string
1027 * @return mixed
1028 * @access public
1029 */
1030 function getPrefix($str){
1031 if($pos = strrpos($str,':')){
1032 // get prefix
1033 return substr($str,0,$pos);
1034 }
1035 return false;
1036 }
1037
1038 /**
1039 * pass it a prefix, it returns a namespace
1040 * returns false if no namespace registered with the given prefix
1041 *
1042 * @param string
1043 * @return mixed
1044 * @access public
1045 */
1046 function getNamespaceFromPrefix($prefix){
1047 if(isset($this->namespaces[$prefix])){
1048 return $this->namespaces[$prefix];
1049 }
1050 //$this->setError("No namespace registered for prefix '$prefix'");
1051 return false;
1052 }
1053
1054 /**
1055 * returns the prefix for a given namespace (or prefix)
1056 * or false if no prefixes registered for the given namespace
1057 *
1058 * @param string
1059 * @return mixed
1060 * @access public
1061 */
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;
1067 }
1068 }
1069 return false;
1070 }
1071
1072 /**
1073 * returns an array of information about a given type
1074 * returns false if no type exists by the given name
1075 *
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
1082 * )
1083 *
1084 * @param string
1085 * @return mixed
1086 * @access public
1087 */
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];
1095 }
1096 return false;
1097 }
1098
1099 /**
1100 * returns a sample serialization of a given type, or false if no type by the given name
1101 *
1102 * @param string $type, name of type
1103 * @return mixed
1104 * @access public
1105 */
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']."}\"";
1113 }
1114 }
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);
1120 }
1121 $str .= "</$type>";
1122 } elseif($typeDef['typeClass'] == 'element') {
1123 $str .= "></$type>";
1124 } else {
1125 $str .= "/>";
1126 }
1127 return $str;
1128 }
1129 return false;
1130 }
1131
1132 /**
1133 * returns HTML form elements that allow a user
1134 * to enter values for creating an instance of the given type.
1135 *
1136 * @param string $name, name for type instance
1137 * @param string $type, name of type
1138 * @return string
1139 * @access public
1140 */
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>";
1151 }
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>";
1160 }
1161 $buffer .= '</table>';
1162 // if scalar
1163 } else {
1164 $buffer .= "<input type='text' name='parameters[$name]'>";
1165 }
1166 } else {
1167 $buffer .= "<input type='text' name='parameters[$name]'>";
1168 }
1169 return $buffer;
1170 }
1171
1172 /**
1173 * adds an XML Schema complex type to the WSDL types
1174 *
1175 * example: array
1176 *
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 * );
1186 *
1187 * example: PHP associative array ( SOAP Struct )
1188 *
1189 * addType(
1190 * 'SOAPStruct',
1191 * 'complexType',
1192 * 'struct',
1193 * 'all',
1194 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1195 * );
1196 *
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[]"
1207 * )
1208 * )
1209 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1210 *
1211 */
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
1222 );
1223 }
1224}
1225
1226
1227
1228?><?php
1229
1230
1231
1232/**
1233* for creating serializable abstractions of native PHP types
1234* NOTE: this is only really used when WSDL is not available.
1235*
1236* @author Dietrich Ayala <dietrich@ganx4.com>
1237* @version v 0.6.3
1238* @access public
1239*/
1240class soapval extends nusoap_base {
1241 /**
1242 * constructor
1243 *
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
1251 */
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;
1259 }
1260
1261 /**
1262 * return serialized value
1263 *
1264 * @return string XML data
1265 * @access private
1266 */
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);
1269 }
1270
1271 /**
1272 * decodes a soapval object into a PHP native type
1273 *
1274 * @param object $soapval optional SOAPx4 soapval object, else uses self
1275 * @return mixed
1276 * @access public
1277 */
1278 function decode(){
1279 return $this->value;
1280 }
1281}
1282
1283
1284
1285?><?php
1286
1287
1288
1289/**
1290* transport class for sending/receiving data via HTTP and HTTPS
1291* NOTE: PHP must be compiled with the CURL extension for HTTPS support
1292*
1293* @author Dietrich Ayala <dietrich@ganx4.com>
1294* @version v 0.6.3
1295* @access public
1296*/
1297class soap_transport_http extends nusoap_base {
1298
1299 var $username = '';
1300 var $password = '';
1301 var $url = '';
1302 var $proxyhost = 'phoebe.bcuc.ac.uk';
1303 var $proxyport = '8080';
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;
1313
1314 /**
1315 * constructor
1316 */
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;
1323 }
1324 if(isset($u['query']) && $u['query'] != ''){
1325 $this->path .= '?' . $u['query'];
1326 }
1327 if(!isset($u['port']) && $u['scheme'] == 'http'){
1328 $this->port = 80;
1329 }
1330 }
1331
1332 function connect($timeout){
1333
1334 // $this->error_str = $s
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;
1342 }
1343 // ssl
1344 if($this->scheme == 'https'){
1345 $host = 'ssl://'.$host;
1346 $port = 443;
1347 }
1348
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);
1355 }
1356
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;
1362 }
1363 return $fp;
1364 }
1365
1366 /**
1367 * send the SOAP message via HTTP
1368 *
1369 * @param string $data message data
1370 * @param integer $timeout set timeout in seconds
1371 * @return string data
1372 * @access public
1373 */
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;
1379 }
1380 $this->debug('socket connected');
1381
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";
1388 }
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";
1398 }
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);
1406 }
1407 // set soapaction
1408 if($this->useSOAPAction){
1409 $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n";
1410 }
1411 $this->outgoing_payload .= "\r\n";
1412 // add data
1413 $this->outgoing_payload .= $data;
1414
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');
1419 }
1420 $this->debug('wrote data to socket');
1421
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);
1428 }
1429 $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server');
1430
1431 // close filepointer
1432 fclose($fp);
1433 $this->debug('closed socket');
1434
1435 // connection was closed unexpectedly
1436 if($this->incoming_payload == ''){
1437 $this->setError('no response from server');
1438 return false;
1439 }
1440
1441 $this->debug('received incoming payload: '.strlen($this->incoming_payload));
1442 $data = $this->incoming_payload."\r\n\r\n\r\n\r\n";
1443
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));
1450 }
1451 }//
1452
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;
1461 }
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]);
1472 }
1473 }
1474 //print "headers: <pre>$header_data</pre><br>";
1475 //print "data: <pre>$data</pre><br>";
1476
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;
1483 }
1484 //$timer->setMarker('finished decoding of chunked content');
1485 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1486 }
1487
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');
1500 }
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.');
1505 }
1506 }
1507 }
1508
1509 if(strlen($data) == 0){
1510 $this->debug('no data after headers!');
1511 $this->setError('no data present after HTTP headers');
1512 return false;
1513 }
1514 $this->debug('end of send()');
1515 return $data;
1516 }
1517
1518
1519 /**
1520 * send the SOAP message via HTTPS 1.0 using CURL
1521 *
1522 * @param string $msg message data
1523 * @param integer $timeout set timeout in seconds
1524 * @return string data
1525 * @access public
1526 */
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;
1541 }
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');
1553 }
1554 // persistent connection
1555 //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1556
1557 // set timeout
1558 if($timeout != 0){
1559 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1560 }
1561
1562 $credentials = '';
1563 if($this->username != '') {
1564 $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n';
1565 }
1566
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);
1572 }
1573 }
1574
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";
1579 }
1580
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;
1590
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;
1598
1599 $cErr = curl_error($ch);
1600
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>";
1605 }
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>';
1613 }
1614 // close curl
1615 curl_close($ch);
1616 //$t->setMarker('closed curl');
1617
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));
1624 }
1625 }//
1626
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;
1635 }
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]);
1645 }
1646 if(strlen($data) == 0){
1647 $this->debug('no data after headers!');
1648 $this->setError('no data present after HTTP headers.');
1649 return false;
1650 }
1651
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;
1657 }
1658 }
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');
1670 }
1671 } else {
1672 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1673 }
1674 }
1675 }
1676 // set decoded payload
1677 $this->incoming_payload = $header_data."\r\n\r\n".$data;
1678 return $data;
1679 }
1680
1681 /**
1682 * if authenticating, set user credentials here
1683 *
1684 * @param string $user
1685 * @param string $pass
1686 * @access public
1687 */
1688 function setCredentials($username, $password) {
1689 $this->username = $username;
1690 $this->password = $password;
1691 }
1692
1693 /**
1694 * set the soapaction value
1695 *
1696 * @param string $soapaction
1697 * @access public
1698 */
1699 function setSOAPAction($soapaction) {
1700 $this->soapaction = $soapaction;
1701 }
1702
1703 /**
1704 * use http encoding
1705 *
1706 * @param string $enc encoding style. supported values: gzip, deflate, or both
1707 * @access public
1708 */
1709 function setEncoding($enc='gzip, deflate'){
1710 $this->encoding = $enc;
1711 $this->protocol_version = '1.1';
1712 }
1713
1714 /**
1715 * set proxy info here
1716 *
1717 * @param string $proxyhost
1718 * @param string $proxyport
1719 * @access public
1720 */
1721 function setProxy($proxyhost, $proxyport) {
1722 $this->proxyhost = $proxyhost;
1723 $this->proxyport = $proxyport;
1724 }
1725
1726 /**
1727 * decode a string that is encoded w/ "chunked' transfer encoding
1728 * as defined in RFC2068 19.4.6
1729 *
1730 * @param string $buffer
1731 * @returns string
1732 * @access public
1733 */
1734 function decodeChunked($buffer){
1735 // length := 0
1736 $length = 0;
1737 $new = '';
1738
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) {
1747
1748 $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size);
1749
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;
1757 }
1758
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;
1767
1768 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
1769 if ($chunkend == FALSE) {
1770 break; //Just in case we got a broken connection
1771 }
1772 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1773 $chunk_size = hexdec( trim($temp) );
1774 $chunkstart = $chunkend;
1775 }
1776 // Update headers
1777 //$this->Header['content-length'] = $length;
1778 //unset($this->Header['transfer-encoding']);
1779 return $new;
1780 }
1781
1782}
1783
1784
1785
1786?><?php
1787
1788
1789
1790/**
1791*
1792* soap_server allows the user to create a SOAP server
1793* that is capable of receiving messages and returning responses
1794*
1795* NOTE: WSDL functionality is experimental
1796*
1797* @author Dietrich Ayala <dietrich@ganx4.com>
1798* @version v 0.6.3
1799* @access public
1800*/
1801class soap_server extends nusoap_base {
1802
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;
1814
1815 /**
1816 * constructor
1817 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1818 *
1819 * @param string $wsdl path or URL to a WSDL file
1820 * @access public
1821 */
1822 function soap_server($wsdl=false){
1823
1824 // turn on debugging?
1825 global $debug;
1826 if(isset($debug)){
1827 $this->debug_flag = 1;
1828 }
1829
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);
1836 }
1837 }
1838 }
1839
1840 /**
1841 * processes request and returns response
1842 *
1843 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
1844 * @access public
1845 */
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;
1855 }
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();
1865 }
1866 }
1867
1868 // print web interface
1869 if($data == '' && $this->wsdl){
1870 print $this->webDescription();
1871 } else {
1872
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-->";
1880 }
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";
1887 }
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);
1895 }
1896 $this->response = join("\r\n",$header).$payload;
1897 print $payload;
1898 }
1899 }
1900
1901 /**
1902 * parses request and posts response
1903 *
1904 * @param string $data XML string
1905 * @return string XML response msg
1906 * @access private
1907 */
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");
1917 }
1918 // get SOAPAction header
1919 if(isset($this->headers['SOAPAction'])){
1920 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
1921 }
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';
1929 }
1930 }
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'] : '';
1935 }
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();
1960 }
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();
1966 }
1967 }
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\",";
1985 }
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);
1992 }
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();
1999 }
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);
2022 }
2023 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2024 } else {
2025 $return_val = $this->serialize_val($method_response);
2026 }
2027 }
2028 $this->debug('return val:'.$this->varDump($return_val));
2029 } else {
2030 $return_val = '';
2031 $this->debug('got no response from method');
2032 }
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);
2044 }
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();
2052 }
2053 }
2054 }
2055
2056 /**
2057 * takes the value that was created by parsing the request
2058 * and compares to the method's signature, if available.
2059 *
2060 * @param mixed
2061 * @return boolean
2062 * @access private
2063 */
2064 function verify_method($operation,$request){
2065 if(isset($this->wsdl) && is_object($this->wsdl)){
2066 if($this->wsdl->getOperationData($operation)){
2067 return true;
2068 }
2069 } elseif(isset($this->operations[$operation])){
2070 return true;
2071 }
2072 return false;
2073 }
2074
2075 /**
2076 * add a method to the dispatch map
2077 *
2078 * @param string $methodname
2079 * @param string $in array of input values
2080 * @param string $out array of output values
2081 * @access public
2082 */
2083 function add_to_map($methodname,$in,$out){
2084 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2085 }
2086
2087 /**
2088 * register a service with the server
2089 *
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
2097 */
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.');
2101 }
2102 if(false == $in) {
2103 }
2104 if(false == $out) {
2105 }
2106 if(false == $namespace) {
2107 }
2108 if(false == $soapaction) {
2109 global $SERVER_NAME, $SCRIPT_NAME;
2110 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2111 }
2112 if(false == $style) {
2113 $style = "rpc";
2114 }
2115 if(false == $use) {
2116 $use = "encoded";
2117 }
2118
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);
2128 }
2129 return true;
2130 }
2131
2132 /**
2133 * create a fault. this also acts as a flag to the server that a fault has occured.
2134 *
2135 * @param string faultcode
2136 * @param string faultactor
2137 * @param string faultstring
2138 * @param string faultdetail
2139 * @access public
2140 */
2141 function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){
2142 $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2143 }
2144
2145 /**
2146 * prints html description of services
2147 *
2148 * @access private
2149 */
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
2195 }
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
2205 }
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
2211 }
2212 //Shows the messages
2213 var oDesc;
2214 function popup(divid){
2215 if(oDesc = new makeObj(divid)){
2216 oDesc.css.visibility = "visible"
2217 }
2218 }
2219 function popout(){ // Hides message
2220 if(oDesc) oDesc.css.visibility = "hidden"
2221 }
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'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 .= " $captain:<br>";
2244 //if(is_array($tenille)){
2245 foreach($tenille as $joanie => $chachi){
2246 $b .= " $joanie: $chachi<br>";
2247 }
2248 //}
2249 } else {
2250 $b .= " $captain: $tenille<br>";
2251 }
2252 }
2253 } else {
2254 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
2255 }
2256 }
2257 $b .= '</div>';
2258 }
2259 $b .= '
2260 <ul>
2261 </div>
2262 </div></body></html>';
2263 return $b;
2264 }
2265
2266 /**
2267 * sets up wsdl object
2268 * this acts as a flag to enable internal WSDL generation
2269 * NOTE: NOT FUNCTIONAL
2270 *
2271 * @param string $serviceName, name of the service
2272 * @param string $namespace, tns namespace
2273 */
2274 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http')
2275 {
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";
2280 }
2281
2282 if(false == $endpoint) {
2283 $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2284 }
2285
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/');
2301 }
2302}
2303
2304
2305
2306?><?php
2307
2308
2309
2310/**
2311* parses a WSDL file, allows access to it's data, other utility methods
2312*
2313* @author Dietrich Ayala <dietrich@ganx4.com>
2314* @version v 0.6.3
2315* @access public
2316*/
2317class 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 = '';
2346
2347 /**
2348 * constructor
2349 *
2350 * @param string $wsdl WSDL document URL
2351 * @access public
2352 */
2353 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){
2354 $this->wsdl = $wsdl;
2355 $this->proxyhost = $proxyhost;
2356 $this->proxyport = $proxyport;
2357
2358 // parse wsdl file
2359 if ($wsdl != "") {
2360 $this->debug('initial wsdl file: ' . $wsdl);
2361 $this->parseWSDL($wsdl);
2362 }
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);
2368 }
2369 }
2370 }
2371
2372 /**
2373 * parses the wsdl document
2374 *
2375 * @param string $wsdl path or URL
2376 * @access private
2377 */
2378 function parseWSDL($wsdl = '')
2379 {
2380 if ($wsdl == '') {
2381 $this->debug('no wsdl passed to parseWSDL()!!');
2382 $this->setError('no wsdl passed to parseWSDL()!!');
2383 return false;
2384 }
2385
2386 $this->debug('getting ' . $wsdl);
2387
2388 // parse $wsdl for url format
2389 $wsdl_props = parse_url($wsdl);
2390
2391 if (isset($wsdl_props['host'])) {
2392
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);
2399 }
2400 if (isset($wsdl_props['user'])) {
2401 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
2402 }
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;
2409 }
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;
2428 }
2429 }
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'];
2437 }
2438 $sHeader .= " HTTP/1.0\r\n";
2439
2440 if (isset($wsdl_props['user'])) {
2441 $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2442 $sHeader .= "Authorization: Basic $base64auth\r\n";
2443 }
2444 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2445 fputs($fp, $sHeader);
2446
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
2451 }
2452 // read in WSDL just like regular fopen()
2453 $wsdl_string = '';
2454 while ($data = fread($fp, 32768)) {
2455 $wsdl_string .= $data;
2456 }
2457 fclose($fp);
2458 } else {
2459 $this->setError('bad path to WSDL file.');
2460 return false;
2461 }
2462 */
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;
2469 }
2470 fclose($fp);
2471 } else {
2472 $this->setError('bad path to WSDL file.');
2473 return false;
2474 }
2475 }
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))
2494 );
2495 $this->debug('XML parse error: ' . $errstr);
2496 $this->setError('Parser error: ' . $errstr);
2497 return false;
2498 }
2499 // free the parser
2500 xml_parser_free($this->parser);
2501 // catch wsdl parse errors
2502 if($this->getError()){
2503 return false;
2504 }
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'] ];
2520 }
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'] ];
2523 }
2524 if (isset($bindingData['style'])) {
2525 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2526 }
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'] : '';
2530 }
2531 }
2532 }
2533 return true;
2534 }
2535
2536 /**
2537 * start-element handler
2538 *
2539 * @param string $parser XML parser object
2540 * @param string $name element name
2541 * @param string $attrs associative array of attributes
2542 * @access private
2543 */
2544 function start_element($parser, $name, $attrs)
2545 {
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);
2565 }
2566
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;
2575 }
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';
2579 }
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;
2585 }
2586 $eAttrs[$k] = $v;
2587 }
2588 $attrs = $eAttrs;
2589 } else {
2590 $attrs = array();
2591 }
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'];
2599 }
2600 if (isset($attrs['element'])) {
2601 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2602 }
2603 }
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'];
2612 }
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;
2622 }
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;
2630 }
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'];
2639 }
2640 if (isset($attrs['style'])) {
2641 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2642 }
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'] : '';
2649 }
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;
2662 }
2663 break;
2664 }
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']);
2672
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;
2680 }
2681 break;
2682 }
2683 // set status
2684 switch ($name) {
2685 case "import":
2686 if (isset($attrs['location'])) {
2687 $this->import[$attrs['namespace']] = $attrs['location'];
2688 }
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'];
2710 }
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']);
2714 }
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;
2724 }
2725 break;
2726 }
2727 }
2728 }
2729
2730 /**
2731 * end-element handler
2732 *
2733 * @param string $parser XML parser object
2734 * @param string $name element name
2735 * @access private
2736 */
2737 function end_element($parser, $name){
2738 // unset schema status
2739 if (ereg('types$', $name) || ereg('schema$', $name)) {
2740 $this->status = "";
2741 }
2742 if ($this->status == 'schema') {
2743 $this->schemaEndElement($parser, $name);
2744 } else {
2745 // bring depth down a notch
2746 $this->depth--;
2747 }
2748 // end documentation
2749 if ($this->documentation) {
2750 $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2751 $this->documentation = false;
2752 }
2753 }
2754
2755 /**
2756 * element content handler
2757 *
2758 * @param string $parser XML parser object
2759 * @param string $data element content
2760 * @access private
2761 */
2762 function character_data($parser, $data)
2763 {
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;
2767 }
2768 if ($this->documentation) {
2769 $this->documentation .= $data;
2770 }
2771 }
2772
2773 function getBindingData($binding)
2774 {
2775 if (is_array($this->bindings[$binding])) {
2776 return $this->bindings[$binding];
2777 }
2778 }
2779
2780 /**
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
2784 *
2785 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2786 * @return array
2787 * @access public
2788 */
2789 function getOperations($bindingType = 'soap')
2790 {
2791 if ($bindingType == 'soap') {
2792 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2793 }
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'];
2800 }
2801 }
2802 return array();
2803 }
2804
2805 /**
2806 * returns an associative array of data necessary for calling an operation
2807 *
2808 * @param string $operation , name of operation
2809 * @param string $bindingType , type of binding eg: soap
2810 * @return array
2811 * @access public
2812 */
2813 function getOperationData($operation, $bindingType = 'soap')
2814 {
2815 if ($bindingType == 'soap') {
2816 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2817 }
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;
2828 }
2829 }
2830 }
2831 }
2832 }
2833
2834 /**
2835 * serialize the parsed wsdl
2836 *
2837 * @return string , serialization of WSDL
2838 * @access public
2839 */
2840 function serialize()
2841 {
2842 $xml = '<?xml version="1.0"?><definitions';
2843 foreach($this->namespaces as $k => $v) {
2844 $xml .= " xmlns:$k=\"$v\"";
2845 }
2846 // 10.9.02 - add poulter fix for wsdl and tns declarations
2847 if (isset($this->namespaces['wsdl'])) {
2848 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2849 }
2850 if (isset($this->namespaces['tns'])) {
2851 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2852 }
2853 $xml .= '>';
2854 // imports
2855 if (sizeof($this->import) > 0) {
2856 foreach($this->import as $ns => $url) {
2857 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2858 }
2859 }
2860 // types
2861 if (count($this->complexTypes)>=1) {
2862 $xml .= '<types>';
2863 $xml .= $this->serializeSchema();
2864 $xml .= '</types>';
2865 }
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);
2881 }
2882 }
2883 if (!isset($typePrefix)) {
2884 die("$partType has no namespace!");
2885 }
2886 }
2887 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2888 }
2889 $xml .= '</message>';
2890 }
2891 }
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'] . '"';
2909 }
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>';
2914 }
2915 $portType_xml .= '</portType>';
2916 $binding_xml .= '</binding>';
2917 }
2918 $xml .= $portType_xml . $binding_xml;
2919 }
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>';
2927 }
2928 }
2929 $xml .= '</service>';
2930 return $xml . '</definitions>';
2931 }
2932
2933 /**
2934 * serialize a PHP value according to a WSDL message definition
2935 *
2936 * TODO
2937 * - multi-ref serialization
2938 * - validate PHP values against type definitions, return errors if invalid
2939 *
2940 * @param string $ type name
2941 * @param mixed $ param value
2942 * @return mixed new param or false if initial value didn't validate
2943 */
2944 function serializeRPCParameters($operation, $direction, $parameters)
2945 {
2946 $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
2947
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;
2952 }
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;
2957 }
2958 $this->debug($this->varDump($opData));
2959 // set input params
2960 $xml = '';
2961 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
2962
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.');
2978 }
2979 }
2980 }
2981 return $xml;
2982 }
2983
2984 /**
2985 * serializes a PHP value according a given type definition
2986 *
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
2993 */
2994 function serializeType($name, $type, $value, $use='encoded')
2995 {
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");
3002
3003 if($ns == $this->XMLSchemaVersion ||
3004 ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){
3005
3006 if ($uqType == 'boolean' && !$value) {
3007 $value = 0;
3008 } elseif ($uqType == 'boolean') {
3009 $value = 1;
3010 }
3011 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
3012 $value = htmlspecialchars($value);
3013 }
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>";
3019 }
3020 }
3021 } else {
3022 $uqType = $type;
3023 }
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");
3030 //}
3031 }
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 = '';
3044 }
3045 if ($use == 'literal') {
3046 $xml = "<$elementName$elementNS>";
3047 } else {
3048 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3049 }
3050
3051 if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3052
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;
3057 }
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);
3068 }
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);
3076 }
3077 }
3078 }
3079 }
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);
3088 }
3089 $value = $nv;
3090 } else {
3091 $cols = '';
3092 }
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);
3102 }
3103 }
3104 $this->debug('contents: '.$this->varDump($contents));
3105 } else {
3106 $contents = null;
3107 }
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>";
3120 }
3121 }
3122 $this->debug('returning: '.$this->varDump($xml));
3123 return $xml;
3124 }
3125
3126 /**
3127 * register a service with the server
3128 *
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
3136 */
3137 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3138 {
3139 if ($style == 'rpc' && $use == 'encoded') {
3140 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3141 } else {
3142 $encodingStyle = '';
3143 }
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)
3170 {
3171 foreach($in as $pName => $pType)
3172 {
3173 if(strpos($pType,':')) {
3174 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3175 }
3176 $this->messages[$name.'Request'][$pName] = $pType;
3177 }
3178 }
3179
3180 if($out)
3181 {
3182 foreach($out as $pName => $pType)
3183 {
3184 if(strpos($pType,':')) {
3185 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3186 }
3187 $this->messages[$name.'Response'][$pName] = $pType;
3188 }
3189 }
3190 return true;
3191 }
3192}
3193
3194
3195
3196?><?php
3197
3198
3199
3200/**
3201*
3202* soap_parser class parses SOAP XML messages into native PHP values
3203*
3204* @author Dietrich Ayala <dietrich@ganx4.com>
3205* @version v 0.6.3
3206* @access public
3207*/
3208class soap_parser extends nusoap_base {
3209
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();
3239
3240 /**
3241 * constructor
3242 *
3243 * @param string $xml SOAP message
3244 * @param string $encoding character encoding scheme of message
3245 * @access public
3246 */
3247 function soap_parser($xml,$encoding='UTF-8',$method=''){
3248 $this->xml = $xml;
3249 $this->xml_encoding = $encoding;
3250 $this->method = $method;
3251
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');
3265
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'];
3281 }
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;
3290 }
3291 }
3292 }
3293 }
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!';
3298 }
3299 }
3300
3301 /**
3302 * start-element handler
3303 *
3304 * @param string $parser XML parser object
3305 * @param string $name element name
3306 * @param string $attrs associative array of attributes
3307 * @access private
3308 */
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++;
3318
3319 // else add self as child to whoever the current parent is
3320 if($pos != 0){
3321 $this->message[$this->parent]['children'] .= '|'.$pos;
3322 }
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);
3335 }
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");
3352 }
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;
3359
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';
3371 }
3372 $this->namespaces[$key_localpart] = $value;
3373 // set method namespace
3374 if($name == $this->root_struct_name){
3375 $this->methodNamespace = $value;
3376 }
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];
3387 }
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+ ','
3398 */
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];
3404 }
3405 }
3406 // log id
3407 if($key == 'id'){
3408 $this->ids[$value] = $pos;
3409 }
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");
3416 }
3417 // for doclit
3418 $attstr .= " $key=\"$value\"";
3419 }
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;
3426 }
3427 if($this->status == 'header'){
3428 $this->responseHeaders .= "<$name$attstr>";
3429 } elseif($this->root_struct_name != ''){
3430 $this->document .= "<$name$attstr>";
3431 }
3432 }
3433
3434 /**
3435 * end-element handler
3436 *
3437 * @param string $parser XML parser object
3438 * @param string $name element name
3439 * @access private
3440 */
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--];
3444
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);
3451 }
3452
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']);
3473 }
3474 } else {
3475 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3476 }
3477 }
3478 }
3479
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'){
3488 //
3489 }
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>";
3497 }
3498 }
3499
3500 /**
3501 * element content handler
3502 *
3503 * @param string $parser XML parser object
3504 * @param string $data element content
3505 * @access private
3506 */
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);
3511 }
3512 $this->message[$pos]['cdata'] .= $data;
3513 // for doclit
3514 if($this->status == 'header'){
3515 $this->responseHeaders .= $data;
3516 } else {
3517 $this->document .= $data;
3518 }
3519 }
3520
3521 /**
3522 * get the parsed message
3523 *
3524 * @return mixed
3525 * @access public
3526 */
3527 function get_response(){
3528 return $this->soapresponse;
3529 }
3530
3531 /**
3532 * get the parsed headers
3533 *
3534 * @return string XML or empty if no headers
3535 * @access public
3536 */
3537 function getHeaders(){
3538 return $this->responseHeaders;
3539 }
3540
3541 /**
3542 * decodes entities
3543 *
3544 * @param string $text string to translate
3545 * @access private
3546 */
3547 function decode_entities($text){
3548 foreach($this->entities as $entity => $encoded){
3549 $text = str_replace($encoded,$entity,$text);
3550 }
3551 return $text;
3552 }
3553
3554 /**
3555 * builds response structures for compound values (arrays/structs)
3556 *
3557 * @param string $pos position in node tree
3558 * @access private
3559 */
3560 function buildVal($pos){
3561 if(!isset($this->message[$pos]['type'])){
3562 $this->message[$pos]['type'] = '';
3563 }
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++;
3580 }
3581 }
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'];
3587 }
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'];
3593 }
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;
3602 }
3603 $keys[$this->message[$child_pos]['name']] = 1;
3604 }
3605 //
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'];
3611 }
3612 }
3613 }
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'];
3621 }
3622 }
3623 }
3624}
3625
3626
3627
3628?><?php
3629
3630
3631
3632/**
3633*
3634* soapclient higher level class for easy usage.
3635*
3636* usage:
3637*
3638* // instantiate client with server info
3639* $soapclient = new soapclient( string path [ ,boolean wsdl] );
3640*
3641* // call method, get results
3642* echo $soapclient->call( string methodname [ ,array parameters] );
3643*
3644* // bye bye client
3645* unset($soapclient);
3646*
3647* @author Dietrich Ayala <dietrich@ganx4.com>
3648* @version v 0.6.3
3649* @access public
3650*/
3651class soapclient extends nusoap_base {
3652
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;
3667
3668 /**
3669 * fault related variables
3670 *
3671 * @var fault
3672 * @var faultcode
3673 * @var faultstring
3674 * @var faultdetail
3675 * @access public
3676 */
3677 var $fault, $faultcode, $faultstring, $faultdetail;
3678
3679 /**
3680 * constructor
3681 *
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
3686 */
3687 function soapclient($endpoint,$wsdl = false){
3688 $this->endpoint = $endpoint;
3689
3690 // make values
3691 if($wsdl){
3692 $this->endpointType = 'wsdl';
3693 $this->wsdlFile = $this->endpoint;
3694
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!');
3709 }
3710 }
3711 }
3712
3713 /**
3714 * calls method, returns PHP native type
3715 *
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
3725 */
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();
3735
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)){
3740
3741 $this->opData = $opData;
3742 foreach($opData as $key => $value){
3743 $this->debug("$key -> $value");
3744 }
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;
3752 }
3753 // serialize payload
3754
3755 if($opData['input']['use'] == 'literal') {
3756 if (is_null($rpcParams)) {
3757 $rpcParams = $this->defaultRpcParams;
3758 }
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;
3766 }
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>";
3772 }
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';
3788 }
3789 if($namespace == ''){
3790 $namespace = 'http://testuri.org';
3791 $this->wsdl->namespaces['ns1'] = $namespace;
3792 }
3793 // serialize envelope
3794 $payload = '';
3795 foreach($params as $k => $v){
3796 $payload .= $this->serialize_val($v,$k);
3797 }
3798 $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n".$payload."</ns1:$operation>\n";
3799 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders);
3800 }
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');
3811
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>");
3820 }
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;
3828 }
3829 // single 'out' parameter
3830 return array_shift($return);
3831 // nothing returned (ie, echoVoid)
3832 } else {
3833 return "";
3834 }
3835 }
3836 }
3837 }
3838
3839 /**
3840 * get available data pertaining to an operation
3841 *
3842 * @param string $operation operation name
3843 * @return array array of data pertaining to the operation
3844 * @access public
3845 */
3846 function getOperationData($operation){
3847 if(isset($this->operations[$operation])){
3848 return $this->operations[$operation];
3849 }
3850 $this->debug("No data for operation: $operation");
3851 }
3852
3853 /**
3854 * send the SOAP message
3855 *
3856 * Note: if the operation has multiple return values
3857 * the return value of this method will be an array
3858 * of those values.
3859 *
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
3865 */
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;
3878 }
3879 $http->setSOAPAction($soapaction);
3880 if($this->proxyhost && $this->proxyport){
3881 $http->setProxy($this->proxyhost,$this->proxyport);
3882 }
3883 if($this->username != '' && $this->password != '') {
3884 $http->setCredentials($this->username,$this->password);
3885 }
3886 if($this->http_encoding != ''){
3887 $http->setEncoding($this->http_encoding);
3888 }
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');
3904 }
3905 } else {
3906 $this->setError('no http/s in endpoint url');
3907 }
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;
3914 }
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);
3923 }
3924 break;
3925 default:
3926 $this->setError('no transport found, or selected transport is not yet supported!');
3927 return false;
3928 break;
3929 }
3930 }
3931
3932 /**
3933 * processes SOAP message returned from server
3934 *
3935 * @param string unprocessed response data from server
3936 * @return mixed value of the message, decoded into a PHP type
3937 * @access private
3938 */
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;
3961 }
3962 }
3963
3964 /**
3965 * set the SOAP headers
3966 *
3967 * @param $headers string XML
3968 * @access public
3969 */
3970 function setHeaders($headers){
3971 $this->requestHeaders = $headers;
3972 }
3973
3974 /**
3975 * get the response headers
3976 *
3977 * @return mixed object SOAPx4 soapval object or empty if no headers
3978 * @access public
3979 */
3980 function getHeaders(){
3981 if($this->responseHeaders != '') {
3982 return $this->responseHeaders;
3983 }
3984 }
3985
3986 /**
3987 * set proxy info here
3988 *
3989 * @param string $proxyhost
3990 * @param string $proxyport
3991 * @access public
3992 */
3993 function setHTTPProxy($proxyhost, $proxyport) {
3994 $this->proxyhost = $proxyhost;
3995 $this->proxyport = $proxyport;
3996 }
3997
3998 /**
3999 * if authenticating, set user credentials here
4000 *
4001 * @param string $username
4002 * @param string $password
4003 * @access public
4004 */
4005 function setCredentials($username, $password) {
4006 $this->username = $username;
4007 $this->password = $password;
4008 }
4009
4010 /**
4011 * use HTTP encoding
4012 *
4013 * @param string $enc
4014 * @access public
4015 */
4016 function setHTTPEncoding($enc='gzip, deflate'){
4017 $this->http_encoding = $enc;
4018 }
4019
4020 /**
4021 * use HTTP persistent connections if possible
4022 *
4023 * @access public
4024 */
4025 function useHTTPPersistentConnection(){
4026 $this->persistentConnection = true;
4027 }
4028
4029 /**
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.
4033 *
4034 * @access public
4035 */
4036 function getDefaultRpcParams() {
4037 return $this->defaultRpcParams;
4038 }
4039
4040 /**
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.
4044 *
4045 * @param boolean $rpcParams
4046 * @access public
4047 */
4048 function setDefaultRpcParams($rpcParams) {
4049 $this->defaultRpcParams = $rpcParams;
4050 }
4051
4052 /**
4053 * dynamically creates proxy class, allowing user to directly call methods from wsdl
4054 *
4055 * @return object soap_proxy object
4056 * @access public
4057 */
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,";
4067 }
4068 $paramStr = substr($paramStr,0,strlen($paramStr)-1);
4069 }
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']."');
4075 }";
4076 unset($paramStr);
4077 }
4078 }
4079 $r = rand();
4080 $evalStr = 'class soap_proxy_'.$r.' extends soapclient {
4081 '.$evalStr.'
4082 }';
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;
4095 }
4096}
4097
4098?>