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");
516 * soap_fault class, allows for creation of faults
517 * mainly used for returning faults from deployed functions
518 * in a server instance.
519 * @author Dietrich Ayala <dietrich@ganx4.com>
523 class soap_fault extends nusoap_base
534 * @param string $faultcode (client | server)
535 * @param string $faultactor only used when msg routed between multiple actors
536 * @param string $faultstring human readable error message
537 * @param string $faultdetail
539 function soap_fault($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
541 $this->faultcode = $faultcode;
542 $this->faultactor = $faultactor;
543 $this->faultstring = $faultstring;
544 $this->faultdetail = $faultdetail;
555 foreach ($this->namespaces as $k => $v) {
556 $ns_string .= "\n xmlns:$k=\"$v\"";
559 '<?xml version="1.0"?' . ">\n" .
560 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string . ">\n" .
563 '<faultcode>' . $this->faultcode . '</faultcode>' .
564 '<faultactor>' . $this->faultactor . '</faultactor>' .
565 '<faultstring>' . $this->faultstring . '</faultstring>' .
566 '<detail>' . $this->serialize_val($this->faultdetail) . '</detail>' .
567 '</SOAP-ENV:Fault>' .
569 '</SOAP-ENV:Envelope>';
577 * parses an XML Schema, allows access to it's data, other utility methods
578 * no validation... yet.
579 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
580 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
581 * tutorials I refer to :)
583 * @author Dietrich Ayala <dietrich@ganx4.com>
587 class XMLSchema extends nusoap_base
593 // define internal arrays of bindings, ports, operations, messages, etc.
594 var $complexTypes = array();
596 var $schemaTargetNamespace = '';
601 var $depth_array = array();
606 * @param string $schema schema document URI
607 * @param string $xml xml document URI
610 function XMLSchema($schema = '', $xml = '')
613 $this->debug('xmlschema class instantiated, inside constructor');
615 $this->schema = $schema;
620 $this->debug('initial schema file: ' . $schema);
621 $this->parseFile($schema);
626 $this->debug('initial xml file: ' . $xml);
627 $this->parseFile($xml);
635 * @param string $xml, path/URL to XML file
636 * @param string $type, (schema | xml)
640 function parseFile($xml, $type)
644 $this->debug('parsing $xml');
645 $xmlStr = @join("", @file($xml));
647 $this->setError('No file at the specified URL: ' . $xml);
650 $this->parseString($xmlStr, $type);
658 * parse an XML string
660 * @param string $xml path or URL
661 * @param string $type, (schema|xml)
664 function parseString($xml, $type)
669 // Create an XML parser.
670 $this->parser = xml_parser_create();
671 // Set the options for parsing the XML data.
672 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
674 // Set the object for the parser.
675 xml_set_object($this->parser, $this);
677 // Set the element handlers for the parser.
678 if ($type == "schema") {
679 xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
680 xml_set_character_data_handler($this->parser, 'schemaCharacterData');
681 } elseif ($type == "xml") {
682 xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
683 xml_set_character_data_handler($this->parser, 'xmlCharacterData');
686 // Parse the XML file.
687 if (!xml_parse($this->parser, $xml, true)) {
688 // Display an error message.
689 $errstr = sprintf('XML error on line %d: %s',
690 xml_get_current_line_number($this->parser),
691 xml_error_string(xml_get_error_code($this->parser))
693 $this->debug('XML parse error: ' . $errstr);
694 $this->setError('Parser error: ' . $errstr);
697 xml_parser_free($this->parser);
699 $this->debug('no xml passed to parseString()!!');
700 $this->setError('no xml passed to parseString()!!');
705 * start-element handler
707 * @param string $parser XML parser object
708 * @param string $name element name
709 * @param string $attrs associative array of attributes
712 function schemaStartElement($parser, $name, $attrs)
715 // position in the total number of elements, starting from 0
716 $pos = $this->position++;
717 $depth = $this->depth++;
718 // set self as current value for this depth
719 $this->depth_array[$depth] = $pos;
721 // get element prefix
722 if ($prefix = $this->getPrefix($name)) {
723 // get unqualified name
724 $name = $this->getLocalPart($name);
729 // loop thru attributes, expanding, and registering namespace declarations
730 if (count($attrs) > 0) {
731 foreach ($attrs as $k => $v) {
732 // if ns declarations, add to class level array of valid namespaces
733 if (ereg("^xmlns", $k)) {
734 //$this->xdebug("$k: $v");
735 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
736 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
737 $this->namespaces[$ns_prefix] = $v;
739 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
741 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
742 $this->XMLSchemaVersion = $v;
743 $this->namespaces['xsi'] = $v . '-instance';
747 foreach ($attrs as $k => $v) {
748 // expand each attribute
749 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
750 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
757 // find status, register data
759 case ('all' | 'choice' | 'sequence'):
760 //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
761 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
762 if ($name == 'all') {
763 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
767 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
768 if (isset($attrs['name'])) {
769 $this->attributes[$attrs['name']] = $attrs;
770 $aname = $attrs['name'];
771 } elseif (isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType') {
772 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
773 } elseif (isset($attrs['ref'])) {
774 $aname = $attrs['ref'];
775 $this->attributes[$attrs['ref']] = $attrs;
778 if (isset($this->currentComplexType)) {
779 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
780 } elseif (isset($this->currentElement)) {
781 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
783 // arrayType attribute
784 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType') {
785 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
786 $prefix = $this->getPrefix($aname);
787 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
788 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
792 if (strpos($v, '[,]')) {
793 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
795 $v = substr($v, 0, strpos($v, '[')); // clip the []
796 if (!strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
797 $v = $this->XMLSchemaVersion . ':' . $v;
799 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
803 if (isset($attrs['name'])) {
804 $this->currentElement = false;
805 $this->currentComplexType = $attrs['name'];
806 $this->complexTypes[$this->currentComplexType] = $attrs;
807 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
808 if (isset($attrs['base']) && ereg(':Array$', $attrs['base'])) {
809 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
811 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
813 $this->xdebug('processing complexType ' . $attrs['name']);
817 if (isset($attrs['type'])) {
818 $this->xdebug("processing element " . $attrs['name']);
819 $this->currentElement = $attrs['name'];
820 $this->elements[$attrs['name']] = $attrs;
821 $this->elements[$attrs['name']]['typeClass'] = 'element';
822 $ename = $attrs['name'];
823 } elseif (isset($attrs['ref'])) {
824 $ename = $attrs['ref'];
826 $this->xdebug('adding complexType ' . $attrs['name']);
827 $this->currentComplexType = $attrs['name'];
828 $this->complexTypes[$attrs['name']] = $attrs;
829 $this->complexTypes[$attrs['name']]['element'] = 1;
830 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
832 if (isset($ename) && $this->currentComplexType) {
833 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
837 $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
838 if ($this->currentElement) {
839 $this->elements[$this->currentElement]['type'] = $attrs['base'];
840 } elseif ($this->currentComplexType) {
841 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
842 if (strstr($attrs['base'], ':') == ':Array') {
843 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
848 $this->schema = $attrs;
849 $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
852 $this->currentElement = $attrs['name'];
853 $this->elements[$attrs['name']] = $attrs;
854 $this->elements[$attrs['name']]['typeClass'] = 'element';
860 * end-element handler
862 * @param string $parser XML parser object
863 * @param string $name element name
866 function schemaEndElement($parser, $name)
868 // position of current element is equal to the last value left in depth_array for my depth
869 if (isset($this->depth_array[$this->depth])) {
870 $pos = $this->depth_array[$this->depth];
872 // bring depth down a notch
875 if ($name == 'complexType') {
876 $this->currentComplexType = false;
877 $this->currentElement = false;
879 if ($name == 'element') {
880 $this->currentElement = false;
885 * element content handler
887 * @param string $parser XML parser object
888 * @param string $data element content
891 function schemaCharacterData($parser, $data)
893 $pos = $this->depth_array[$this->depth];
894 $this->message[$pos]['cdata'] .= $data;
898 * serialize the schema
902 function serializeSchema()
905 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
908 foreach ($this->complexTypes as $typeName => $attrs) {
910 // serialize child elements
911 if (count($attrs['elements']) > 0) {
912 foreach ($attrs['elements'] as $element => $eParts) {
913 if (isset($eParts['ref'])) {
914 $contentStr .= "<element ref=\"$element\"/>";
916 $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
921 if (count($attrs['attrs']) >= 1) {
922 foreach ($attrs['attrs'] as $attr => $aParts) {
923 $contentStr .= '<attribute ref="' . $aParts['ref'] . '"';
924 if (isset($aParts['wsdl:arrayType'])) {
925 $contentStr .= ' wsdl:arrayType="' . $aParts['wsdl:arrayType'] . '"';
931 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != '') {
932 $contentStr = "<$schemaPrefix:restriction base=\"" . $attrs['restrictionBase'] . "\">" . $contentStr . "</$schemaPrefix:restriction>";
934 // "all" compositor obviates complex/simple content
935 if (isset($attrs['compositor']) && $attrs['compositor'] == 'all') {
936 $contentStr = "<$schemaPrefix:$attrs[compositor]>" . $contentStr . "</$schemaPrefix:$attrs[compositor]>";
937 } // complex or simple content
938 elseif (count($attrs['elements']) > 0 || count($attrs['attrs']) > 0) {
939 $contentStr = "<$schemaPrefix:complexContent>" . $contentStr . "</$schemaPrefix:complexContent>";
942 if (isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all') {
943 $contentStr = "<$schemaPrefix:$attrs[compositor]>" . $contentStr . "</$schemaPrefix:$attrs[compositor]>";
945 // finalize complex type
946 if ($contentStr != '') {
947 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">" . $contentStr . "</$schemaPrefix:complexType>";
949 $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
954 if (isset($this->elements) && count($this->elements) > 0) {
955 foreach ($this->elements as $element => $eParts) {
956 $xml .= "<$schemaPrefix:element name=\"$element\" type=\"" . $eParts['type'] . "\"/>";
960 if (isset($this->attributes) && count($this->attributes) > 0) {
961 foreach ($this->attributes as $attr => $aParts) {
962 $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"" . $aParts['type'] . "\"/>";
966 $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">" . $xml . "</$schemaPrefix:schema>";
971 * expands a qualified name
973 * @param string $string qname
974 * @return string expanded qname
977 function expandQname($qname)
979 // get element prefix
980 if (strpos($qname, ':') && !ereg('^http://', $qname)) {
981 // get unqualified name
982 $name = substr(strstr($qname, ':'), 1);
984 $prefix = substr($qname, 0, strpos($qname, ':'));
985 if (isset($this->namespaces[$prefix])) {
986 return $this->namespaces[$prefix] . ':' . $name;
996 * adds debug data to the clas level debug string
998 * @param string $string debug data
1001 function xdebug($string)
1003 $this->debug(' xmlschema: ' . $string);
1007 * get the PHP type of a user defined type in the schema
1008 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1009 * returns false if no type exists, or not w/ the given namespace
1010 * else returns a string that is either a native php type, or 'struct'
1012 * @param string $type, name of defined type
1013 * @param string $ns, namespace of type
1017 function getPHPType($type, $ns)
1020 if (isset($typemap[$ns][$type])) {
1021 //print "found type '$type' and ns $ns in typemap<br>";
1022 return $typemap[$ns][$type];
1023 } elseif (isset($this->complexTypes[$type])) {
1024 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1025 return $this->complexTypes[$type]['phpType'];
1031 * returns the local part of a prefixed string
1032 * returns the original string, if not prefixed
1038 function getLocalPart($str)
1040 if ($sstr = strrchr($str, ':')) {
1041 // get unqualified name
1042 return substr($sstr, 1);
1049 * returns the prefix part of a prefixed string
1050 * returns false, if not prefixed
1056 function getPrefix($str)
1058 if ($pos = strrpos($str, ':')) {
1060 return substr($str, 0, $pos);
1066 * pass it a prefix, it returns a namespace
1067 * returns false if no namespace registered with the given prefix
1073 function getNamespaceFromPrefix($prefix)
1075 if (isset($this->namespaces[$prefix])) {
1076 return $this->namespaces[$prefix];
1078 //$this->setError("No namespace registered for prefix '$prefix'");
1083 * returns the prefix for a given namespace (or prefix)
1084 * or false if no prefixes registered for the given namespace
1090 function getPrefixFromNamespace($ns)
1092 foreach ($this->namespaces as $p => $n) {
1093 if ($ns == $n || $ns == $p) {
1094 $this->usedNamespaces[$p] = $n;
1102 * returns an array of information about a given type
1103 * returns false if no type exists by the given name
1106 * 'elements' => array(), // refs to elements array
1107 * 'restrictionBase' => '',
1109 * 'order' => '(sequence|all)',
1110 * 'attrs' => array() // refs to attributes array
1117 function getTypeDef($type)
1119 if (isset($this->complexTypes[$type])) {
1120 return $this->complexTypes[$type];
1121 } elseif (isset($this->elements[$type])) {
1122 return $this->elements[$type];
1123 } elseif (isset($this->attributes[$type])) {
1124 return $this->attributes[$type];
1130 * returns a sample serialization of a given type, or false if no type by the given name
1132 * @param string $type, name of type
1136 function serializeTypeDef($type)
1138 //print "in sTD() for type $type<br>";
1139 if ($typeDef = $this->getTypeDef($type)) {
1140 $str .= '<' . $type;
1141 if (is_array($typeDef['attrs'])) {
1142 foreach ($attrs as $attName => $data) {
1143 $str .= " $attName=\"{type = " . $data['type'] . "}\"";
1146 $str .= " xmlns=\"" . $this->schema['targetNamespace'] . "\"";
1147 if (count($typeDef['elements']) > 0) {
1149 foreach ($typeDef['elements'] as $element => $eData) {
1150 $str .= $this->serializeTypeDef($element);
1153 } elseif ($typeDef['typeClass'] == 'element') {
1154 $str .= "></$type>";
1164 * returns HTML form elements that allow a user
1165 * to enter values for creating an instance of the given type.
1167 * @param string $name, name for type instance
1168 * @param string $type, name of type
1172 function typeToForm($name, $type)
1175 if ($typeDef = $this->getTypeDef($type)) {
1177 if ($typeDef['phpType'] == 'struct') {
1178 $buffer .= '<table>';
1179 foreach ($typeDef['elements'] as $child => $childDef) {
1180 $buffer .= "<tr><td align='right'>$childDef[name] (type: " . $this->getLocalPart($childDef['type']) . "):</td>" .
1181 "<td><input type='text' name='parameters[" . $name . "][$childDef[name]]'></td></tr>";
1183 $buffer .= '</table>';
1185 } elseif ($typeDef['phpType'] == 'array') {
1186 $buffer .= '<table>';
1187 for ($i = 0; $i < 3; $i++) {
1188 $buffer .= "<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>" .
1189 "<td><input type='text' name='parameters[" . $name . "][]'></td></tr>";
1191 $buffer .= '</table>';
1194 $buffer .= "<input type='text' name='parameters[$name]'>";
1197 $buffer .= "<input type='text' name='parameters[$name]'>";
1203 * adds an XML Schema complex type to the WSDL types
1213 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1217 * example: PHP associative array ( SOAP Struct )
1224 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1228 * @param typeClass (complexType|simpleType|attribute)
1229 * @param phpType: currently supported are array and struct (php assoc array)
1230 * @param compositor (all|sequence|choice)
1231 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1232 * @param elements = array ( name = array(name=>'',type=>'') )
1233 * @param attrs = array(
1235 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1236 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1239 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1242 function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
1244 $this->complexTypes[$name] = array(
1246 'typeClass' => $typeClass,
1247 'phpType' => $phpType,
1248 'compositor' => $compositor,
1249 'restrictionBase' => $restrictionBase,
1250 'elements' => $elements,
1252 'arrayType' => $arrayType
1260 * for creating serializable abstractions of native PHP types
1261 * NOTE: this is only really used when WSDL is not available.
1263 * @author Dietrich Ayala <dietrich@ganx4.com>
1267 class soapval extends nusoap_base
1272 * @param string $name optional name
1273 * @param string $type optional type name
1274 * @param mixed $value optional value
1275 * @param string $namespace optional namespace of value
1276 * @param string $type_namespace optional namespace of type
1277 * @param array $attributes associative array of attributes to add to element serialization
1280 function soapval($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false)
1282 $this->name = $name;
1283 $this->value = $value;
1284 $this->type = $type;
1285 $this->element_ns = $element_ns;
1286 $this->type_ns = $type_ns;
1287 $this->attributes = $attributes;
1291 * return serialized value
1293 * @return string XML data
1296 function serialize($use = 'encoded')
1298 return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use);
1302 * decodes a soapval object into a PHP native type
1304 * @param object $soapval optional SOAPx4 soapval object, else uses self
1310 return $this->value;
1317 * transport class for sending/receiving data via HTTP and HTTPS
1318 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1320 * @author Dietrich Ayala <dietrich@ganx4.com>
1324 class soap_transport_http extends nusoap_base
1330 var $proxyhost = '';
1331 var $proxyport = '';
1333 var $request_method = 'POST';
1334 var $protocol_version = '1.0';
1336 var $outgoing_headers = array();
1337 var $incoming_headers = array();
1338 var $outgoing_payload = '';
1339 var $incoming_payload = '';
1340 var $useSOAPAction = true;
1345 function soap_transport_http($url)
1348 $u = parse_url($url);
1349 foreach ($u as $k => $v) {
1350 $this->debug("$k = $v");
1353 if (isset($u['query']) && $u['query'] != '') {
1354 $this->path .= '?' . $u['query'];
1356 if (!isset($u['port']) && $u['scheme'] == 'http') {
1361 function connect($timeout)
1365 if ($this->proxyhost != '' && $this->proxyport != '') {
1366 $host = $this->proxyhost;
1367 $port = $this->proxyport;
1368 $this->debug("using http proxy: $host, $port");
1370 $host = $this->host;
1371 $port = $this->port;
1374 if ($this->scheme == 'https') {
1375 $host = 'ssl://' . $host;
1379 $this->debug("connection params: $host, $port");
1382 $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
1384 $fp = fsockopen($host, $port, $this->errno, $this->error_str);
1389 $this->debug('Couldn\'t open socket connection to server ' . $this->url . ', Error: ' . $this->error_str);
1390 $this->setError('Couldn\'t open socket connection to server: ' . $this->url . ', Error: ' . $this->error_str);
1397 * send the SOAP message via HTTP
1399 * @param string $data message data
1400 * @param integer $timeout set timeout in seconds
1401 * @return string data
1404 function send($data, $timeout = 0)
1406 $this->debug('entered send() with data of length: ' . strlen($data));
1408 if (!$fp = $this->connect($timeout)) {
1411 $this->debug('socket connected');
1413 // start building outgoing payload:
1414 // swap url for path if going through a proxy
1415 if ($this->proxyhost != '' && $this->proxyport != '') {
1416 $this->outgoing_payload = "$this->request_method $this->url " . strtoupper($this->scheme) . "/$this->protocol_version\r\n";
1418 $this->outgoing_payload = "$this->request_method $this->path " . strtoupper($this->scheme) . "/$this->protocol_version\r\n";
1421 $this->outgoing_payload .=
1422 "User-Agent: $this->title/$this->version\r\n" .
1423 "Host: " . $this->host . "\r\n";
1426 if ($this->username != '') {
1427 $this->debug('setting http auth credentials');
1428 $this->outgoing_payload .= 'Authorization: Basic ' . base64_encode("$this->username:$this->password") . "\r\n";
1431 $this->outgoing_payload .= 'Content-Type: text/xml; charset=' . $this->soap_defencoding . "\r\nContent-Length: " . strlen($data) . "\r\n";
1433 if ($this->encoding != '' && function_exists('gzdeflate')) {
1434 $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n" .
1435 "Connection: close\r\n";
1436 if (!check_php_version(5, 3)) {
1437 set_magic_quotes_runtime(0);
1441 if ($this->useSOAPAction) {
1442 $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\"" . "\r\n";
1444 $this->outgoing_payload .= "\r\n";
1446 $this->outgoing_payload .= $data;
1449 if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1450 $this->setError('couldn\'t write message data to socket');
1451 $this->debug('Write error');
1453 $this->debug('wrote data to socket');
1456 $this->incoming_payload = '';
1458 while ($data = fread($fp, 32768)) {
1459 $this->incoming_payload .= $data;
1460 //$strlen += strlen($data);
1462 $this->debug('received ' . strlen($this->incoming_payload) . ' bytes of data from server');
1464 // close filepointer
1466 $this->debug('closed socket');
1468 // connection was closed unexpectedly
1469 if ($this->incoming_payload == '') {
1470 $this->setError('no response from server');
1474 $this->debug('received incoming payload: ' . strlen($this->incoming_payload));
1475 $data = $this->incoming_payload . "\r\n\r\n\r\n\r\n";
1477 // remove 100 header
1478 if (ereg('^HTTP/1.1 100', $data)) {
1479 if ($pos = strpos($data, "\r\n\r\n")) {
1480 $data = ltrim(substr($data, $pos));
1481 } elseif ($pos = strpos($data, "\n\n")) {
1482 $data = ltrim(substr($data, $pos));
1487 // separate content from HTTP headers
1488 if ($pos = strpos($data, "\r\n\r\n")) {
1490 } elseif ($pos = strpos($data, "\n\n")) {
1493 $this->setError('no proper separation of headers and document');
1496 $header_data = trim(substr($data, 0, $pos));
1497 $header_array = explode($lb, $header_data);
1498 $data = ltrim(substr($data, $pos));
1499 $this->debug('found proper separation of headers and document');
1500 $this->debug('cleaned data, stringlen: ' . strlen($data));
1502 foreach ($header_array as $header_line) {
1503 $arr = explode(':', $header_line);
1504 if (count($arr) >= 2) {
1505 $headers[trim($arr[0])] = trim($arr[1]);
1508 //print "headers: <pre>$header_data</pre><br>";
1509 //print "data: <pre>$data</pre><br>";
1511 // decode transfer-encoding
1512 if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked') {
1513 //$timer->setMarker('starting to decode chunked content');
1514 if (!$data = $this->decodeChunked($data)) {
1515 $this->setError('Decoding of chunked data failed');
1518 //$timer->setMarker('finished decoding of chunked content');
1519 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1522 // decode content-encoding
1523 if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != '') {
1524 if ($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip') {
1525 // if decoding works, use it. else assume data wasn't gzencoded
1526 if (function_exists('gzinflate')) {
1527 //$timer->setMarker('starting decoding of gzip/deflated content');
1528 if ($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)) {
1530 } elseif ($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
1533 $this->setError('Errors occurred when trying to decode the data');
1535 //$timer->setMarker('finished decoding of gzip/deflated content');
1536 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
1538 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1543 if (strlen($data) == 0) {
1544 $this->debug('no data after headers!');
1545 $this->setError('no data present after HTTP headers');
1548 $this->debug('end of send()');
1554 * send the SOAP message via HTTPS 1.0 using CURL
1556 * @param string $msg message data
1557 * @param integer $timeout set timeout in seconds
1558 * @return string data
1561 function sendHTTPS($data, $timeout = 0)
1564 //$t->setMarker('inside sendHTTPS()');
1565 $this->debug('entered sendHTTPS() with data of length: ' . strlen($data));
1568 //$t->setMarker('got curl handle');
1570 if ($this->proxyhost && $this->proxyport) {
1571 $host = $this->proxyhost;
1572 $port = $this->proxyport;
1574 $host = $this->host;
1575 $port = $this->port;
1578 $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
1580 $hostURL .= $this->path;
1581 curl_setopt($ch, CURLOPT_URL, $hostURL);
1582 // set other options
1583 curl_setopt($ch, CURLOPT_HEADER, 1);
1584 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1586 if (function_exists('gzinflate')) {
1587 curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
1589 // persistent connection
1590 //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1593 if ($timeout != 0) {
1594 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1598 if ($this->username != '') {
1599 $credentials = 'Authorization: Basic ' . base64_encode("$this->username:$this->password") . '\r\n';
1602 if ($this->encoding != '') {
1603 if (function_exists('gzdeflate')) {
1604 $encoding_headers = "Accept-Encoding: $this->encoding\r\n" .
1605 "Connection: close\r\n";
1606 if (!check_php_version(5, 3)) {
1607 set_magic_quotes_runtime(0);
1612 if ($this->proxyhost && $this->proxyport) {
1613 $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
1615 $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
1618 $this->outgoing_payload .=
1619 "User-Agent: $this->title v$this->version\r\n" .
1620 "Host: " . $this->host . "\r\n" .
1623 "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n" .
1624 "Content-Length: " . strlen($data) . "\r\n" .
1625 "SOAPAction: \"$this->soapaction\"" . "\r\n\r\n" .
1629 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1630 //$t->setMarker('set curl options, executing...');
1632 $this->incoming_payload = curl_exec($ch);
1633 //$t->setMarker('executed transfer');
1634 $data = $this->incoming_payload;
1636 $cErr = curl_error($ch);
1639 $err = 'cURL ERROR: ' . curl_errno($ch) . ': ' . $cErr . '<br>';
1640 foreach (curl_getinfo($ch) as $k => $v) {
1641 $err .= "$k: $v<br>";
1643 $this->setError($err);
1648 //var_dump(curl_getinfo($ch));
1653 //$t->setMarker('closed curl');
1655 // remove 100 header
1656 if (ereg('^HTTP/1.1 100', $data)) {
1657 if ($pos = strpos($data, "\r\n\r\n")) {
1658 $data = ltrim(substr($data, $pos));
1659 } elseif ($pos = strpos($data, "\n\n")) {
1660 $data = ltrim(substr($data, $pos));
1665 // separate content from HTTP headers
1666 if ($pos = strpos($data, "\r\n\r\n")) {
1668 } elseif ($pos = strpos($data, "\n\n")) {
1671 $this->setError('no proper separation of headers and document');
1674 $header_data = trim(substr($data, 0, $pos));
1675 $header_array = explode($lb, $header_data);
1676 $data = ltrim(substr($data, $pos));
1677 $this->debug('found proper separation of headers and document');
1678 $this->debug('cleaned data, stringlen: ' . strlen($data));
1680 foreach ($header_array as $header_line) {
1681 $arr = explode(':', $header_line);
1682 $headers[trim($arr[0])] = trim($arr[1]);
1684 if (strlen($data) == 0) {
1685 $this->debug('no data after headers!');
1686 $this->setError('no data present after HTTP headers.');
1690 // decode transfer-encoding
1691 if ($headers['Transfer-Encoding'] == 'chunked') {
1692 if (!$data = $this->decodeChunked($data)) {
1693 $this->setError('Decoding of chunked data failed');
1697 // decode content-encoding
1698 if ($headers['Content-Encoding'] != '') {
1699 if ($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip') {
1700 // if decoding works, use it. else assume data wasn't gzencoded
1701 if (function_exists('gzinflate')) {
1702 if ($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)) {
1704 } elseif ($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
1707 $this->setError('Errors occurred when trying to decode the data');
1710 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1714 // set decoded payload
1715 $this->incoming_payload = $header_data . "\r\n\r\n" . $data;
1720 * if authenticating, set user credentials here
1722 * @param string $user
1723 * @param string $pass
1726 function setCredentials($username, $password)
1728 $this->username = $username;
1729 $this->password = $password;
1733 * set the soapaction value
1735 * @param string $soapaction
1738 function setSOAPAction($soapaction)
1740 $this->soapaction = $soapaction;
1746 * @param string $enc encoding style. supported values: gzip, deflate, or both
1749 function setEncoding($enc = 'gzip, deflate')
1751 $this->encoding = $enc;
1752 $this->protocol_version = '1.1';
1756 * set proxy info here
1758 * @param string $proxyhost
1759 * @param string $proxyport
1762 function setProxy($proxyhost, $proxyport)
1764 $this->proxyhost = $proxyhost;
1765 $this->proxyport = $proxyport;
1769 * decode a string that is encoded w/ "chunked' transfer encoding
1770 * as defined in RFC2068 19.4.6
1772 * @param string $buffer
1776 function decodeChunked($buffer)
1782 // read chunk-size, chunk-extension (if any) and CRLF
1783 // get the position of the linebreak
1784 $chunkend = strpos($buffer, "\r\n") + 2;
1785 $temp = substr($buffer, 0, $chunkend);
1786 $chunk_size = hexdec(trim($temp));
1787 $chunkstart = $chunkend;
1788 // while (chunk-size > 0) {
1789 while ($chunk_size > 0) {
1791 $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
1793 // Just in case we got a broken connection
1794 if ($chunkend == FALSE) {
1795 $chunk = substr($buffer, $chunkstart);
1796 // append chunk-data to entity-body
1798 $length += strlen($chunk);
1802 // read chunk-data and CRLF
1803 $chunk = substr($buffer, $chunkstart, $chunkend - $chunkstart);
1804 // append chunk-data to entity-body
1806 // length := length + chunk-size
1807 $length += strlen($chunk);
1808 // read chunk-size and CRLF
1809 $chunkstart = $chunkend + 2;
1811 $chunkend = strpos($buffer, "\r\n", $chunkstart) + 2;
1812 if ($chunkend == FALSE) {
1813 break; //Just in case we got a broken connection
1815 $temp = substr($buffer, $chunkstart, $chunkend - $chunkstart);
1816 $chunk_size = hexdec(trim($temp));
1817 $chunkstart = $chunkend;
1820 //$this->Header['content-length'] = $length;
1821 //unset($this->Header['transfer-encoding']);
1831 * soap_server allows the user to create a SOAP server
1832 * that is capable of receiving messages and returning responses
1834 * NOTE: WSDL functionality is experimental
1836 * @author Dietrich Ayala <dietrich@ganx4.com>
1840 class soap_server extends nusoap_base
1843 var $service = ''; // service name
1844 var $operations = array(); // assoc array of operations => opData
1845 var $responseHeaders = false;
1848 var $charset_encoding = 'UTF-8';
1850 var $result = 'successful';
1852 var $externalWSDLURL = false;
1853 var $debug_flag = true;
1857 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1859 * @param string $wsdl path or URL to a WSDL file
1862 function soap_server($wsdl = false)
1865 // turn on debugging?
1867 if (isset($debug)) {
1868 $this->debug_flag = 1;
1873 $this->wsdl = new wsdl($wsdl);
1874 $this->externalWSDLURL = $wsdl;
1875 if ($err = $this->wsdl->getError()) {
1876 die('WSDL ERROR: ' . $err);
1882 * processes request and returns response
1884 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
1887 function service($data)
1890 global $QUERY_STRING, $HTTP_SERVER_VARS;
1891 if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
1892 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
1893 } elseif (isset($GLOBALS['QUERY_STRING'])) {
1894 $qs = $GLOBALS['QUERY_STRING'];
1895 } elseif (isset($QUERY_STRING) && $QUERY_STRING != '') {
1896 $qs = $QUERY_STRING;
1899 if (isset($qs) && ereg('wsdl', $qs)) {
1900 if ($this->externalWSDLURL) {
1901 header('Location: ' . $this->externalWSDLURL);
1904 header("Content-Type: text/xml\r\n");
1905 print $this->wsdl->serialize();
1910 // print web interface
1911 if ($data == '' && $this->wsdl) {
1912 print $this->webDescription();
1915 // $response is the serialized response message
1916 $response = $this->parse_request($data);
1917 $this->debug('server sending...');
1918 $payload = $response;
1919 // add debug data if in debug mode
1920 if (isset($this->debug_flag) && $this->debug_flag == 1) {
1921 $payload .= "<!--\n" . str_replace('--', '- -', $this->debug_str) . "\n-->";
1925 $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
1926 $header[] = "Status: 500 Internal Server Error\r\n";
1928 $header[] = "Status: 200 OK\r\n";
1930 $header[] = "Server: $this->title Server v$this->version\r\n";
1931 $header[] = "Connection: Close\r\n";
1932 $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
1933 $header[] = "Content-Length: " . strlen($payload) . "\r\n\r\n";
1935 foreach ($header as $hdr) {
1938 $this->response = join("\r\n", $header) . $payload;
1944 * parses request and posts response
1946 * @param string $data XML string
1947 * @return string XML response msg
1950 function parse_request($data = '')
1952 if (!isset($_SERVER))
1953 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
1954 $this->debug('entering parseRequest() on ' . date('H:i Y-m-d'));
1957 if (function_exists('getallheaders')) {
1958 $this->headers = getallheaders();
1959 foreach ($this->headers as $k => $v) {
1960 $dump .= "$k: $v\r\n";
1961 $this->debug("$k: $v");
1963 // get SOAPAction header
1964 if (isset($this->headers['SOAPAction'])) {
1965 $this->SOAPAction = str_replace('"', '', $this->headers['SOAPAction']);
1967 // get the character encoding of the incoming request
1968 if (strpos($this->headers['Content-Type'], '=')) {
1969 $enc = str_replace('"', '', substr(strstr($this->headers["Content-Type"], '='), 1));
1970 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
1971 $this->xml_encoding = $enc;
1973 $this->xml_encoding = 'us-ascii';
1976 $this->debug('got encoding: ' . $this->charset_encoding);
1977 } elseif (is_array($_SERVER)) {
1978 $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
1979 $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
1981 $this->request = $dump . "\r\n\r\n" . $data;
1982 // parse response, get soap parser obj
1983 $parser = new soap_parser($data, $this->charset_encoding);
1984 // if fault occurred during message parsing
1985 if ($err = $parser->getError()) {
1987 $this->debug("parser debug: \n" . $parser->debug_str);
1988 $this->result = 'fault: error in msg parsing: ' . $err;
1989 $this->fault('Server', "error in msg parsing:\n" . $err);
1991 return $this->fault->serialize();
1992 // else successfully parsed request into soapval object
1994 // get/set methodname
1995 $this->methodname = $parser->root_struct_name;
1996 $this->debug('method name: ' . $this->methodname);
1997 // does method exist?
1998 if (!function_exists($this->methodname)) {
1999 // "method not found" fault here
2000 $this->debug("method '$this->methodname' not found!");
2001 $this->debug("parser debug: \n" . $parser->debug_str);
2002 $this->result = 'fault: method not found';
2003 $this->fault('Server', "method '$this->methodname' not defined in service '$this->service'");
2004 return $this->fault->serialize();
2007 if (!$this->opData = $this->wsdl->getOperationData($this->methodname)) {
2009 $this->fault('Server', "Operation '$this->methodname' is not defined in the WSDL for this service");
2010 return $this->fault->serialize();
2013 $this->debug("method '$this->methodname' exists");
2014 // evaluate message, getting back parameters
2015 $this->debug('calling parser->get_response()');
2016 $request_data = $parser->get_response();
2018 $this->debug("parser debug: \n" . $parser->debug_str);
2019 // verify that request parameters match the method's signature
2020 if ($this->verify_method($this->methodname, $request_data)) {
2021 // if there are parameters to pass
2022 $this->debug('params var dump ' . $this->varDump($request_data));
2023 if ($request_data) {
2024 $this->debug("calling '$this->methodname' with params");
2025 if (!function_exists('call_user_func_array')) {
2026 $this->debug('calling method using eval()');
2027 $funcCall = $this->methodname . '(';
2028 foreach ($request_data as $param) {
2029 $funcCall .= "\"$param\",";
2031 $funcCall = substr($funcCall, 0, -1) . ')';
2032 $this->debug('function call:<br>' . $funcCall);
2033 @eval("\$method_response = $funcCall;");
2035 $this->debug('calling method using call_user_func_array()');
2036 $method_response = call_user_func_array("$this->methodname", $request_data);
2038 $this->debug('response var dump' . $this->varDump($method_response));
2040 // call method w/ no parameters
2041 $this->debug("calling $this->methodname w/ no params");
2042 $m = $this->methodname;
2043 $method_response = @$m();
2045 $this->debug("done calling method: $this->methodname, received $method_response of type" . gettype($method_response));
2046 // if we got nothing back. this might be ok (echoVoid)
2047 if (isset($method_response) && $method_response != '' || is_bool($method_response)) {
2049 if (strtolower(get_class($method_response)) == 'soap_fault') {
2050 $this->debug('got a fault object from method');
2051 $this->fault = $method_response;
2052 return $method_response->serialize();
2053 // if return val is soapval object
2054 } elseif (strtolower(get_class($method_response)) == 'soapval') {
2055 $this->debug('got a soapval object from method');
2056 $return_val = $method_response->serialize();
2059 $this->debug('got a(n) ' . gettype($method_response) . ' from method');
2060 $this->debug('serializing return value');
2062 // weak attempt at supporting multiple output params
2063 if (sizeof($this->opData['output']['parts']) > 1) {
2064 $opParams = $method_response;
2066 $opParams = array($method_response);
2068 $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
2070 $return_val = $this->serialize_val($method_response);
2073 $this->debug('return val:' . $this->varDump($return_val));
2076 $this->debug('got no response from method');
2078 $this->debug('serializing response');
2079 $payload = '<' . $this->methodname . "Response>" . $return_val . '</' . $this->methodname . "Response>";
2080 $this->result = 'successful';
2082 //if($this->debug_flag){
2083 $this->debug("WSDL debug data:\n" . $this->wsdl->debug_str);
2085 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2086 return $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style']);
2088 return $this->serializeEnvelope($payload, $this->responseHeaders);
2092 $this->debug('ERROR: request not verified against method signature');
2093 $this->result = 'fault: request failed validation against method signature';
2095 $this->fault('Server', "Operation '$this->methodname' not defined in service.");
2096 return $this->fault->serialize();
2102 * takes the value that was created by parsing the request
2103 * and compares to the method's signature, if available.
2109 function verify_method($operation, $request)
2111 if (isset($this->wsdl) && is_object($this->wsdl)) {
2112 if ($this->wsdl->getOperationData($operation)) {
2115 } elseif (isset($this->operations[$operation])) {
2122 * add a method to the dispatch map
2124 * @param string $methodname
2125 * @param string $in array of input values
2126 * @param string $out array of output values
2129 function add_to_map($methodname, $in, $out)
2131 $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out);
2135 * register a service with the server
2137 * @param string $methodname
2138 * @param string $in assoc array of input values: key = param name, value = param type
2139 * @param string $out assoc array of output values: key = param name, value = param type
2140 * @param string $namespace
2141 * @param string $soapaction
2142 * @param string $style (rpc|literal)
2145 function register($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = false, $use = false)
2147 if ($this->externalWSDLURL) {
2148 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2152 if (false == $out) {
2154 if (false == $namespace) {
2156 if (false == $soapaction) {
2157 global $SERVER_NAME, $SCRIPT_NAME;
2158 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2160 if (false == $style) {
2163 if (false == $use) {
2167 $this->operations[] = array($name => array());
2168 $this->operations[$name] = array(
2172 'namespace' => $namespace,
2173 'soapaction' => $soapaction,
2176 $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use);
2182 * create a fault. this also acts as a flag to the server that a fault has occured.
2184 * @param string faultcode
2185 * @param string faultactor
2186 * @param string faultstring
2187 * @param string faultdetail
2190 function fault($faultcode, $faultactor, $faultstring = '', $faultdetail = '')
2192 $this->fault = new soap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
2196 * prints html description of services
2200 function webDescription()
2203 <html><head><title>NuSOAP: ' . $this->wsdl->serviceName . '</title>
2204 <style type="text/css">
2205 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2206 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2207 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2208 ul { margin-top: 10px; margin-left: 20px; }
2209 li { list-style-type: none; margin-top: 10px; color: #000000; }
2211 margin-left: 0px; padding-bottom: 2em; }
2213 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2214 margin-top: 10px; margin-left: 0px; color: #000000;
2215 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2217 font-family: arial; font-size: 26px; color: #ffffff;
2218 background-color: #999999; width: 105%; margin-left: 0px;
2219 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2221 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2222 font-family: arial; overflow: hidden; width: 600;
2223 padding: 20px; font-size: 10px; background-color: #999999;
2224 layer-background-color:#FFFFFF; }
2225 a,a:active { color: charcoal; font-weight: bold; }
2226 a:visited { color: #666666; font-weight: bold; }
2227 a:hover { color: cc3300; font-weight: bold; }
2229 <script type="text/javascript">
2231 // POP-UP CAPTIONS...
2232 function lib_bwcheck(){ //Browsercheck (needed)
2233 this.ver=navigator.appVersion
2234 this.agent=navigator.userAgent
2235 this.dom=document.getElementById?1:0
2236 this.opera5=this.agent.indexOf("Opera 5")>-1
2237 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2238 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2239 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2240 this.ie=this.ie4||this.ie5||this.ie6
2241 this.mac=this.agent.indexOf("Mac")>-1
2242 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2243 this.ns4=(document.layers && !this.dom)?1:0;
2244 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2247 var bw = new lib_bwcheck()
2248 //Makes crossbrowser object.
2249 function makeObj(obj){
2250 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2251 if(!this.evnt) return false
2252 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2253 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2254 this.writeIt=b_writeIt;
2257 // A unit of measure that will be added when setting the position of a layer.
2258 //var px = bw.ns4||window.opera?"":"px";
2259 function b_writeIt(text){
2260 if (bw.ns4){this.wref.write(text);this.wref.close()}
2261 else this.wref.innerHTML = text
2263 //Shows the messages
2265 function popup(divid){
2266 if(oDesc = new makeObj(divid)){
2267 oDesc.css.visibility = "visible"
2270 function popout(){ // Hides message
2271 if(oDesc) oDesc.css.visibility = "hidden"
2279 <div class=title>' . $this->wsdl->serviceName . '</div>
2281 <p>View the <a href="' . $GLOBALS['PHP_SELF'] . '?wsdl">WSDL</a> for the service.
2282 Click on an operation name to view it's details.</p>
2284 foreach ($this->wsdl->getOperations() as $op => $data) {
2285 $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2286 // create hidden div
2287 $b .= "<div id='$op' class='hidden'>
2288 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2289 foreach ($data as $donnie => $marie) { // loop through opdata
2290 if ($donnie == 'input' || $donnie == 'output') { // show input/output data
2291 $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
2292 foreach ($marie as $captain => $tenille) { // loop through data
2293 if ($captain == 'parts') { // loop thru parts
2294 $b .= " $captain:<br>";
2295 //if(is_array($tenille)){
2296 foreach ($tenille as $joanie => $chachi) {
2297 $b .= " $joanie: $chachi<br>";
2301 $b .= " $captain: $tenille<br>";
2305 $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
2313 </div></body></html>';
2318 * sets up wsdl object
2319 * this acts as a flag to enable internal WSDL generation
2320 * NOTE: NOT FUNCTIONAL
2322 * @param string $serviceName, name of the service
2323 * @param string $namespace, tns namespace
2325 function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http')
2327 if (!isset($_SERVER))
2328 $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
2329 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2330 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2331 if (false == $namespace) {
2332 $namespace = "http://$SERVER_NAME/soap/$serviceName";
2335 if (false == $endpoint) {
2336 $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2339 $this->wsdl = new wsdl;
2340 $this->wsdl->serviceName = $serviceName;
2341 $this->wsdl->endpoint = $endpoint;
2342 $this->wsdl->namespaces['tns'] = $namespace;
2343 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
2344 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
2345 $this->wsdl->bindings[$serviceName . 'Binding'] = array(
2346 'name' => $serviceName . 'Binding',
2348 'transport' => $transport,
2349 'portType' => $serviceName . 'PortType');
2350 $this->wsdl->ports[$serviceName . 'Port'] = array(
2351 'binding' => $serviceName . 'Binding',
2352 'location' => $endpoint,
2353 'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/');
2360 * parses a WSDL file, allows access to it's data, other utility methods
2362 * @author Dietrich Ayala <dietrich@ganx4.com>
2366 class wsdl extends XMLSchema
2369 // define internal arrays of bindings, ports, operations, messages, etc.
2370 var $message = array();
2371 var $complexTypes = array();
2372 var $messages = array();
2373 var $currentMessage;
2374 var $currentOperation;
2375 var $portTypes = array();
2376 var $currentPortType;
2377 var $bindings = array();
2378 var $currentBinding;
2379 var $ports = array();
2381 var $opData = array();
2383 var $documentation = false;
2385 // array of wsdl docs to import
2386 var $import = array();
2391 var $depth_array = array();
2392 var $usedNamespaces = array();
2394 var $proxyhost = '';
2395 var $proxyport = '';
2400 * @param string $wsdl WSDL document URL
2403 function wsdl($wsdl = '', $proxyhost = false, $proxyport = false)
2405 $this->wsdl = $wsdl;
2406 $this->proxyhost = $proxyhost;
2407 $this->proxyport = $proxyport;
2411 $this->debug('initial wsdl file: ' . $wsdl);
2412 $this->parseWSDL($wsdl);
2415 if (sizeof($this->import) > 0) {
2416 foreach ($this->import as $ns => $url) {
2417 $this->debug('importing wsdl from ' . $url);
2418 $this->parseWSDL($url);
2424 * parses the wsdl document
2426 * @param string $wsdl path or URL
2429 function parseWSDL($wsdl = '')
2432 $this->debug('no wsdl passed to parseWSDL()!!');
2433 $this->setError('no wsdl passed to parseWSDL()!!');
2437 $this->debug('getting ' . $wsdl);
2439 // parse $wsdl for url format
2440 $wsdl_props = parse_url($wsdl);
2442 if (isset($wsdl_props['host'])) {
2445 $tr = new soap_transport_http($wsdl);
2446 $tr->request_method = 'GET';
2447 $tr->useSOAPAction = false;
2448 if ($this->proxyhost && $this->proxyport) {
2449 $tr->setProxy($this->proxyhost, $this->proxyport);
2451 if (isset($wsdl_props['user'])) {
2452 $tr->setCredentials($wsdl_props['user'], $wsdl_props['pass']);
2454 $wsdl_string = $tr->send('');
2456 if ($err = $tr->getError()) {
2457 $this->debug('HTTP ERROR: ' . $err);
2458 $this->setError('HTTP ERROR: ' . $err);
2462 /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
2463 $fsockopen_timeout = 30;
2464 // check if a port value is supplied in url
2465 if (isset($wsdl_props['port'])) {
2467 $wsdl_url_port = $wsdl_props['port'];
2469 // no, assign port number, based on url protocol (scheme)
2470 switch ($wsdl_props['scheme']) {
2474 $wsdl_url_port = 443;
2478 $wsdl_url_port = 80;
2481 // FIXME: should implement SSL/TLS support here if CURL is available
2482 if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
2483 // perform HTTP GET for WSDL file
2484 // 10.9.02 - added poulter fix for doing this properly
2485 $sHeader = "GET " . $wsdl_props['path'];
2486 if (isset($wsdl_props['query'])) {
2487 $sHeader .= "?" . $wsdl_props['query'];
2489 $sHeader .= " HTTP/1.0\r\n";
2491 if (isset($wsdl_props['user'])) {
2492 $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2493 $sHeader .= "Authorization: Basic $base64auth\r\n";
2495 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2496 fputs($fp, $sHeader);
2498 while (fgets($fp, 1024) != "\r\n") {
2499 // do nothing, just read/skip past HTTP headers
2500 // FIXME: should actually detect HTTP response code, and act accordingly if error
2501 // HTTP headers end with extra CRLF before content body
2503 // read in WSDL just like regular fopen()
2505 while ($data = fread($fp, 32768)) {
2506 $wsdl_string .= $data;
2510 $this->setError('bad path to WSDL file.');
2515 // $wsdl seems to be a non-url file path, do the regular fopen
2516 if ($fp = @fopen($wsdl, 'r')) {
2518 while ($data = fread($fp, 32768)) {
2519 $wsdl_string .= $data;
2523 $this->setError('bad path to WSDL file.');
2527 // end new code added
2528 // Create an XML parser.
2529 $this->parser = xml_parser_create();
2530 // Set the options for parsing the XML data.
2531 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2532 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
2533 // Set the object for the parser.
2534 xml_set_object($this->parser, $this);
2535 // Set the element handlers for the parser.
2536 xml_set_element_handler($this->parser, 'start_element', 'end_element');
2537 xml_set_character_data_handler($this->parser, 'character_data');
2538 // Parse the XML file.
2539 if (!xml_parse($this->parser, $wsdl_string, true)) {
2540 // Display an error message.
2542 'XML error on line %d: %s',
2543 xml_get_current_line_number($this->parser),
2544 xml_error_string(xml_get_error_code($this->parser))
2546 $this->debug('XML parse error: ' . $errstr);
2547 $this->setError('Parser error: ' . $errstr);
2551 xml_parser_free($this->parser);
2552 // catch wsdl parse errors
2553 if ($this->getError()) {
2556 // add new data to operation data
2557 foreach ($this->bindings as $binding => $bindingData) {
2558 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
2559 foreach ($bindingData['operations'] as $operation => $data) {
2560 $this->debug('post-parse data gathering for ' . $operation);
2561 $this->bindings[$binding]['operations'][$operation]['input'] =
2562 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
2563 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) :
2564 $this->portTypes[$bindingData['portType']][$operation]['input'];
2565 $this->bindings[$binding]['operations'][$operation]['output'] =
2566 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
2567 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) :
2568 $this->portTypes[$bindingData['portType']][$operation]['output'];
2569 if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
2570 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
2572 if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
2573 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
2575 if (isset($bindingData['style'])) {
2576 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2578 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
2579 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
2580 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
2588 * start-element handler
2590 * @param string $parser XML parser object
2591 * @param string $name element name
2592 * @param string $attrs associative array of attributes
2595 function start_element($parser, $name, $attrs)
2597 if ($this->status == 'schema' || ereg('schema$', $name)) {
2598 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
2599 $this->status = 'schema';
2600 $this->schemaStartElement($parser, $name, $attrs);
2602 // position in the total number of elements, starting from 0
2603 $pos = $this->position++;
2604 $depth = $this->depth++;
2605 // set self as current value for this depth
2606 $this->depth_array[$depth] = $pos;
2607 $this->message[$pos] = array('cdata' => '');
2608 // get element prefix
2609 if (ereg(':', $name)) {
2611 $prefix = substr($name, 0, strpos($name, ':'));
2613 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
2614 // get unqualified name
2615 $name = substr(strstr($name, ':'), 1);
2618 if (count($attrs) > 0) {
2619 foreach ($attrs as $k => $v) {
2620 // if ns declarations, add to class level array of valid namespaces
2621 if (ereg("^xmlns", $k)) {
2622 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
2623 $this->namespaces[$ns_prefix] = $v;
2625 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
2627 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
2628 $this->XMLSchemaVersion = $v;
2629 $this->namespaces['xsi'] = $v . '-instance';
2632 // expand each attribute
2633 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
2634 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
2635 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
2643 // find status, register data
2644 switch ($this->status) {
2646 if ($name == 'part') {
2647 if (isset($attrs['type'])) {
2648 $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
2649 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
2651 if (isset($attrs['element'])) {
2652 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2659 $this->currentPortOperation = $attrs['name'];
2660 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
2661 if (isset($attrs['parameterOrder'])) {
2662 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
2665 case 'documentation':
2666 $this->documentation = true;
2668 // merge input/output data
2670 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
2671 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
2679 if (isset($attrs['style'])) {
2680 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
2682 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
2685 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
2688 if (isset($attrs['soapAction'])) {
2689 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
2691 if (isset($attrs['style'])) {
2692 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2694 if (isset($attrs['name'])) {
2695 $this->currentOperation = $attrs['name'];
2696 $this->debug("current binding operation: $this->currentOperation");
2697 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
2698 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
2699 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
2703 $this->opStatus = 'input';
2706 $this->opStatus = 'output';
2709 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
2710 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
2712 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
2720 $this->currentPort = $attrs['name'];
2721 $this->debug('current port: ' . $this->currentPort);
2722 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
2726 $this->ports[$this->currentPort]['location'] = $attrs['location'];
2727 $this->ports[$this->currentPort]['bindingType'] = $namespace;
2728 $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
2729 $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
2737 if (isset($attrs['location'])) {
2738 $this->import[$attrs['namespace']] = $attrs['location'];
2742 $this->status = 'schema';
2745 $this->status = 'message';
2746 $this->messages[$attrs['name']] = array();
2747 $this->currentMessage = $attrs['name'];
2750 $this->status = 'portType';
2751 $this->portTypes[$attrs['name']] = array();
2752 $this->currentPortType = $attrs['name'];
2755 if (isset($attrs['name'])) {
2757 if (strpos($attrs['name'], ':')) {
2758 $this->currentBinding = $this->getLocalPart($attrs['name']);
2760 $this->currentBinding = $attrs['name'];
2762 $this->status = 'binding';
2763 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
2764 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
2768 $this->serviceName = $attrs['name'];
2769 $this->status = 'service';
2770 $this->debug('current service: ' . $this->serviceName);
2773 foreach ($attrs as $name => $value) {
2774 $this->wsdl_info[$name] = $value;
2782 * end-element handler
2784 * @param string $parser XML parser object
2785 * @param string $name element name
2788 function end_element($parser, $name)
2790 // unset schema status
2791 if (ereg('types$', $name) || ereg('schema$', $name)) {
2794 if ($this->status == 'schema') {
2795 $this->schemaEndElement($parser, $name);
2797 // bring depth down a notch
2800 // end documentation
2801 if ($this->documentation) {
2802 $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2803 $this->documentation = false;
2808 * element content handler
2810 * @param string $parser XML parser object
2811 * @param string $data element content
2814 function character_data($parser, $data)
2816 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
2817 if (isset($this->message[$pos]['cdata'])) {
2818 $this->message[$pos]['cdata'] .= $data;
2820 if ($this->documentation) {
2821 $this->documentation .= $data;
2825 function getBindingData($binding)
2827 if (is_array($this->bindings[$binding])) {
2828 return $this->bindings[$binding];
2833 * returns an assoc array of operation names => operation data
2834 * NOTE: currently only supports multiple services of differing binding types
2835 * This method needs some work
2837 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2841 function getOperations($bindingType = 'soap')
2843 if ($bindingType == 'soap') {
2844 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2847 foreach ($this->ports as $port => $portData) {
2848 // binding type of port matches parameter
2849 if ($portData['bindingType'] == $bindingType) {
2851 return $this->bindings[$portData['binding']]['operations'];
2858 * returns an associative array of data necessary for calling an operation
2860 * @param string $operation , name of operation
2861 * @param string $bindingType , type of binding eg: soap
2865 function getOperationData($operation, $bindingType = 'soap')
2867 if ($bindingType == 'soap') {
2868 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2871 foreach ($this->ports as $port => $portData) {
2872 // binding type of port matches parameter
2873 if ($portData['bindingType'] == $bindingType) {
2875 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
2876 foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
2877 if ($operation == $bOperation) {
2878 $opData = $this->bindings[$portData['binding']]['operations'][$operation];
2887 * serialize the parsed wsdl
2889 * @return string , serialization of WSDL
2892 function serialize()
2894 $xml = '<?xml version="1.0"?><definitions';
2895 foreach ($this->namespaces as $k => $v) {
2896 $xml .= " xmlns:$k=\"$v\"";
2898 // 10.9.02 - add poulter fix for wsdl and tns declarations
2899 if (isset($this->namespaces['wsdl'])) {
2900 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2902 if (isset($this->namespaces['tns'])) {
2903 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2907 if (sizeof($this->import) > 0) {
2908 foreach ($this->import as $ns => $url) {
2909 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2913 if (count($this->complexTypes) >= 1) {
2915 $xml .= $this->serializeSchema();
2919 if (count($this->messages) >= 1) {
2920 foreach ($this->messages as $msgName => $msgParts) {
2921 $xml .= '<message name="' . $msgName . '">';
2922 foreach ($msgParts as $partName => $partType) {
2923 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
2924 if (strpos($partType, ':')) {
2925 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
2926 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
2927 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
2928 $typePrefix = 'xsd';
2930 foreach ($this->typemap as $ns => $types) {
2931 if (isset($types[$partType])) {
2932 $typePrefix = $this->getPrefixFromNamespace($ns);
2935 if (!isset($typePrefix)) {
2936 die("$partType has no namespace!");
2939 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2941 $xml .= '</message>';
2944 // bindings & porttypes
2945 if (count($this->bindings) >= 1) {
2948 foreach ($this->bindings as $bindingName => $attrs) {
2949 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
2950 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
2951 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
2952 foreach ($attrs['operations'] as $opName => $opParts) {
2953 $binding_xml .= '<operation name="' . $opName . '">';
2954 $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $attrs['style'] . '"/>';
2955 $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
2956 $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
2957 $binding_xml .= '</operation>';
2958 $portType_xml .= '<operation name="' . $opParts['name'] . '"';
2959 if (isset($opParts['parameterOrder'])) {
2960 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
2962 $portType_xml .= '>';
2963 $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
2964 $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
2965 $portType_xml .= '</operation>';
2967 $portType_xml .= '</portType>';
2968 $binding_xml .= '</binding>';
2970 $xml .= $portType_xml . $binding_xml;
2973 $xml .= '<service name="' . $this->serviceName . '">';
2974 if (count($this->ports) >= 1) {
2975 foreach ($this->ports as $pName => $attrs) {
2976 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
2977 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
2981 $xml .= '</service>';
2982 return $xml . '</definitions>';
2986 * serialize a PHP value according to a WSDL message definition
2989 * - multi-ref serialization
2990 * - validate PHP values against type definitions, return errors if invalid
2992 * @param string $ type name
2993 * @param mixed $ param value
2994 * @return mixed new param or false if initial value didn't validate
2996 function serializeRPCParameters($operation, $direction, $parameters)
2998 $this->debug('in serializeRPCParameters with operation ' . $operation . ', direction ' . $direction . ' and ' . count($parameters) . ' param(s), and xml schema version ' . $this->XMLSchemaVersion);
3000 if ($direction != 'input' && $direction != 'output') {
3001 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
3002 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
3005 if (!$opData = $this->getOperationData($operation)) {
3006 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
3007 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
3010 $this->debug($this->varDump($opData));
3013 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3015 $use = $opData[$direction]['use'];
3016 $this->debug("use=$use");
3017 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3018 foreach ($opData[$direction]['parts'] as $name => $type) {
3019 $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
3020 // NOTE: add error handling here
3021 // if serializeType returns false, then catch global error and fault
3022 if (isset($parameters[$name])) {
3023 $this->debug('calling serializeType w/ named param');
3024 $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
3025 } elseif (is_array($parameters)) {
3026 $this->debug('calling serializeType w/ unnamed param');
3027 $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
3029 $this->debug('no parameters passed.');
3037 * serializes a PHP value according a given type definition
3039 * @param string $name , name of type (part)
3040 * @param string $type , type of type, heh (type or element)
3041 * @param mixed $value , a native PHP value (parameter value)
3042 * @param string $use , use for part (encoded|literal)
3043 * @return string serialization
3046 function serializeType($name, $type, $value, $use = 'encoded')
3048 $this->debug("in serializeType: $name, $type, $value, $use");
3050 if (strpos($type, ':')) {
3051 $uqType = substr($type, strrpos($type, ':') + 1);
3052 $ns = substr($type, 0, strrpos($type, ':'));
3053 $this->debug("got a prefixed type: $uqType, $ns");
3055 if ($ns == $this->XMLSchemaVersion ||
3056 ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion
3059 if ($uqType == 'boolean' && !$value) {
3061 } elseif ($uqType == 'boolean') {
3064 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
3065 $value = htmlspecialchars($value);
3068 if ($use == 'literal') {
3069 return "<$name>$value</$name>";
3071 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3077 if (!$typeDef = $this->getTypeDef($uqType)) {
3078 $this->setError("$uqType is not a supported type.");
3081 //foreach($typeDef as $k => $v) {
3082 //$this->debug("typedef, $k: $v");
3085 $phpType = $typeDef['phpType'];
3086 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
3087 // if php type == struct, map value to the <all> element names
3088 if ($phpType == 'struct') {
3089 if (isset($typeDef['element']) && $typeDef['element']) {
3090 $elementName = $uqType;
3091 // TODO: use elementFormDefault="qualified|unqualified" to determine
3092 // how to scope the namespace
3093 $elementNS = " xmlns=\"$ns\"";
3095 $elementName = $name;
3098 if ($use == 'literal') {
3099 $xml = "<$elementName$elementNS>";
3101 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3104 if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3106 //if (is_array($this->complexTypes[$uqType]['elements'])) {
3107 // toggle whether all elements are present - ideally should validate against schema
3108 if (count($this->complexTypes[$uqType]['elements']) != count($value)) {
3111 foreach ($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
3112 // if user took advantage of a minOccurs=0, then only serialize named parameters
3113 if (isset($optionals) && !isset($value[$eName])) {
3117 if (isset($value[$eName])) {
3118 $v = $value[$eName];
3119 } elseif (is_array($value)) {
3120 $v = array_shift($value);
3122 // serialize schema-defined type
3123 if (!isset($attrs['type'])) {
3124 $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
3125 // serialize generic type
3127 $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
3128 $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
3133 $xml .= "</$elementName>";
3134 } elseif ($phpType == 'array') {
3135 $rows = sizeof($value);
3136 if (isset($typeDef['multidimensional'])) {
3138 foreach ($value as $v) {
3139 $cols = ',' . sizeof($v);
3140 $nv = array_merge($nv, $v);
3146 if (is_array($value) && sizeof($value) >= 1) {
3148 foreach ($value as $k => $v) {
3149 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
3150 //if (strpos($typeDef['arrayType'], ':') ) {
3151 if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
3152 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
3154 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
3157 $this->debug('contents: ' . $this->varDump($contents));
3161 if ($use == 'literal') {
3166 $xml = "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
3167 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
3169 . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
3170 . ":" . $this->getLocalPart($typeDef['arrayType']) . "[$rows$cols]\">"
3175 $this->debug('returning: ' . $this->varDump($xml));
3180 * register a service with the server
3182 * @param string $methodname
3183 * @param string $in assoc array of input values: key = param name, value = param type
3184 * @param string $out assoc array of output values: key = param name, value = param type
3185 * @param string $namespace
3186 * @param string $soapaction
3187 * @param string $style (rpc|literal)
3190 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3192 if ($style == 'rpc' && $use == 'encoded') {
3193 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3195 $encodingStyle = '';
3198 $this->bindings[$this->serviceName . 'Binding']['operations'][$name] =
3201 'binding' => $this->serviceName . 'Binding',
3202 'endpoint' => $this->endpoint,
3203 'soapAction' => $soapaction,
3207 'namespace' => $namespace,
3208 'encodingStyle' => $encodingStyle,
3209 'message' => $name . 'Request',
3213 'namespace' => $namespace,
3214 'encodingStyle' => $encodingStyle,
3215 'message' => $name . 'Response',
3217 'namespace' => $namespace,
3218 'transport' => 'http://schemas.xmlsoap.org/soap/http',
3219 'documentation' => $documentation);
3223 foreach ($in as $pName => $pType) {
3224 if (strpos($pType, ':')) {
3225 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
3227 $this->messages[$name . 'Request'][$pName] = $pType;
3232 foreach ($out as $pName => $pType) {
3233 if (strpos($pType, ':')) {
3234 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
3236 $this->messages[$name . 'Response'][$pName] = $pType;
3247 * soap_parser class parses SOAP XML messages into native PHP values
3249 * @author Dietrich Ayala <dietrich@ganx4.com>
3253 class soap_parser extends nusoap_base
3257 var $xml_encoding = '';
3259 var $root_struct = '';
3260 var $root_struct_name = '';
3261 var $root_header = '';
3263 // determines where in the message we are (envelope,header,body,method)
3267 var $default_namespace = '';
3268 var $namespaces = array();
3269 var $message = array();
3272 var $fault_code = '';
3273 var $fault_str = '';
3274 var $fault_detail = '';
3275 var $depth_array = array();
3276 var $debug_flag = true;
3277 var $soapresponse = NULL;
3278 var $responseHeaders = '';
3279 var $body_position = 0;
3280 // for multiref parsing:
3281 // array of id => pos
3283 // array of id => hrefs => pos
3284 var $multirefs = array();
3289 * @param string $xml SOAP message
3290 * @param string $encoding character encoding scheme of message
3293 function soap_parser($xml, $encoding = 'UTF-8', $method = '')
3296 $this->xml_encoding = $encoding;
3297 $this->method = $method;
3299 // Check whether content has been read.
3301 $this->debug('Entering soap_parser()');
3302 // Create an XML parser.
3303 $this->parser = xml_parser_create($this->xml_encoding);
3304 // Set the options for parsing the XML data.
3305 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3306 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3307 // Set the object for the parser.
3308 xml_set_object($this->parser, $this);
3309 // Set the element handlers for the parser.
3310 xml_set_element_handler($this->parser, 'start_element', 'end_element');
3311 xml_set_character_data_handler($this->parser, 'character_data');
3313 // Parse the XML file.
3314 if (!xml_parse($this->parser, $xml, true)) {
3315 // Display an error message.
3316 $err = sprintf('XML error on line %d: %s',
3317 xml_get_current_line_number($this->parser),
3318 xml_error_string(xml_get_error_code($this->parser)));
3319 $this->debug('parse error: ' . $err);
3320 $this->errstr = $err;
3322 $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
3324 $this->soapresponse = $this->message[$this->root_struct]['result'];
3326 if ($this->root_header != '' && isset($this->message[$this->root_header]['result'])) {
3327 $this->responseHeaders = $this->message[$this->root_header]['result'];
3329 // resolve hrefs/ids
3330 if (sizeof($this->multirefs) > 0) {
3331 foreach ($this->multirefs as $id => $hrefs) {
3332 $this->debug('resolving multirefs for id: ' . $id);
3333 $idVal = $this->buildVal($this->ids[$id]);
3334 foreach ($hrefs as $refPos => $ref) {
3335 $this->debug('resolving href at pos ' . $refPos);
3336 $this->multirefs[$id][$refPos] = $idVal;
3341 xml_parser_free($this->parser);
3343 $this->debug('xml was empty, didn\'t parse!');
3344 $this->errstr = 'xml was empty, didn\'t parse!';
3349 * start-element handler
3351 * @param string $parser XML parser object
3352 * @param string $name element name
3353 * @param string $attrs associative array of attributes
3356 function start_element($parser, $name, $attrs)
3358 // position in a total number of elements, starting from 0
3359 // update class level pos
3360 $pos = $this->position++;
3362 $this->message[$pos] = array('pos' => $pos, 'children' => '', 'cdata' => '');
3363 // depth = how many levels removed from root?
3364 // set mine as current global depth and increment global depth value
3365 $this->message[$pos]['depth'] = $this->depth++;
3367 // else add self as child to whoever the current parent is
3369 $this->message[$this->parent]['children'] .= '|' . $pos;
3372 $this->message[$pos]['parent'] = $this->parent;
3373 // set self as current parent
3374 $this->parent = $pos;
3375 // set self as current value for this depth
3376 $this->depth_array[$this->depth] = $pos;
3377 // get element prefix
3378 if (strpos($name, ':')) {
3380 $prefix = substr($name, 0, strpos($name, ':'));
3381 // get unqualified name
3382 $name = substr(strstr($name, ':'), 1);
3385 if ($name == 'Envelope') {
3386 $this->status = 'envelope';
3387 } elseif ($name == 'Header') {
3388 $this->root_header = $pos;
3389 $this->status = 'header';
3390 } elseif ($name == 'Body') {
3391 $this->status = 'body';
3392 $this->body_position = $pos;
3394 } elseif ($this->status == 'body' && $pos == ($this->body_position + 1)) {
3395 $this->status = 'method';
3396 $this->root_struct_name = $name;
3397 $this->root_struct = $pos;
3398 $this->message[$pos]['type'] = 'struct';
3399 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
3402 $this->message[$pos]['status'] = $this->status;
3404 $this->message[$pos]['name'] = htmlspecialchars($name);
3406 $this->message[$pos]['attrs'] = $attrs;
3408 // loop through atts, logging ns and type declarations
3410 foreach ($attrs as $key => $value) {
3411 $key_prefix = $this->getPrefix($key);
3412 $key_localpart = $this->getLocalPart($key);
3413 // if ns declarations, add to class level array of valid namespaces
3414 if ($key_prefix == 'xmlns') {
3415 if (ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$', $value)) {
3416 $this->XMLSchemaVersion = $value;
3417 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
3418 $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
3420 $this->namespaces[$key_localpart] = $value;
3421 // set method namespace
3422 if ($name == $this->root_struct_name) {
3423 $this->methodNamespace = $value;
3425 // if it's a type declaration, set type
3426 } elseif ($key_localpart == 'type') {
3427 $value_prefix = $this->getPrefix($value);
3428 $value_localpart = $this->getLocalPart($value);
3429 $this->message[$pos]['type'] = $value_localpart;
3430 $this->message[$pos]['typePrefix'] = $value_prefix;
3431 if (isset($this->namespaces[$value_prefix])) {
3432 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
3433 } else if (isset($attrs['xmlns:' . $value_prefix])) {
3434 $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
3436 // should do something here with the namespace of specified type?
3437 } elseif ($key_localpart == 'arrayType') {
3438 $this->message[$pos]['type'] = 'array';
3439 /* do arrayType ereg here
3440 [1] arrayTypeValue ::= atype asize
3441 [2] atype ::= QName rank*
3442 [3] rank ::= '[' (',')* ']'
3443 [4] asize ::= '[' length~ ']'
3444 [5] length ::= nextDimension* Digit+
3445 [6] nextDimension ::= Digit+ ','
3447 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
3448 if (ereg($expr, $value, $regs)) {
3449 $this->message[$pos]['typePrefix'] = $regs[1];
3450 $this->message[$pos]['arraySize'] = $regs[3];
3451 $this->message[$pos]['arrayCols'] = $regs[4];
3456 $this->ids[$value] = $pos;
3459 if ($key_localpart == 'root' && $value == 1) {
3460 $this->status = 'method';
3461 $this->root_struct_name = $name;
3462 $this->root_struct = $pos;
3463 $this->debug("found root struct $this->root_struct_name, pos $pos");
3466 $attstr .= " $key=\"$value\"";
3468 // get namespace - must be done after namespace atts are processed
3469 if (isset($prefix)) {
3470 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
3471 $this->default_namespace = $this->namespaces[$prefix];
3473 $this->message[$pos]['namespace'] = $this->default_namespace;
3475 if ($this->status == 'header') {
3476 $this->responseHeaders .= "<$name$attstr>";
3477 } elseif ($this->root_struct_name != '') {
3478 $this->document .= "<$name$attstr>";
3483 * end-element handler
3485 * @param string $parser XML parser object
3486 * @param string $name element name
3489 function end_element($parser, $name)
3491 // position of current element is equal to the last value left in depth_array for my depth
3492 $pos = $this->depth_array[$this->depth--];
3494 // get element prefix
3495 if (strpos($name, ':')) {
3497 $prefix = substr($name, 0, strpos($name, ':'));
3498 // get unqualified name
3499 $name = substr(strstr($name, ':'), 1);
3502 // build to native type
3503 if (isset($this->body_position) && $pos > $this->body_position) {
3504 // deal w/ multirefs
3505 if (isset($this->message[$pos]['attrs']['href'])) {
3507 $id = substr($this->message[$pos]['attrs']['href'], 1);
3508 // add placeholder to href array
3509 $this->multirefs[$id][$pos] = "placeholder";
3510 // add set a reference to it as the result value
3511 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
3512 // build complex values
3513 } elseif ($this->message[$pos]['children'] != "") {
3514 $this->message[$pos]['result'] = $this->buildVal($pos);
3516 $this->debug('adding data for scalar value ' . $this->message[$pos]['name'] . ' of value ' . $this->message[$pos]['cdata']);
3517 if (is_numeric($this->message[$pos]['cdata'])) {
3518 if (strpos($this->message[$pos]['cdata'], '.')) {
3519 $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
3521 $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
3524 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3530 if ($pos == $this->root_struct) {
3531 $this->status = 'body';
3532 } elseif ($name == 'Body') {
3533 $this->status = 'header';
3534 } elseif ($name == 'Header') {
3535 $this->status = 'envelope';
3536 } elseif ($name == 'Envelope') {
3539 // set parent back to my parent
3540 $this->parent = $this->message[$pos]['parent'];
3542 if ($this->status == 'header') {
3543 $this->responseHeaders .= "</$name>";
3544 } elseif ($pos >= $this->root_struct) {
3545 $this->document .= "</$name>";
3550 * element content handler
3552 * @param string $parser XML parser object
3553 * @param string $data element content
3556 function character_data($parser, $data)
3558 $pos = $this->depth_array[$this->depth];
3559 if ($this->xml_encoding == 'UTF-8') {
3560 $data = utf8_decode($data);
3562 $this->message[$pos]['cdata'] .= $data;
3564 if ($this->status == 'header') {
3565 $this->responseHeaders .= $data;
3567 $this->document .= $data;
3572 * get the parsed message
3577 function get_response()
3579 return $this->soapresponse;
3583 * get the parsed headers
3585 * @return string XML or empty if no headers
3588 function getHeaders()
3590 return $this->responseHeaders;
3596 * @param string $text string to translate
3599 function decode_entities($text)
3601 foreach ($this->entities as $entity => $encoded) {
3602 $text = str_replace($encoded, $entity, $text);
3608 * builds response structures for compound values (arrays/structs)
3610 * @param string $pos position in node tree
3613 function buildVal($pos)
3615 if (!isset($this->message[$pos]['type'])) {
3616 $this->message[$pos]['type'] = '';
3618 $this->debug('inside buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
3619 // if there are children...
3620 if ($this->message[$pos]['children'] != '') {
3621 $children = explode('|', $this->message[$pos]['children']);
3622 array_shift($children); // knock off empty
3624 if (isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') {
3627 foreach ($children as $child_pos) {
3628 $this->debug("got an MD array element: $r, $c");
3629 $params[$r][] = $this->message[$child_pos]['result'];
3631 if ($c == $this->message[$pos]['arrayCols']) {
3637 } elseif ($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') {
3638 $this->debug('adding array ' . $this->message[$pos]['name']);
3639 foreach ($children as $child_pos) {
3640 $params[] = &$this->message[$child_pos]['result'];
3642 // apache Map type: java hashtable
3643 } elseif ($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
3644 foreach ($children as $child_pos) {
3645 $kv = explode("|", $this->message[$child_pos]['children']);
3646 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
3648 // generic compound type
3649 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
3651 // is array or struct? better way to do this probably
3652 foreach ($children as $child_pos) {
3653 if (isset($keys) && isset($keys[$this->message[$child_pos]['name']])) {
3657 $keys[$this->message[$child_pos]['name']] = 1;
3660 foreach ($children as $child_pos) {
3661 if (isset($struct)) {
3662 $params[] = &$this->message[$child_pos]['result'];
3664 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
3668 return is_array($params) ? $params : array();
3670 $this->debug('no children');
3671 if (strpos($this->message[$pos]['cdata'], '&')) {
3672 return strtr($this->message[$pos]['cdata'], array_flip($this->entities));
3674 return $this->message[$pos]['cdata'];
3687 * soapclient higher level class for easy usage.
3691 * // instantiate client with server info
3692 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
3694 * // call method, get results
3695 * echo $soapclient->call( string methodname [ ,array parameters] );
3698 * unset($soapclient);
3700 * @author Dietrich Ayala <dietrich@ganx4.com>
3704 class pwsoapclient extends nusoap_base
3709 var $requestHeaders = false;
3710 var $responseHeaders;
3712 var $error_str = false;
3713 var $proxyhost = '';
3714 var $proxyport = '';
3715 var $xml_encoding = '';
3716 var $http_encoding = false;
3718 var $endpointType = '';
3719 var $persistentConnection = false;
3720 var $defaultRpcParams = false;
3723 * fault related variables
3731 var $fault, $faultcode, $faultstring, $faultdetail;
3736 * @param string $endpoint SOAP server or WSDL URL
3737 * @param bool $wsdl optional, set to true if using WSDL
3738 * @param int $portName optional portName in WSDL document
3741 function soapclient($endpoint, $wsdl = false)
3743 $this->endpoint = $endpoint;
3747 $this->endpointType = 'wsdl';
3748 $this->wsdlFile = $this->endpoint;
3750 // instantiate wsdl object and parse wsdl file
3751 $this->debug('instantiating wsdl class with doc: ' . $endpoint);
3752 $this->wsdl =& new wsdl($this->wsdlFile, $this->proxyhost, $this->proxyport);
3753 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3754 $this->wsdl->debug_str = '';
3756 if ($errstr = $this->wsdl->getError()) {
3757 $this->debug('got wsdl error: ' . $errstr);
3758 $this->setError('wsdl error: ' . $errstr);
3759 } elseif ($this->operations = $this->wsdl->getOperations()) {
3760 $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile);
3762 $this->debug('getOperations returned false');
3763 $this->setError('no operations defined in the WSDL document!');
3769 * calls method, returns PHP native type
3771 * @param string $method SOAP server URL or path
3772 * @param array $params array of parameters, can be associative or not
3773 * @param string $namespace optional method namespace
3774 * @param string $soapAction optional SOAPAction value
3775 * @param boolean $headers optional array of soapval objects for headers
3776 * @param boolean $rpcParams optional treat params as RPC for use="literal"
3777 * This can be used on a per-call basis to overrider defaultRpcParams.
3781 function call($operation, $params = array(), $namespace = '', $soapAction = '', $headers = false, $rpcParams = null)
3783 $this->operation = $operation;
3784 $this->fault = false;
3785 $this->error_str = '';
3786 $this->request = '';
3787 $this->response = '';
3788 $this->faultstring = '';
3789 $this->faultcode = '';
3790 $this->opData = array();
3792 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
3793 $this->debug("endpointType: $this->endpointType");
3794 // if wsdl, get operation data and process parameters
3795 if ($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)) {
3797 $this->opData = $opData;
3798 foreach ($opData as $key => $value) {
3799 $this->debug("$key -> $value");
3801 $soapAction = $opData['soapAction'];
3802 $this->endpoint = $opData['endpoint'];
3803 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
3804 $style = $opData['style'];
3805 // add ns to ns array
3806 if ($namespace != '' && !isset($this->wsdl->namespaces[$namespace])) {
3807 $this->wsdl->namespaces['nu'] = $namespace;
3809 // serialize payload
3811 if ($opData['input']['use'] == 'literal') {
3812 if (is_null($rpcParams)) {
3813 $rpcParams = $this->defaultRpcParams;
3816 $this->debug("serializing literal params for operation $operation");
3817 $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params);
3818 $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
3820 $this->debug("serializing literal document for operation $operation");
3821 $payload = is_array($params) ? array_shift($params) : $params;
3824 $this->debug("serializing encoded params for operation $operation");
3825 $payload = "<" . $this->wsdl->getPrefixFromNamespace($namespace) . ":$operation>" .
3826 $this->wsdl->serializeRPCParameters($operation, 'input', $params) .
3827 '</' . $this->wsdl->getPrefixFromNamespace($namespace) . ":$operation>";
3829 $this->debug('payload size: ' . strlen($payload));
3830 // serialize envelope
3831 $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $this->wsdl->usedNamespaces, $style);
3832 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3833 $this->wsdl->debug_str = '';
3834 } elseif ($this->endpointType == 'wsdl') {
3835 $this->setError('operation ' . $operation . ' not present.');
3836 $this->debug("operation '$operation' not present.");
3837 $this->debug("wsdl debug: \n" . $this->wsdl->debug_str);
3842 if (!isset($style)) {
3845 if ($namespace == '') {
3846 $namespace = 'http://testuri.org';
3847 $this->wsdl->namespaces['ns1'] = $namespace;
3849 // serialize envelope
3851 foreach ($params as $k => $v) {
3852 $payload .= $this->serialize_val($v, $k);
3854 $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n" . $payload . "</ns1:$operation>\n";
3855 $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders);
3857 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
3859 $this->debug('sending msg (len: ' . strlen($soapmsg) . ") w/ soapaction '$soapAction'...");
3860 $return = $this->send($soapmsg, $soapAction, $this->timeout);
3861 if ($errstr = $this->getError()) {
3862 $this->debug('Error: ' . $errstr);
3865 $this->return = $return;
3866 $this->debug('sent message successfully and got a(n) ' . gettype($return) . ' back');
3869 if (is_array($return) && isset($return['faultcode'])) {
3870 $this->debug('got fault');
3871 $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
3872 $this->fault = true;
3873 foreach ($return as $k => $v) {
3875 $this->debug("$k = $v<br>");
3879 // array of return values
3880 if (is_array($return)) {
3881 // multiple 'out' parameters
3882 if (sizeof($return) > 1) {
3885 // single 'out' parameter
3886 return array_shift($return);
3887 // nothing returned (ie, echoVoid)
3896 * get available data pertaining to an operation
3898 * @param string $operation operation name
3899 * @return array array of data pertaining to the operation
3902 function getOperationData($operation)
3904 if (isset($this->operations[$operation])) {
3905 return $this->operations[$operation];
3907 $this->debug("No data for operation: $operation");
3911 * send the SOAP message
3913 * Note: if the operation has multiple return values
3914 * the return value of this method will be an array
3917 * @param string $msg a SOAPx4 soapmsg object
3918 * @param string $soapaction SOAPAction value
3919 * @param integer $timeout set timeout in seconds
3920 * @return mixed native PHP types.
3923 function send($msg, $soapaction = '', $timeout = 0)
3928 case ereg('^http', $this->endpoint):
3929 $this->debug('transporting via HTTP');
3930 if ($this->persistentConnection && is_object($this->persistentConnection)) {
3931 $http =& $this->persistentConnection;
3933 $http = new soap_transport_http($this->endpoint);
3934 // pass encoding into transport layer, so appropriate http headers are sent
3935 $http->soap_defencoding = $this->soap_defencoding;
3937 $http->setSOAPAction($soapaction);
3938 if ($this->proxyhost && $this->proxyport) {
3939 $http->setProxy($this->proxyhost, $this->proxyport);
3941 if ($this->username != '' && $this->password != '') {
3942 $http->setCredentials($this->username, $this->password);
3944 if ($this->http_encoding != '') {
3945 $http->setEncoding($this->http_encoding);
3947 $this->debug('sending message, length: ' . strlen($msg));
3948 if (ereg('^http:', $this->endpoint)) {
3949 //if(strpos($this->endpoint,'http:')){
3950 $response = $http->send($msg, $timeout);
3951 } elseif (ereg('^https', $this->endpoint)) {
3952 //} elseif(strpos($this->endpoint,'https:')){
3953 //if(phpversion() == '4.3.0-dev'){
3954 //$response = $http->send($msg,$timeout);
3955 //$this->request = $http->outgoing_payload;
3956 //$this->response = $http->incoming_payload;
3958 if (extension_loaded('curl')) {
3959 $response = $http->sendHTTPS($msg, $timeout);
3961 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
3964 $this->setError('no http/s in endpoint url');
3966 $this->request = $http->outgoing_payload;
3967 $this->response = $http->incoming_payload;
3968 $this->debug("transport debug data...\n" . $http->debug_str);
3969 // save transport object if using persistent connections
3970 if ($this->persistentConnection && !is_object($this->persistentConnection)) {
3971 $this->persistentConnection = $http;
3973 if ($err = $http->getError()) {
3974 $this->setError('HTTP Error: ' . $err);
3976 } elseif ($this->getError()) {
3979 $this->debug('got response, length: ' . strlen($response));
3980 return $this->parseResponse($response);
3984 $this->setError('no transport found, or selected transport is not yet supported!');
3991 * processes SOAP message returned from server
3993 * @param string unprocessed response data from server
3994 * @return mixed value of the message, decoded into a PHP type
3997 function parseResponse($data)
3999 $this->debug('Entering parseResponse(), about to create soap_parser instance');
4000 $parser = new soap_parser($data, $this->xml_encoding, $this->operation);
4002 if ($errstr = $parser->getError()) {
4003 $this->setError($errstr);
4004 // destroy the parser object
4009 $this->responseHeaders = $parser->getHeaders();
4010 // get decoded message
4011 $return = $parser->get_response();
4012 // add parser debug data to our debug
4013 $this->debug($parser->debug_str);
4014 // add document for doclit support
4015 $this->document = $parser->document;
4016 // destroy the parser object
4018 // return decode message
4024 * set the SOAP headers
4026 * @param $headers string XML
4029 function setHeaders($headers)
4031 $this->requestHeaders = $headers;
4035 * get the response headers
4037 * @return mixed object SOAPx4 soapval object or empty if no headers
4040 function getHeaders()
4042 if ($this->responseHeaders != '') {
4043 return $this->responseHeaders;
4048 * set proxy info here
4050 * @param string $proxyhost
4051 * @param string $proxyport
4054 function setHTTPProxy($proxyhost, $proxyport)
4056 $this->proxyhost = $proxyhost;
4057 $this->proxyport = $proxyport;
4061 * if authenticating, set user credentials here
4063 * @param string $username
4064 * @param string $password
4067 function setCredentials($username, $password)
4069 $this->username = $username;
4070 $this->password = $password;
4076 * @param string $enc
4079 function setHTTPEncoding($enc = 'gzip, deflate')
4081 $this->http_encoding = $enc;
4085 * use HTTP persistent connections if possible
4089 function useHTTPPersistentConnection()
4091 $this->persistentConnection = true;
4095 * gets the default RPC parameter setting.
4096 * If true, default is that call params are like RPC even for document style.
4097 * Each call() can override this value.
4101 function getDefaultRpcParams()
4103 return $this->defaultRpcParams;
4107 * sets the default RPC parameter setting.
4108 * If true, default is that call params are like RPC even for document style
4109 * Each call() can override this value.
4111 * @param boolean $rpcParams
4114 function setDefaultRpcParams($rpcParams)
4116 $this->defaultRpcParams = $rpcParams;
4120 * dynamically creates proxy class, allowing user to directly call methods from wsdl
4122 * @return object soap_proxy object
4128 foreach ($this->operations as $operation => $opData) {
4129 if ($operation != '') {
4130 // create param string
4132 if (sizeof($opData['input']['parts']) > 0) {
4133 foreach ($opData['input']['parts'] as $name => $type) {
4134 $paramStr .= "\$$name,";
4136 $paramStr = substr($paramStr, 0, strlen($paramStr) - 1);
4138 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
4139 $evalStr .= "function $operation ($paramStr){
4140 // load params into array
4141 \$params = array($paramStr);
4142 return \$this->call('$operation',\$params,'" . $opData['namespace'] . "','" . $opData['soapAction'] . "');
4148 $evalStr = 'class soap_proxy_' . $r . ' extends soapclient {
4151 //print "proxy class:<pre>$evalStr</pre>";
4154 // instantiate proxy object
4155 eval("\$proxy = new soap_proxy_$r('');");
4156 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
4157 $proxy->endpointType = 'wsdl';
4158 $proxy->wsdlFile = $this->wsdlFile;
4159 $proxy->wsdl = $this->wsdl;
4160 $proxy->operations = $this->operations;
4161 $proxy->defaultRpcParams = $this->defaultRpcParams;
4169 // c-basic-offset: 4
4170 // c-hanging-comment-ender-p: nil
4171 // indent-tabs-mode: nil