3 /* Modified by Ghislain Picard 01/10/04 */
6 $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
8 NuSOAP - Web Services Toolkit for PHP
10 Copyright (c) 2002 NuSphere Corporation
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 2.1 of the License, or (at your option) any later version.
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 If you have any questions or comments, please email:
30 http://dietrich.ganx4.com/nusoap
33 http://www.nusphere.com
40 require_once('class.soapclient.php');
41 require_once('class.soap_val.php');
42 require_once('class.soap_parser.php');
43 require_once('class.soap_fault.php');
46 require_once('class.soap_transport_http.php');
48 // optional add-on classes
49 require_once('class.xmlschema.php');
50 require_once('class.wsdl.php');
53 require_once('class.soap_server.php');*/
59 * @author Dietrich Ayala <dietrich@ganx4.com>
60 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
65 var $title = 'NuSOAP';
66 var $version = '0.6.8';
67 var $revision = '$Revision: 4994 $';
70 // toggles automatic encoding of special characters as entities
71 // (should always be true, I think)
72 var $charencoding = true
;
77 * @var XMLSchemaVersion
80 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
83 * set charset encoding for outgoing messages
85 * @var soap_defencoding
88 //var $soap_defencoding = 'UTF-8';
89 var $soap_defencoding = 'utf-8';
92 * load namespace uris into an array of uri => prefix
97 var $namespaces = array(
98 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
99 'xsd' => 'http://www.w3.org/2001/XMLSchema',
100 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
101 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
102 'si' => 'http://soapinterop.org/xsd');
103 var $usedNamespaces = array();
106 * load types into typemap array
107 * is this legacy yet?
108 * no, this is used by the xmlschema class to verify type => namespace mappings.
112 var $typemap = array(
113 'http://www.w3.org/2001/XMLSchema' => array(
114 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
115 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
116 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
117 // abstract "any" types
118 'anyType'=>'string','anySimpleType'=>'string',
120 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
121 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
122 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
123 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
124 'http://www.w3.org/1999/XMLSchema' => array(
125 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
126 'float'=>'double','dateTime'=>'string',
127 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
128 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
129 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
130 'http://xml.apache.org/xml-soap' => array('Map')
134 * entities to convert
139 var $xmlEntities = array('quot' => '"','amp' => '&',
140 'lt' => '<','gt' => '>','apos' => "'");
143 * adds debug data to the instance debug string with formatting
145 * @param string $string debug data
148 function debug($string){
149 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
153 * adds debug data to the instance debug string without formatting
155 * @param string $string debug data
158 function appendDebug($string){
159 // it would be nice to use a memory stream here to use
160 // memory more efficiently
161 $this->debug_str
.= $string;
165 * clears the current debug data for this instance
169 function clearDebug() {
170 // it would be nice to use a memory stream here to use
171 // memory more efficiently
172 $this->debug_str
= '';
176 * gets the current debug data for this instance
181 function &getDebug() {
182 // it would be nice to use a memory stream here to use
183 // memory more efficiently
184 return $this->debug_str
;
188 * gets the current debug data for this instance as an XML comment
189 * this may change the contents of the debug data
191 * @return debug data as an XML comment
194 function &getDebugAsXMLComment() {
195 // it would be nice to use a memory stream here to use
196 // memory more efficiently
197 while (strpos($this->debug_str
, '--')) {
198 $this->debug_str
= str_replace('--', '- -', $this->debug_str
);
200 return "<!--\n" . $this->debug_str
. "\n-->";
204 * expands entities, e.g. changes '<' to '<'.
206 * @param string $val The string in which to expand entities.
209 function expandEntities($val) {
210 if ($this->charencoding
) {
211 $val = str_replace('&', '&', $val);
212 $val = str_replace("'", ''', $val);
213 $val = str_replace('"', '"', $val);
214 $val = str_replace('<', '<', $val);
215 $val = str_replace('>', '>', $val);
221 * returns error string if present
223 * @return mixed error string or false
227 if($this->error_str
!= ''){
228 return $this->error_str
;
236 * @return boolean $string error string
239 function setError($str){
240 $this->error_str
= $str;
244 * detect if array is a simple array or a struct (associative array)
246 * @param $val The PHP array
247 * @return string (arraySimple|arrayStruct)
250 function isArraySimpleOrStruct($val) {
251 $keyList = array_keys($val);
252 foreach ($keyList as $keyListValue) {
253 if (!is_int($keyListValue)) {
254 return 'arrayStruct';
257 return 'arraySimple';
261 * serializes PHP values in accordance w/ section 5. Type information is
262 * not serialized if $use == 'literal'.
267 function serialize_val($val,$name=false
,$type=false
,$name_ns=false
,$type_ns=false
,$attributes=false
,$use='encoded'){
268 if(is_object($val) &&
269 (get_class($val) == 'soapval') ||
270 (get_class($val) == 'servooattachment') ) {
271 return $val->serialize($use);
273 $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
274 // if no name, use item
275 $name = (!$name||
is_numeric($name)) ?
'soapVal' : $name;
276 // if name has ns, add ns prefix to name
279 $prefix = 'nu'.rand(1000,9999);
280 $name = $prefix.':'.$name;
281 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
283 // if type is prefixed, create type prefix
284 if($type_ns != '' && $type_ns == $this->namespaces
['xsd']){
285 // need to fix this. shouldn't default to xsd if no ns specified
286 // w/o checking against typemap
287 $type_prefix = 'xsd';
289 $type_prefix = 'ns'.rand(1000,9999);
290 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
292 // serialize attributes if present
295 foreach($attributes as $k => $v){
296 $atts .= " $k=\"$v\"";
299 // serialize if an xsd built-in primitive type
300 if($type != '' && isset($this->typemap
[$this->XMLSchemaVersion
][$type])){
302 if ($type == 'boolean') {
303 $val = $val ?
'true' : 'false';
307 } else if (is_string($val)) {
308 $val = $this->expandEntities($val);
310 if ($use == 'literal') {
311 return "<$name$xmlns>$val</$name>";
313 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
316 // detect type and serialize
319 case ($type == '' && is_null($val)):
320 if ($use == 'literal') {
321 // TODO: depends on nillable
322 $xml .= "<$name$xmlns/>";
324 $xml .= "<$name$xmlns xsi:nil=\"true\"/>";
327 case (is_bool($val) ||
$type == 'boolean'):
328 if ($type == 'boolean') {
329 $val = $val ?
'true' : 'false';
333 if ($use == 'literal') {
334 $xml .= "<$name$xmlns $atts>$val</$name>";
336 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
339 case (is_int($val) ||
is_long($val) ||
$type == 'int'):
340 if ($use == 'literal') {
341 $xml .= "<$name$xmlns $atts>$val</$name>";
343 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
346 case (is_float($val)||
is_double($val) ||
$type == 'float'):
347 if ($use == 'literal') {
348 $xml .= "<$name$xmlns $atts>$val</$name>";
350 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
353 case (is_string($val) ||
$type == 'string'):
354 $val = $this->expandEntities($val);
355 if ($use == 'literal') {
356 $xml .= "<$name$xmlns $atts>$val</$name>";
358 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
361 case is_object($val):
362 $name = get_class($val);
363 foreach(get_object_vars($val) as $k => $v){
364 $pXml = isset($pXml) ?
$pXml.$this->serialize_val($v,$k,false
,false
,false
,false
,$use) : $this->serialize_val($v,$k,false
,false
,false
,false
,$use);
366 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
369 case (is_array($val) ||
$type):
370 // detect if struct or array
371 $valueType = $this->isArraySimpleOrStruct($val);
372 if($valueType=='arraySimple' ||
ereg('^ArrayOf',$type)){
374 if(is_array($val) && count($val)> 0){
376 if(is_object($v) && get_class($v) == 'soapval'){
377 $tt_ns = $v->type_ns
;
379 } elseif (is_array($v)) {
380 $tt = $this->isArraySimpleOrStruct($v);
384 $array_types[$tt] = 1;
385 $xml .= $this->serialize_val($v,'item',false
,false
,false
,false
,$use);
388 if(count($array_types) > 1){
389 $array_typename = 'xsd:ur-type';
390 } elseif(isset($tt) && isset($this->typemap
[$this->XMLSchemaVersion
][$tt])) {
391 if ($tt == 'integer') {
394 $array_typename = 'xsd:'.$tt;
395 } elseif(isset($tt) && $tt == 'arraySimple'){
396 $array_typename = 'SOAP-ENC:Array';
397 } elseif(isset($tt) && $tt == 'arrayStruct'){
398 $array_typename = 'unnamed_struct_use_soapval';
400 // if type is prefixed, create type prefix
401 if ($tt_ns != '' && $tt_ns == $this->namespaces
['xsd']){
402 $array_typename = 'xsd:' . $tt;
404 $tt_prefix = 'ns' . rand(1000, 9999);
405 $array_typename = "$tt_prefix:$tt";
406 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
408 $array_typename = $tt;
412 if ($use == 'literal') {
414 } else if (isset($type) && isset($type_prefix)) {
415 $type_str = " xsi:type=\"$type_prefix:$type\"";
417 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
421 if ($use == 'literal') {
423 } else if (isset($type) && isset($type_prefix)) {
424 $type_str = " xsi:type=\"$type_prefix:$type\"";
426 $type_str = " xsi:type=\"SOAP-ENC:Array\"";
429 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
432 if(isset($type) && isset($type_prefix)){
433 $type_str = " xsi:type=\"$type_prefix:$type\"";
437 if ($use == 'literal') {
438 $xml .= "<$name$xmlns $atts>";
440 $xml .= "<$name$xmlns$type_str$atts>";
442 foreach($val as $k => $v){
444 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
446 $xml .= $this->serialize_val($k,'key',false
,false
,false
,false
,$use);
447 $xml .= $this->serialize_val($v,'value',false
,false
,false
,false
,$use);
450 $xml .= $this->serialize_val($v,$k,false
,false
,false
,false
,$use);
457 $xml .= 'not detected, got '.gettype($val).' for '.$val;
467 * @param string headers optional
468 * @param array namespaces optional
469 * @param string style optional (rpc|document)
470 * @param string use optional (encoded|literal)
471 * @return string message
474 function serializeEnvelope($body,$headers=false
,$namespaces=array(),$style='rpc',$use='encoded'){
475 // TODO: add an option to automatically run utf8_encode on $body and $headers
476 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
477 // one to send arbitrary UTF-8 characters, not just characters that map to utf-8
479 // serialize namespaces
481 foreach(array_merge($this->namespaces
,$namespaces) as $k => $v){
482 $ns_string .= " xmlns:$k=\"$v\"";
484 if($style == 'rpc' && $use == 'encoded') {
485 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
490 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
492 // serialize envelope
494 '<?xml version="1.0" encoding="'.$this->soap_defencoding
.'"?'.">".
495 '<SOAP-ENV:Envelope'.$ns_string.">".
500 "</SOAP-ENV:Envelope>";
503 function formatDump($str){
504 $str = htmlspecialchars($str);
509 * contracts a qualified name
511 * @param string $string qname
512 * @return string contracted qname
515 function contractQname($qname){
516 // get element namespace
517 //$this->xdebug("Contract $qname");
518 if (strrpos($qname, ':')) {
519 // get unqualified name
520 $name = substr($qname, strrpos($qname, ':') +
1);
522 $ns = substr($qname, 0, strrpos($qname, ':'));
523 $p = $this->getPrefixFromNamespace($ns);
525 return $p . ':' . $name;
534 * expands a qualified name
536 * @param string $string qname
537 * @return string expanded qname
540 function expandQname($qname){
541 // get element prefix
542 if(strpos($qname,':') && !ereg('^http://',$qname)){
543 // get unqualified name
544 $name = substr(strstr($qname,':'),1);
546 $prefix = substr($qname,0,strpos($qname,':'));
547 if(isset($this->namespaces
[$prefix])){
548 return $this->namespaces
[$prefix].':'.$name;
558 * returns the local part of a prefixed string
559 * returns the original string, if not prefixed
565 function getLocalPart($str){
566 if($sstr = strrchr($str,':')){
567 // get unqualified name
568 return substr( $sstr, 1 );
575 * returns the prefix part of a prefixed string
576 * returns false, if not prefixed
582 function getPrefix($str){
583 if($pos = strrpos($str,':')){
585 return substr($str,0,$pos);
591 * pass it a prefix, it returns a namespace
592 * returns false if no namespace registered with the given prefix
598 function getNamespaceFromPrefix($prefix){
599 if (isset($this->namespaces
[$prefix])) {
600 return $this->namespaces
[$prefix];
602 //$this->setError("No namespace registered for prefix '$prefix'");
607 * returns the prefix for a given namespace (or prefix)
608 * or false if no prefixes registered for the given namespace
614 function getPrefixFromNamespace($ns) {
615 foreach ($this->namespaces
as $p => $n) {
616 if ($ns == $n ||
$ns == $p) {
617 $this->usedNamespaces
[$p] = $n;
625 * returns the time in ODBC canonical form with microseconds
630 function getmicrotime() {
631 if (function_exists('gettimeofday')) {
632 $tod = gettimeofday();
634 $usec = $tod['usec'];
639 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
642 function varDump($data) {
645 $ret_val = ob_get_contents();
651 // XML Schema Datatype Helper Functions
653 //xsd:dateTime helpers
656 * convert unix timestamp to ISO 8601 compliant date string
658 * @param string $timestamp Unix time stamp
661 function timestamp_to_iso8601($timestamp,$utc=true
){
662 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
665 '([0-9]{4})-'. // centuries & years CCYY-
666 '([0-9]{2})-'. // months MM-
667 '([0-9]{2})'. // days DD
669 '([0-9]{2}):'. // hours hh:
670 '([0-9]{2}):'. // minutes mm:
671 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
672 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
674 if(ereg($eregStr,$datestr,$regs)){
675 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
684 * convert ISO 8601 compliant date string to unix timestamp
686 * @param string $datestr ISO 8601 compliant date string
689 function iso8601_to_timestamp($datestr){
691 '([0-9]{4})-'. // centuries & years CCYY-
692 '([0-9]{2})-'. // months MM-
693 '([0-9]{2})'. // days DD
695 '([0-9]{2}):'. // hours hh:
696 '([0-9]{2}):'. // minutes mm:
697 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
698 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
699 if(ereg($eregStr,$datestr,$regs)){
702 $op = substr($regs[8],0,1);
703 $h = substr($regs[8],1,2);
704 $m = substr($regs[8],strlen($regs[8])-2,2);
706 $regs[4] = $regs[4] +
$h;
707 $regs[5] = $regs[5] +
$m;
708 } elseif($op == '+'){
709 $regs[4] = $regs[4] - $h;
710 $regs[5] = $regs[5] - $m;
713 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
719 function usleepWindows($usec)
721 $start = gettimeofday();
725 $stop = gettimeofday();
726 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
727 +
$stop['usec'] - $start['usec'];
729 while ($timePassed < $usec);
737 * soap_fault class, allows for creation of faults
738 * mainly used for returning faults from deployed functions
739 * in a server instance.
740 * @author Dietrich Ayala <dietrich@ganx4.com>
741 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
744 class soap_fault
extends nusoap_base
{
754 * @param string $faultcode (client | server)
755 * @param string $faultactor only used when msg routed between multiple actors
756 * @param string $faultstring human readable error message
757 * @param string $faultdetail
759 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
760 $this->faultcode
= $faultcode;
761 $this->faultactor
= $faultactor;
762 $this->faultstring
= $faultstring;
763 $this->faultdetail
= $faultdetail;
771 function serialize(){
773 foreach($this->namespaces
as $k => $v){
774 $ns_string .= "\n xmlns:$k=\"$v\"";
777 '<?xml version="1.0" encoding="'.$this->soap_defencoding
.'"?>'.
778 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
781 '<faultcode>'.$this->expandEntities($this->faultcode
).'</faultcode>'.
782 '<faultactor>'.$this->expandEntities($this->faultactor
).'</faultactor>'.
783 '<faultstring>'.$this->expandEntities($this->faultstring
).'</faultstring>'.
784 '<detail>'.$this->serialize_val($this->faultdetail
).'</detail>'.
787 '</SOAP-ENV:Envelope>';
799 * parses an XML Schema, allows access to it's data, other utility methods
800 * no validation... yet.
801 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
802 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
803 * tutorials I refer to :)
805 * @author Dietrich Ayala <dietrich@ganx4.com>
806 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
809 class XMLSchema
extends nusoap_base
{
815 var $enclosingNamespaces;
817 var $schemaInfo = array();
818 var $schemaTargetNamespace = '';
819 // types, elements, attributes defined by the schema
820 var $attributes = array();
821 var $complexTypes = array();
822 var $currentComplexType = false
;
823 var $elements = array();
824 var $currentElement = false
;
825 var $simpleTypes = array();
826 var $currentSimpleType = false
;
828 var $imports = array();
833 var $depth_array = array();
834 var $message = array();
835 var $defaultNamespace = array();
840 * @param string $schema schema document URI
841 * @param string $xml xml document URI
842 * @param string $namespaces namespaces defined in enclosing XML
845 function XMLSchema($schema='',$xml='',$namespaces=array()){
847 $this->debug('xmlschema class instantiated, inside constructor');
849 $this->schema
= $schema;
853 $this->enclosingNamespaces
= $namespaces;
854 $this->namespaces
= array_merge($this->namespaces
, $namespaces);
858 $this->debug('initial schema file: '.$schema);
859 $this->parseFile($schema, 'schema');
864 $this->debug('initial xml file: '.$xml);
865 $this->parseFile($xml, 'xml');
873 * @param string $xml, path/URL to XML file
874 * @param string $type, (schema | xml)
878 function parseFile($xml,$type){
881 $xmlStr = @join("",@file($xml));
883 $msg = 'Error reading XML from '.$xml;
884 $this->setError($msg);
888 $this->debug("parsing $xml");
889 $this->parseString($xmlStr,$type);
890 $this->debug("done parsing $xml");
898 * parse an XML string
900 * @param string $xml path or URL
901 * @param string $type, (schema|xml)
904 function parseString($xml,$type){
908 // Create an XML parser.
909 $this->parser
= xml_parser_create();
910 // Set the options for parsing the XML data.
911 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
913 // Set the object for the parser.
914 xml_set_object($this->parser
, $this);
916 // Set the element handlers for the parser.
917 if($type == "schema"){
918 xml_set_element_handler($this->parser
, 'schemaStartElement','schemaEndElement');
919 xml_set_character_data_handler($this->parser
,'schemaCharacterData');
920 } elseif($type == "xml"){
921 xml_set_element_handler($this->parser
, 'xmlStartElement','xmlEndElement');
922 xml_set_character_data_handler($this->parser
,'xmlCharacterData');
925 // Parse the XML file.
926 if(!xml_parse($this->parser
,$xml,true
)){
927 // Display an error message.
928 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
929 xml_get_current_line_number($this->parser
),
930 xml_error_string(xml_get_error_code($this->parser
))
932 $this->debug($errstr);
933 $this->debug("XML payload:\n" . $xml);
934 $this->setError($errstr);
937 xml_parser_free($this->parser
);
939 $this->debug('no xml passed to parseString()!!');
940 $this->setError('no xml passed to parseString()!!');
945 * start-element handler
947 * @param string $parser XML parser object
948 * @param string $name element name
949 * @param string $attrs associative array of attributes
952 function schemaStartElement($parser, $name, $attrs) {
954 // position in the total number of elements, starting from 0
955 $pos = $this->position++
;
956 $depth = $this->depth++
;
957 // set self as current value for this depth
958 $this->depth_array
[$depth] = $pos;
959 $this->message
[$pos] = array('cdata' => '');
961 $this->defaultNamespace
[$pos] = $this->defaultNamespace
[$this->depth_array
[$depth - 1]];
963 $this->defaultNamespace
[$pos] = false
;
966 // get element prefix
967 if($prefix = $this->getPrefix($name)){
968 // get unqualified name
969 $name = $this->getLocalPart($name);
974 // loop thru attributes, expanding, and registering namespace declarations
975 if(count($attrs) > 0){
976 foreach($attrs as $k => $v){
977 // if ns declarations, add to class level array of valid namespaces
978 if(ereg("^xmlns",$k)){
979 //$this->xdebug("$k: $v");
980 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
981 if($ns_prefix = substr(strrchr($k,':'),1)){
982 //$this->xdebug("Add namespace[$ns_prefix] = $v");
983 $this->namespaces
[$ns_prefix] = $v;
985 $this->defaultNamespace
[$pos] = $v;
986 if (! $this->getPrefixFromNamespace($v)) {
987 $this->namespaces
['ns'.(count($this->namespaces
)+
1)] = $v;
990 if($v == 'http://www.w3.org/2001/XMLSchema' ||
$v == 'http://www.w3.org/1999/XMLSchema'){
991 $this->XMLSchemaVersion
= $v;
992 $this->namespaces
['xsi'] = $v.'-instance';
996 foreach($attrs as $k => $v){
997 // expand each attribute
998 $k = strpos($k,':') ?
$this->expandQname($k) : $k;
999 $v = strpos($v,':') ?
$this->expandQname($v) : $v;
1006 // find status, register data
1011 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1012 $this->complexTypes
[$this->currentComplexType
]['compositor'] = $name;
1013 if($name == 'all' ||
$name == 'sequence'){
1014 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'struct';
1018 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1019 $this->xdebug("parsing attribute:");
1020 $this->appendDebug($this->varDump($attrs));
1021 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1022 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1023 if (!strpos($v, ':')) {
1024 // no namespace in arrayType attribute value...
1025 if ($this->defaultNamespace
[$pos]) {
1026 // ...so use the default
1027 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace
[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1031 if(isset($attrs['name'])){
1032 $this->attributes
[$attrs['name']] = $attrs;
1033 $aname = $attrs['name'];
1034 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1035 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1036 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1040 } elseif(isset($attrs['ref'])){
1041 $aname = $attrs['ref'];
1042 $this->attributes
[$attrs['ref']] = $attrs;
1045 if(isset($this->currentComplexType
)){
1046 $this->complexTypes
[$this->currentComplexType
]['attrs'][$aname] = $attrs;
1047 } elseif(isset($this->currentElement
)){
1048 $this->elements
[$this->currentElement
]['attrs'][$aname] = $attrs;
1050 // arrayType attribute
1051 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) ||
$this->getLocalPart($aname) == 'arrayType'){
1052 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1053 $prefix = $this->getPrefix($aname);
1054 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1055 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1059 if(strpos($v,'[,]')){
1060 $this->complexTypes
[$this->currentComplexType
]['multidimensional'] = true
;
1062 $v = substr($v,0,strpos($v,'[')); // clip the []
1063 if(!strpos($v,':') && isset($this->typemap
[$this->XMLSchemaVersion
][$v])){
1064 $v = $this->XMLSchemaVersion
.':'.$v;
1066 $this->complexTypes
[$this->currentComplexType
]['arrayType'] = $v;
1070 if(isset($attrs['name'])){
1071 $this->xdebug('processing named complexType '.$attrs['name']);
1072 $this->currentElement
= false
;
1073 $this->currentComplexType
= $attrs['name'];
1074 $this->complexTypes
[$this->currentComplexType
] = $attrs;
1075 $this->complexTypes
[$this->currentComplexType
]['typeClass'] = 'complexType';
1076 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1077 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1079 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'struct';
1082 $this->xdebug('processing unnamed complexType for element '.$this->currentElement
);
1083 $this->currentComplexType
= $this->currentElement
. '_ContainedType';
1084 $this->currentElement
= false
;
1085 $this->complexTypes
[$this->currentComplexType
] = $attrs;
1086 $this->complexTypes
[$this->currentComplexType
]['typeClass'] = 'complexType';
1087 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1088 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1090 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'struct';
1095 // elements defined as part of a complex type should
1096 // not really be added to $this->elements, but for some
1098 if(isset($attrs['type'])){
1099 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1100 if (! $this->getPrefix($attrs['type'])) {
1101 if ($this->defaultNamespace
[$pos]) {
1102 $attrs['type'] = $this->defaultNamespace
[$pos] . ':' . $attrs['type'];
1103 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1106 $this->currentElement
= $attrs['name'];
1107 $this->elements
[ $attrs['name'] ] = $attrs;
1108 $this->elements
[ $attrs['name'] ]['typeClass'] = 'element';
1109 if (!isset($this->elements
[ $attrs['name'] ]['form'])) {
1110 $this->elements
[ $attrs['name'] ]['form'] = $this->schemaInfo
['elementFormDefault'];
1112 $ename = $attrs['name'];
1113 } elseif(isset($attrs['ref'])){
1114 $ename = $attrs['ref'];
1116 $this->xdebug("processing untyped element ".$attrs['name']);
1117 $this->currentElement
= $attrs['name'];
1118 $this->elements
[ $attrs['name'] ] = $attrs;
1119 $this->elements
[ $attrs['name'] ]['typeClass'] = 'element';
1120 $this->elements
[ $attrs['name'] ]['type'] = $this->schemaTargetNamespace
. ':' . $attrs['name'] . '_ContainedType';
1121 if (!isset($this->elements
[ $attrs['name'] ]['form'])) {
1122 $this->elements
[ $attrs['name'] ]['form'] = $this->schemaInfo
['elementFormDefault'];
1125 if(isset($ename) && $this->currentComplexType
){
1126 $this->complexTypes
[$this->currentComplexType
]['elements'][$ename] = $attrs;
1129 // we ignore enumeration values
1130 //case 'enumeration':
1133 if (isset($attrs['schemaLocation'])) {
1134 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1135 $this->imports
[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false
);
1137 //$this->xdebug('import namespace ' . $attrs['namespace']);
1138 $this->imports
[$attrs['namespace']][] = array('location' => '', 'loaded' => true
);
1139 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1140 $this->namespaces
['ns'.(count($this->namespaces
)+
1)] = $attrs['namespace'];
1145 //$this->xdebug("in restriction for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1146 if($this->currentElement
){
1147 $this->elements
[$this->currentElement
]['type'] = $attrs['base'];
1148 } elseif($this->currentSimpleType
){
1149 $this->simpleTypes
[$this->currentSimpleType
]['type'] = $attrs['base'];
1150 } elseif($this->currentComplexType
){
1151 $this->complexTypes
[$this->currentComplexType
]['restrictionBase'] = $attrs['base'];
1152 if(strstr($attrs['base'],':') == ':Array'){
1153 $this->complexTypes
[$this->currentComplexType
]['phpType'] = 'array';
1158 $this->schemaInfo
= $attrs;
1159 $this->schemaInfo
['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1160 if (isset($attrs['targetNamespace'])) {
1161 $this->schemaTargetNamespace
= $attrs['targetNamespace'];
1163 if (!isset($attrs['elementFormDefault'])) {
1164 $this->schemaInfo
['elementFormDefault'] = 'unqualified';
1168 if(isset($attrs['name'])){
1169 $this->xdebug("processing simpleType for name " . $attrs['name']);
1170 $this->currentSimpleType
= $attrs['name'];
1171 $this->simpleTypes
[ $attrs['name'] ] = $attrs;
1172 $this->simpleTypes
[ $attrs['name'] ]['typeClass'] = 'simpleType';
1173 $this->simpleTypes
[ $attrs['name'] ]['phpType'] = 'scalar';
1175 //echo 'not parsing: '.$name;
1180 //$this->xdebug("do not have anything to do for element $name");
1185 * end-element handler
1187 * @param string $parser XML parser object
1188 * @param string $name element name
1191 function schemaEndElement($parser, $name) {
1192 // bring depth down a notch
1194 // position of current element is equal to the last value left in depth_array for my depth
1195 if(isset($this->depth_array
[$this->depth
])){
1196 $pos = $this->depth_array
[$this->depth
];
1199 if($name == 'complexType'){
1200 $this->currentComplexType
= false
;
1201 $this->currentElement
= false
;
1203 if($name == 'element'){
1204 $this->currentElement
= false
;
1206 if($name == 'simpleType'){
1207 $this->currentSimpleType
= false
;
1212 * element content handler
1214 * @param string $parser XML parser object
1215 * @param string $data element content
1218 function schemaCharacterData($parser, $data){
1219 $pos = $this->depth_array
[$this->depth
- 1];
1220 $this->message
[$pos]['cdata'] .= $data;
1224 * serialize the schema
1228 function serializeSchema(){
1230 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion
);
1233 if (sizeof($this->imports
) > 0) {
1234 foreach($this->imports
as $ns => $list) {
1235 foreach ($list as $ii) {
1236 if ($ii['location'] != '') {
1237 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1239 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1245 foreach($this->complexTypes
as $typeName => $attrs){
1247 // serialize child elements
1248 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1249 foreach($attrs['elements'] as $element => $eParts){
1250 if(isset($eParts['ref'])){
1251 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1253 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
1258 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1259 foreach($attrs['attrs'] as $attr => $aParts){
1260 $contentStr .= " <$schemaPrefix:attribute ref=\"".$this->contractQName($aParts['ref']).'"';
1261 if(isset($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1262 $this->usedNamespaces
['wsdl'] = $this->namespaces
['wsdl'];
1263 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType']).'"';
1265 $contentStr .= "/>\n";
1269 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1270 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1272 // compositor obviates complex/simple content
1273 if(isset($attrs['compositor']) && ($attrs['compositor'] != '')){
1274 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1276 // complex or simple content
1277 elseif((isset($attrs['elements']) && count($attrs['elements']) > 0) ||
(isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1278 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1280 // finalize complex type
1281 if($contentStr != ''){
1282 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1284 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1286 $xml .= $contentStr;
1289 if(isset($this->simpleTypes
) && count($this->simpleTypes
) > 0){
1290 foreach($this->simpleTypes
as $typeName => $attr){
1291 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n </$schemaPrefix:simpleType>";
1295 if(isset($this->elements
) && count($this->elements
) > 0){
1296 foreach($this->elements
as $element => $eParts){
1297 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1301 if(isset($this->attributes
) && count($this->attributes
) > 0){
1302 foreach($this->attributes
as $attr => $aParts){
1303 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1307 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1308 foreach (array_diff($this->usedNamespaces
, $this->enclosingNamespaces
) as $nsp => $ns) {
1309 $el .= " xmlns:$nsp=\"$ns\"\n";
1311 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1316 * adds debug data to the clas level debug string
1318 * @param string $string debug data
1321 function xdebug($string){
1322 $this->debug('<' . $this->schemaTargetNamespace
. '> '.$string);
1326 * get the PHP type of a user defined type in the schema
1327 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1328 * returns false if no type exists, or not w/ the given namespace
1329 * else returns a string that is either a native php type, or 'struct'
1331 * @param string $type, name of defined type
1332 * @param string $ns, namespace of type
1336 function getPHPType($type,$ns){
1337 if(isset($this->typemap
[$ns][$type])){
1338 //print "found type '$type' and ns $ns in typemap<br>";
1339 return $this->typemap
[$ns][$type];
1340 } elseif(isset($this->complexTypes
[$type])){
1341 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1342 return $this->complexTypes
[$type]['phpType'];
1348 * returns an array of information about a given type
1349 * returns false if no type exists by the given name
1352 * 'elements' => array(), // refs to elements array
1353 * 'restrictionBase' => '',
1355 * 'order' => '(sequence|all)',
1356 * 'attrs' => array() // refs to attributes array
1363 function getTypeDef($type){
1364 //$this->debug("in getTypeDef for type $type");
1365 if(isset($this->complexTypes
[$type])){
1366 $this->xdebug("in getTypeDef, found complexType $type");
1367 return $this->complexTypes
[$type];
1368 } elseif(isset($this->simpleTypes
[$type])){
1369 $this->xdebug("in getTypeDef, found simpleType $type");
1370 if (!isset($this->simpleTypes
[$type]['phpType'])) {
1371 // get info for type to tack onto the simple type
1372 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1373 $uqType = substr($this->simpleTypes
[$type]['type'], strrpos($this->simpleTypes
[$type]['type'], ':') +
1);
1374 $ns = substr($this->simpleTypes
[$type]['type'], 0, strrpos($this->simpleTypes
[$type]['type'], ':'));
1375 $etype = $this->getTypeDef($uqType);
1377 if (isset($etype['phpType'])) {
1378 $this->simpleTypes
[$type]['phpType'] = $etype['phpType'];
1380 if (isset($etype['elements'])) {
1381 $this->simpleTypes
[$type]['elements'] = $etype['elements'];
1385 return $this->simpleTypes
[$type];
1386 } elseif(isset($this->elements
[$type])){
1387 $this->xdebug("in getTypeDef, found element $type");
1388 if (!isset($this->elements
[$type]['phpType'])) {
1389 // get info for type to tack onto the element
1390 $uqType = substr($this->elements
[$type]['type'], strrpos($this->elements
[$type]['type'], ':') +
1);
1391 $ns = substr($this->elements
[$type]['type'], 0, strrpos($this->elements
[$type]['type'], ':'));
1392 $etype = $this->getTypeDef($uqType);
1394 if (isset($etype['phpType'])) {
1395 $this->elements
[$type]['phpType'] = $etype['phpType'];
1397 if (isset($etype['elements'])) {
1398 $this->elements
[$type]['elements'] = $etype['elements'];
1400 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1401 $this->elements
[$type]['phpType'] = 'scalar';
1404 return $this->elements
[$type];
1405 } elseif(isset($this->attributes
[$type])){
1406 $this->xdebug("in getTypeDef, found attribute $type");
1407 return $this->attributes
[$type];
1409 $this->xdebug("in getTypeDef, did not find $type");
1414 * returns a sample serialization of a given type, or false if no type by the given name
1416 * @param string $type, name of type
1420 function serializeTypeDef($type){
1421 //print "in sTD() for type $type<br>";
1422 if($typeDef = $this->getTypeDef($type)){
1424 if(is_array($typeDef['attrs'])){
1425 foreach($attrs as $attName => $data){
1426 $str .= " $attName=\"{type = ".$data['type']."}\"";
1429 $str .= " xmlns=\"".$this->schema
['targetNamespace']."\"";
1430 if(count($typeDef['elements']) > 0){
1432 foreach($typeDef['elements'] as $element => $eData){
1433 $str .= $this->serializeTypeDef($element);
1436 } elseif($typeDef['typeClass'] == 'element') {
1437 $str .= "></$type>";
1447 * returns HTML form elements that allow a user
1448 * to enter values for creating an instance of the given type.
1450 * @param string $name, name for type instance
1451 * @param string $type, name of type
1455 function typeToForm($name,$type){
1457 if($typeDef = $this->getTypeDef($type)){
1459 if($typeDef['phpType'] == 'struct'){
1460 $buffer .= '<table>';
1461 foreach($typeDef['elements'] as $child => $childDef){
1463 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1464 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1466 $buffer .= '</table>';
1468 } elseif($typeDef['phpType'] == 'array'){
1469 $buffer .= '<table>';
1470 for($i=0;$i < 3; $i++
){
1472 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1473 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1475 $buffer .= '</table>';
1478 $buffer .= "<input type='text' name='parameters[$name]'>";
1481 $buffer .= "<input type='text' name='parameters[$name]'>";
1487 * adds a complex type to the schema
1497 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1501 * example: PHP associative array ( SOAP Struct )
1508 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1512 * @param typeClass (complexType|simpleType|attribute)
1513 * @param phpType: currently supported are array and struct (php assoc array)
1514 * @param compositor (all|sequence|choice)
1515 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1516 * @param elements = array ( name = array(name=>'',type=>'') )
1517 * @param attrs = array(
1519 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1520 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1523 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1526 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1527 $this->complexTypes
[$name] = array(
1529 'typeClass' => $typeClass,
1530 'phpType' => $phpType,
1531 'compositor'=> $compositor,
1532 'restrictionBase' => $restrictionBase,
1533 'elements' => $elements,
1535 'arrayType' => $arrayType
1538 $this->xdebug("addComplexType $name:");
1539 $this->appendDebug($this->varDump($this->complexTypes
[$name]));
1543 * adds a simple type to the schema
1546 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1547 * @param typeClass (simpleType)
1548 * @param phpType: (scalar)
1552 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
1553 $this->simpleTypes
[$name] = array(
1555 'typeClass' => $typeClass,
1556 'phpType' => $phpType,
1557 'type' => $restrictionBase
1560 $this->xdebug("addSimpleType $name:");
1561 $this->appendDebug($this->varDump($this->simpleTypes
[$name]));
1572 * for creating serializable abstractions of native PHP types
1573 * NOTE: this is only really used when WSDL is not available.
1575 * @author Dietrich Ayala <dietrich@ganx4.com>
1576 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
1579 class soapval
extends nusoap_base
{
1583 * @param string $name optional name
1584 * @param string $type optional type name
1585 * @param mixed $value optional value
1586 * @param string $namespace optional namespace of value
1587 * @param string $type_namespace optional namespace of type
1588 * @param array $attributes associative array of attributes to add to element serialization
1591 function soapval($name='soapval',$type=false
,$value=-1,$element_ns=false
,$type_ns=false
,$attributes=false
) {
1592 $this->name
= $name;
1593 $this->value
= $value;
1594 $this->type
= $type;
1595 $this->element_ns
= $element_ns;
1596 $this->type_ns
= $type_ns;
1597 $this->attributes
= $attributes;
1601 * return serialized value
1603 * @return string XML data
1606 function serialize($use='encoded') {
1607 return $this->serialize_val($this->value
,$this->name
,$this->type
,$this->element_ns
,$this->type_ns
,$this->attributes
,$use);
1611 * decodes a soapval object into a PHP native type
1613 * @param object $soapval optional SOAPx4 soapval object, else uses self
1618 return $this->value
;
1629 * transport class for sending/receiving data via HTTP and HTTPS
1630 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1632 * @author Dietrich Ayala <dietrich@ganx4.com>
1633 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
1636 class soap_transport_http
extends nusoap_base
{
1640 var $digest_uri = '';
1645 var $request_method = 'POST';
1646 var $protocol_version = '1.0';
1648 var $outgoing_headers = array();
1649 var $incoming_headers = array();
1650 var $outgoing_payload = '';
1651 var $incoming_payload = '';
1652 var $useSOAPAction = true
;
1653 var $persistentConnection = false
;
1654 var $ch = false
; // cURL handle
1661 function soap_transport_http($url){
1662 $this->setURL($url);
1663 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision
, $rev);
1664 $this->outgoing_headers
['User-Agent'] = $this->title
.'/'.$this->version
.' ('.$rev[1].')';
1667 function setURL($url) {
1670 $u = parse_url($url);
1671 foreach($u as $k => $v){
1672 $this->debug("$k = $v");
1676 // add any GET params to path
1677 if(isset($u['query']) && $u['query'] != ''){
1678 $this->path
.= '?' . $u['query'];
1682 if(!isset($u['port'])){
1683 if($u['scheme'] == 'https'){
1690 $this->uri
= $this->path
;
1691 $this->digest_uri
= $this->uri
;
1694 if (!isset($u['port'])) {
1695 $this->outgoing_headers
['Host'] = $this->host
;
1697 $this->outgoing_headers
['Host'] = $this->host
.':'.$this->port
;
1700 if (isset($u['user']) && $u['user'] != '') {
1701 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ?
urldecode($u['pass']) : '');
1705 function connect($connection_timeout=0,$response_timeout=30){
1706 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
1707 // "regular" socket.
1708 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
1709 // loaded), and until PHP5 stream_get_wrappers is not available.
1710 // if ($this->scheme == 'https') {
1711 // if (version_compare(phpversion(), '4.3.0') >= 0) {
1712 // if (extension_loaded('openssl')) {
1713 // $this->scheme = 'ssl';
1714 // $this->debug('Using SSL over OpenSSL');
1718 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
1719 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
1720 // use persistent connection
1721 if($this->persistentConnection
&& isset($this->fp
) && is_resource($this->fp
)){
1722 if (!feof($this->fp
)) {
1723 $this->debug('Re-use persistent connection');
1727 $this->debug('Closed persistent connection at EOF');
1730 // munge host if using OpenSSL
1731 if ($this->scheme
== 'ssl') {
1732 $host = 'ssl://' . $this->host
;
1734 $host = $this->host
;
1736 $this->debug('calling fsockopen with host ' . $host);
1739 if($connection_timeout > 0){
1740 $this->fp
= @fsockopen( $host, $this->port
, $this->errno
, $this->error_str
, $connection_timeout);
1742 $this->fp
= @fsockopen( $host, $this->port
, $this->errno
, $this->error_str
);
1747 $msg = 'Couldn\'t open socket connection to server ' . $this->url
;
1749 $msg .= ', Error ('.$this->errno
.'): '.$this->error_str
;
1751 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
1754 $this->setError($msg);
1758 // set response timeout
1759 socket_set_timeout( $this->fp
, $response_timeout);
1761 $this->debug('socket connected');
1763 } else if ($this->scheme
== 'https') {
1764 if (!extension_loaded('curl')) {
1765 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
1768 $this->debug('connect using https');
1770 $this->ch
= curl_init();
1772 $hostURL = ($this->port
!= '') ?
"https://$this->host:$this->port" : "https://$this->host";
1774 $hostURL .= $this->path
;
1775 curl_setopt($this->ch
, CURLOPT_URL
, $hostURL);
1776 // ask for headers in the response output
1777 curl_setopt($this->ch
, CURLOPT_HEADER
, 1);
1778 // ask for the response output as the return value
1779 curl_setopt($this->ch
, CURLOPT_RETURNTRANSFER
, 1);
1781 // We manage this ourselves through headers and encoding
1782 // if(function_exists('gzuncompress')){
1783 // curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
1785 // persistent connection
1786 if ($this->persistentConnection
) {
1787 // The way we send data, we cannot use persistent connections, since
1788 // there will be some "junk" at the end of our request.
1789 //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
1790 $this->persistentConnection
= false
;
1791 $this->outgoing_headers
['Connection'] = 'close';
1793 // set timeout (NOTE: cURL does not have separate connection and response timeouts)
1794 if ($connection_timeout != 0) {
1795 curl_setopt($this->ch
, CURLOPT_TIMEOUT
, $connection_timeout);
1798 // recent versions of cURL turn on peer/host checking by default,
1799 // while PHP binaries are not compiled with a default location for the
1800 // CA cert bundle, so disable peer/host checking.
1801 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
1802 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYPEER
, 0);
1803 curl_setopt($this->ch
, CURLOPT_SSL_VERIFYHOST
, 0);
1806 TODO: support client certificates (thanks Tobias Boes)
1807 curl_setopt($this->ch, CURLOPT_CAINFO, '$pathToPemFiles/rootca.pem');
1808 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
1809 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
1810 curl_setopt($this->ch, CURLOPT_SSLCERT, '$pathToPemFiles/mycert.pem');
1811 curl_setopt($this->ch, CURLOPT_SSLKEY, '$pathToPemFiles/mykey.pem');
1812 curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $passphrase);
1814 $this->debug('cURL connection set up');
1817 $this->setError('Unknown scheme ' . $this->scheme
);
1818 $this->debug('Unknown scheme ' . $this->scheme
);
1824 * send the SOAP message via HTTP
1826 * @param string $data message data
1827 * @param integer $timeout set connection timeout in seconds
1828 * @param integer $response_timeout set response timeout in seconds
1829 * @return string data
1832 function send($data, $timeout=0, $response_timeout=30) {
1834 $this->debug('entered send() with data of length: '.strlen($data));
1836 $this->tryagain
= true
;
1838 while ($this->tryagain
) {
1839 $this->tryagain
= false
;
1842 if (!$this->connect($timeout, $response_timeout)){
1847 if (!$this->sendRequest($data)){
1852 $respdata = $this->getResponse();
1854 $this->setError('Too many tries to get an OK response');
1857 $this->debug('end of send()');
1863 * send the SOAP message via HTTPS 1.0 using CURL
1865 * @param string $msg message data
1866 * @param integer $timeout set connection timeout in seconds
1867 * @param integer $response_timeout set response timeout in seconds
1868 * @return string data
1871 function sendHTTPS($data, $timeout=0, $response_timeout=30) {
1872 return $this->send($data, $timeout, $response_timeout);
1876 * if authenticating, set user credentials here
1878 * @param string $username
1879 * @param string $password
1880 * @param string $authtype
1881 * @param array $digestRequest
1884 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array()) {
1887 $this->debug("Set credentials for authtype $authtype");
1889 if ($authtype == 'basic') {
1890 $this->outgoing_headers
['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
1891 } elseif ($authtype == 'digest') {
1892 if (isset($digestRequest['nonce'])) {
1893 $digestRequest['nc'] = isset($digestRequest['nc']) ?
$digestRequest['nc']++
: 1;
1895 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
1897 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
1898 $A1 = $username. ':' . $digestRequest['realm'] . ':' . $password;
1903 // A2 = Method ":" digest-uri-value
1904 $A2 = 'POST:' . $this->digest_uri
;
1909 // KD(secret, data) = H(concat(secret, ":", data))
1911 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
1913 // ":" unq(cnonce-value)
1914 // ":" unq(qop-value)
1917 // if qop is missing,
1918 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
1920 $unhashedDigest = '';
1921 $nonce = isset($digestRequest['nonce']) ?
$digestRequest['nonce'] : '';
1923 if ($digestRequest['qop'] != '') {
1924 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
1926 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
1929 $hashedDigest = md5($unhashedDigest);
1931 $this->outgoing_headers
['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri
. '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
1934 $this->username
= $username;
1935 $this->password
= $password;
1936 $this->authtype
= $authtype;
1937 $this->digestRequest
= $digestRequest;
1939 if (isset($this->outgoing_headers
['Authorization'])) {
1940 $this->debug('Authorization header set: ' . substr($this->outgoing_headers
['Authorization'], 0, 12) . '...');
1942 $this->debug('Authorization header not set');
1947 * set the soapaction value
1949 * @param string $soapaction
1952 function setSOAPAction($soapaction) {
1953 $this->outgoing_headers
['SOAPAction'] = '"' . $soapaction . '"';
1959 * @param string $enc encoding style. supported values: gzip, deflate, or both
1962 function setEncoding($enc='gzip, deflate'){
1963 $this->protocol_version
= '1.1';
1964 $this->outgoing_headers
['Accept-Encoding'] = $enc;
1965 if (!isset($this->outgoing_headers
['Connection'])) {
1966 $this->outgoing_headers
['Connection'] = 'close';
1967 $this->persistentConnection
= false
;
1969 set_magic_quotes_runtime(0);
1971 $this->encoding
= $enc;
1975 * set proxy info here
1977 * @param string $proxyhost
1978 * @param string $proxyport
1979 * @param string $proxyusername
1980 * @param string $proxypassword
1983 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
1984 $this->uri
= $this->url
;
1985 $this->host
= $proxyhost;
1986 $this->port
= $proxyport;
1987 if ($proxyusername != '' && $proxypassword != '') {
1988 $this->outgoing_headers
['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
1993 * decode a string that is encoded w/ "chunked' transfer encoding
1994 * as defined in RFC2068 19.4.6
1996 * @param string $buffer
2002 function decodeChunked($buffer, $lb){
2007 // read chunk-size, chunk-extension (if any) and CRLF
2008 // get the position of the linebreak
2009 $chunkend = strpos($buffer, $lb);
2010 if ($chunkend == FALSE
) {
2011 $this->debug('no linebreak found in decodeChunked');
2014 $temp = substr($buffer,0,$chunkend);
2015 $chunk_size = hexdec( trim($temp) );
2016 $chunkstart = $chunkend +
strlen($lb);
2017 // while (chunk-size > 0) {
2018 while ($chunk_size > 0) {
2019 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2020 $chunkend = strpos( $buffer, $lb, $chunkstart +
$chunk_size);
2022 // Just in case we got a broken connection
2023 if ($chunkend == FALSE
) {
2024 $chunk = substr($buffer,$chunkstart);
2025 // append chunk-data to entity-body
2027 $length +
= strlen($chunk);
2031 // read chunk-data and CRLF
2032 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2033 // append chunk-data to entity-body
2035 // length := length + chunk-size
2036 $length +
= strlen($chunk);
2037 // read chunk-size and CRLF
2038 $chunkstart = $chunkend +
strlen($lb);
2040 $chunkend = strpos($buffer, $lb, $chunkstart) +
strlen($lb);
2041 if ($chunkend == FALSE
) {
2042 break; //Just in case we got a broken connection
2044 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2045 $chunk_size = hexdec( trim($temp) );
2046 $chunkstart = $chunkend;
2052 * Writes payload, including HTTP headers, to $this->outgoing_payload.
2054 function buildPayload($data) {
2055 // add content-length header
2056 $this->outgoing_headers
['Content-Length'] = strlen($data);
2058 // start building outgoing payload:
2059 $req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2060 $this->debug("HTTP request: $req");
2061 $this->outgoing_payload
= "$req\r\n";
2063 // loop thru headers, serializing
2064 foreach($this->outgoing_headers
as $k => $v){
2066 $this->debug("HTTP header: $hdr");
2067 $this->outgoing_payload
.= "$hdr\r\n";
2070 // header/body separator
2071 $this->outgoing_payload
.= "\r\n";
2074 $this->outgoing_payload
.= $data;
2076 ### trigger_error($this->outgoing_payload, E_USER_ERROR);
2079 function sendRequest($data){
2081 $this->buildPayload($data);
2083 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
2085 if(!fputs($this->fp
, $this->outgoing_payload
, strlen($this->outgoing_payload
))) {
2086 $this->setError('couldn\'t write message data to socket');
2087 $this->debug('couldn\'t write message data to socket');
2090 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload
));
2092 } else if ($this->scheme
== 'https') {
2094 // TODO: cURL does say this should only be the verb, and in fact it
2095 // turns out that the URI and HTTP version are appended to this, which
2096 // some servers refuse to work with
2097 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2098 foreach($this->outgoing_headers
as $k => $v){
2099 $curl_headers[] = "$k: $v";
2101 curl_setopt($this->ch
, CURLOPT_HTTPHEADER
, $curl_headers);
2102 if ($this->request_method
== "POST") {
2103 curl_setopt($this->ch
, CURLOPT_POST
, 1);
2104 curl_setopt($this->ch
, CURLOPT_POSTFIELDS
, $data);
2107 $this->debug('set cURL payload');
2112 function getResponse(){
2113 $this->incoming_payload
= '';
2115 if ($this->scheme
== 'http' ||
$this->scheme
== 'ssl') {
2116 // loop until headers have been retrieved
2118 while (!isset($lb)){
2120 // We might EOF during header read.
2121 if(feof($this->fp
)) {
2122 $this->incoming_payload
= $data;
2123 $this->debug('found no headers before EOF after length ' . strlen($data));
2124 $this->debug("received before EOF:\n" . $data);
2125 $this->setError('server failed to send headers');
2129 $tmp = fgets($this->fp
, 256);
2130 $tmplen = strlen($tmp);
2131 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2134 $this->incoming_payload
= $data;
2135 $this->debug('socket read of headers timed out after length ' . strlen($data));
2136 $this->debug("read before timeout:\n" . $data);
2137 $this->setError('socket read of headers timed out');
2142 $pos = strpos($data,"\r\n\r\n");
2146 $pos = strpos($data,"\n\n");
2151 // remove 100 header
2152 if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2157 // store header data
2158 $this->incoming_payload
.= $data;
2159 $this->debug('found end of headers after length ' . strlen($data));
2162 $header_data = trim(substr($data,0,$pos));
2163 $header_array = explode($lb,$header_data);
2164 $this->incoming_headers
= array();
2165 foreach($header_array as $header_line){
2166 $arr = explode(':',$header_line, 2);
2167 if(count($arr) > 1){
2168 $header_name = strtolower(trim($arr[0]));
2169 $this->incoming_headers
[$header_name] = trim($arr[1]);
2170 } else if (isset($header_name)) {
2171 $this->incoming_headers
[$header_name] .= $lb . ' ' . $header_line;
2175 // loop until msg has been received
2176 if (isset($this->incoming_headers
['transfer-encoding']) && strtolower($this->incoming_headers
['transfer-encoding']) == 'chunked') {
2177 $content_length = 2147483647; // ignore any content-length header
2179 $this->debug("want to read chunked content");
2180 } elseif (isset($this->incoming_headers
['content-length'])) {
2181 $content_length = $this->incoming_headers
['content-length'];
2183 $this->debug("want to read content of length $content_length");
2185 $content_length = 2147483647;
2187 $this->debug("want to read content to EOF");
2193 $tmp = fgets($this->fp
, 256);
2194 $tmplen = strlen($tmp);
2195 $this->debug("read chunk line of $tmplen bytes");
2197 $this->incoming_payload
= $data;
2198 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2199 $this->debug("read before timeout:\n" . $data);
2200 $this->setError('socket read of chunk length timed out');
2203 $content_length = hexdec(trim($tmp));
2204 $this->debug("chunk length $content_length");
2207 while (($strlen < $content_length) && (!feof($this->fp
))) {
2208 $readlen = min(8192, $content_length - $strlen);
2209 $tmp = fread($this->fp
, $readlen);
2210 $tmplen = strlen($tmp);
2211 $this->debug("read buffer of $tmplen bytes");
2212 if (($tmplen == 0) && (!feof($this->fp
))) {
2213 $this->incoming_payload
= $data;
2214 $this->debug('socket read of body timed out after length ' . strlen($data));
2215 $this->debug("read before timeout:\n" . $data);
2216 $this->setError('socket read of body timed out');
2222 if ($chunked && ($content_length > 0)) {
2223 $tmp = fgets($this->fp
, 256);
2224 $tmplen = strlen($tmp);
2225 $this->debug("read chunk terminator of $tmplen bytes");
2227 $this->incoming_payload
= $data;
2228 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2229 $this->debug("read before timeout:\n" . $data);
2230 $this->setError('socket read of chunk terminator timed out');
2234 } while ($chunked && ($content_length > 0) && (!feof($this->fp
)));
2235 if (feof($this->fp
)) {
2236 $this->debug('read to EOF');
2238 $this->debug('read body of length ' . strlen($data));
2239 $this->incoming_payload
.= $data;
2240 $this->debug('received a total of '.strlen($this->incoming_payload
).' bytes of data from server');
2242 // close filepointer
2244 (isset($this->incoming_headers
['connection']) && strtolower($this->incoming_headers
['connection']) == 'close') ||
2245 (! $this->persistentConnection
) ||
feof($this->fp
)){
2248 $this->debug('closed socket');
2251 // connection was closed unexpectedly
2252 if($this->incoming_payload
== ''){
2253 $this->setError('no response from server');
2257 // decode transfer-encoding
2258 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2259 // if(!$data = $this->decodeChunked($data, $lb)){
2260 // $this->setError('Decoding of chunked data failed');
2263 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2264 // set decoded payload
2265 // $this->incoming_payload = $header_data.$lb.$lb.$data;
2268 } else if ($this->scheme
== 'https') {
2270 $this->debug('send and receive with cURL');
2271 $this->incoming_payload
= curl_exec($this->ch
);
2272 $data = $this->incoming_payload
;
2274 $cErr = curl_error($this->ch
);
2276 $err = 'cURL ERROR: '.curl_errno($this->ch
).': '.$cErr.'<br>';
2277 foreach(curl_getinfo($this->ch
) as $k => $v){
2278 $err .= "$k: $v<br>";
2281 $this->setError($err);
2282 curl_close($this->ch
);
2286 //var_dump(curl_getinfo($this->ch));
2290 $this->debug('No cURL error, closing cURL');
2291 curl_close($this->ch
);
2293 // remove 100 header
2294 if (ereg('^HTTP/1.1 100',$data)) {
2295 if ($pos = strpos($data,"\r\n\r\n")) {
2296 $data = ltrim(substr($data,$pos));
2297 } elseif($pos = strpos($data,"\n\n") ) {
2298 $data = ltrim(substr($data,$pos));
2302 // separate content from HTTP headers
2303 if ($pos = strpos($data,"\r\n\r\n")) {
2305 } elseif( $pos = strpos($data,"\n\n")) {
2308 $this->debug('no proper separation of headers and document');
2309 $this->setError('no proper separation of headers and document');
2312 $header_data = trim(substr($data,0,$pos));
2313 $header_array = explode($lb,$header_data);
2314 $data = ltrim(substr($data,$pos));
2315 $this->debug('found proper separation of headers and document');
2316 $this->debug('cleaned data, stringlen: '.strlen($data));
2318 foreach ($header_array as $header_line) {
2319 $arr = explode(':',$header_line,2);
2320 if (count($arr) > 1) {
2321 $this->incoming_headers
[strtolower(trim($arr[0]))] = trim($arr[1]);
2326 $arr = explode(' ', $header_array[0], 3);
2327 $http_version = $arr[0];
2328 $http_status = intval($arr[1]);
2329 $http_reason = count($arr) > 2 ?
$arr[2] : '';
2331 // see if we need to resend the request with http digest authentication
2332 if (isset($this->incoming_headers
['location']) && $http_status == 301) {
2333 $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers
['location']);
2334 $this->setURL($this->incoming_headers
['location']);
2335 $this->tryagain
= true
;
2339 // see if we need to resend the request with http digest authentication
2340 if (isset($this->incoming_headers
['www-authenticate']) && $http_status == 401) {
2341 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers
['www-authenticate']);
2342 if (substr("Digest ", $this->incoming_headers
['www-authenticate'])) {
2343 $this->debug('Server wants digest authentication');
2344 // remove "Digest " from our elements
2345 $digestString = str_replace('Digest ', '', $this->incoming_headers
['www-authenticate']);
2347 // parse elements into array
2348 $digestElements = explode(',', $digestString);
2349 foreach ($digestElements as $val) {
2350 $tempElement = explode('=', trim($val));
2351 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2354 // should have (at least) qop, realm, nonce
2355 if (isset($digestRequest['nonce'])) {
2356 $this->setCredentials($this->username
, $this->password
, 'digest', $digestRequest);
2357 $this->tryagain
= true
;
2361 $this->debug('HTTP authentication failed');
2362 $this->setError('HTTP authentication failed');
2367 ($http_status >= 300 && $http_status <= 307) ||
2368 ($http_status >= 400 && $http_status <= 417) ||
2369 ($http_status >= 501 && $http_status <= 505)
2371 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
2375 // decode content-encoding
2376 if(isset($this->incoming_headers
['content-encoding']) && $this->incoming_headers
['content-encoding'] != ''){
2377 if(strtolower($this->incoming_headers
['content-encoding']) == 'deflate' ||
strtolower($this->incoming_headers
['content-encoding']) == 'gzip'){
2378 // if decoding works, use it. else assume data wasn't gzencoded
2379 if(function_exists('gzinflate')){
2380 //$timer->setMarker('starting decoding of gzip/deflated content');
2381 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2382 // this means there are no Zlib headers, although there should be
2383 $this->debug('The gzinflate function exists');
2384 $datalen = strlen($data);
2385 if ($this->incoming_headers
['content-encoding'] == 'deflate') {
2386 if ($degzdata = @gzinflate($data)) {
2388 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2389 if (strlen($data) < $datalen) {
2390 // test for the case that the payload has been compressed twice
2391 $this->debug('The inflated payload is smaller than the gzipped one; try again');
2392 if ($degzdata = @gzinflate($data)) {
2394 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2398 $this->debug('Error using gzinflate to inflate the payload');
2399 $this->setError('Error using gzinflate to inflate the payload');
2401 } elseif ($this->incoming_headers
['content-encoding'] == 'gzip') {
2402 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
2404 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2405 if (strlen($data) < $datalen) {
2406 // test for the case that the payload has been compressed twice
2407 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2408 if ($degzdata = @gzinflate(substr($data, 10))) {
2410 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2414 $this->debug('Error using gzinflate to un-gzip the payload');
2415 $this->setError('Error using gzinflate to un-gzip the payload');
2418 //$timer->setMarker('finished decoding of gzip/deflated content');
2419 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2420 // set decoded payload
2421 $this->incoming_payload
= $header_data.$lb.$lb.$data;
2423 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2424 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2427 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers
['content-encoding']);
2428 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers
['content-encoding']);
2431 $this->debug('No Content-Encoding header');
2434 if(strlen($data) == 0){
2435 $this->debug('no data after headers!');
2436 $this->setError('no data present after HTTP headers');
2443 function setContentType($type, $charset = false
) {
2444 $this->outgoing_headers
['Content-Type'] = $type . ($charset ?
'; charset=' . $charset : '');
2447 function usePersistentConnection(){
2448 if (isset($this->outgoing_headers
['Accept-Encoding'])) {
2451 $this->protocol_version
= '1.1';
2452 $this->persistentConnection
= true
;
2453 $this->outgoing_headers
['Connection'] = 'Keep-Alive';
2464 * soap_server allows the user to create a SOAP server
2465 * that is capable of receiving messages and returning responses
2467 * NOTE: WSDL functionality is experimental
2469 * @author Dietrich Ayala <dietrich@ganx4.com>
2470 * @version $Id: nusoap.php 4994 2010-04-08 10:06:41Z cenou $
2473 class soap_server
extends nusoap_base
{
2474 var $headers = array(); // HTTP headers of request
2475 var $request = ''; // HTTP request
2476 var $requestHeaders = ''; // SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
2477 var $document = ''; // SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
2478 var $requestSOAP = ''; // SOAP payload for request (text)
2479 var $methodURI = ''; // requested method namespace URI
2480 var $methodname = ''; // name of method requested
2481 var $methodparams = array(); // method parameters from request
2482 var $xml_encoding = ''; // character set encoding of incoming (request) messages
2483 var $SOAPAction = ''; // SOAP Action from request
2485 var $outgoing_headers = array();// HTTP headers of response
2486 var $response = ''; // HTTP response
2487 var $responseHeaders = ''; // SOAP headers for response (text)
2488 var $responseSOAP = ''; // SOAP payload for response (text)
2489 var $methodreturn = false
; // method return to place in response
2490 var $methodreturnisliteralxml = false
; // whether $methodreturn is a string of literal XML
2491 var $fault = false
; // SOAP fault for response
2492 var $result = 'successful'; // text indication of result (for debugging)
2494 var $operations = array(); // assoc array of operations => opData
2495 var $wsdl = false
; // wsdl instance
2496 var $externalWSDLURL = false
; // URL for WSDL
2497 var $debug_flag = false
; // whether to append debug to response as XML comment
2501 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
2503 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
2506 function soap_server($wsdl=false
){
2508 // turn on debugging?
2512 global $HTTP_SERVER_VARS;
2514 if (isset($debug)) {
2515 $this->debug_flag
= $debug;
2516 } else if (isset($_REQUEST['debug'])) {
2517 $this->debug_flag
= $_REQUEST['debug'];
2518 } else if (isset($_SERVER['QUERY_STRING'])) {
2519 $qs = explode('&', $_SERVER['QUERY_STRING']);
2520 foreach ($qs as $v) {
2521 if (substr($v, 0, 6) == 'debug=') {
2522 $this->debug_flag
= substr($v, 6);
2525 } else if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
2526 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
2527 foreach ($qs as $v) {
2528 if (substr($v, 0, 6) == 'debug=') {
2529 $this->debug_flag
= substr($v, 6);
2536 if (is_object($wsdl) && is_a($wsdl, 'wsdl')) {
2537 $this->wsdl
= $wsdl;
2538 $this->externalWSDLURL
= $this->wsdl
->wsdl
;
2539 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL
);
2541 $this->debug('Create wsdl from ' . $wsdl);
2542 $this->wsdl
= new wsdl($wsdl);
2543 $this->externalWSDLURL
= $wsdl;
2545 $this->appendDebug($this->wsdl
->getDebug());
2546 $this->wsdl
->clearDebug();
2547 if($err = $this->wsdl
->getError()){
2548 trigger_error('WSDL ERROR: '.$err, E_USER_ERROR
);
2554 * processes request and returns response
2556 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
2559 function service($data){
2560 global $QUERY_STRING;
2561 if(isset($_SERVER['QUERY_STRING'])){
2562 $qs = $_SERVER['QUERY_STRING'];
2563 } elseif(isset($GLOBALS['QUERY_STRING'])){
2564 $qs = $GLOBALS['QUERY_STRING'];
2565 } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
2566 $qs = $QUERY_STRING;
2569 if(isset($qs) && ereg('wsdl', $qs) ){
2570 // This is a request for WSDL
2571 if($this->externalWSDLURL
){
2572 if (strpos($this->externalWSDLURL
,"://")!==false
) { // assume URL
2573 header('Location: '.$this->externalWSDLURL
);
2574 } else { // assume file
2575 header("Content-Type: text/xml\r\n");
2576 $fp = fopen($this->externalWSDLURL
, 'r');
2580 header("Content-Type: text/xml; charset=utf-8\r\n");
2581 print $this->wsdl
->serialize($this->debug_flag
);
2583 } elseif($data == '' && $this->wsdl
){
2584 // print web interface
2585 print $this->wsdl
->webDescription();
2587 // handle the request
2588 $this->parse_request($data);
2589 if (! $this->fault
) {
2590 $this->invoke_method();
2592 if (! $this->fault
) {
2593 $this->serialize_return();
2595 $this->send_response();
2600 * parses HTTP request headers.
2602 * The following fields are set by this function (when successful)
2611 function parse_http_headers() {
2612 global $HTTP_SERVER_VARS;
2615 $this->request
= '';
2616 if(function_exists('getallheaders')){
2617 $this->headers
= getallheaders();
2618 foreach($this->headers
as $k=>$v){
2619 $this->request
.= "$k: $v\r\n";
2620 $this->debug("$k: $v");
2622 // get SOAPAction header
2623 if(isset($this->headers
['SOAPAction'])){
2624 $this->SOAPAction
= str_replace('"','',$this->headers
['SOAPAction']);
2626 // get the character encoding of the incoming request
2627 if(strpos($this->headers
['Content-Type'],'=')){
2628 $enc = str_replace('"','',substr(strstr($this->headers
["Content-Type"],'='),1));
2629 if(eregi('^(utf-8|US-ASCII|UTF-8)$',$enc)){
2630 $this->xml_encoding
= strtoupper($enc);
2632 $this->xml_encoding
= 'US-ASCII';
2635 // should be US-ASCII for HTTP 1.0 or utf-8 for HTTP 1.1
2636 $this->xml_encoding
= 'utf-8';
2638 } elseif(isset($_SERVER) && is_array($_SERVER)){
2639 foreach ($_SERVER as $k => $v) {
2640 if (substr($k, 0, 5) == 'HTTP_') {
2641 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2643 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k))));
2645 if ($k == 'Soapaction') {
2646 // get SOAPAction header
2648 $v = str_replace('"', '', $v);
2649 $v = str_replace('\\', '', $v);
2650 $this->SOAPAction
= $v;
2651 } else if ($k == 'Content-Type') {
2652 // get the character encoding of the incoming request
2653 if (strpos($v, '=')) {
2654 $enc = substr(strstr($v, '='), 1);
2655 $enc = str_replace('"', '', $enc);
2656 $enc = str_replace('\\', '', $enc);
2657 if (eregi('^(utf-8|US-ASCII|UTF-8)$', $enc)) {
2658 $this->xml_encoding
= strtoupper($enc);
2660 $this->xml_encoding
= 'US-ASCII';
2663 // should be US-ASCII for HTTP 1.0 or utf-8 for HTTP 1.1
2664 $this->xml_encoding
= 'utf-8';
2667 $this->headers
[$k] = $v;
2668 $this->request
.= "$k: $v\r\n";
2669 $this->debug("$k: $v");
2671 } elseif (is_array($HTTP_SERVER_VARS)) {
2672 foreach ($HTTP_SERVER_VARS as $k => $v) {
2673 if (substr($k, 0, 5) == 'HTTP_') {
2674 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2675 if ($k == 'Soapaction') {
2676 // get SOAPAction header
2678 $v = str_replace('"', '', $v);
2679 $v = str_replace('\\', '', $v);
2680 $this->SOAPAction
= $v;
2681 } else if ($k == 'Content-Type') {
2682 // get the character encoding of the incoming request
2683 if (strpos($v, '=')) {
2684 $enc = substr(strstr($v, '='), 1);
2685 $enc = str_replace('"', '', $enc);
2686 $enc = str_replace('\\', '', $enc);
2687 if (eregi('^(utf-8|US-ASCII|UTF-8)$', $enc)) {
2688 $this->xml_encoding
= strtoupper($enc);
2690 $this->xml_encoding
= 'US-ASCII';
2693 // should be US-ASCII for HTTP 1.0 or utf-8 for HTTP 1.1
2694 $this->xml_encoding
= 'utf-8';
2697 $this->headers
[$k] = $v;
2698 $this->request
.= "$k: $v\r\n";
2699 $this->debug("$k: $v");
2708 * The following fields are set by this function (when successful)