Source for file WSDL.php
Documentation is available at WSDL.php
* This file contains the code for dealing with WSDL access and services.
* LICENSE: This source file is subject to version 2.02 of the PHP license,
* that is bundled with this package in the file LICENSE, and is available at
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
* did not receive a copy of the PHP license and are unable to obtain it
* through the world-wide-web, please send a note to license@php.net so we can
* mail you a copy immediately.
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
* @author Jan Schneider <jan@horde.org> Maintenance
* @copyright 2003-2005 The PHP Group
* @license http://www.php.net/license/2_02.txt PHP License 2.02
* @link http://pear.php.net/package/SOAP
require_once 'SOAP/Base.php';
require_once 'SOAP/Fault.php';
require_once 'HTTP/Request.php';
define('WSDL_CACHE_MAX_AGE', 43200 );
* This class parses WSDL files, and can be used by SOAP::Client to properly
* register soap values for services.
* Originally based on SOAPx4 by Dietrich Ayala
* http://dietrich.ganx4.com/soapx4
* - refactor namespace handling ($namespace/$ns)
* - implement IDL type syntax declaration so we can generate WSDL
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
var $xsd = SOAP_XML_SCHEMA_VERSION;
* Parse documentation in the WSDL?
* Enable tracing in the generated proxy class?
* Cache maximum lifetime (in seconds).
* Class to use for WSDL parsing. Can be overridden for special cases,
* @link http://www.php.net/manual/en/reserved.php
var $_reserved = array ('abstract', 'and', 'array', 'as', 'break', 'case',
'catch', 'cfunction', 'class', 'clone', 'const',
'continue', 'declare', 'default', 'die', 'do',
'echo', 'else', 'elseif', 'empty', 'enddeclare',
'endfor', 'endforeach', 'endif', 'endswitch',
'endwhile', 'eval', 'exception', 'exit', 'extends',
'final', 'for', 'foreach', 'function', 'global',
'if', 'implements', 'include', 'include_once',
'interface', 'isset', 'list', 'new', 'old_function',
'or', 'php_user_filter', 'print', 'private',
'protected', 'public', 'require', 'require_once',
'return', 'static', 'switch', 'this', 'throw',
'try', 'unset', 'use', 'var', 'while', 'xor');
* Regular expressions for invalid PHP labels.
* @link http://www.php.net/manual/en/language.variables.php.
var $_invalid = array ('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
* @param string $wsdl_uri URL to WSDL file.
* @param array $proxy Options for HTTP_Request class
* @param boolean|string$cacheUse Use WSDL caching? The cache directory
* @param integer $cacheMaxAge Cache maximum lifetime (in seconds).
* @param boolean $docs Parse documentation in the WSDL?
$cacheMaxAge = WSDL_CACHE_MAX_AGE ,
if (!PEAR ::isError ($this->parseURL($wsdl_uri))) {
* @deprecated Use setService().
* Sets the service currently to be used.
* @param string $service An (existing) service name.
* Fills the WSDL array tree with data from a WSDL file.
* @param string $wsdl_uri URL to WSDL file.
$parser = & new $this->wsdlParserClass ($wsdl_uri, $this, $this->docs);
$this->_raiseSoapFault ($parser->fault );
* Fills the WSDL array tree with data from one or more PHP class objects.
* @param mixed $wsdl_obj An object or array of objects to add to
* the internal WSDL tree.
* @param string $targetNamespace The target namespace of schema types
* @param string $service_name Name of the WSDL service.
* @param string $service_desc Optional description of the WSDL
function parseObject($wsdl_obj, $targetNamespace, $service_name,
$targetNamespace, $service_name,
$this->_raiseSoapFault ($parser->fault );
return $this->_getfault ();
return (isset ($this->services[$this->service]['ports'][$portName]['address']['location']))
? $this->services[$this->service]['ports'][$portName]['address']['location']
: $this->_raiseSoapFault (" No endpoint for port for $portName" , $this->uri);
function _getPortName ($operation, $service)
if (isset ($this->services[$service]['ports'])) {
$ports = $this->services[$service]['ports'];
foreach ($ports as $port => $portAttrs) {
$type = $ports[$port]['type'];
isset ($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
* Finds the name of the first port that contains an operation of name
* $operation. Always returns a SOAP portName.
return $this->_getfault ();
if (isset ($this->services[$service]['ports'])) {
if ($portName = $this->_getPortName ($operation, $service)) {
// Try any service in the WSDL.
foreach ($this->services as $serviceName => $service) {
if (isset ($this->services[$serviceName]['ports'])) {
if ($portName = $this->_getPortName ($operation, $serviceName)) {
return $this->_raiseSoapFault (" No operation $operation in WSDL." , $this->uri);
return $this->_getfault ();
if (!isset ($this->services[$this->service]['ports'][$portName]['binding']) ||
!($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
return $this->_raiseSoapFault (" No binding for port $portName in WSDL." , $this->uri);
// Get operation data from binding.
$opData = $this->bindings[$binding]['operations'][$operation];
// Get operation data from porttype.
$portType = $this->bindings[$binding]['type'];
return $this->_raiseSoapFault (" No port type for binding $binding in WSDL." , $this->uri);
if (isset ($type['parameterOrder'])) {
$opData['parameterOrder'] = $type['parameterOrder'];
$opData['input'] = array_merge($opData['input'], $type['input']);
$opData['output'] = array_merge($opData['output'], $type['output']);
return $this->_raiseSoapFault (" No operation $operation for port $portName in WSDL." , $this->uri);
$opData['parameters'] = false;
if (isset ($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
$opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
// Message data from messages.
$inputMsg = $opData['input']['message'];
foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
if ($opData['style'] == 'document' &&
$opData['input']['use'] == 'literal' &&
$pname == 'parameters') {
$opData['parameters'] = true;
$opData['namespace'] = $this->namespaces[$pattrs['namespace']];
$el = $this->elements[$pattrs['namespace']][$pattrs['type']];
if (isset ($el['elements'])) {
foreach ($el['elements'] as $elname => $elattrs) {
$opData['input']['parts'][$elname] = $elattrs;
$opData['input']['parts'][$pname] = $pattrs;
$outputMsg = $opData['output']['message'];
foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
if ($opData['style'] == 'document' &&
$opData['output']['use'] == 'literal' &&
$pname == 'parameters') {
$el = $this->elements[$pattrs['namespace']][$pattrs['type']];
if (isset ($el['elements'])) {
foreach ($el['elements'] as $elname => $elattrs) {
$opData['output']['parts'][$elname] = $elattrs;
$opData['output']['parts'][$pname] = $pattrs;
return $this->_getfault ();
// Overloading lowercases function names :(
* Given a datatype, what function handles the processing?
* This is used for doc/literal requests where we receive a datatype, and
* we need to pass it to a method in out server class.
* @param string $datatype
* @param string $namespace
// See if we have an element by this name.
if (!isset ($this->ns[$namespace])) {
$nsp = $this->ns[$namespace];
//if (!isset($this->elements[$nsp]))
// $nsp = $this->namespaces[$nsp];
if (!isset ($this->elements[$nsp][$datatype])) {
$checkmessages = array ();
// Find what messages use this datatype.
foreach ($this->messages as $messagename => $message) {
foreach ($message as $part) {
if ($part['type'] == $datatype) {
$checkmessages[] = $messagename;
// Find the operation that uses this message.
foreach ($porttype as $opname => $opinfo) {
foreach ($checkmessages as $messagename) {
if ($opinfo['input']['message'] == $messagename) {
return $this->_getfault ();
if (!empty ($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
return $this->_getfault ();
if (!empty ($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
/* If it doesn't exist at first, flip the array and check again. */
if (empty ($this->ns[$namespace])) {
/* If it doesn't exist now, add it. */
if (empty ($this->ns[$namespace])) {
return $this->ns[$namespace];
if (!empty ($this->ns[$namespace])) {
return $this->ns[$namespace];
$this->ns[$namespace] = $attr;
function _validateString ($string)
function _addArg (&$args, &$argarray, $argname)
if (!$this->_validateString ($argname)) {
$argarray .= " '$argname' => $" . $argname;
function _elementArg (&$args, &$argarray, &$_argtype, $_argname)
$el = $this->elements[$_argtype['namespace']][$_argtype['type']];
$tns = isset ($this->ns[$el['namespace']])
? $this->ns[$el['namespace']]
: $_argtype['namespace'];
if (!empty ($el['complex']) ||
// The element is a complex type.
$comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
$attrname = "{ $_argtype['type']}_attr";
if (isset ($el['type']) &&
isset ($this->complexTypes[$tns][$el['type']]['attribute'])) {
$comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
$comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
$comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
$this->_addArg ($args, $argarray, $_argtype['type']);
if (isset ($el['type']) &&
isset ($this->complexTypes[$tns][$el['type']]['attribute'])) {
$args .= '$' . $attrname;
} elseif (isset ($el['elements'])) {
foreach ($el['elements'] as $ename => $element) {
$comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}} $ename', '" .
(isset ($element['type']) ? $element['type'] : false ) .
$this->_addArg ($args, $argarray, $ename);
$comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}} $_argname', '{ $el['type']}', \$ $_argname);\n";
$this->_addArg ($args, $argarray, $_argname);
function _complexTypeArg (&$args, &$argarray, &$_argtype, $_argname)
if (isset ($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
$comments = " // $_argname is a ComplexType {$_argtype['type']},\n" .
" // refer to wsdl for more info\n";
if (isset ($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
$comments .= " // $_argname may require attributes, refer to wsdl for more info\n";
$wrapname = '{' . $this->namespaces[$_argtype['namespace']]. '}' . $_argtype['type'];
$comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
$this->_addArg ($args, $argarray, $_argname);
* Generates stub code from the WSDL that can be saved to a file or eval'd
return $this->_getfault ();
// XXX currently do not support HTTP ports
if ($port['type'] != 'soap') {
// XXX currentPort is BAD
$clienturl = $port['address']['location'];
if ($multiport || $port) {
$classname = 'WebService_' . $this->service . '_' . $port['name'];
$classname = 'WebService_' . $this->service;
$classname = $this->_sanitize ($classname);
if (!$this->_validateString ($classname)) {
$class = " class $classname extends SOAP_Client\n{\n" .
" function $classname(\$path = '$clienturl')\n {\n" .
" \$this->SOAP_Client(\$path, 0, 0,\n" .
foreach ($this->proxy as $key => $val) {
$class .= " '$key' => array(";
foreach ($val as $key2 => $val2) {
$class .= " '$key2' => '$val2', ";
$class .= " '$key' => '$val', ";
$class = " class $classname extends SOAP_Client\n{\n" .
" function $classname(\$path = '$clienturl')\n {\n" .
" \$this->SOAP_Client(\$path, 0);\n" .
// Get the binding, from that get the port type.
$primaryBinding = $port['binding'];
$primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
$portType = $this->bindings[$primaryBinding]['type'];
$style = $this->bindings[$primaryBinding]['style'];
// XXX currentPortType is BAD
foreach ($this->portTypes[$portType] as $opname => $operation) {
$binding = $this->bindings[$primaryBinding]['operations'][$opname];
if (isset ($binding['soapAction'])) {
$soapaction = $binding['soapAction'];
if (isset ($binding['style'])) {
$opstyle = $binding['style'];
$use = $binding['input']['use'];
$namespace = $binding['input']['namespace'];
$bindingType = $this->bindings[$primaryBinding]['type'];
$ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
foreach ($operation['input'] as $argname => $argtype) {
if ($argname == 'message') {
foreach ($this->messages[$argtype] as $_argname => $_argtype) {
$_argname = $this->_sanitize ($_argname);
if ($opstyle == 'document' && $use == 'literal' &&
$_argtype['name'] == 'parameters') {
// The type or element refered to is used for
$el = $this->elements[$_argtype['namespace']][$_argtype['type']];
$namespace = $this->namespaces[$_argtype['namespace']];
// XXX need to wrap the parameters in a
if (isset ($el['elements'])) {
foreach ($el['elements'] as $elname => $elattrs) {
$elname = $this->_sanitize ($elname);
// Is the element a complex type?
if (isset ($this->complexTypes[$elattrs['namespace']][$elname])) {
$comments .= $this->_complexTypeArg ($args, $argarray, $_argtype, $_argname);
$this->_addArg ($args, $argarray, $elname);
if ($el['complex'] && $argarray) {
$wrapname = '{' . $this->namespaces[$_argtype['namespace']]. '}' . $el['name'];
$comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
$argarray = " '{$el['name']}' => \${$el['name']}";
if (isset ($_argtype['element'])) {
$comments .= $this->_elementArg ($args, $argarray, $_argtype, $_argtype['type']);
// Complex type argument.
$comments .= $this->_complexTypeArg ($args, $argarray, $_argtype, $_argname);
// Operation names are function names, so try to make sure it's
// legal. This could potentially cause collisions, but let's try
// to make everything callable and see how many problems that
$opname_php = $this->_sanitize ($opname);
if (!$this->_validateString ($opname_php)) {
$argarray = " array($argarray)";
$class .= " function &$opname_php($args)\n {\n$comments$wrappers" .
" \$result = \$this->call('$opname',\n" .
" array('namespace' => '$namespace',\n" .
" 'soapaction' => '$soapaction',\n" .
" 'style' => '$opstyle',\n" .
($this->trace ? ",\n 'trace' => true" : '') . "));\n" .
function &getProxy($port = '', $name = '')
$fault = & $this->_getfault ();
if ($multiport || $port) {
$classname = 'WebService_' . $this->service . '_' . $port['name'];
$classname = 'WebService_' . $this->service;
$classname = $name . '_' . $classname;
$classname = $this->_sanitize ($classname);
require_once 'SOAP/Client.php';
$proxy = & new $classname;
* Sanitizes a SOAP value, method or class name so that it can be used as
* a valid PHP identifier. Invalid characters are converted into
* underscores and reserved words are prefixed with an underscore.
* @param string $name The identifier to sanitize.
* @return string The sanitized identifier.
function _sanitize ($name)
if (in_array($name, $this->_reserved)) {
function &_getComplexTypeForElement ($name, $namespace)
if (isset ($this->ns[$namespace]) &&
isset ($this->elements[$this->ns[$namespace]][$name]['type'])) {
$type = $this->elements[$this->ns[$namespace]][$name]['type'];
$ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
$t = $this->_getComplexTypeForElement ($name, $namespace);
// Is the type an element?
$t = $this->_getComplexTypeForElement ($name, $ns);
// No, get it from complex types directly.
if (isset ($t['elements'][$child_name]['type']))
return $t['elements'][$child_name]['type'];
} elseif (isset ($this->ns[$ns]) &&
isset ($this->elements[$this->ns[$ns]][$name]['complex']) &&
$this->elements[$this->ns[$ns]][$name]['complex']) {
// Type is not an element but complex.
return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
* @param QName $name A parameter name.
* @param QName $type A parameter type.
* @return array A list of [type, array element type, array element
* namespace, array length].
// see if it's a complex type so we can deal properly with
// look up the name in the wsdl and validate the type.
if (isset ($types[$type->name ])) {
if (isset ($types[$type->name ]['type'])) {
list ($arraytype_ns, $arraytype, $array_depth) = isset ($types[$type->name ]['arrayType'])
? $this->_getDeepestArrayType ($types[$type->name ]['namespace'], $types[$type->name ]['arrayType'])
: array ($this->namespaces[$types[$type->name ]['namespace']], null , 0 );
return array ($types[$type->name ]['type'], $arraytype, $arraytype_ns, $array_depth);
if (isset ($types[$type->name ]['arrayType'])) {
list ($arraytype_ns, $arraytype, $array_depth) =
$this->_getDeepestArrayType ($types[$type->name ]['namespace'], $types[$type->name ]['arrayType']);
return array ('Array', $arraytype, $arraytype_ns, $array_depth);
if (!empty ($types[$type->name ]['elements'][$name->name ])) {
$type->name = $types[$type->name ]['elements']['type'];
return array ($type->name , null , $this->namespaces[$types[$type->name ]['namespace']], null );
if ($type && $type->namespace ) {
// this code currently handles only one way of encoding array
// types in wsdl need to do a generalized function to figure out
$p = $this->ns[$type->namespace ];
if ($arrayType = $this->complexTypes[$p][$type->name ]['arrayType']) {
} elseif ($this->complexTypes[$p][$type->name ]['order'] == 'sequence' &&
$arrayType = $arg['type'];
foreach ($this->complexTypes[$p][$type->name ]['elements'] as $element) {
if ($element['name'] == $type->name ) {
$arrayType = $element['type'];
$type->name = $element['type'];
return array ($type->name , $arrayType, $type->namespace , null );
return array (null , null , null , null );
* Recurse through the WSDL structure looking for the innermost array type
* of multi-dimensional arrays.
* Takes a namespace prefix and a type, which can be in the form 'type' or
* 'type[]', and returns the full namespace URI, the type of the most
* deeply nested array type found, and the number of levels of nesting.
* @return mixed array or nothing
function _getDeepestArrayType ($nsPrefix, $arrayType)
$arrayType = ereg_replace ('\[\]$', '', $arrayType);
// Protect against circular references XXX We really need to remove
// trail from this altogether (it's very inefficient and in the wrong
// place!) and put circular reference checking in when the WSDL info
// is generated in the first place
return array (null , null , - count($trail));
$trail[] = $nsPrefix . ':' . $arrayType;
$result = $this->_getDeepestArrayType ($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
return array ($result[0 ], $result[1 ], $result[2 ] + 1 );
return array ($this->namespaces[$nsPrefix], $arrayType, 0 );
* Cache maximum lifetime (in seconds)
* @param boolean $cashUse Use caching?
* @param integer $cacheMaxAge Cache maximum lifetime (in seconds)
$cacheMaxAge = WSDL_CACHE_MAX_AGE ,
$this->_cacheUse = $cacheUse;
$this->_cacheDir = $cacheDir;
$this->_cacheMaxAge = $cacheMaxAge;
* Returns the path to the cache and creates it, if it doesn't exist.
* @return string The directory to use for the cache.
if (!empty ($this->_cacheDir)) {
* Retrieves a file from cache if it exists, otherwise retreive from net,
* add to cache, and return from cache.
* @param string URL to WSDL
* @param array proxy parameters
* @param int expected MD5 of WSDL URL
function get($wsdl_fname, $proxy_params = array (), $cache = 0 )
$cachename = $md5_wsdl = $file_data = '';
// Try to retrieve WSDL from cache
$cachename = $this->_cacheDir () . '/' . md5($wsdl_fname). ' .wsdl';
$md5_wsdl = md5($file_data);
if ($cache != $md5_wsdl) {
return $this->_raiseSoapFault ('WSDL Checksum error!', $wsdl_fname);
if ($cache_mtime + $this->_cacheMaxAge < time()) {
// Not cached or not using cache. Retrieve WSDL from URL
if (strpos($wsdl_fname, 'file://') === 0 ) {
$wsdl_fname = substr($wsdl_fname, 7 );
return $this->_raiseSoapFault ('Unable to read local WSDL file', $wsdl_fname);
} elseif (!preg_match('|^https?://|', $wsdl_fname)) {
return $this->_raiseSoapFault ('Unknown schema of WSDL URL', $wsdl_fname);
$rq = new HTTP_Request ($uri[0 ], $proxy_params);
// the user agent HTTP_Request uses fouls things up
$rq->addRawQueryString ($uri[1 ]);
if (isset ($proxy_params['proxy_host']) &&
isset ($proxy_params['proxy_port']) &&
isset ($proxy_params['proxy_user']) &&
isset ($proxy_params['proxy_pass'])) {
$rq->setProxy ($proxy_params['proxy_host'],
$proxy_params['proxy_port'],
$proxy_params['proxy_user'],
$proxy_params['proxy_pass']);
} elseif (isset ($proxy_params['proxy_host']) &&
isset ($proxy_params['proxy_port'])) {
$rq->setProxy ($proxy_params['proxy_host'],
$proxy_params['proxy_port']);
$result = $rq->sendRequest ();
if (PEAR ::isError ($result)) {
return $this->_raiseSoapFault (" Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode (), $wsdl_fname);
$file_data = $rq->getResponseBody ();
return $this->_raiseSoapFault (" Unable to retrieve WSDL $wsdl_fname, no http body" , $wsdl_fname);
$md5_wsdl = md5($file_data);
$fp = fopen($cachename, "wb");
if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
return $this->_raiseSoapFault ('WSDL Checksum error!', $wsdl_fname);
* Define internal arrays of bindings, ports, operations,
// Check whether content has been read.
$fd = $this->cache->get ($uri, $this->wsdl->proxy );
if (PEAR ::isError ($fd)) {
return $this->_raiseSoapFault ($fd);
$detail = sprintf('XML error on line %d: %s',
return $this->_raiseSoapFault (" Unable to parse WSDL file $uri\n$detail" );
$qname = new QName($name);
if ($ns && ((!$this->tns && strcasecmp($qname->name , 'definitions') == 0 ) || $ns == $this->tns)) {
$this->currentTag = $qname->name;
// Find status, register data.
// No parent should be in the stack.
if (!$parent_tag || $parent_tag == 'types') {
$this->schema = $this->wsdl->getNamespaceAttributeName ($attrs['targetNamespace']);
$this->schema = $this->wsdl->getNamespaceAttributeName ($this->wsdl->tns );
$this->wsdl->complexTypes [$this->schema] = array ();
if ($parent_tag == 'schema') {
if (!isset ($attrs['namespace'])) {
$attrs['namespace'] = $this->schema;
$qn = new QName($attrs['base']);
if (isset ($attrs['type'])) {
$qn = new QName($attrs['type']);
$attrs['type'] = $qn->name;
$attrs['namespace'] = $qn->ns;
if (isset ($attrs['ref'])) {
$qn = new QName($attrs['ref']);
if (!isset ($attrs['namespace'])) {
$attrs['namespace'] = $this->schema;
if ($parent_tag == 'schema') {
// we're inside a complexType
if (!empty ($attrs['base'])) {
$qn = new QName($attrs['base']);
// Types that extend from other types aren't
// *of* those types. Reflect this by denoting
// which type they extend. I'm leaving the
// 'type' setting here since I'm not sure what
// removing it might break at the moment.
if ($qname->name == 'extension') {
if (isset ($attrs['name'])) {
if (isset ($attrs['ref'])) {
$q = new QName($attrs['ref']);
foreach ($attrs as $k => $v) {
if ($k != 'ref' && strstr($k, $q->name )) {
if ($q->name == 'arrayType') {
// sect 2.3 wsdl:message child wsdl:part
if (isset ($attrs['type'])) {
$qn = new QName($attrs['type']);
} elseif (isset ($attrs['element'])) {
$qn = new QName($attrs['element']);
$attrs['type'] = $qn->name;
$attrs['namespace'] = $qn->ns;
// children: wsdl:input wsdl:output wsdl:fault
// wsdl:input wsdl:output wsdl:fault
// attributes: name message parameterOrder(optional)
$qn = new QName($attrs['message']);
// this deals with wsdl section 3 soap binding
// soap:binding, attributes: transport(required), style(optional, default = document)
// if style is missing, it is assumed to be 'document'
if (!isset ($attrs['style'])) {
$attrs['style'] = 'document';
// soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
if (!isset ($attrs['style'])) {
// part - optional. listed parts must appear in body, missing means all parts appear in body
// use - required. encoded|literal
// encodingStyle - optional. space seperated list of encodings (uri's)
// soap:fault attributes: name use encodingStyle namespace
// soap:header attributes: message part use encodingStyle namespace
// soap:header attributes: message part use encodingStyle namespace
['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
// error! not a valid element inside binding
// XXX verify correct namespace
// for now, default is the 'wsdl' namespace
// other possible namespaces include smtp, http, etc. for alternate bindings
// wsdl:operation attributes: name
// wsdl:input attributes: name
$this->opStatus = $qname->name;
// http:binding attributes: verb
// http:operation attributes: location
// parent: wsdl:operation
// http:urlEncoded attributes: location
// parent: wsdl:input wsdl:output etc.
// http:urlReplacement attributes: location
// parent: wsdl:input wsdl:output etc.
// all mime parts are children of wsdl:input, wsdl:output, etc.
// <mime:content part="nmtoken"? type="string"?/>
// part attribute only required if content is child of multipart related,
// it contains the name of the part
// type attribute contains the mime type
// sect 5.4 mime:multipartRelated
// <mime:mimeXml part="nmtoken"?/>
// http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
// all DIME parts are children of wsdl:input, wsdl:output, etc.
// appears in binding section
// sect 2.6 wsdl:port attributes: name binding
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort] = $attrs;
// XXX hack to deal with binding namespaces
$qn = new QName($attrs['binding']);
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns;
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
// what TYPE of port is it? SOAP or HTTP?
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['type']= 'http';
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['type']= 'soap';
// Shouldn't happen, we'll assume SOAP.
$this->wsdl->services [$this->currentService]['ports'][$this->currentPort]['type']= 'soap';
// Top level elements found under wsdl:definitions.
// sect 2.1.1 wsdl:import attributes: namespace location
if ((isset ($attrs['location']) || isset ($attrs['schemaLocation'])) &&
!isset ($this->wsdl->imports [$attrs['namespace']])) {
$uri = isset ($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation'];
if (!isset ($location['scheme'])) {
$uri = $this->mergeUrl ($base, $uri);
$this->wsdl->imports [$attrs['namespace']] = $attrs;
$import_parser = & new $import_parser_class($uri, $this->wsdl, $this->docs);
if ($import_parser->fault ) {
unset ($this->wsdl->imports [$attrs['namespace']]);
$this->currentImport = $attrs['namespace'];
// Continue on to the 'types' case - lack of break; is
// We can hit this at the top level if we've been asked to
if (!empty ($attrs['targetNamespace'])) {
$this->schema = $this->wsdl->getNamespaceAttributeName ($attrs['targetNamespace']);
$this->schema = $this->wsdl->getNamespaceAttributeName ($this->wsdl->tns );
$this->wsdl->complexTypes [$this->schema] = array ();
// sect 2.3 wsdl:message attributes: name children:wsdl:part
if (isset ($attrs['name'])) {
// sect 2.4 wsdl:portType
// children: wsdl:operation
// sect 2.5 wsdl:binding attributes: name type
// children: wsdl:operation soap:binding http:binding
if ($qname->ns && $qname->ns != $this->tns) {
$qn = new QName($attrs['type']);
// sect 2.7 wsdl:service attributes: name children: ports
$this->currentService = $attrs['name'];
$this->wsdl->services [$this->currentService]['ports'] = array ();
// sec 2.1 wsdl:definitions
// attributes: name targetNamespace xmlns:*
// children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
$this->wsdl->definition = $attrs;
foreach ($attrs as $key => $value) {
if (strstr($key, 'xmlns:') !== false ) {
// XXX need to refactor ns handling.
$this->wsdl->namespaces [$qn->name ] = $value;
$this->wsdl->ns [$value] = $qn->name;
if ($key == 'targetNamespace' ||
if (in_array($value, $this->_XMLSchema)) {
$this->wsdl->xsd = $value;
$namespace = 'xmlns:' . $ns;
if (!$this->wsdl->definition [$namespace]) {
return $this->_raiseSoapFault (" parse error, no namespace for $namespace" , $this->uri);
/* Correct the type for sequences with multiple
if (stristr($name, 'complexType')) {
} elseif (stristr($name, 'element')) {
* Element content handler.
// Store the documentation in the WSDL file.
if ($this->currentTag == 'documentation') {
$ptr = & $this->wsdl->services [$this->currentService];
if (!isset ($ptr['documentation'])) {
$ptr['documentation'] = '';
$ptr['documentation'] .= ' ';
$ptr['documentation'] .= $data;
* $parsed is an array returned by parse_url().
function mergeUrl ($parsed, $path)
if (!empty ($parsed['scheme'])) {
$sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
$uri = $parsed['scheme'] . $sep;
if (isset ($parsed['pass'])) {
$uri .= " $parsed[user]:$parsed[pass]@";
} elseif (isset ($parsed['user'])) {
$uri .= " $parsed[user]@";
if (isset ($parsed['host'])) {
if (isset ($parsed['port'])) {
$uri .= " :$parsed[port]";
if ($path[0 ] != '/' && isset ($parsed['path'])) {
if ($parsed['path'][strlen($parsed['path']) - 1 ] != '/') {
$path = dirname($parsed['path']) . '/' . $path;
$path = $parsed['path'] . $path;
$path = $this->_normalize ($path);
$sep = $path[0 ] == '/' ? '' : '/';
function _normalize ($path_str)
$strArr = preg_split('/(\/)/', $path_str, -1 , PREG_SPLIT_NO_EMPTY );
for ($i = 0; $i < count($strArr); $i++ ) {
if ($strArr[$i] != ' ..') {
if ($strArr[$i] != ' .') {
$pwdArr[$j] = $strArr[$i];
$pwd = (strlen($pStr) > 0 ) ? ('/' . $pStr) : '/';
* Parses the types and methods used in web service objects into the internal
* data structures used by SOAP_WSDL.
* Assumes the SOAP_WSDL class is unpopulated to start with.
|