Source for file WSDL.php
Documentation is available at WSDL.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | 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. |
// +----------------------------------------------------------------------+
// | Authors: Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more |
// | Authors: Dietrich Ayala <dietrich@ganx4.com> Original Author |
// +----------------------------------------------------------------------+
// $Id: WSDL.php,v 1.51.2.4 2004/08/22 20:39:31 arnaud Exp $
require_once 'SOAP/Base.php';
require_once 'SOAP/Fault.php';
require_once 'HTTP/Request.php';
define('WSDL_CACHE_MAX_AGE', 43200 );
define('WSDL_CACHE_USE', 0 ); // set to zero to turn off caching
* this class parses wsdl files, and can be used by SOAP::Client to properly register
* soap values for services
* originaly 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
* @version $Id: WSDL.php,v 1.51.2.4 2004/08/22 20:39:31 arnaud Exp $
* @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;
* Cache max lifetime (in seconds)
* @param string endpoint_uri (URL to WSDL file)
* @param array contains options for HTTP_Request class (see HTTP/Request.php)
* @param boolean use WSDL caching
* @param int cache max lifetime (in seconds)
function SOAP_WSDL($wsdl_uri = false , $proxy = array (),
$cacheUse = WSDL_CACHE_USE ,
$cacheMaxAge = WSDL_CACHE_MAX_AGE ) {
* @deprecated use parseURL instead
function parse($wsdl_uri, $proxy = array ()) {
* Fill the WSDL array tree with data from a WSDL file
* @param array proxi related parameters
function parseURL($wsdl_uri, $proxy = array ()) {
$this->_raiseSoapFault ($parser->fault );
* Fill 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 $service_name Name of the WSDL <service>
* @param string $service_desc Optional description of the WSDL <service>
function parseObject(&$wsdl_obj, $targetNamespace, $service_name, $service_desc = '')
$this->_raiseSoapFault ($parser->fault );
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'])) {
foreach ($this->services[$service]['ports'] as $port => $portAttrs) {
$type = $this->services[$service]['ports'][$port]['type'];
isset ($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
// find the name of the first port that contains an operation of name $operation
// always returns a the soap portName
if (!$service) $service = $this->service;
if (isset ($this->services[$service]['ports'])) {
$portName = $this->_getPortName ($operation,$service);
if ($portName) return $portName;
// try any service in the wsdl
foreach ($this->services as $serviceName=> $service) {
if (isset ($this->services[$serviceName]['ports'])) {
$portName = $this->_getPortName ($operation,$serviceName);
return $this->_raiseSoapFault (" no operation $operation in wsdl" , $this->uri);
&& $binding = $this->services[$this->service]['ports'][$portName]['binding']) {
// 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 ($this->portTypes[$portType][$operation]['parameterOrder']))
$opData['parameterOrder'] = $this->portTypes[$portType][$operation]['parameterOrder'];
$opData['output'] = array_merge($opData['output'], $this->portTypes[$portType][$operation]['output']);
return $this->_raiseSoapFault (" no operation $operation for port $portName, in wsdl" , $this->uri);
$opData['parameters'] = false;
if (isset ($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']))
$opData['namespace'] = $this->bindings[$this->services[$this->service]['ports'][$portName]['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->_raiseSoapFault (" no binding for port $portName in wsdl" , $this->uri);
// Overloading lowercases function names :(
foreach ($this->services[$this->service]['ports'] as $port => $portAttrs) {
* 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
* @param string namespace
* @returns string methodname
// 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 $partname=> $part) {
if ($part['type']== $datatype) {
$checkmessages[] = $messagename;
// find the operation that uses this message
foreach($this->portTypes as $portname=> $porttype) {
foreach ($porttype as $opname=> $opinfo) {
foreach ($checkmessages as $messagename) {
if ($opinfo['input']['message'] == $messagename) {
if (isset ($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']) &&
$soapAction = $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']) {
isset ($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']) &&
$namespace = $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 it doesn't exist now, add it */
return $this->ns[$namespace];
return $this->ns[$namespace];
$this->ns[$namespace] = $attr;
function _validateString ($string) {
// XXX this should be done sooner or later
#if (preg_match("/^[\w_:#\/]+$/",$string)) return true;
function _addArg (&$args, &$argarray, $argname)
if ($args) $args .= ", ";
if (!$this->_validateString ($argname)) return NULL;
if ($argarray) $argarray .= ", ";
$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'];
// the element is actually a complex type!
$comments = " // {$el['type']} is a ComplexType, refer to wsdl for more info\n";
$attrname = "{ $_argtype['type']}_attr";
if (isset ($this->complexTypes[$tns][$el['type']]['attribute'])) {
$comments .= " // {$el['type']} may require attributes, refer to 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 ($this->complexTypes[$tns][$el['type']]['attribute'])) {
if ($args) $args .= ", ";
#$comments = $this->_complexTypeArg($args,$argarray,$el,$_argtype['type']);
} else if (isset ($el['elements'])) {
foreach ($el['elements'] as $ename => $element) {
$comments .= " \$$ename =& new SOAP_Value('{{$this->namespaces[$element['namespace']]}} $ename','{ $element['type']}',\$ $ename);\n";
$this->_addArg ($args,$argarray,$ename);
#$echoStringParam =& new SOAP_Value('{http://soapinterop.org/xsd}echoStringParam',false,$echoStringParam);
$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";
$comments .= " //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 into existence
// XXX currently do not support HTTP ports
if ($port['type'] != 'soap') return NULL;
// XXX currentPort is BAD
$clienturl = $port['address']['location'];
if ($multiport || $port) {
$classname = 'WebService_'. $this->service. '_'. $port['name'];
$classname = 'WebService_'. $this->service;
if (!$this->_validateString ($classname)) return NULL;
$class = " class $classname extends SOAP_Client\n{\n".
" function $classname()\n{\n".
" \$this->SOAP_Client(\"$clienturl\", 0, 0,
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()\n{\n".
" \$this->SOAP_Client(\"$clienturl\", 0);\n".
// get the binding, from that get the port type
$primaryBinding = $port['binding']; //$this->services[$this->service]['ports'][$port['name']]["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) {
$soapaction = $this->bindings[$primaryBinding]['operations'][$opname]['soapAction'];
if (isset ($this->bindings[$primaryBinding]['operations'][$opname]['style'])) {
$opstyle = $this->bindings[$primaryBinding]['operations'][$opname]['style'];
$use = $this->bindings[$primaryBinding]['operations'][$opname]['input']['use'];
$namespace = $this->bindings[$primaryBinding]['operations'][$opname]['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) {
if ($opstyle == 'document' && $use == 'literal' &&
$_argtype['name'] == 'parameters') {
// the type or element refered to is used for parameters!
$element = $_argtype['element'];
$el = $this->elements[$_argtype['namespace']][$_argtype['type']];
$namespace = $this->namespaces[$_argtype['namespace']];
// XXX need to wrap the parameters in a soap_value
if (isset ($el['elements'])) {
foreach ($el['elements'] as $elname => $elattrs) {
// 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);
$comments = $this->_complexTypeArg($args, $argarray, $elattrs, $elattrs['name']);
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']);
$comments = $this->_complexTypeArg ($args, $argarray, $_argtype, $_argname);
if (!$this->_validateString ($opname)) return NULL;
if (!$this->_validateString ($namespace)) return NULL;
if (!$this->_validateString ($soapaction)) return NULL;
$argarray = " array($argarray)";
$class .= " function &$opname($args) {\n$comments$wrappers".
" return \$this->call(\"$opname\", \n".
" array('namespace'=>'$namespace',\n".
" 'soapaction'=>'$soapaction',\n".
" 'style'=>'$opstyle',\n".
($this->trace? ",'trace'=>1": ""). " ));\n".
function &getProxy($port = '', $name = '')
if ($multiport || $port) {
$classname = 'WebService_' . $this->service . '_' . $port['name'];
$classname = 'WebService_' . $this->service;
$classname = $name . '_' . $classname;
$classname = preg_replace('/[ .\-\(\)]+/', '_', $classname);
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'];
# see if it's a complex type so we can deal properly with SOAPENC:arrayType
# look up the name in the wsdl and validate the type
list ($arraytype_ns, $arraytype, $array_depth) = isset ($types[$type]['arrayType'])?
$this->_getDeepestArrayType ($types[$type]['namespace'], $types[$type]['arrayType'])
: array ($this->namespaces[$types[$type]['namespace']], NULL , 0 );
return array ($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth);
list ($arraytype_ns, $arraytype, $array_depth) =
$this->_getDeepestArrayType ($types[$type]['namespace'], $types[$type]['arrayType']);
return array ('Array', $arraytype, $arraytype_ns, $array_depth);
$type = $types[$type]['elements']['type'];
return array ($type, NULL , $this->namespaces[$types[$type]['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 complex types
$p = $this->ns[$type_namespace];
if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) {
} else if ($this->complexTypes[$p][$type]['order']== 'sequence' &&
$arrayType = $arg['type'];
foreach($this->complexTypes[$p][$type]['elements'] as $element) {
if ($element['name'] == $type) {
$arrayType = $element['type'];
$type = $element['type'];
return array ($type, $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 max lifetime (in seconds)
var $_cacheMaxAge = null;
* SOAP_WSDL_Cache constructor
* @param boolean use caching
* @param int cache max lifetime (in seconds)
$cacheMaxAge = WSDL_CACHE_MAX_AGE ) {
$this->_cacheUse = $cacheUse;
$this->_cacheMaxAge = $cacheMaxAge;
* return the path to the cache, if it doesn't exist, make it
if (!$dir) $dir = "./wsdlcache";
* 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
$wf = fopen($cachename,'rb');
$md5_wsdl = md5($file_data);
if ($cache != $md5_wsdl) {
return $this->_raiseSoapFault ('WSDL Checksum error!', $wsdl_fname);
#print cache_mtime, time()
if ($cache_mtime + $this->_cacheMaxAge < time()) {
$md5_wsdl = ''; # refetch
// Not cached or not using cache. Retrieve WSDL from URL
// this section should be replace by curl at some point
if (!preg_match('/^(https?|file):\/\//',$wsdl_fname)) {
return $this->_raiseSoapFault (" Unable to read local WSDL $wsdl_fname" , $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, messages, etc.
// 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;
#$this->wsdl->getNamespaceAttributeName
$attrs['namespace'] = $qn->ns;
if (isset ($attrs['ref'])) {
if (!isset ($attrs['namespace'])) $attrs['namespace'] = $this->schema;
if ($parent_tag == 'schema') {
// we're inside a complexType
$qn = & new QName($attrs['base']);
#if (!array_key_exists('type',$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
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']);
} else if (isset ($attrs['element'])) {
$qn = & new QName($attrs['element']);
$attrs['type'] = $qn->name;
$attrs['namespace'] = $qn->ns;
// children: wsdl:input wsdl:output wsdl:fault
#$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation]['parameterOrder'] = $attrs['parameterOrder'];
// 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'])) $attrs['style'] = $this->wsdl->bindings [$this->currentBinding]['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
!isset ($this->wsdl->imports [$attrs['namespace']])) {
$uri = $attrs['location'];
if (!isset ($location['scheme'])) {
if ($import_parser->fault ) {
$this->currentImport = $attrs['namespace'];
$this->wsdl->imports [$this->currentImport] = $attrs;
// 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) break;
$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->status = 'definitions';
$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 elements */
if (stristr($name,'complexType')) {
} else if (stristr($name,'element')) {
// position of current element is equal to the last value left in depth_array for my depth
//$pos = $this->depth_array[$this->depth];
// bring depth down a notch
// element content handler
# store the documentation in the WSDL file
if ($this->currentTag == 'documentation') {
if ($this->status == 'service') {
$this->wsdl->services [$this->currentService][$this->currentTag] .= $data;
} else if ($this->status == 'portType') {
} else if ($this->status == 'binding') {
} else if ($this->status == 'message') {
} else if ($this->status == 'operation') {
// $parsed is a parse_url() resulting array
if (isset ($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'])) $uri .= $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++ ){
$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.
* @author Chris Coe <info@intelligentstreaming.com>
// Target namespace for the WSDL document will have the following prefix
// Reference to the SOAP_WSDL object to populate
* @param $objects Reference to the object or array of objects to parse
* @param $wsdl Reference to the SOAP_WSDL object to populate
* @param $targetNamespace The target namespace of schema types etc.
* @param $service_name Name of the WSDL <service>
* @param $service_desc Optional description of the WSDL <service>
// Set up the SOAP_WSDL object
$this->_initialise ($service_name);
// Parse each web service object
$wsdl_ref = (is_array($objects)? $objects : array (&$objects));
foreach ($wsdl_ref as $ref_item) {
return $this->_raiseSoapFault ('Invalid web service object passed to object parser', 'urn:' . get_class($object));
if ($this->_parse ($ref_item, $targetNamespace, $service_name) != true )
// Build bindings from abstract data
if ($this->fault == NULL )
$this->_generateBindingsAndServices ($targetNamespace, $service_name, $service_desc);
/** Initialise the SOAP_WSDL tree (destructive)
* If the object has already been initialised, the only effect will be to
* change the tns namespace to the new service name
* @param $service_name Name of the WSDL <service>
function _initialise ($service_name) {
// Set up the basic namespaces that all WSDL definitions use
$this->wsdl->namespaces [$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace
$this->wsdl->namespaces ['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema
$this->wsdl->namespaces ['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types
// XXX Refactor $namespace/$ns for Shane :-)
unset ($this->wsdl->ns ['urn:' . $service_name]);
// Imports are not implemented in WSDL generation from classes
/** Parser - takes a single object to add to tree (non-destructive)
* @param $object Reference to the object to parse
* @param $service_name Name of the WSDL <service>
function _parse (&$object, $schemaNamespace, $service_name) {
// Create namespace prefix for the schema
// XXX not very elegant :-(
list ($schPrefix, $foo) = $this->_getTypeNs ('{'. $schemaNamespace. '}');
// Parse all the types defined by the object in whatever
// schema language we are using (currently __typedef arrays)
foreach ($object->__typedef as $typeName => $typeValue)
// Get/create namespace definition
list ($nsPrefix, $typeName) = $this->_getTypeNs ($typeName);
// Create type definition
$this->wsdl->complexTypes [$schPrefix][$typeName] = array ("name" => $typeName);
$thisType = & $this->wsdl->complexTypes [$schPrefix][$typeName];
// According to Dmitri's documentation, __typedef comes in two
// Array = array(array("item" => "value"))
// Struct = array("item1" => "value1", "item2" => "value2", ...)
$thisType['type'] = 'Array';
list ($nsPrefix, $typeName) = $this->_getTypeNs (current(current($typeValue)));
$thisType['namespace'] = $nsPrefix;
$thisType['arrayType'] = $typeName . '[]';
$thisType['type'] = 'Struct';
$thisType['order'] = 'all';
$thisType['namespace'] = $nsPrefix;
$thisType['elements'] = array ();
foreach ($typeValue as $elementName => $elementType)
list ($nsPrefix, $typeName) = $this->_getTypeNs ($elementType);
$thisType['elements'][$elementName]['name'] = $elementName;
$thisType['elements'][$elementName]['type'] = $typeName;
$thisType['elements'][$elementName]['namespace'] = $nsPrefix;
return $this->_raiseSoapFault (" The type definition for $nsPrefix:$typeName is invalid." , 'urn:' . get_class($object));
return $this->_raiseSoapFault (" The type definition for $nsPrefix:$typeName is invalid." , 'urn:' . get_class($object));
// Create an empty element array with the target namespace prefix,
// to match the results of WSDL parsing
$this->wsdl->elements [$schPrefix] = array ();
// Populate tree with message information
// *** <wsdl:message> ***
foreach ($object->__dispatch_map as $operationName => $messages)
foreach ($messages as $messageType => $messageParts)
$this->wsdl->messages [$operationName . 'Request'] = array ();
$thisMessage = & $this->wsdl->messages [$operationName . 'Request'];
$this->wsdl->messages [$operationName . 'Response'] = array ();
$thisMessage = & $this->wsdl->messages [$operationName . 'Response'];
foreach ($messageParts as $partName => $partType)
list ($nsPrefix, $typeName) = $this->_getTypeNs ($partType);
$thisMessage[$partName] = array (
// Populate tree with portType information
// XXX Current implementation only supports one portType that
// encompasses all of the operations available.
// *** <wsdl:portType> ***
if (!isset ($this->wsdl->portTypes [$service_name . 'Port']))
$this->wsdl->portTypes [$service_name . 'Port'] = array ();
$thisPortType = & $this->wsdl->portTypes [$service_name . 'Port'];
foreach ($object->__dispatch_map as $operationName => $messages)
$thisPortType[$operationName] = array ('name' => $operationName);
foreach ($messages as $messageType => $messageParts)
$thisPortType[$operationName]['input'] = array (
'message' => $operationName . 'Request',
$thisPortType[$operationName]['output'] = array (
'message' => $operationName . 'Response',
/** Take all the abstract WSDL data and build concrete bindings and services (destructive)
* XXX Current implementation discards $service_desc.
* @param $schemaNamespace Namespace for types etc.
* @param $service_name Name of the WSDL <service>
* @param $service_desc Optional description of the WSDL <service>
function _generateBindingsAndServices ($schemaNamespace, $service_name, $service_desc = '')
// Populate tree with bindings information
// XXX Current implementation only supports one binding that
// matches the single portType and all of its operations.
// XXX Is this the correct use of $schemaNamespace here?
// *** <wsdl:binding> ***
$this->wsdl->bindings [$service_name . 'Binding'] = array (
'type' => $service_name . 'Port',
'operations' => array ());
$thisBinding = & $this->wsdl->bindings [$service_name . 'Binding'];
foreach ($this->wsdl->portTypes [$service_name . 'Port'] as $operationName => $operationData)
$thisBinding['operations'][$operationName] = array (
'soapAction' => $schemaNamespace . '#' . $operationName,
'style' => $thisBinding['style']);
foreach (array ('input', 'output') as $messageType)
if (isset ($operationData[$messageType]))
$thisBinding['operations'][$operationName][$messageType] = array (
'namespace' => $schemaNamespace,
// Populate tree with service information
// XXX Current implementation supports one service which groups
// all of the ports together, one port per binding
// *** <wsdl:service> ***
$this->wsdl->services [$service_name . 'Service'] = array ('ports' => array ());
$thisService = & $this->wsdl->services [$service_name . 'Service']['ports'];
foreach ($this->wsdl->bindings as $bindingName => $bindingData)
$thisService[$bindingData['type']] = array (
'name' => $bindingData['type'],
'binding' => $bindingName,
'address' => array ('location' =>
'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
(isset ($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
$this->wsdl->set_service ($service_name . 'Service');
// Create WSDL definition
// *** <wsdl:definitions> ***
$this->wsdl->definition = array (
foreach ($this->wsdl->namespaces as $nsPrefix => $namespace)
$this->wsdl->definition ['xmlns:' . $nsPrefix] = $namespace;
// This function is adapted from Dmitri V's implementation of
// DISCO/WSDL generation. It separates namespace from type name in a
// __typedef key and creates a new namespace entry in the WSDL structure
// if the namespace has not been used before. The namespace prefix and
// type name are returned. If no namespace is specified, xsd is assumed.
// We will not need this function anymore once __typedef is eliminated.
function _getTypeNs ($type) {
if (isset ($m[1 ][0 ]) && $m[1 ][0 ] != '') {
$ns_pref = 'ns' . count($this->wsdl->namespaces );
$this->wsdl->ns [$m[1 ][0 ]] = $ns_pref;
$this->wsdl->namespaces [$ns_pref] = $m[1 ][0 ];
$typens = $this->wsdl->ns [$m[1 ][0 ]];
return array ($typens,$type);
Documentation generated on Mon, 11 Mar 2019 13:59:48 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|