PHP5's namespaces lead to an incorrect WSDL type name while implementing a webservice

php, web-services, wsdl, zend-framework

Solution

The XSD object type `\Library\IncrementedInt` is not a PHP class within a PHP namespace like you outline:

<?php
namespace Library;

class IncrementedInt
{
    ...

The prefixed type actualy is:

tns:\Library\IncrementedInt

And mind the `tns:` in front which is the prefix. You could not express is in PHP which becomes even more clear when you expand it to it's namespace URI:

{http://localhost/webservice.php}\Library\IncrementedInt

Also Zend SOAP comes from a time before PHP Namespaces (PHP 5.2), therefore it needs to take a different strategy to alias this type to a PHP type (classname). It takes the local-name of the type:

\Library\IncrementedInt

And replaces each "invalid" character in that type-name with an underscore:

_Library_IncrementedInt

You therefore need to name the class that way:

<?php

class _Library_IncrementedInt
{
    ...

If you want to have it working out of the box.

You then asked via comments for clarification:

So what you are saying is that, there's nothing I can do to have a namespaced class as a type name, right?

Well, as far as for the default behavior most likely not, however as the term Strategy is involved, I'd say there is some mechanism to extend Zend Soap for that operation and inject a different one which for example does allow namespaced classes (classes by their FQCN), see the Strategy Pattern.

And if this is only a single class you can do some cheap trick in the meanwhile and extend your namespaced class as the short name or alias it with `class_alias`.

Problem

I'm trying to implement a SOAP server using `Zend_Soap_Server` class in PHP. Here's the `webservice.php` file which is the entry point of request: ``` <?php require_once 'library.php'; require_once 'Zend/Loader/Autoloader.php'; $autoloader = \Zend_Loader_Autoloader::getInstance(); class Math { /** * This method takes ... * * @param integer $inputParam * @return \Library\IncrementedInt */ public function increment($inputParam) { return new \Library\IncrementedInt($inputParam); } } $options = array('uri' => 'http://localhost' . $_SERVER['REQUEST_URI']); if (isset($_GET['wsdl'])){ $server = new Zend_Soap_AutoDiscover(); $server->setClass('Math'); } else { $server = new Zend_Soap_Server(null, $options); $server->setClass('Math'); $server->setObject(new Math()); } $server->handle(); ``` And I've got `library.php` file like this: ``` <?php namespace Library; class IncrementedInt { public $original; public $incremented; public function __construct($num) { $this->original = $num; $this->incremented = ++$num; } } ``` A call to `http://localhost/webservice.php?wsdl` will output: ``` <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://localhost/webservice.php" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="Math" targetNamespace="http://localhost/webservice.php"> <script/> <types> <xsd:schema targetNamespace="http://localhost/webservice.php"> <xsd:complexType name="\Library\IncrementedInt"> <xsd:all/> </xsd:complexType> </xsd:schema> </types> <portType name="MathPort"> <operation name="increment"> <documentation>This method takes ...</documentation> <input message="tns:incrementIn"/> <output message="tns:incrementOut"/> </operation> </portType> <binding name="MathBinding" type="tns:MathPort"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="increment"> <soap:operation soapAction="http://localhost/webservice.php#increment"/> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/webservice.php"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/webservice.php"/> </output> </operation> </binding> <service name="MathService"> <port name="MathPort" binding="tns:MathBinding"> <soap:address location="http://localhost/webservice.php"/> </port> </service> <message name="incrementIn"> <part name="inputParam" type="xsd:int"/> </message> <message name="incrementOut"> <part name="return" type="tns:\Library\IncrementedInt"/> </message> </definitions> ``` Now to test the functionality I use `soapUI 4.5.1` which is a Java application that implements a SOAP Client. Giving it the URI `http://localhost/webservice.php?wsdl` should lead to function `increment` extracted but it won't. Instead it prompts an error: `The Value '\Library\IncrementInt' is an invalid name`. It seems to me that it's having a problem accepting `\` as a part of a type name. On the other hand PHP can not do without them. To make sure that everything else is OK, I tested the exact same files without the namespace and it works smoothly. Has anyone faced a similar problem and more importantly, does anyone know how to overcome this problem? [UPDATE] I managed to test the same scenario with ZF2 and it works. Perhaps I have to give up on ZF1!

Original source