3 * NuSOAP - Web Services Toolkit for PHP
5 * @author: Dietrich Ayala
9 Copyright (c) 2002 NuSphere Corporation
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 If you have any questions or comments, please email:
29 http://dietrich.ganx4.com/nusoap
32 http://www.nusphere.com
39 require_once 'class.soapclient.php';
40 require_once 'class.soap_val.php';
41 require_once 'class.soap_parser.php';
42 require_once 'class.soap_fault.php';
45 require_once 'class.soap_transport_http.php';
47 // optional add-on classes
48 require_once 'class.xmlschema.php';
49 require_once 'class.wsdl.php';
52 require_once 'class.soap_server.php';*/
58 * @author Dietrich Ayala <dietrich@ganx4.com>
65 var $title = 'NuSOAP';
66 var $version = '0.6.3';
67 var $error_str = false;
69 // toggles automatic encoding of special characters
70 var $charencoding = true;
75 * @var XMLSchemaVersion
78 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
81 * set default encoding
83 * @var soap_defencoding
86 //var $soap_defencoding = 'UTF-8';
87 var $soap_defencoding = 'ISO-8859-1';
90 * load namespace uris into an array of uri => prefix
95 var $namespaces = array(
96 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
97 'xsd' => 'http://www.w3.org/2001/XMLSchema',
98 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
99 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
100 'si' => 'http://soapinterop.org/xsd');
102 * load types into typemap array
103 * is this legacy yet?
104 * no, this is used by the xmlschema class to verify type => namespace mappings.
110 'http://www.w3.org/2001/XMLSchema' =>
112 'string' => 'string', 'boolean' => 'boolean', 'float' => 'double', 'double' => 'double', 'decimal' => 'double',
113 'duration' => '', 'dateTime' => 'string', 'time' => 'string', 'date' => 'string', 'gYearMonth' => '',
114 'gYear' => '', 'gMonthDay' => '', 'gDay' => '', 'gMonth' => '', 'hexBinary' => 'string', 'base64Binary' => 'string',
116 'normalizedString' => 'string', 'token' => 'string', 'language' => '', 'NMTOKEN' => '', 'NMTOKENS' => '', 'Name' => '', 'NCName' => '', 'ID' => '',
117 'IDREF' => '', 'IDREFS' => '', 'ENTITY' => '', 'ENTITIES' => '', 'integer' => 'integer', 'nonPositiveInteger' => 'integer',
118 'negativeInteger' => 'integer', 'long' => 'integer', 'int' => 'integer', 'short' => 'integer', 'byte' => 'integer', 'nonNegativeInteger' => 'integer',
119 'unsignedLong' => '', 'unsignedInt' => '', 'unsignedShort' => '', 'unsignedByte' => '', 'positiveInteger' => ''),
120 'http://www.w3.org/1999/XMLSchema' =>
122 'i4' => '', 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', 'double' => 'double',
123 'float' => 'double', 'dateTime' => 'string',
124 'timeInstant' => 'string', 'base64Binary' => 'string', 'base64' => 'string', 'ur-type' => 'array'),
125 'http://soapinterop.org/xsd' => array('SOAPStruct' => 'struct'),
126 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64' => 'string', 'array' => 'array', 'Array' => 'array'),
127 'http://xml.apache.org/xml-soap' => array('Map')
131 * entities to convert
136 var $xmlEntities = array('quot' => '"', 'amp' => '&',
137 'lt' => '<', 'gt' => '>', 'apos' => "'");
140 * adds debug data to the class level debug string
142 * @param string $string debug data
145 function debug($string)
147 $this->debug_str .= get_class($this) . ": $string\n";
151 * returns error string if present
153 * @return boolean $string error string
158 if ($this->error_str != '') {
159 return $this->error_str;
167 * @return boolean $string error string
170 function setError($str)
172 $this->error_str = $str;
176 * serializes PHP values in accordance w/ section 5. Type information is
177 * not serialized if $use == 'literal'.
182 function serialize_val($val, $name = false, $type = false, $name_ns = false,
183 $type_ns = false, $attributes = false, $use = 'encoded')
185 if (is_object($val) && strtolower(get_class($val)) == 'soapval') {
186 return $val->serialize($use);
188 $this->debug("in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
189 // if no name, use item
190 $name = (!$name || is_numeric($name)) ? 'soapVal' : $name;
191 // if name has ns, add ns prefix to name
194 $prefix = 'nu' . rand(1000, 9999);
195 $name = $prefix . ':' . $name;
196 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
198 // if type is prefixed, create type prefix
199 if ($type_ns != '' && $type_ns == $this->namespaces['xsd']) {
200 // need to fix this. shouldn't default to xsd if no ns specified
201 // w/o checking against typemap
202 $type_prefix = 'xsd';
203 } elseif ($type_ns) {
204 $type_prefix = 'ns' . rand(1000, 9999);
205 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
207 // serialize attributes if present
209 foreach ($attributes as $k => $v) {
210 $atts .= " $k=\"$v\"";
213 // serialize if an xsd built-in primitive type
214 if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
215 if ($use == 'literal') {
216 return "<$name$xmlns>$val</$name>";
218 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
221 // detect type and serialize
225 case ($type == '' && is_null($val)):
226 if ($use == 'literal') {
227 // TODO: depends on nillable
228 $xml .= "<$name$xmlns/>";
230 $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>";
233 case (is_bool($val) || $type == 'boolean'):
237 if ($use == 'literal') {
238 $xml .= "<$name$xmlns $atts>$val</$name>";
240 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
243 case (is_int($val) || is_long($val) || $type == 'int'):
244 if ($use == 'literal') {
245 $xml .= "<$name$xmlns $atts>$val</$name>";
247 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
250 case (is_float($val) || is_double($val) || $type == 'float'):
251 if ($use == 'literal') {
252 $xml .= "<$name$xmlns $atts>$val</$name>";
254 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
257 case (is_string($val) || $type == 'string'):
258 if ($this->charencoding) {
259 $val = htmlspecialchars($val, ENT_QUOTES);
261 if ($use == 'literal') {
262 $xml .= "<$name$xmlns $atts>$val</$name>";
264 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
267 case is_object($val):
268 $name = strtolower(get_class($val));
269 foreach (get_object_vars($val) as $k => $v) {
270 $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
272 $xml .= '<' . $name . '>' . $pXml . '</' . $name . '>';
275 case (is_array($val) || $type):
276 // detect if struct or array
277 $keyList = array_keys($val);
278 $valueType = 'arraySimple';
279 foreach ($keyList as $keyListValue) {
280 if (!is_int($keyListValue)) {
281 $valueType = 'arrayStruct';
285 if ($valueType == 'arraySimple' || ereg('^ArrayOf', $type)) {
287 if (is_array($val) && count($val) > 0) {
288 foreach ($val as $v) {
289 if (is_object($v) && strtolower(get_class($v)) == 'soapval') {
294 $array_types[$tt] = 1;
295 $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
296 if (is_array($v) && is_numeric(key($v))) {
302 if (count($array_types) > 1) {
303 $array_typename = 'xsd:ur-type';
304 } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
305 $array_typename = 'xsd:' . $tt;
306 } elseif ($tt == 'array' || $tt == 'Array') {
307 $array_typename = 'SOAP-ENC:Array';
309 $array_typename = $tt;
311 if (isset($array_types['array'])) {
312 $array_type = $i . "," . $i;
316 if ($use == 'literal') {
317 $xml = "<$name $atts>" . $xml . "</$name>";
319 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"" . $array_typename . "[$array_type]\"$atts>" . $xml . "</$name>";
323 if ($use == 'literal') {
324 $xml = "<$name $atts>" . $xml . "</$name>";
327 $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>" . $xml . "</$name>";
333 if (isset($type) && isset($type_prefix)) {
334 $type_str = " xsi:type=\"$type_prefix:$type\"";
338 if ($use == 'literal') {
339 $xml .= "<$name$xmlns $atts>";
341 $xml .= "<$name$xmlns$type_str$atts>";
343 foreach ($val as $k => $v) {
344 $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
350 $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
360 * @param string headers
361 * @param array namespaces
362 * @param string style
363 * @return string message
366 function serializeEnvelope($body, $headers = false, $namespaces = array(), $style = 'rpc')
368 // serialize namespaces
370 foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
371 $ns_string .= " xmlns:$k=\"$v\"";
373 if ($style == 'rpc') {
374 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
379 $headers = "<SOAP-ENV:Header>" . $headers . "</SOAP-ENV:Header>";
381 // serialize envelope
383 '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . ">" .
384 '<SOAP-ENV:Envelope' . $ns_string . ">" .
389 "</SOAP-ENV:Envelope>";
392 function formatDump($str)
394 $str = htmlspecialchars($str);
399 * returns the local part of a prefixed string
400 * returns the original string, if not prefixed
406 function getLocalPart($str)
408 if ($sstr = strrchr($str, ':')) {
409 // get unqualified name
410 return substr($sstr, 1);
417 * returns the prefix part of a prefixed string
418 * returns false, if not prefixed
424 function getPrefix($str)
426 if ($pos = strrpos($str, ':')) {
428 return substr($str, 0, $pos);
433 function varDump($data)
437 $ret_val = ob_get_contents();
443 // XML Schema Datatype Helper Functions
445 //xsd:dateTime helpers
448 * convert unix timestamp to ISO 8601 compliant date string
450 * @param string $timestamp Unix time stamp
453 function timestamp_to_iso8601($timestamp, $utc = true)
455 $datestr = date('Y-m-d\TH:i:sO', $timestamp);
458 '([0-9]{4})-' . // centuries & years CCYY-
459 '([0-9]{2})-' . // months MM-
460 '([0-9]{2})' . // days DD
462 '([0-9]{2}):' . // hours hh:
463 '([0-9]{2}):' . // minutes mm:
464 '([0-9]{2})(\.[0-9]*)?' . // seconds ss.ss...
465 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
467 if (ereg($eregStr, $datestr, $regs)) {
468 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
477 * convert ISO 8601 compliant date string to unix timestamp
479 * @param string $datestr ISO 8601 compliant date string
482 function iso8601_to_timestamp($datestr)
485 '([0-9]{4})-' . // centuries & years CCYY-
486 '([0-9]{2})-' . // months MM-
487 '([0-9]{2})' . // days DD
489 '([0-9]{2}):' . // hours hh:
490 '([0-9]{2}):' . // minutes mm:
491 '([0-9]{2})(\.[0-9]+)?' . // seconds ss.ss...
492 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
493 if (ereg($eregStr, $datestr, $regs)) {
495 if ($regs[8] != 'Z') {
496 $op = substr($regs[8], 0, 1);
497 $h = substr($regs[8], 1, 2);
498 $m = substr($regs[8], strlen($regs[8]) - 2, 2);
500 $regs[4] = $regs[4] + $h;
501 $regs[5] = $regs[5] + $m;
502 } elseif ($op == '+') {
503 $regs[4] = $regs[4] - $h;
504 $regs[5] = $regs[5] - $m;
507 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
514 * soap_fault class, allows for creation of faults
515 * mainly used for returning faults from deployed functions
516 * in a server instance.
517 * @author Dietrich Ayala <dietrich@ganx4.com>
521 class soap_fault extends nusoap_base
532 * @param string $faultcode (client | server)
533 * @param string $faultactor only used when msg routed between multiple actors
534 * @param string $faultstring human readable error message
535 * @param string $faultdetail
537 function soap_fault($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
539 $this->faultcode = $faultcode;
540 $this->faultactor = $faultactor;
541 $this->faultstring = $faultstring;
542 $this->faultdetail = $faultdetail;
553 foreach ($this->namespaces as $k => $v) {
554 $ns_string .= "\n xmlns:$k=\"$v\"";
557 '<?xml version="1.0"?' . ">\n" .
558 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string . ">\n" .
561 '<faultcode>' . $this->faultcode . '</faultcode>' .
562 '<faultactor>' . $this->faultactor . '</faultactor>' .
563 '<faultstring>' . $this->faultstring . '</faultstring>' .
564 '<detail>' . $this->serialize_val($this->faultdetail) . '</detail>' .
565 '</SOAP-ENV:Fault>' .
567 '</SOAP-ENV:Envelope>';
573 * parses an XML Schema, allows access to it's data, other utility methods
574 * no validation... yet.
575 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
576 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
577 * tutorials I refer to :)
579 * @author Dietrich Ayala <dietrich@ganx4.com>
583 class XMLSchema extends nusoap_base
589 // define internal arrays of bindings, ports, operations, messages, etc.
590 var $complexTypes = array();
592 var $schemaTargetNamespace = '';
597 var $depth_array = array();
602 * @param string $schema schema document URI
603 * @param string $xml xml document URI
606 function XMLSchema($schema = '', $xml = '')
609 $this->debug('xmlschema class instantiated, inside constructor');
611 $this->schema = $schema;
616 $this->debug('initial schema file: ' . $schema);
617 $this->parseFile($schema);
622 $this->debug('initial xml file: ' . $xml);
623 $this->parseFile($xml);
631 * @param string $xml, path/URL to XML file
632 * @param string $type, (schema | xml)
636 function parseFile($xml, $type)
640 $this->debug('parsing $xml');
641 $xmlStr = @join("", @file($xml));
643 $this->setError('No file at the specified URL: ' . $xml);
646 $this->parseString($xmlStr, $type);
654 * parse an XML string
656 * @param string $xml path or URL
657 * @param string $type, (schema|xml)
660 function parseString($xml, $type)
665 // Create an XML parser.
666 $this->parser = xml_parser_create();
667 // Set the options for parsing the XML data.
668 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
670 // Set the object for the parser.
671 xml_set_object($this->parser, $this);
673 // Set the element handlers for the parser.
674 if ($type == "schema") {
675 xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
676 xml_set_character_data_handler($this->parser, 'schemaCharacterData');
677 } elseif ($type == "xml") {
678 xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
679 xml_set_character_data_handler($this->parser, 'xmlCharacterData');
682 // Parse the XML file.
683 if (!xml_parse($this->parser, $xml, true)) {
684 // Display an error message.
685 $errstr = sprintf('XML error on line %d: %s',
686 xml_get_current_line_number($this->parser),
687 xml_error_string(xml_get_error_code($this->parser))
689 $this->debug('XML parse error: ' . $errstr);
690 $this->setError('Parser error: ' . $errstr);
693 xml_parser_free($this->parser);
695 $this->debug('no xml passed to parseString()!!');
696 $this->setError('no xml passed to parseString()!!');
701 * start-element handler
703 * @param string $parser XML parser object
704 * @param string $name element name
705 * @param string $attrs associative array of attributes
708 function schemaStartElement($parser, $name, $attrs)
711 // position in the total number of elements, starting from 0
712 $pos = $this->position++;
713 $depth = $this->depth++;
714 // set self as current value for this depth
715 $this->depth_array[$depth] = $pos;
717 // get element prefix
718 if ($prefix = $this->getPrefix($name)) {
719 // get unqualified name
720 $name = $this->getLocalPart($name);
725 // loop thru attributes, expanding, and registering namespace declarations
726 if (count($attrs) > 0) {
727 foreach ($attrs as $k => $v) {
728 // if ns declarations, add to class level array of valid namespaces
729 if (ereg("^xmlns", $k)) {
730 //$this->xdebug("$k: $v");
731 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
732 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
733 $this->namespaces[$ns_prefix] = $v;
735 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
737 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
738 $this->XMLSchemaVersion = $v;
739 $this->namespaces['xsi'] = $v . '-instance';
743 foreach ($attrs as $k => $v) {
744 // expand each attribute
745 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
746 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
753 // find status, register data
755 case ('all' | 'choice' | 'sequence'):
756 //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
757 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
758 if ($name == 'all') {
759 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
763 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
764 if (isset($attrs['name'])) {
765 $this->attributes[$attrs['name']] = $attrs;
766 $aname = $attrs['name'];
767 } elseif (isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType') {
768 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
769 } elseif (isset($attrs['ref'])) {
770 $aname = $attrs['ref'];
771 $this->attributes[$attrs['ref']] = $attrs;
774 if (isset($this->currentComplexType)) {
775 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
776 } elseif (isset($this->currentElement)) {
777 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
779 // arrayType attribute
780 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType') {
781 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
782 $prefix = $this->getPrefix($aname);
783 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
784 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
788 if (strpos($v, '[,]')) {
789 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
791 $v = substr($v, 0, strpos($v, '[')); // clip the []
792 if (!strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
793 $v = $this->XMLSchemaVersion . ':' . $v;
795 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
799 if (isset($attrs['name'])) {
800 $this->currentElement = false;
801 $this->currentComplexType = $attrs['name'];
802 $this->complexTypes[$this->currentComplexType] = $attrs;
803 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
804 if (isset($attrs['base']) && ereg(':Array$', $attrs['base'])) {
805 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
807 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
809 $this->xdebug('processing complexType ' . $attrs['name']);
813 if (isset($attrs['type'])) {
814 $this->xdebug("processing element " . $attrs['name']);
815 $this->currentElement = $attrs['name'];
816 $this->elements[$attrs['name']] = $attrs;
817 $this->elements[$attrs['name']]['typeClass'] = 'element';
818 $ename = $attrs['name'];
819 } elseif (isset($attrs['ref'])) {
820 $ename = $attrs['ref'];
822 $this->xdebug('adding complexType ' . $attrs['name']);
823 $this->currentComplexType = $attrs['name'];
824 $this->complexTypes[$attrs['name']] = $attrs;
825 $this->complexTypes[$attrs['name']]['element'] = 1;
826 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
828 if (isset($ename) && $this->currentComplexType) {
829 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
833 $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
834 if ($this->currentElement) {
835 $this->elements[$this->currentElement]['type'] = $attrs['base'];
836 } elseif ($this->currentComplexType) {
837 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
838 if (strstr($attrs['base'], ':') == ':Array') {
839 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
844 $this->schema = $attrs;
845 $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
848 $this->currentElement = $attrs['name'];
849 $this->elements[$attrs['name']] = $attrs;
850 $this->elements[$attrs['name']]['typeClass'] = 'element';
856 * end-element handler
858 * @param string $parser XML parser object
859 * @param string $name element name
862 function schemaEndElement($parser, $name)
864 // position of current element is equal to the last value left in depth_array for my depth
865 if (isset($this->depth_array[$this->depth])) {
866 $pos = $this->depth_array[$this->depth];
868 // bring depth down a notch
871 if ($name == 'complexType') {
872 $this->currentComplexType = false;
873 $this->currentElement = false;
875 if ($name == 'element') {
876 $this->currentElement = false;
881 * element content handler
883 * @param string $parser XML parser object
884 * @param string $data element content
887 function schemaCharacterData($parser, $data)
889 $pos = $this->depth_array[$this->depth];
890 $this->message[$pos]['cdata'] .= $data;
894 * serialize the schema
898 function serializeSchema()
901 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
904 foreach ($this->complexTypes as $typeName => $attrs) {
906 // serialize child elements
907 if (count($attrs['elements']) > 0) {
908 foreach ($attrs['elements'] as $element => $eParts) {
909 if (isset($eParts['ref'])) {
910 $contentStr .= "<element ref=\"$element\"/>";
912 $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
917 if (count($attrs['attrs']) >= 1) {
918 foreach ($attrs['attrs'] as $attr => $aParts) {
919 $contentStr .= '<attribute ref="' . $aParts['ref'] . '"';
920 if (isset($aParts['wsdl:arrayType'])) {
921 $contentStr .= ' wsdl:arrayType="' . $aParts['wsdl:arrayType'] . '"';
927 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != '') {
928 $contentStr = "<$schemaPrefix:restriction base=\"" . $attrs['restrictionBase'] . "\">" . $contentStr . "</$schemaPrefix:restriction>";
930 // "all" compositor obviates complex/simple content
931 if (isset($attrs['compositor']) && $attrs['compositor'] == 'all') {
932 $contentStr = "<$schemaPrefix:$attrs[compositor]>" . $contentStr . "</$schemaPrefix:$attrs[compositor]>";
933 } // complex or simple content
934 elseif (count($attrs['elements']) > 0 || count($attrs['attrs']) > 0) {
935 $contentStr = "<$schemaPrefix:complexContent>" . $contentStr . "</$schemaPrefix:complexContent>";
938 if (isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all') {
939 $contentStr = "<$schemaPrefix:$attrs[compositor]>" . $contentStr . "</$schemaPrefix:$attrs[compositor]>";
941 // finalize complex type
942 if ($contentStr != '') {
943 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">" . $contentStr . "</$schemaPrefix:complexType>";
945 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
950 if (isset($this->elements) && count($this->elements) > 0) {
951 foreach ($this->elements as $element => $eParts) {
952 $xml .= "<$schemaPrefix:element name=\"$element\" type=\"" . $eParts['type'] . "\"/>";
956 if (isset($this->attributes) && count($this->attributes) > 0) {
957 foreach ($this->attributes as $attr => $aParts) {
958 $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"" . $aParts['type'] . "\"/>";
962 $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">" . $xml . "</$schemaPrefix:schema>";
967 * expands a qualified name
969 * @param string $string qname
970 * @return string expanded qname
973 function expandQname($qname)
975 // get element prefix
976 if (strpos($qname, ':') && !ereg('^http://', $qname)) {
977 // get unqualified name
978 $name = substr(strstr($qname, ':'), 1);
980 $prefix = substr($qname, 0, strpos($qname, ':'));
981 if (isset($this->namespaces[$prefix])) {
982 return $this->namespaces[$prefix] . ':' . $name;
992 * adds debug data to the clas level debug string
994 * @param string $string debug data
997 function xdebug($string)
999 $this->debug(' xmlschema: ' . $string);
1003 * get the PHP type of a user defined type in the schema
1004 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1005 * returns false if no type exists, or not w/ the given namespace
1006 * else returns a string that is either a native php type, or 'struct'
1008 * @param string $type, name of defined type
1009 * @param string $ns, namespace of type
1013 function getPHPType($type, $ns)
1016 if (isset($typemap[$ns][$type])) {
1017 //print "found type '$type' and ns $ns in typemap<br>";
1018 return $typemap[$ns][$type];
1019 } elseif (isset($this->complexTypes[$type])) {
1020 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1021 return $this->complexTypes[$type]['phpType'];
1027 * returns the local part of a prefixed string
1028 * returns the original string, if not prefixed
1034 function getLocalPart($str)
1036 if ($sstr = strrchr($str, ':')) {
1037 // get unqualified name
1038 return substr($sstr, 1);
1045 * returns the prefix part of a prefixed string
1046 * returns false, if not prefixed
1052 function getPrefix($str)
1054 if ($pos = strrpos($str, ':')) {
1056 return substr($str, 0, $pos);
1062 * pass it a prefix, it returns a namespace
1063 * returns false if no namespace registered with the given prefix
1069 function getNamespaceFromPrefix($prefix)
1071 if (isset($this->namespaces[$prefix])) {
1072 return $this->namespaces[$prefix];
1074 //$this->setError("No namespace registered for prefix '$prefix'");
1079 * returns the prefix for a given namespace (or prefix)
1080 * or false if no prefixes registered for the given namespace
1086 function getPrefixFromNamespace($ns)
1088 foreach ($this->namespaces as $p => $n) {
1089 if ($ns == $n || $ns == $p) {
1090 $this->usedNamespaces[$p] = $n;
1098 * returns an array of information about a given type
1099 * returns false if no type exists by the given name
1102 * 'elements' => array(), // refs to elements array
1103 * 'restrictionBase' => '',
1105 * 'order' => '(sequence|all)',
1106 * 'attrs' => array() // refs to attributes array
1113 function getTypeDef($type)
1115 if (isset($this->complexTypes[$type])) {
1116 return $this->complexTypes[$type];
1117 } elseif (isset($this->elements[$type])) {
1118 return $this->elements[$type];
1119 } elseif (isset($this->attributes[$type])) {
1120 return $this->attributes[$type];
1126 * returns a sample serialization of a given type, or false if no type by the given name
1128 * @param string $type, name of type
1132 function serializeTypeDef($type)
1134 //print "in sTD() for type $type<br>";
1135 if ($typeDef = $this->getTypeDef($type)) {
1136 $str .= '<' . $type;
1137 if (is_array($typeDef['attrs'])) {
1138 foreach ($attrs as $attName => $data) {
1139 $str .= " $attName=\"{type = " . $data['type'] . "}\"";
1142 $str .= " xmlns=\"" . $this->schema['targetNamespace'] . "\"";
1143 if (count($typeDef['elements']) > 0) {
1145 foreach ($typeDef['elements'] as $element => $eData) {
1146 $str .= $this->serializeTypeDef($element);
1149 } elseif ($typeDef['typeClass'] == 'element') {
1150 $str .= "></$type>";
1160 * returns HTML form elements that allow a user
1161 * to enter values for creating an instance of the given type.
1163 * @param string $name, name for type instance
1164 * @param string $type, name of type
1168 function typeToForm($name, $type)
1171 if ($typeDef = $this->getTypeDef($type)) {
1173 if ($typeDef['phpType'] == 'struct') {
1174 $buffer .= '<table>';
1175 foreach ($typeDef['elements'] as $child => $childDef) {
1176 $buffer .= "<tr><td align='right'>$childDef[name] (type: " . $this->getLocalPart($childDef['type']) . "):</td>" .
1177 "<td><input type='text' name='parameters[" . $name . "][$childDef[name]]'></td></tr>";
1179 $buffer .= '</table>';
1181 } elseif ($typeDef['phpType'] == 'array') {
1182 $buffer .= '<table>';
1183 for ($i = 0; $i < 3; $i++) {
1184 $buffer .= "<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>" .
1185 "<td><input type='text' name='parameters[" . $name . "][]'></td></tr>";
1187 $buffer .= '</table>';
1190 $buffer .= "<input type='text' name='parameters[$name]'>";
1193 $buffer .= "<input type='text' name='parameters[$name]'>";
1199 * adds an XML Schema complex type to the WSDL types
1209 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1213 * example: PHP associative array ( SOAP Struct )
1220 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1224 * @param typeClass (complexType|simpleType|attribute)
1225 * @param phpType: currently supported are array and struct (php assoc array)
1226 * @param compositor (all|sequence|choice)
1227 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1228 * @param elements = array ( name = array(name=>'',type=>'') )
1229 * @param attrs = array(
1231 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1232 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1235 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1238 function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
1240 $this->complexTypes[$name] = array(
1242 'typeClass' => $typeClass,
1243 'phpType' => $phpType,
1244 'compositor' => $compositor,
1245 'restrictionBase' => $restrictionBase,
1246 'elements' => $elements,
1248 'arrayType' => $arrayType
1254 * for creating serializable abstractions of native PHP types
1255 * NOTE: this is only really used when WSDL is not available.
1257 * @author Dietrich Ayala <dietrich@ganx4.com>
1261 class soapval extends nusoap_base
1266 * @param string $name optional name
1267 * @param string $type optional type name
1268 * @param mixed $value optional value
1269 * @param string $namespace optional namespace of value
1270 * @param string $type_namespace optional namespace of type
1271 * @param array $attributes associative array of attributes to add to element serialization
1274 function soapval($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false)
1276 $this->name = $name;
1277 $this->value = $value;
1278 $this->type = $type;
1279 $this->element_ns = $element_ns;
1280 $this->type_ns = $type_ns;
1281 $this->attributes = $attributes;
1285 * return serialized value
1287 * @return string XML data
1290 function serialize($use = 'encoded')
1292 return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use);
1296 * decodes a soapval object into a PHP native type
1298 * @param object $soapval optional SOAPx4 soapval object, else uses self
1304 return $this->value;
1309 * transport class for sending/receiving data via HTTP and HTTPS
1310 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1312 * @author Dietrich Ayala <dietrich@ganx4.com>
1316 class soap_transport_http extends nusoap_base
1322 var $proxyhost = '';
1323 var $proxyport = '';
1325 var $request_method = 'POST';
1326 var $protocol_version = '1.0';
1328 var $outgoing_headers = array();
1329 var $incoming_headers = array();
1330 var $outgoing_payload = '';
1331 var $incoming_payload = '';
1332 var $useSOAPAction = true;
1337 function soap_transport_http($url)
1340 $u = parse_url($url);
1341 foreach ($u as $k => $v) {
1342 $this->debug("$k = $v");
1345 if (isset($u['query']) && $u['query'] != '') {
1346 $this->path .= '?' . $u['query'];
1348 if (!isset($u['port']) && $u['scheme'] == 'http') {
1353 function connect($timeout)
1357 if ($this->proxyhost != '' && $this->proxyport != '') {
1358 $host = $this->proxyhost;
1359 $port = $this->proxyport;
1360 $this->debug("using http proxy: $host, $port");
1362 $host = $this->host;
1363 $port = $this->port;
1366 if ($this->scheme == 'https') {
1367 $host = 'ssl://' . $host;
1371 $this->debug("connection params: $host, $port");
1374 $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
1376 $fp = fsockopen($host, $port, $this->errno, $this->error_str);
1381 $this->debug('Couldn\'t open socket connection to server ' . $this->url . ', Error: ' . $this->error_str);
1382 $this->setError('Couldn\'t open socket connection to server: ' . $this->url . ', Error: ' . $this->error_str);
1389 * send the SOAP message via HTTP
1391 * @param string $data message data
1392 * @param integer $timeout set timeout in seconds
1393 * @return string data
1396 function send($data, $timeout = 0)
1398 $this->debug('entered send() with data of length: ' . strlen($data));
1400 if (!$fp = $this->connect($timeout)) {
1403 $this->debug('socket connected');
1405 // start building outgoing payload:
1406 // swap url for path if going through a proxy
1407 if ($this->proxyhost != '' && $this->proxyport != '') {
1408 $this->outgoing_payload = "$this->request_method $this->url " . strtoupper($this->scheme) . "/$this->protocol_version\r\n";
1410 $this->outgoing_payload = "$this->request_method $this->path " . strtoupper($this->scheme) . "/$this->protocol_version\r\n";
1413 $this->outgoing_payload .=
1414 "User-Agent: $this->title/$this->version\r\n" .
1415 "Host: " . $this->host . "\r\n";
1418 if ($this->username != '') {
1419 $this->debug('setting http auth credentials');
1420 $this->outgoing_payload .= 'Authorization: Basic ' . base64_encode("$this->username:$this->password") . "\r\n";
1423 $this->outgoing_payload .= 'Content-Type: text/xml; charset=' . $this->soap_defencoding . "\r\nContent-Length: " . strlen($data) . "\r\n";
1425 if ($this->encoding != '' && function_exists('gzdeflate')) {
1426 $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n" .
1427 "Connection: close\r\n";
1428 if (!check_php_version(5, 3)) {
1429 set_magic_quotes_runtime(0);
1433 if ($this->useSOAPAction) {
1434 $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\"" . "\r\n";
1436 $this->outgoing_payload .= "\r\n";
1438 $this->outgoing_payload .= $data;
1441 if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1442 $this->setError('couldn\'t write message data to socket');
1443 $this->debug('Write error');
1445 $this->debug('wrote data to socket');
1448 $this->incoming_payload = '';
1450 while ($data = fread($fp, 32768)) {
1451 $this->incoming_payload .= $data;
1452 //$strlen += strlen($data);
1454 $this->debug('received ' . strlen($this->incoming_payload) . ' bytes of data from server');
1456 // close filepointer
1458 $this->debug('closed socket');
1460 // connection was closed unexpectedly
1461 if ($this->incoming_payload == '') {
1462 $this->setError('no response from server');
1466 $this->debug('received incoming payload: ' . strlen($this->incoming_payload));
1467 $data = $this->incoming_payload . "\r\n\r\n\r\n\r\n";
1469 // remove 100 header
1470 if (ereg('^HTTP/1.1 100', $data)) {
1471 if ($pos = strpos($data, "\r\n\r\n")) {
1472 $data = ltrim(substr($data, $pos));
1473 } elseif ($pos = strpos($data, "\n\n")) {
1474 $data = ltrim(substr($data, $pos));
1479 // separate content from HTTP headers
1480 if ($pos = strpos($data, "\r\n\r\n")) {
1482 } elseif ($pos = strpos($data, "\n\n")) {
1485 $this->setError('no proper separation of headers and document');
1488 $header_data = trim(substr($data, 0, $pos));
1489 $header_array = explode($lb, $header_data);
1490 $data = ltrim(substr($data, $pos));
1491 $this->debug('found proper separation of headers and document');
1492 $this->debug('cleaned data, stringlen: ' . strlen($data));
1494 foreach ($header_array as $header_line) {
1495 $arr = explode(':', $header_line);
1496 if (count($arr) >= 2) {
1497 $headers[trim($arr[0])] = trim($arr[1]);
1500 //print "headers: <pre>$header_data</pre><br>";
1501 //print "data: <pre>$data</pre><br>";
1503 // decode transfer-encoding
1504 if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked') {
1505 //$timer->setMarker('starting to decode chunked content');
1506 if (!$data = $this->decodeChunked($data)) {
1507 $this->setError('Decoding of chunked data failed');
1510 //$timer->setMarker('finished decoding of chunked content');
1511 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1514 // decode content-encoding
1515 if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != '') {
1516 if ($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip') {
1517 // if decoding works, use it. else assume data wasn't gzencoded
1518 if (function_exists('gzinflate')) {
1519 //$timer->setMarker('starting decoding of gzip/deflated content');
1520 if ($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)) {
1522 } elseif ($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
1525 $this->setError('Errors occurred when trying to decode the data');
1527 //$timer->setMarker('finished decoding of gzip/deflated content');
1528 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
1530 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1535 if (strlen($data) == 0) {
1536 $this->debug('no data after headers!');
1537 $this->setError('no data present after HTTP headers');
1540 $this->debug('end of send()');
1545 * send the SOAP message via HTTPS 1.0 using CURL
1547 * @param string $msg message data
1548 * @param integer $timeout set timeout in seconds
1549 * @return string data
1552 function sendHTTPS($data, $timeout = 0)
1555 //$t->setMarker('inside sendHTTPS()');
1556 $this->debug('entered sendHTTPS() with data of length: ' . strlen($data));
1559 //$t->setMarker('got curl handle');
1561 if ($this->proxyhost && $this->proxyport) {
1562 $host = $this->proxyhost;
1563 $port = $this->proxyport;
1565 $host = $this->host;
1566 $port = $this->port;
1569 $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
1571 $hostURL .= $this->path;
1572 curl_setopt($ch, CURLOPT_URL, $hostURL);
1573 // set other options
1574 curl_setopt($ch, CURLOPT_HEADER, 1);
1575 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1577 if (function_exists('gzinflate')) {
1578 curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
1580 // persistent connection
1581 //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1584 if ($timeout != 0) {
1585 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1589 if ($this->username != '') {
1590 $credentials = 'Authorization: Basic ' . base64_encode("$this->username:$this->password") . '\r\n';
1593 if ($this->encoding != '') {
1594 if (function_exists('gzdeflate')) {
1595 $encoding_headers = "Accept-Encoding: $this->encoding\r\n" .
1596 "Connection: close\r\n";
1597 if (!check_php_version(5, 3)) {
1598 set_magic_quotes_runtime(0);
1603 if ($this->proxyhost && $this->proxyport) {
1604 $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
1606 $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
1609 $this->outgoing_payload .=
1610 "User-Agent: $this->title v$this->version\r\n" .
1611 "Host: " . $this->host . "\r\n" .
1614 "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n" .
1615 "Content-Length: " . strlen($data) . "\r\n" .
1616 "SOAPAction: \"$this->soapaction\"" . "\r\n\r\n" .
1620 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1621 //$t->setMarker('set curl options, executing...');
1623 $this->incoming_payload = curl_exec($ch);
1624 //$t->setMarker('executed transfer');
1625 $data = $this->incoming_payload;
1627 $cErr = curl_error($ch);
1630 $err = 'cURL ERROR: ' . curl_errno($ch) . ': ' . $cErr . '<br>';
1631 foreach (curl_getinfo($ch) as $k => $v) {
1632 $err .= "$k: $v<br>";
1634 $this->setError($err);
1639 //var_dump(curl_getinfo($ch));
1644 //$t->setMarker('closed curl');
1646 // remove 100 header
1647 if (ereg('^HTTP/1.1 100', $data)) {
1648 if ($pos = strpos($data, "\r\n\r\n")) {
1649 $data = ltrim(substr($data, $pos));
1650 } elseif ($pos = strpos($data, "\n\n")) {
1651 $data = ltrim(substr($data, $pos));
1656 // separate content from HTTP headers
1657 if ($pos = strpos($data, "\r\n\r\n")) {
1659 } elseif ($pos = strpos($data, "\n\n")) {
1662 $this->setError('no proper separation of headers and document');
1665 $header_data = trim(substr($data, 0, $pos));
1666 $header_array = explode($lb, $header_data);
1667 $data = ltrim(substr($data, $pos));
1668 $this->debug('found proper separation of headers and document');
1669 $this->debug('cleaned data, stringlen: ' . strlen($data));
1671 foreach ($header_array as $header_line) {
1672 $arr = explode(':', $header_line);
1673 $headers[trim($arr[0])] = trim($arr[1]);
1675 if (strlen($data) == 0) {
1676 $this->debug('no data after headers!');
1677 $this->setError('no data present after HTTP headers.');
1681 // decode transfer-encoding
1682 if ($headers['Transfer-Encoding'] == 'chunked') {
1683 if (!$data = $this->decodeChunked($data)) {
1684 $this->setError('Decoding of chunked data failed');
1688 // decode content-encoding
1689 if ($headers['Content-Encoding'] != '') {
1690 if ($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip') {
1691 // if decoding works, use it. else assume data wasn't gzencoded
1692 if (function_exists('gzinflate')) {
1693 if ($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)) {
1695 } elseif ($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
1698 $this->setError('Errors occurred when trying to decode the data');
1701 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1705 // set decoded payload
1706 $this->incoming_payload = $header_data . "\r\n\r\n" . $data;
1711 * if authenticating, set user credentials here
1713 * @param string $user
1714 * @param string $pass
1717 function setCredentials($username, $password)
1719 $this->username = $username;
1720 $this->password = $password;
1724 * set the soapaction value
1726 * @param string $soapaction
1729 function setSOAPAction($soapaction)
1731 $this->soapaction = $soapaction;
1737 * @param string $enc encoding style. supported values: gzip, deflate, or both
1740 function setEncoding($enc = 'gzip, deflate')
1742 $this->encoding = $enc;
1743 $this->protocol_version = '1.1';
1747 * set proxy info here
1749 * @param string $proxyhost
1750 * @param string $proxyport
1753 function setProxy($proxyhost, $proxyport)
1755 $this->proxyhost = $proxyhost;
1756 $this->proxyport = $proxyport;
1760 * decode a string that is encoded w/ "chunked' transfer encoding
1761 * as defined in RFC2068 19.4.6
1763 * @param string $buffer
1767 function decodeChunked($buffer)
1772 // read chunk-size, chunk-extension (if any) and CRLF
1773 // get the position of the linebreak
1774 $chunkend = strpos($buffer, "\r\n") + 2;
1775 $temp = substr($buffer, 0, $chunkend);
1776 $chunk_size = hexdec(trim($temp));
1777 $chunkstart = $chunkend;
1778 // while (chunk-size > 0) {
1779 while ($chunk_size > 0) {
1781 $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
1783 // Just in case we got a broken connection
1784 if ($chunkend == FALSE) {
1785 $chunk = substr($buffer, $chunkstart);
1786 // append chunk-data to entity-body
1788 $length += strlen($chunk);
1792 // read chunk-data and CRLF
1793 $chunk = substr($buffer, $chunkstart, $chunkend - $chunkstart);
1794 // append chunk-data to entity-body
1796 // length := length + chunk-size
1797 $length += strlen($chunk);
1798 // read chunk-size and CRLF
1799 $chunkstart = $chunkend + 2;
1801 $chunkend = strpos($buffer, "\r\n", $chunkstart) + 2;
1802 if ($chunkend == FALSE) {
1803 break; //Just in case we got a broken connection
1805 $temp = substr($buffer, $chunkstart, $chunkend - $chunkstart);
1806 $chunk_size = hexdec(trim($temp));
1807 $chunkstart = $chunkend;
1810 //$this->Header['content-length'] = $length;
1811 //unset($this->Header['transfer-encoding']);
1819 * soap_server allows the user to create a SOAP server
1820 * that is capable of receiving messages and returning responses
1822 * NOTE: WSDL functionality is experimental
1824 * @author Dietrich Ayala <dietrich@ganx4.com>
1828 class soap_server extends nusoap_base
1831 var $service = ''; // service name
1832 var $operations = array(); // assoc array of operations => opData
1833 var $responseHeaders = false;
1836 var $charset_encoding = 'UTF-8';
1838 var $result = 'successful';
1840 var $externalWSDLURL = false;
1841 var $debug_flag = true;
1845 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1847 * @param string $wsdl path or URL to a WSDL file
1850 function soap_server($wsdl = false)
1853 // turn on debugging?
1855 if (isset($debug)) {
1856 $this->debug_flag = 1;
1861 $this->wsdl = new wsdl($wsdl);
1862 $this->externalWSDLURL = $wsdl;
1863 if ($err = $this->wsdl->getError()) {
1864 die('WSDL ERROR: ' . $err);
1870 * processes request and returns response
1872 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
1875 function service($data)
1878 global $QUERY_STRING, $HTTP_SERVER_VARS;
1879 if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
1880 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
1881 } elseif (isset($GLOBALS['QUERY_STRING'])) {
1882 $qs = $GLOBALS['QUERY_STRING'];
1883 } elseif (isset($QUERY_STRING) && $QUERY_STRING != '') {
1884 $qs = $QUERY_STRING;
1887 if (isset($qs) && ereg('wsdl', $qs)) {
1888 if ($this->externalWSDLURL) {
1889 header('Location: ' . $this->externalWSDLURL);
1892 header("Content-Type: text/xml\r\n");
1893 print $this->wsdl->serialize();
1898 // print web interface
1899 if ($data == '' && $this->wsdl) {
1900 print $this->webDescription();
1903 // $response is the serialized response message
1904 $response = $this->parse_request($data);
1905 $this->debug('server sending...');
1906 $payload = $response;
1907 // add debug data if in debug mode
1908 if (isset($this->debug_flag) && $this->debug_flag == 1) {
1909 $payload .= "<!--\n" . str_replace('--', '- -', $this->debug_str) . "\n-->";
1913 $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
1914 $header[] = "Status: 500 Internal Server Error\r\n";
1916 $header[] = "Status: 200 OK\r\n";
1918 $header[] = "Server: $this->title Server v$this->version\r\n";
1919 $header[] = "Connection: Close\r\n";
1920 $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
1921 $header[] = "Content-Length: " . strlen($payload) . "\r\n\r\n";
1923 foreach ($header as $hdr) {
1926 $this->response = join("\r\n", $header) . $payload;
1932 * parses request and posts response
1934 * @param string $data XML string
1935 * @return string XML response msg
1938 function parse_request($data = '')
1940 if (!isset($_SERVER))
1941 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
1942 $this->debug('entering parseRequest() on ' . date('H:i Y-m-d'));
1945 if (function_exists('getallheaders')) {
1946 $this->headers = getallheaders();
1947 foreach ($this->headers as $k => $v) {
1948 $dump .= "$k: $v\r\n";
1949 $this->debug("$k: $v");
1951 // get SOAPAction header
1952 if (isset($this->headers['SOAPAction'])) {
1953 $this->SOAPAction = str_replace('"', '', $this->headers['SOAPAction']);
1955 // get the character encoding of the incoming request
1956 if (strpos($this->headers['Content-Type'], '=')) {
1957 $enc = str_replace('"', '', substr(strstr($this->headers["Content-Type"], '='), 1));
1958 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
1959 $this->xml_encoding = $enc;
1961 $this->xml_encoding = 'us-ascii';
1964 $this->debug('got encoding: ' . $this->charset_encoding);
1965 } elseif (is_array($_SERVER)) {
1966 $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
1967 $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
1969 $this->request = $dump . "\r\n\r\n" . $data;
1970 // parse response, get soap parser obj
1971 $parser = new soap_parser($data, $this->charset_encoding);
1972 // if fault occurred during message parsing
1973 if ($err = $parser->getError()) {
1975 $this->debug("parser debug: \n" . $parser->debug_str);
1976 $this->result = 'fault: error in msg parsing: ' . $err;
1977 $this->fault('Server', "error in msg parsing:\n" . $err);
1979 return $this->fault->serialize();
1980 // else successfully parsed request into soapval object
1982 // get/set methodname
1983 $this->methodname = $parser->root_struct_name;
1984 $this->debug('method name: ' . $this->methodname);
1985 // does method exist?
1986 if (!function_exists($this->methodname)) {
1987 // "method not found" fault here
1988 $this->debug("method '$this->methodname' not found!");
1989 $this->debug("parser debug: \n" . $parser->debug_str);
1990 $this->result = 'fault: method not found';
1991 $this->fault('Server', "method '$this->methodname' not defined in service '$this->service'");
1992 return $this->fault->serialize();
1995 if (!$this->opData = $this->wsdl->getOperationData($this->methodname)) {
1997 $this->fault('Server', "Operation '$this->methodname' is not defined in the WSDL for this service");
1998 return $this->fault->serialize();
2001 $this->debug("method '$this->methodname' exists");
2002 // evaluate message, getting back parameters
2003 $this->debug('calling parser->get_response()');
2004 $request_data = $parser->get_response();
2006 $this->debug("parser debug: \n" . $parser->debug_str);
2007 // verify that request parameters match the method's signature
2008 if ($this->verify_method($this->methodname, $request_data)) {
2009 // if there are parameters to pass
2010 $this->debug('params var dump ' . $this->varDump($request_data));
2011 if ($request_data) {
2012 $this->debug("calling '$this->methodname' with params");
2013 if (!function_exists('call_user_func_array')) {
2014 $this->debug('calling method using eval()');
2015 $funcCall = $this->methodname . '(';
2016 foreach ($request_data as $param) {
2017 $funcCall .= "\"$param\",";
2019 $funcCall = substr($funcCall, 0, -1) . ')';
2020 $this->debug('function call:<br>' . $funcCall);
2021 @eval("\$method_response = $funcCall;");
2023 $this->debug('calling method using call_user_func_array()');
2024 $method_response = call_user_func_array("$this->methodname", $request_data);
2026 $this->debug('response var dump' . $this->varDump($method_response));
2028 // call method w/ no parameters
2029 $this->debug("calling $this->methodname w/ no params");
2030 $m = $this->methodname;
2031 $method_response = @$m();
2033 $this->debug("done calling method: $this->methodname, received $method_response of type" . gettype($method_response));
2034 // if we got nothing back. this might be ok (echoVoid)
2035 if (isset($method_response) && $method_response != '' || is_bool($method_response)) {
2037 if (strtolower(get_class($method_response)) == 'soap_fault') {
2038 $this->debug('got a fault object from method');
2039 $this->fault = $method_response;
2040 return $method_response->serialize();
2041 // if return val is soapval object
2042 } elseif (strtolower(get_class($method_response)) == 'soapval') {
2043 $this->debug('got a soapval object from method');
2044 $return_val = $method_response->serialize();
2047 $this->debug('got a(n) ' . gettype($method_response) . ' from method');
2048 $this->debug('serializing return value');
2050 // weak attempt at supporting multiple output params
2051 if (sizeof($this->opData['output']['parts']) > 1) {
2052 $opParams = $method_response;
2054 $opParams = array($method_response);
2056 $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
2058 $return_val = $this->serialize_val($method_response);
2061 $this->debug('return val:' . $this->varDump($return_val));
2064 $this->debug('got no response from method');
2066 $this->debug('serializing response');
2067 $payload = '<' . $this->methodname . "Response>" . $return_val . '</' . $this->methodname . "Response>";
2068 $this->result = 'successful';
2070 //if($this->debug_flag){
2071 $this->debug("WSDL debug data:\n" . $this->wsdl->debug_str);
2073 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2074 return $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style']);
2076 return $this->serializeEnvelope($payload, $this->responseHeaders);
2080 $this->debug('ERROR: request not verified against method signature');
2081 $this->result = 'fault: request failed validation against method signature';
2083 $this->fault('Server', "Operation '$this->methodname' not defined in service.");
2084 return $this->fault->serialize();
2090 * takes the value that was created by parsing the request
2091 * and compares to the method's signature, if available.
2097 function verify_method($operation, $request)
2099 if (isset($this->wsdl) && is_object($this->wsdl)) {
2100 if ($this->wsdl->getOperationData($operation)) {
2103 } elseif (isset($this->operations[$operation])) {
2110 * add a method to the dispatch map
2112 * @param string $methodname
2113 * @param string $in array of input values
2114 * @param string $out array of output values
2117 function add_to_map($methodname, $in, $out)
2119 $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out);
2123 * register a service with the server
2125 * @param string $methodname
2126 * @param string $in assoc array of input values: key = param name, value = param type
2127 * @param string $out assoc array of output values: key = param name, value = param type
2128 * @param string $namespace
2129 * @param string $soapaction
2130 * @param string $style (rpc|literal)
2133 function register($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = false, $use = false)
2135 if ($this->externalWSDLURL) {
2136 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2140 if (false == $out) {
2142 if (false == $namespace) {
2144 if (false == $soapaction) {
2145 global $SERVER_NAME, $SCRIPT_NAME;
2146 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2148 if (false == $style) {
2151 if (false == $use) {
2155 $this->operations[] = array($name => array());
2156 $this->operations[$name] = array(
2160 'namespace' => $namespace,
2161 'soapaction' => $soapaction,
2164 $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use);
2170 * create a fault. this also acts as a flag to the server that a fault has occured.
2172 * @param string faultcode
2173 * @param string faultactor
2174 * @param string faultstring
2175 * @param string faultdetail
2178 function fault($faultcode, $faultactor, $faultstring = '', $faultdetail = '')
2180 $this->fault = new soap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
2184 * prints html description of services
2188 function webDescription()
2191 <html><head><title>NuSOAP: ' . $this->wsdl->serviceName . '</title>
2192 <style type="text/css">
2193 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2194 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2195 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2196 ul { margin-top: 10px; margin-left: 20px; }
2197 li { list-style-type: none; margin-top: 10px; color: #000000; }
2199 margin-left: 0px; padding-bottom: 2em; }
2201 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2202 margin-top: 10px; margin-left: 0px; color: #000000;
2203 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2205 font-family: arial; font-size: 26px; color: #ffffff;
2206 background-color: #999999; width: 105%; margin-left: 0px;
2207 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2209 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2210 font-family: arial; overflow: hidden; width: 600;
2211 padding: 20px; font-size: 10px; background-color: #999999;
2212 layer-background-color:#FFFFFF; }
2213 a,a:active { color: charcoal; font-weight: bold; }
2214 a:visited { color: #666666; font-weight: bold; }
2215 a:hover { color: cc3300; font-weight: bold; }
2217 <script type="text/javascript">
2219 // POP-UP CAPTIONS...
2220 function lib_bwcheck(){ //Browsercheck (needed)
2221 this.ver=navigator.appVersion
2222 this.agent=navigator.userAgent
2223 this.dom=document.getElementById?1:0
2224 this.opera5=this.agent.indexOf("Opera 5")>-1
2225 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2226 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2227 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2228 this.ie=this.ie4||this.ie5||this.ie6
2229 this.mac=this.agent.indexOf("Mac")>-1
2230 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2231 this.ns4=(document.layers && !this.dom)?1:0;
2232 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2235 var bw = new lib_bwcheck()
2236 //Makes crossbrowser object.
2237 function makeObj(obj){
2238 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2239 if(!this.evnt) return false
2240 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2241 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2242 this.writeIt=b_writeIt;
2245 // A unit of measure that will be added when setting the position of a layer.
2246 //var px = bw.ns4||window.opera?"":"px";
2247 function b_writeIt(text){
2248 if (bw.ns4){this.wref.write(text);this.wref.close()}
2249 else this.wref.innerHTML = text
2251 //Shows the messages
2253 function popup(divid){
2254 if(oDesc = new makeObj(divid)){
2255 oDesc.css.visibility = "visible"
2258 function popout(){ // Hides message
2259 if(oDesc) oDesc.css.visibility = "hidden"
2267 <div class=title>' . $this->wsdl->serviceName . '</div>
2269 <p>View the <a href="' . $GLOBALS['PHP_SELF'] . '?wsdl">WSDL</a> for the service.
2270 Click on an operation name to view it's details.</p>
2272 foreach ($this->wsdl->getOperations() as $op => $data) {
2273 $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2274 // create hidden div
2275 $b .= "<div id='$op' class='hidden'>
2276 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2277 foreach ($data as $donnie => $marie) { // loop through opdata
2278 if ($donnie == 'input' || $donnie == 'output') { // show input/output data
2279 $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
2280 foreach ($marie as $captain => $tenille) { // loop through data
2281 if ($captain == 'parts') { // loop thru parts
2282 $b .= " $captain:<br>";
2283 //if(is_array($tenille)){
2284 foreach ($tenille as $joanie => $chachi) {
2285 $b .= " $joanie: $chachi<br>";
2289 $b .= " $captain: $tenille<br>";
2293 $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
2301 </div></body></html>';
2306 * sets up wsdl object
2307 * this acts as a flag to enable internal WSDL generation
2308 * NOTE: NOT FUNCTIONAL
2310 * @param string $serviceName, name of the service
2311 * @param string $namespace, tns namespace
2313 function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http')
2315 if (!isset($_SERVER))
2316 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
2317 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2318 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2319 if (false == $namespace) {
2320 $namespace = "http://$SERVER_NAME/soap/$serviceName";
2323 if (false == $endpoint) {
2324 $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2327 $this->wsdl = new wsdl;
2328 $this->wsdl->serviceName = $serviceName;
2329 $this->wsdl->endpoint = $endpoint;
2330 $this->wsdl->namespaces['tns'] = $namespace;
2331 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
2332 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
2333 $this->wsdl->bindings[$serviceName . 'Binding'] = array(
2334 'name' => $serviceName . 'Binding',
2336 'transport' => $transport,
2337 'portType' => $serviceName . 'PortType');
2338 $this->wsdl->ports[$serviceName . 'Port'] = array(
2339 'binding' => $serviceName . 'Binding',
2340 'location' => $endpoint,
2341 'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/');
2346 * parses a WSDL file, allows access to it's data, other utility methods
2348 * @author Dietrich Ayala <dietrich@ganx4.com>
2352 class wsdl extends XMLSchema
2355 // define internal arrays of bindings, ports, operations, messages, etc.
2356 var $message = array();
2357 var $complexTypes = array();
2358 var $messages = array();
2359 var $currentMessage;
2360 var $currentOperation;
2361 var $portTypes = array();
2362 var $currentPortType;
2363 var $bindings = array();
2364 var $currentBinding;
2365 var $ports = array();
2367 var $opData = array();
2369 var $documentation = false;
2371 // array of wsdl docs to import
2372 var $import = array();
2377 var $depth_array = array();
2378 var $usedNamespaces = array();
2380 var $proxyhost = '';
2381 var $proxyport = '';
2386 * @param string $wsdl WSDL document URL
2389 function wsdl($wsdl = '', $proxyhost = false, $proxyport = false)
2391 $this->wsdl = $wsdl;
2392 $this->proxyhost = $proxyhost;
2393 $this->proxyport = $proxyport;
2397 $this->debug('initial wsdl file: ' . $wsdl);
2398 $this->parseWSDL($wsdl);
2401 if (sizeof($this->import) > 0) {
2402 foreach ($this->import as $ns => $url) {
2403 $this->debug('importing wsdl from ' . $url);
2404 $this->parseWSDL($url);
2410 * parses the wsdl document
2412 * @param string $wsdl path or URL
2415 function parseWSDL($wsdl = '')
2418 $this->debug('no wsdl passed to parseWSDL()!!');
2419 $this->setError('no wsdl passed to parseWSDL()!!');
2423 $this->debug('getting ' . $wsdl);
2425 // parse $wsdl for url format
2426 $wsdl_props = parse_url($wsdl);
2428 if (isset($wsdl_props['host'])) {
2431 $tr = new soap_transport_http($wsdl);
2432 $tr->request_method = 'GET';
2433 $tr->useSOAPAction = false;
2434 if ($this->proxyhost && $this->proxyport) {
2435 $tr->setProxy($this->proxyhost, $this->proxyport);
2437 if (isset($wsdl_props['user'])) {
2438 $tr->setCredentials($wsdl_props['user'], $wsdl_props['pass']);
2440 $wsdl_string = $tr->send('');
2442 if ($err = $tr->getError()) {
2443 $this->debug('HTTP ERROR: ' . $err);
2444 $this->setError('HTTP ERROR: ' . $err);
2448 /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
2449 $fsockopen_timeout = 30;
2450 // check if a port value is supplied in url
2451 if (isset($wsdl_props['port'])) {
2453 $wsdl_url_port = $wsdl_props['port'];
2455 // no, assign port number, based on url protocol (scheme)
2456 switch ($wsdl_props['scheme']) {
2460 $wsdl_url_port = 443;
2464 $wsdl_url_port = 80;
2467 // FIXME: should implement SSL/TLS support here if CURL is available
2468 if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
2469 // perform HTTP GET for WSDL file
2470 // 10.9.02 - added poulter fix for doing this properly
2471 $sHeader = "GET " . $wsdl_props['path'];
2472 if (isset($wsdl_props['query'])) {
2473 $sHeader .= "?" . $wsdl_props['query'];
2475 $sHeader .= " HTTP/1.0\r\n";
2477 if (isset($wsdl_props['user'])) {
2478 $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2479 $sHeader .= "Authorization: Basic $base64auth\r\n";
2481 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2482 fputs($fp, $sHeader);
2484 while (fgets($fp, 1024) != "\r\n") {
2485 // do nothing, just read/skip past HTTP headers
2486 // FIXME: should actually detect HTTP response code, and act accordingly if error
2487 // HTTP headers end with extra CRLF before content body
2489 // read in WSDL just like regular fopen()
2491 while ($data = fread($fp, 32768)) {
2492 $wsdl_string .= $data;
2496 $this->setError('bad path to WSDL file.');
2501 // $wsdl seems to be a non-url file path, do the regular fopen
2502 if ($fp = @fopen($wsdl, 'r')) {
2504 while ($data = fread($fp, 32768)) {
2505 $wsdl_string .= $data;
2509 $this->setError('bad path to WSDL file.');
2513 // end new code added
2514 // Create an XML parser.
2515 $this->parser = xml_parser_create();
2516 // Set the options for parsing the XML data.
2517 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2518 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
2519 // Set the object for the parser.
2520 xml_set_object($this->parser, $this);
2521 // Set the element handlers for the parser.
2522 xml_set_element_handler($this->parser, 'start_element', 'end_element');
2523 xml_set_character_data_handler($this->parser, 'character_data');
2524 // Parse the XML file.
2525 if (!xml_parse($this->parser, $wsdl_string, true)) {
2526 // Display an error message.
2528 'XML error on line %d: %s',
2529 xml_get_current_line_number($this->parser),
2530 xml_error_string(xml_get_error_code($this->parser))
2532 $this->debug('XML parse error: ' . $errstr);
2533 $this->setError('Parser error: ' . $errstr);
2537 xml_parser_free($this->parser);
2538 // catch wsdl parse errors
2539 if ($this->getError()) {
2542 // add new data to operation data
2543 foreach ($this->bindings as $binding => $bindingData) {
2544 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
2545 foreach ($bindingData['operations'] as $operation => $data) {
2546 $this->debug('post-parse data gathering for ' . $operation);
2547 $this->bindings[$binding]['operations'][$operation]['input'] =
2548 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
2549 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) :
2550 $this->portTypes[$bindingData['portType']][$operation]['input'];
2551 $this->bindings[$binding]['operations'][$operation]['output'] =
2552 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
2553 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) :
2554 $this->portTypes[$bindingData['portType']][$operation]['output'];
2555 if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
2556 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
2558 if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
2559 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
2561 if (isset($bindingData['style'])) {
2562 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2564 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
2565 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
2566 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
2574 * start-element handler
2576 * @param string $parser XML parser object
2577 * @param string $name element name
2578 * @param string $attrs associative array of attributes
2581 function start_element($parser, $name, $attrs)
2583 if ($this->status == 'schema' || ereg('schema$', $name)) {
2584 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
2585 $this->status = 'schema';
2586 $this->schemaStartElement($parser, $name, $attrs);
2588 // position in the total number of elements, starting from 0
2589 $pos = $this->position++;
2590 $depth = $this->depth++;
2591 // set self as current value for this depth
2592 $this->depth_array[$depth] = $pos;
2593 $this->message[$pos] = array('cdata' => '');
2594 // get element prefix
2595 if (ereg(':', $name)) {
2597 $prefix = substr($name, 0, strpos($name, ':'));
2599 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
2600 // get unqualified name
2601 $name = substr(strstr($name, ':'), 1);
2604 if (count($attrs) > 0) {
2605 foreach ($attrs as $k => $v) {
2606 // if ns declarations, add to class level array of valid namespaces
2607 if (ereg("^xmlns", $k)) {
2608 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
2609 $this->namespaces[$ns_prefix] = $v;
2611 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
2613 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
2614 $this->XMLSchemaVersion = $v;
2615 $this->namespaces['xsi'] = $v . '-instance';
2618 // expand each attribute
2619 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
2620 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
2621 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
2629 // find status, register data
2630 switch ($this->status) {
2632 if ($name == 'part') {
2633 if (isset($attrs['type'])) {
2634 $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
2635 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
2637 if (isset($attrs['element'])) {
2638 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2645 $this->currentPortOperation = $attrs['name'];
2646 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
2647 if (isset($attrs['parameterOrder'])) {
2648 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
2651 case 'documentation':
2652 $this->documentation = true;
2654 // merge input/output data
2656 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
2657 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
2665 if (isset($attrs['style'])) {
2666 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
2668 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
2671 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
2674 if (isset($attrs['soapAction'])) {
2675 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
2677 if (isset($attrs['style'])) {
2678 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2680 if (isset($attrs['name'])) {
2681 $this->currentOperation = $attrs['name'];
2682 $this->debug("current binding operation: $this->currentOperation");
2683 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
2684 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
2685 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
2689 $this->opStatus = 'input';
2692 $this->opStatus = 'output';
2695 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
2696 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
2698 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
2706 $this->currentPort = $attrs['name'];
2707 $this->debug('current port: ' . $this->currentPort);
2708 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
2712 $this->ports[$this->currentPort]['location'] = $attrs['location'];
2713 $this->ports[$this->currentPort]['bindingType'] = $namespace;
2714 $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
2715 $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
2723 if (isset($attrs['location'])) {
2724 $this->import[$attrs['namespace']] = $attrs['location'];
2728 $this->status = 'schema';
2731 $this->status = 'message';
2732 $this->messages[$attrs['name']] = array();
2733 $this->currentMessage = $attrs['name'];
2736 $this->status = 'portType';
2737 $this->portTypes[$attrs['name']] = array();
2738 $this->currentPortType = $attrs['name'];
2741 if (isset($attrs['name'])) {
2743 if (strpos($attrs['name'], ':')) {
2744 $this->currentBinding = $this->getLocalPart($attrs['name']);
2746 $this->currentBinding = $attrs['name'];
2748 $this->status = 'binding';
2749 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
2750 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
2754 $this->serviceName = $attrs['name'];
2755 $this->status = 'service';
2756 $this->debug('current service: ' . $this->serviceName);
2759 foreach ($attrs as $name => $value) {
2760 $this->wsdl_info[$name] = $value;
2768 * end-element handler
2770 * @param string $parser XML parser object
2771 * @param string $name element name
2774 function end_element($parser, $name)
2776 // unset schema status
2777 if (ereg('types$', $name) || ereg('schema$', $name)) {
2780 if ($this->status == 'schema') {
2781 $this->schemaEndElement($parser, $name);
2783 // bring depth down a notch
2786 // end documentation
2787 if ($this->documentation) {
2788 $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2789 $this->documentation = false;
2794 * element content handler
2796 * @param string $parser XML parser object
2797 * @param string $data element content
2800 function character_data($parser, $data)
2802 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
2803 if (isset($this->message[$pos]['cdata'])) {
2804 $this->message[$pos]['cdata'] .= $data;
2806 if ($this->documentation) {
2807 $this->documentation .= $data;
2811 function getBindingData($binding)
2813 if (is_array($this->bindings[$binding])) {
2814 return $this->bindings[$binding];
2819 * returns an assoc array of operation names => operation data
2820 * NOTE: currently only supports multiple services of differing binding types
2821 * This method needs some work
2823 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2827 function getOperations($bindingType = 'soap')
2829 if ($bindingType == 'soap') {
2830 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2833 foreach ($this->ports as $port => $portData) {
2834 // binding type of port matches parameter
2835 if ($portData['bindingType'] == $bindingType) {
2837 return $this->bindings[$portData['binding']]['operations'];
2844 * returns an associative array of data necessary for calling an operation
2846 * @param string $operation , name of operation
2847 * @param string $bindingType , type of binding eg: soap
2851 function getOperationData($operation, $bindingType = 'soap')
2853 if ($bindingType == 'soap') {
2854 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2857 foreach ($this->ports as $port => $portData) {
2858 // binding type of port matches parameter
2859 if ($portData['bindingType'] == $bindingType) {
2861 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
2862 foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
2863 if ($operation == $bOperation) {
2864 $opData = $this->bindings[$portData['binding']]['operations'][$operation];
2873 * serialize the parsed wsdl
2875 * @return string , serialization of WSDL
2878 function serialize()
2880 $xml = '<?xml version="1.0"?><definitions';
2881 foreach ($this->namespaces as $k => $v) {
2882 $xml .= " xmlns:$k=\"$v\"";
2884 // 10.9.02 - add poulter fix for wsdl and tns declarations
2885 if (isset($this->namespaces['wsdl'])) {
2886 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2888 if (isset($this->namespaces['tns'])) {
2889 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2893 if (sizeof($this->import) > 0) {
2894 foreach ($this->import as $ns => $url) {
2895 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2899 if (count($this->complexTypes) >= 1) {
2901 $xml .= $this->serializeSchema();
2905 if (count($this->messages) >= 1) {
2906 foreach ($this->messages as $msgName => $msgParts) {
2907 $xml .= '<message name="' . $msgName . '">';
2908 foreach ($msgParts as $partName => $partType) {
2909 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
2910 if (strpos($partType, ':')) {
2911 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
2912 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
2913 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
2914 $typePrefix = 'xsd';
2916 foreach ($this->typemap as $ns => $types) {
2917 if (isset($types[$partType])) {
2918 $typePrefix = $this->getPrefixFromNamespace($ns);
2921 if (!isset($typePrefix)) {
2922 die("$partType has no namespace!");
2925 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2927 $xml .= '</message>';
2930 // bindings & porttypes
2931 if (count($this->bindings) >= 1) {
2934 foreach ($this->bindings as $bindingName => $attrs) {
2935 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
2936 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
2937 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
2938 foreach ($attrs['operations'] as $opName => $opParts) {
2939 $binding_xml .= '<operation name="' . $opName . '">';
2940 $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $attrs['style'] . '"/>';
2941 $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
2942 $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
2943 $binding_xml .= '</operation>';
2944 $portType_xml .= '<operation name="' . $opParts['name'] . '"';
2945 if (isset($opParts['parameterOrder'])) {
2946 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
2948 $portType_xml .= '>';
2949 $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
2950 $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
2951 $portType_xml .= '</operation>';
2953 $portType_xml .= '</portType>';
2954 $binding_xml .= '</binding>';
2956 $xml .= $portType_xml . $binding_xml;
2959 $xml .= '<service name="' . $this->serviceName . '">';
2960 if (count($this->ports) >= 1) {
2961 foreach ($this->ports as $pName => $attrs) {
2962 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
2963 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
2967 $xml .= '</service>';
2968 return $xml . '</definitions>';
2972 * serialize a PHP value according to a WSDL message definition
2975 * - multi-ref serialization
2976 * - validate PHP values against type definitions, return errors if invalid
2978 * @param string $ type name
2979 * @param mixed $ param value
2980 * @return mixed new param or false if initial value didn't validate
2982 function serializeRPCParameters($operation, $direction, $parameters)
2984 $this->debug('in serializeRPCParameters with operation ' . $operation . ', direction ' . $direction . ' and ' . count($parameters) . ' param(s), and xml schema version ' . $this->XMLSchemaVersion);
2986 if ($direction != 'input' && $direction != 'output') {
2987 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
2988 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
2991 if (!$opData = $this->getOperationData($operation)) {
2992 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
2993 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
2996 $this->debug($this->varDump($opData));
2999 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3001 $use = $opData[$direction]['use'];
3002 $this->debug("use=$use");
3003 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3004 foreach ($opData[$direction]['parts'] as $name => $type) {
3005 $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
3006 // NOTE: add error handling here
3007 // if serializeType returns false, then catch global error and fault
3008 if (isset($parameters[$name])) {
3009 $this->debug('calling serializeType w/ named param');
3010 $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
3011 } elseif (is_array($parameters)) {
3012 $this->debug('calling serializeType w/ unnamed param');
3013 $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
3015 $this->debug('no parameters passed.');
3023 * serializes a PHP value according a given type definition
3025 * @param string $name , name of type (part)
3026 * @param string $type , type of type, heh (type or element)
3027 * @param mixed $value , a native PHP value (parameter value)
3028 * @param string $use , use for part (encoded|literal)
3029 * @return string serialization
3032 function serializeType($name, $type, $value, $use = 'encoded')
3034 $this->debug("in serializeType: $name, $type, $value, $use");
3036 if (strpos($type, ':')) {
3037 $uqType = substr($type, strrpos($type, ':') + 1);
3038 $ns = substr($type, 0, strrpos($type, ':'));
3039 $this->debug("got a prefixed type: $uqType, $ns");
3041 if ($ns == $this->XMLSchemaVersion ||
3042 ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion
3045 if ($uqType == 'boolean' && !$value) {
3047 } elseif ($uqType == 'boolean') {
3050 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
3051 $value = htmlspecialchars($value);
3054 if ($use == 'literal') {
3055 return "<$name>$value</$name>";
3057 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3063 if (!$typeDef = $this->getTypeDef($uqType)) {
3064 $this->setError("$uqType is not a supported type.");
3067 //foreach($typeDef as $k => $v) {
3068 //$this->debug("typedef, $k: $v");
3071 $phpType = $typeDef['phpType'];
3072 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
3073 // if php type == struct, map value to the <all> element names
3074 if ($phpType == 'struct') {
3075 if (isset($typeDef['element']) && $typeDef['element']) {
3076 $elementName = $uqType;
3077 // TODO: use elementFormDefault="qualified|unqualified" to determine
3078 // how to scope the namespace
3079 $elementNS = " xmlns=\"$ns\"";
3081 $elementName = $name;
3084 if ($use == 'literal') {
3085 $xml = "<$elementName$elementNS>";
3087 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3090 if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3092 //if (is_array($this->complexTypes[$uqType]['elements'])) {
3093 // toggle whether all elements are present - ideally should validate against schema
3094 if (count($this->complexTypes[$uqType]['elements']) != count($value)) {
3097 foreach ($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
3098 // if user took advantage of a minOccurs=0, then only serialize named parameters
3099 if (isset($optionals) && !isset($value[$eName])) {
3103 if (isset($value[$eName])) {
3104 $v = $value[$eName];
3105 } elseif (is_array($value)) {
3106 $v = array_shift($value);
3108 // serialize schema-defined type
3109 if (!isset($attrs['type'])) {
3110 $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
3111 // serialize generic type
3113 $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
3114 $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
3119 $xml .= "</$elementName>";
3120 } elseif ($phpType == 'array') {
3121 $rows = sizeof($value);
3122 if (isset($typeDef['multidimensional'])) {
3124 foreach ($value as $v) {
3125 $cols = ',' . sizeof($v);
3126 $nv = array_merge($nv, $v);
3132 if (is_array($value) && sizeof($value) >= 1) {
3134 foreach ($value as $k => $v) {
3135 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
3136 //if (strpos($typeDef['arrayType'], ':') ) {
3137 if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
3138 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
3140 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
3143 $this->debug('contents: ' . $this->varDump($contents));
3147 if ($use == 'literal') {
3152 $xml = "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
3153 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
3155 . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
3156 . ":" . $this->getLocalPart($typeDef['arrayType']) . "[$rows$cols]\">"
3161 $this->debug('returning: ' . $this->varDump($xml));
3166 * register a service with the server
3168 * @param string $methodname
3169 * @param string $in assoc array of input values: key = param name, value = param type
3170 * @param string $out assoc array of output values: key = param name, value = param type
3171 * @param string $namespace
3172 * @param string $soapaction
3173 * @param string $style (rpc|literal)
3176 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3178 if ($style == 'rpc' && $use == 'encoded') {
3179 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3181 $encodingStyle = '';
3184 $this->bindings[$this->serviceName . 'Binding']['operations'][$name] =
3187 'binding' => $this->serviceName . 'Binding',
3188 'endpoint' => $this->endpoint,
3189 'soapAction' => $soapaction,
3193 'namespace' => $namespace,
3194 'encodingStyle' => $encodingStyle,
3195 'message' => $name . 'Request',
3199 'namespace' => $namespace,
3200 'encodingStyle' => $encodingStyle,
3201 'message' => $name . 'Response',
3203 'namespace' => $namespace,
3204 'transport' => 'http://schemas.xmlsoap.org/soap/http',
3205 'documentation' => $documentation);
3209 foreach ($in as $pName => $pType) {
3210 if (strpos($pType, ':')) {
3211 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
3213 $this->messages[$name . 'Request'][$pName] = $pType;
3218 foreach ($out as $pName => $pType) {
3219 if (strpos($pType, ':')) {
3220 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
3222 $this->messages[$name . 'Response'][$pName] = $pType;
3231 * soap_parser class parses SOAP XML messages into native PHP values
3233 * @author Dietrich Ayala <dietrich@ganx4.com>
3237 class soap_parser extends nusoap_base
3241 var $xml_encoding = '';
3243 var $root_struct = '';
3244 var $root_struct_name = '';
3245 var $root_header = '';
3247 // determines where in the message we are (envelope,header,body,method)
3251 var $default_namespace = '';
3252 var $namespaces = array();
3253 var $message = array();
3256 var $fault_code = '';
3257 var $fault_str = '';
3258 var $fault_detail = '';
3259 var $depth_array = array();
3260 var $debug_flag = true;
3261 var $soapresponse = NULL;
3262 var $responseHeaders = '';
3263 var $body_position = 0;
3264 // for multiref parsing:
3265 // array of id => pos
3267 // array of id => hrefs => pos
3268 var $multirefs = array();
3273 * @param string $xml SOAP message
3274 * @param string $encoding character encoding scheme of message
3277 function soap_parser($xml, $encoding = 'UTF-8', $method = '')
3280 $this->xml_encoding = $encoding;
3281 $this->method = $method;
3283 // Check whether content has been read.
3285 $this->debug('Entering soap_parser()');
3286 // Create an XML parser.
3287 $this->parser = xml_parser_create($this->xml_encoding);
3288 // Set the options for parsing the XML data.
3289 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3290 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3291 // Set the object for the parser.
3292 xml_set_object($this->parser, $this);
3293 // Set the element handlers for the parser.
3294 xml_set_element_handler($this->parser, 'start_element', 'end_element');
3295 xml_set_character_data_handler($this->parser, 'character_data');
3297 // Parse the XML file.
3298 if (!xml_parse($this->parser, $xml, true)) {
3299 // Display an error message.
3300 $err = sprintf('XML error on line %d: %s',
3301 xml_get_current_line_number($this->parser),
3302 xml_error_string(xml_get_error_code($this->parser)));
3303 $this->debug('parse error: ' . $err);
3304 $this->errstr = $err;
3306 $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
3308 $this->soapresponse = $this->message[$this->root_struct]['result'];
3310 if ($this->root_header != '' && isset($this->message[$this->root_header]['result'])) {
3311 $this->responseHeaders = $this->message[$this->root_header]['result'];
3313 // resolve hrefs/ids
3314 if (sizeof($this->multirefs) > 0) {
3315 foreach ($this->multirefs as $id => $hrefs) {
3316 $this->debug('resolving multirefs for id: ' . $id);
3317 $idVal = $this->buildVal($this->ids[$id]);
3318 foreach ($hrefs as $refPos => $ref) {
3319 $this->debug('resolving href at pos ' . $refPos);
3320 $this->multirefs[$id][$refPos] = $idVal;
3325 xml_parser_free($this->parser);
3327 $this->debug('xml was empty, didn\'t parse!');
3328 $this->errstr = 'xml was empty, didn\'t parse!';
3333 * start-element handler
3335 * @param string $parser XML parser object
3336 * @param string $name element name
3337 * @param string $attrs associative array of attributes
3340 function start_element($parser, $name, $attrs)
3342 // position in a total number of elements, starting from 0
3343 // update class level pos
3344 $pos = $this->position++;
3346 $this->message[$pos] = array('pos' => $pos, 'children' => '', 'cdata' => '');
3347 // depth = how many levels removed from root?
3348 // set mine as current global depth and increment global depth value
3349 $this->message[$pos]['depth'] = $this->depth++;
3351 // else add self as child to whoever the current parent is
3353 $this->message[$this->parent]['children'] .= '|' . $pos;
3356 $this->message[$pos]['parent'] = $this->parent;
3357 // set self as current parent
3358 $this->parent = $pos;
3359 // set self as current value for this depth
3360 $this->depth_array[$this->depth] = $pos;
3361 // get element prefix
3362 if (strpos($name, ':')) {
3364 $prefix = substr($name, 0, strpos($name, ':'));
3365 // get unqualified name
3366 $name = substr(strstr($name, ':'), 1);
3369 if ($name == 'Envelope') {
3370 $this->status = 'envelope';
3371 } elseif ($name == 'Header') {
3372 $this->root_header = $pos;
3373 $this->status = 'header';
3374 } elseif ($name == 'Body') {
3375 $this->status = 'body';
3376 $this->body_position = $pos;
3378 } elseif ($this->status == 'body' && $pos == ($this->body_position + 1)) {
3379 $this->status = 'method';
3380 $this->root_struct_name = $name;
3381 $this->root_struct = $pos;
3382 $this->message[$pos]['type'] = 'struct';
3383 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
3386 $this->message[$pos]['status'] = $this->status;
3388 $this->message[$pos]['name'] = htmlspecialchars($name);
3390 $this->message[$pos]['attrs'] = $attrs;
3392 // loop through atts, logging ns and type declarations
3394 foreach ($attrs as $key => $value) {
3395 $key_prefix = $this->getPrefix($key);
3396 $key_localpart = $this->getLocalPart($key);
3397 // if ns declarations, add to class level array of valid namespaces
3398 if ($key_prefix == 'xmlns') {
3399 if (ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$', $value)) {
3400 $this->XMLSchemaVersion = $value;
3401 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
3402 $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
3404 $this->namespaces[$key_localpart] = $value;
3405 // set method namespace
3406 if ($name == $this->root_struct_name) {
3407 $this->methodNamespace = $value;
3409 // if it's a type declaration, set type
3410 } elseif ($key_localpart == 'type') {
3411 $value_prefix = $this->getPrefix($value);
3412 $value_localpart = $this->getLocalPart($value);
3413 $this->message[$pos]['type'] = $value_localpart;
3414 $this->message[$pos]['typePrefix'] = $value_prefix;
3415 if (isset($this->namespaces[$value_prefix])) {
3416 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
3417 } elseif (isset($attrs['xmlns:' . $value_prefix])) {
3418 $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
3420 // should do something here with the namespace of specified type?
3421 } elseif ($key_localpart == 'arrayType') {
3422 $this->message[$pos]['type'] = 'array';
3423 /* do arrayType ereg here
3424 [1] arrayTypeValue ::= atype asize
3425 [2] atype ::= QName rank*
3426 [3] rank ::= '[' (',')* ']'
3427 [4] asize ::= '[' length~ ']'
3428 [5] length ::= nextDimension* Digit+
3429 [6] nextDimension ::= Digit+ ','
3431 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
3432 if (ereg($expr, $value, $regs)) {
3433 $this->message[$pos]['typePrefix'] = $regs[1];
3434 $this->message[$pos]['arraySize'] = $regs[3];
3435 $this->message[$pos]['arrayCols'] = $regs[4];
3440 $this->ids[$value] = $pos;
3443 if ($key_localpart == 'root' && $value == 1) {
3444 $this->status = 'method';
3445 $this->root_struct_name = $name;
3446 $this->root_struct = $pos;
3447 $this->debug("found root struct $this->root_struct_name, pos $pos");
3450 $attstr .= " $key=\"$value\"";
3452 // get namespace - must be done after namespace atts are processed
3453 if (isset($prefix)) {
3454 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
3455 $this->default_namespace = $this->namespaces[$prefix];
3457 $this->message[$pos]['namespace'] = $this->default_namespace;
3459 if ($this->status == 'header') {
3460 $this->responseHeaders .= "<$name$attstr>";
3461 } elseif ($this->root_struct_name != '') {
3462 $this->document .= "<$name$attstr>";
3467 * end-element handler
3469 * @param string $parser XML parser object
3470 * @param string $name element name
3473 function end_element($parser, $name)
3475 // position of current element is equal to the last value left in depth_array for my depth
3476 $pos = $this->depth_array[$this->depth--];
3478 // get element prefix
3479 if (strpos($name, ':')) {
3481 $prefix = substr($name, 0, strpos($name, ':'));
3482 // get unqualified name
3483 $name = substr(strstr($name, ':'), 1);
3486 // build to native type
3487 if (isset($this->body_position) && $pos > $this->body_position) {
3488 // deal w/ multirefs
3489 if (isset($this->message[$pos]['attrs']['href'])) {
3491 $id = substr($this->message[$pos]['attrs']['href'], 1);
3492 // add placeholder to href array
3493 $this->multirefs[$id][$pos] = "placeholder";
3494 // add set a reference to it as the result value
3495 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
3496 // build complex values
3497 } elseif ($this->message[$pos]['children'] != "") {
3498 $this->message[$pos]['result'] = $this->buildVal($pos);
3500 $this->debug('adding data for scalar value ' . $this->message[$pos]['name'] . ' of value ' . $this->message[$pos]['cdata']);
3501 if (is_numeric($this->message[$pos]['cdata'])) {
3502 if (strpos($this->message[$pos]['cdata'], '.')) {
3503 $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
3505 $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
3508 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3514 if ($pos == $this->root_struct) {
3515 $this->status = 'body';
3516 } elseif ($name == 'Body') {
3517 $this->status = 'header';
3518 } elseif ($name == 'Header') {
3519 $this->status = 'envelope';
3520 } elseif ($name == 'Envelope') {
3523 // set parent back to my parent
3524 $this->parent = $this->message[$pos]['parent'];
3526 if ($this->status == 'header') {
3527 $this->responseHeaders .= "</$name>";
3528 } elseif ($pos >= $this->root_struct) {
3529 $this->document .= "</$name>";
3534 * element content handler
3536 * @param string $parser XML parser object
3537 * @param string $data element content
3540 function character_data($parser, $data)
3542 $pos = $this->depth_array[$this->depth];
3543 if ($this->xml_encoding == 'UTF-8') {
3544 $data = utf8_decode($data);
3546 $this->message[$pos]['cdata'] .= $data;
3548 if ($this->status == 'header') {
3549 $this->responseHeaders .= $data;
3551 $this->document .= $data;
3556 * get the parsed message
3561 function get_response()
3563 return $this->soapresponse;
3567 * get the parsed headers
3569 * @return string XML or empty if no headers
3572 function getHeaders()
3574 return $this->responseHeaders;
3580 * @param string $text string to translate
3583 function decode_entities($text)
3585 foreach ($this->entities as $entity => $encoded) {
3586 $text = str_replace($encoded, $entity, $text);
3592 * builds response structures for compound values (arrays/structs)
3594 * @param string $pos position in node tree
3597 function buildVal($pos)
3599 if (!isset($this->message[$pos]['type'])) {
3600 $this->message[$pos]['type'] = '';
3602 $this->debug('inside buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
3603 // if there are children...
3604 if ($this->message[$pos]['children'] != '') {
3605 $children = explode('|', $this->message[$pos]['children']);
3606 array_shift($children); // knock off empty
3608 if (isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') {
3611 foreach ($children as $child_pos) {
3612 $this->debug("got an MD array element: $r, $c");
3613 $params[$r][] = $this->message[$child_pos]['result'];
3615 if ($c == $this->message[$pos]['arrayCols']) {
3621 } elseif ($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') {
3622 $this->debug('adding array ' . $this->message[$pos]['name']);
3623 foreach ($children as $child_pos) {
3624 $params[] = &$this->message[$child_pos]['result'];
3626 // apache Map type: java hashtable
3627 } elseif ($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
3628 foreach ($children as $child_pos) {
3629 $kv = explode("|", $this->message[$child_pos]['children']);
3630 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
3632 // generic compound type
3633 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
3635 // is array or struct? better way to do this probably
3636 foreach ($children as $child_pos) {
3637 if (isset($keys) && isset($keys[$this->message[$child_pos]['name']])) {
3641 $keys[$this->message[$child_pos]['name']] = 1;
3644 foreach ($children as $child_pos) {
3645 if (isset($struct)) {
3646 $params[] = &$this->message[$child_pos]['result'];
3648 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
3652 return is_array($params) ? $params : array();
3654 $this->debug('no children');
3655 if (strpos($this->message[$pos]['cdata'], '&')) {
3656 return strtr($this->message[$pos]['cdata'], array_flip($this->entities));
3658 return $this->message[$pos]['cdata'];
3666 * soapclient higher level class for easy usage.
3670 * // instantiate client with server info
3671 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
3673 * // call method, get results
3674 * echo $soapclient->call( string methodname [ ,array parameters] );
3677 * unset($soapclient);
3679 * @author Dietrich Ayala <dietrich@ganx4.com>
3683 class pwsoapclient extends nusoap_base
3688 var $requestHeaders = false;
3689 var $responseHeaders;
3691 var $error_str = false;
3692 var $proxyhost = '';
3693 var $proxyport = '';
3694 var $xml_encoding = '';
3695 var $http_encoding = false;
3697 var $endpointType = '';
3698 var $persistentConnection = false;
3699 var $defaultRpcParams = false;
3702 * fault related variables
3710 var $fault, $faultcode, $faultstring, $faultdetail;
3715 * @param string $endpoint SOAP server or WSDL URL
3716 * @param bool $wsdl optional, set to true if using WSDL
3717 * @param int $portName optional portName in WSDL document
3720 function soapclient($endpoint, $wsdl = false)
3722 $this->endpoint = $endpoint;
3726 $this->endpointType = 'wsdl';
3727 $this->wsdlFile = $this->endpoint;
3729 // instantiate wsdl object and parse wsdl file
3730 $this->debug('instantiating wsdl class with doc: ' . $endpoint);
3731 $this->wsdl =& new wsdl($this->wsdlFile, $this->proxyhost, $this->proxyport);
3732 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3733 $this->wsdl->debug_str = '';
3735 if ($errstr = $this->wsdl->getError()) {
3736 $this->debug('got wsdl error: ' . $errstr);
3737 $this->setError('wsdl error: ' . $errstr);
3738 } elseif ($this->operations = $this->wsdl->getOperations()) {
3739 $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile);
3741 $this->debug('getOperations returned false');
3742 $this->setError('no operations defined in the WSDL document!');
3748 * calls method, returns PHP native type
3750 * @param string $method SOAP server URL or path
3751 * @param array $params array of parameters, can be associative or not
3752 * @param string $namespace optional method namespace
3753 * @param string $soapAction optional SOAPAction value
3754 * @param boolean $headers optional array of soapval objects for headers
3755 * @param boolean $rpcParams optional treat params as RPC for use="literal"
3756 * This can be used on a per-call basis to overrider defaultRpcParams.
3760 function call($operation, $params = array(), $namespace = '', $soapAction = '', $headers = false, $rpcParams = null)
3762 $this->operation = $operation;
3763 $this->fault = false;
3764 $this->error_str = '';
3765 $this->request = '';
3766 $this->response = '';
3767 $this->faultstring = '';
3768 $this->faultcode = '';
3769 $this->opData = array();
3771 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
3772 $this->debug("endpointType: $this->endpointType");
3773 // if wsdl, get operation data and process parameters
3774 if ($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)) {
3776 $this->opData = $opData;
3777 foreach ($opData as $key => $value) {
3778 $this->debug("$key -> $value");
3780 $soapAction = $opData['soapAction'];
3781 $this->endpoint = $opData['endpoint'];
3782 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
3783 $style = $opData['style'];
3784 // add ns to ns array
3785 if ($namespace != '' && !isset($this->wsdl->namespaces[$namespace])) {
3786 $this->wsdl->namespaces['nu'] = $namespace;
3788 // serialize payload
3790 if ($opData['input']['use'] == 'literal') {
3791 if (is_null($rpcParams)) {
3792 $rpcParams = $this->defaultRpcParams;
3795 $this->debug("serializing literal params for operation $operation");
3796 $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params);
3797 $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
3799 $this->debug("serializing literal document for operation $operation");
3800 $payload = is_array($params) ? array_shift($params) : $params;
3803 $this->debug("serializing encoded params for operation $operation");
3804 $payload = "<" . $this->wsdl->getPrefixFromNamespace($namespace) . ":$operation>" .
3805 $this->wsdl->serializeRPCParameters($operation, 'input', $params) .
3806 '</' . $this->wsdl->getPrefixFromNamespace($namespace) . ":$operation>";
3808 $this->debug('payload size: ' . strlen($payload));
3809 // serialize envelope
3810 $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $this->wsdl->usedNamespaces, $style);
3811 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3812 $this->wsdl->debug_str = '';
3813 } elseif ($this->endpointType == 'wsdl') {
3814 $this->setError('operation ' . $operation . ' not present.');
3815 $this->debug("operation '$operation' not present.");
3816 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3821 if (!isset($style)) {
3824 if ($namespace == '') {
3825 $namespace = 'http://testuri.org';
3826 $this->wsdl->namespaces['ns1'] = $namespace;
3828 // serialize envelope
3830 foreach ($params as $k => $v) {
3831 $payload .= $this->serialize_val($v, $k);
3833 $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n" . $payload . "</ns1:$operation>\n";
3834 $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders);
3836 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
3838 $this->debug('sending msg (len: ' . strlen($soapmsg) . ") w/ soapaction '$soapAction'...");
3839 $return = $this->send($soapmsg, $soapAction, $this->timeout);
3840 if ($errstr = $this->getError()) {
3841 $this->debug('Error: ' . $errstr);
3844 $this->return = $return;
3845 $this->debug('sent message successfully and got a(n) ' . gettype($return) . ' back');
3848 if (is_array($return) && isset($return['faultcode'])) {
3849 $this->debug('got fault');
3850 $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
3851 $this->fault = true;
3852 foreach ($return as $k => $v) {
3854 $this->debug("$k = $v<br>");
3858 // array of return values
3859 if (is_array($return)) {
3860 // multiple 'out' parameters
3861 if (sizeof($return) > 1) {
3864 // single 'out' parameter
3865 return array_shift($return);
3866 // nothing returned (ie, echoVoid)
3875 * get available data pertaining to an operation
3877 * @param string $operation operation name
3878 * @return array array of data pertaining to the operation
3881 function getOperationData($operation)
3883 if (isset($this->operations[$operation])) {
3884 return $this->operations[$operation];
3886 $this->debug("No data for operation: $operation");
3890 * send the SOAP message
3892 * Note: if the operation has multiple return values
3893 * the return value of this method will be an array
3896 * @param string $msg a SOAPx4 soapmsg object
3897 * @param string $soapaction SOAPAction value
3898 * @param integer $timeout set timeout in seconds
3899 * @return mixed native PHP types.
3902 function send($msg, $soapaction = '', $timeout = 0)
3907 case ereg('^http', $this->endpoint):
3908 $this->debug('transporting via HTTP');
3909 if ($this->persistentConnection && is_object($this->persistentConnection)) {
3910 $http =& $this->persistentConnection;
3912 $http = new soap_transport_http($this->endpoint);
3913 // pass encoding into transport layer, so appropriate http headers are sent
3914 $http->soap_defencoding = $this->soap_defencoding;
3916 $http->setSOAPAction($soapaction);
3917 if ($this->proxyhost && $this->proxyport) {
3918 $http->setProxy($this->proxyhost, $this->proxyport);
3920 if ($this->username != '' && $this->password != '') {
3921 $http->setCredentials($this->username, $this->password);
3923 if ($this->http_encoding != '') {
3924 $http->setEncoding($this->http_encoding);
3926 $this->debug('sending message, length: ' . strlen($msg));
3927 if (ereg('^http:', $this->endpoint)) {
3928 //if(strpos($this->endpoint,'http:')){
3929 $response = $http->send($msg, $timeout);
3930 } elseif (ereg('^https', $this->endpoint)) {
3931 //} elseif(strpos($this->endpoint,'https:')){
3932 //if(phpversion() == '4.3.0-dev'){
3933 //$response = $http->send($msg,$timeout);
3934 //$this->request = $http->outgoing_payload;
3935 //$this->response = $http->incoming_payload;
3937 if (extension_loaded('curl')) {
3938 $response = $http->sendHTTPS($msg, $timeout);
3940 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
3943 $this->setError('no http/s in endpoint url');
3945 $this->request = $http->outgoing_payload;
3946 $this->response = $http->incoming_payload;
3947 $this->debug("transport debug data...\n" . $http->debug_str);
3948 // save transport object if using persistent connections
3949 if ($this->persistentConnection && !is_object($this->persistentConnection)) {
3950 $this->persistentConnection = $http;
3952 if ($err = $http->getError()) {
3953 $this->setError('HTTP Error: ' . $err);
3955 } elseif ($this->getError()) {
3958 $this->debug('got response, length: ' . strlen($response));
3959 return $this->parseResponse($response);
3963 $this->setError('no transport found, or selected transport is not yet supported!');
3970 * processes SOAP message returned from server
3972 * @param string unprocessed response data from server
3973 * @return mixed value of the message, decoded into a PHP type
3976 function parseResponse($data)
3978 $this->debug('Entering parseResponse(), about to create soap_parser instance');
3979 $parser = new soap_parser($data, $this->xml_encoding, $this->operation);
3981 if ($errstr = $parser->getError()) {
3982 $this->setError($errstr);
3983 // destroy the parser object
3988 $this->responseHeaders = $parser->getHeaders();
3989 // get decoded message
3990 $return = $parser->get_response();
3991 // add parser debug data to our debug
3992 $this->debug($parser->debug_str);
3993 // add document for doclit support
3994 $this->document = $parser->document;
3995 // destroy the parser object
3997 // return decode message
4003 * set the SOAP headers
4005 * @param $headers string XML
4008 function setHeaders($headers)
4010 $this->requestHeaders = $headers;
4014 * get the response headers
4016 * @return mixed object SOAPx4 soapval object or empty if no headers
4019 function getHeaders()
4021 if ($this->responseHeaders != '') {
4022 return $this->responseHeaders;
4027 * set proxy info here
4029 * @param string $proxyhost
4030 * @param string $proxyport
4033 function setHTTPProxy($proxyhost, $proxyport)
4035 $this->proxyhost = $proxyhost;
4036 $this->proxyport = $proxyport;
4040 * if authenticating, set user credentials here
4042 * @param string $username
4043 * @param string $password
4046 function setCredentials($username, $password)
4048 $this->username = $username;
4049 $this->password = $password;
4055 * @param string $enc
4058 function setHTTPEncoding($enc = 'gzip, deflate')
4060 $this->http_encoding = $enc;
4064 * use HTTP persistent connections if possible
4068 function useHTTPPersistentConnection()
4070 $this->persistentConnection = true;
4074 * gets the default RPC parameter setting.
4075 * If true, default is that call params are like RPC even for document style.
4076 * Each call() can override this value.
4080 function getDefaultRpcParams()
4082 return $this->defaultRpcParams;
4086 * sets the default RPC parameter setting.
4087 * If true, default is that call params are like RPC even for document style
4088 * Each call() can override this value.
4090 * @param boolean $rpcParams
4093 function setDefaultRpcParams($rpcParams)
4095 $this->defaultRpcParams = $rpcParams;
4099 * dynamically creates proxy class, allowing user to directly call methods from wsdl
4101 * @return object soap_proxy object
4107 foreach ($this->operations as $operation => $opData) {
4108 if ($operation != '') {
4109 // create param string
4111 if (sizeof($opData['input']['parts']) > 0) {
4112 foreach ($opData['input']['parts'] as $name => $type) {
4113 $paramStr .= "\$$name,";
4115 $paramStr = substr($paramStr, 0, strlen($paramStr) - 1);
4117 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
4118 $evalStr .= "function $operation ($paramStr){
4119 // load params into array
4120 \$params = array($paramStr);
4121 return \$this->call('$operation',\$params,'" . $opData['namespace'] . "','" . $opData['soapAction'] . "');
4127 $evalStr = 'class soap_proxy_' . $r . ' extends soapclient {
4130 //print "proxy class:<pre>$evalStr</pre>";
4133 // instantiate proxy object
4134 eval("\$proxy = new soap_proxy_$r('');");
4135 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
4136 $proxy->endpointType = 'wsdl';
4137 $proxy->wsdlFile = $this->wsdlFile;
4138 $proxy->wsdl = $this->wsdl;
4139 $proxy->operations = $this->operations;
4140 $proxy->defaultRpcParams = $this->defaultRpcParams;
4148 // c-basic-offset: 4
4149 // c-hanging-comment-ender-p: nil
4150 // indent-tabs-mode: nil