The ckWebServicePlugin allows you to build a webservice api for your symfony applications. It comes with a powerful and easy to use wsdl generator, which creates WS-I compliant wsdl files for a maximum of interopability with PHP, .NET and Java clients.

Developers

License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Release 1.4.1 - 19/07/2008

Release 1.4.0 - 17/06/2008

Release 1.2.0 - 10/04/2008

Release 1.1.3 - 29/03/2008

Release 1.0.0 - 12/03/2008

ckWebServicePlugin (for symfony 1.3)

The ckWebServicePlugin allows you to build a webservice api for your symfony applications.
It comes with a powerful and easy to use wsdl generator, which creates WS-I compliant wsdl files for a maximum of interopability with PHP, .NET and Java clients.

Requirements

PHP >= 5.2.4

php_soap extension

Installation

To install the latest release, execute:

> symfony plugin:install ckWebServicePlugin

To install the latest release from SVN, checkout the release tag into a plugins/ckWebServicePlugin folder:

The HEAD revision of the development branch is not guaranteed to be stable all the time!

Now configure the plugin how it is described in the next section and clear your cache afterwards.

Configuration

The configuration can be devided into two parts. A basic one, which is mandatory and has to be done in order to get the plugin working.
The second, advanced, part is only required under certain circumstances and if you want to leverage the full power of the plugin.
So if you are using this plugin the first time you can skip the Advanced section.

Basic

app.yml

Configure general plugin settings in your application's app.yml file.

all:
# because by default every filter condition is true, we have to set this var
# to off in all other environments
enable_soap_parameter: off
# your environment for webservice mode
soap:
# enable the `ckSoapParameterFilter`
enable_soap_parameter: on
ck_web_service_plugin:
# the location of your wsdl file
wsdl: %SF_WEB_DIR%/myWebService.wsdl
# the class that will be registered as handler for webservice requests
handler: ckSoapHandler

You will propably have to change the wsdl and handler option after you have run the webservice:generate-wsdl task.

For more details about the usage of SOAP Headers read the section Using SOAP Headers.

module.yml

Every action, which should be callable in webservice mode, needs some configuration so the parameters are accessable through sfRequest::getParameter() and the proper value is returned as result.
This configuration is automaticly done by the webservice:generate-wsdl task, if you don't use the task or want to customize something you have to change the module.yml file corresponding to the action.

a @WSMethod annotation was added to mark the action as available in the MathApi webservice,

for every parameter accessible through sfRequest::getParameter() a @param doc tag with type, name and description was added,

a @return doc tag with type and description was added,

the @param doc tag for $request was removed, because it is not a real parameter required by the action multiply.

Now we are ready to execute the webservice:generate-wsdl task. It is explained in detail in the section Thewebservice:generate-wsdltask in detail.

Execute the task with:

> symfony webservice:generate-wsdl frontend MathApi http://localhost/

Change http://localhost/ to the url you are using for development!

The task will generate a MathApi.wsdl and MathApi.php in your project's web/ folder.
Further the task will generate a MathApiHandler.class.php and a BaseMathApiHandler.class.php in the application's lib/ folder.

We have to change the wsdl option in the application's app.yml file to MathApi.wsdl and the handler option to MathApiHandler:

You see the name of the webservice method follows the scheme <moduleName>_<actionName>, because this might be not descriptive enough or an alternative scheme is desired,
we will see how to change the method name. To do this we have to change again the action's doc comment:

create a new controller in your project's web/ folder with name 'webservice_name'.php,

add a 'webservice_name'Handler.class.php and a Base'webservice_name'Handler.class.php to the 'app_name''s lib/ folder.

The arguments explained in detail:

app_name:

specifies the application, which is searched for actions marked with the @WSMethod annotation

webservice_name:

specifies the name of the webservice

webservice_base_url:

specifies the url under which the webservice will be accessible

The options explained in detail:

environment (short e):

sets the environment for webservice mode

defaults to soap

if you change it, don't forget to change the configuration files accordingly

enabledebug (short d):

enables the debug mode in the generated controller

defaults to false

Understanding result adapters

Until now it wasn't explained how the result of an action is got, we have just seen, that the result was assigned to the $this->result property and a sfView constant was returned, like sfView::SUCCESS.

Because an action should be reusable in web and webservice mode, we can't rely on the return value, because in web mode it always has to be a template name.
For this reason the result adpater pattern was introduced. This means to get the action result an adapter object of a subclass of ckAbstractResultAdapter is used.
Which one is used is determined by the configuration in the action's module.yml file how it is shown in the Configuration->Advanced->module.yml section.
The param array in the module.yml file is passed to the result adapter's constructor and contains adapter specific settings.

There are three built-in adapters:

ckPropertyResultAdapter:

gets the result from a property of the action object

parameters:

property:

specifies the property name

defaults to result

if there is only one property, this one is returned, also its name doesn't match the specified property

ckMethodResultAdapter:

gets the result from a method call on the action object

parameters:

method:

specifies the method name

ckRenderResultAdapter:

executes the standard render pipeline and returns the resulting text

the sf_format is set to soap so template file names have to end with .soap.php, e.g.: indexSuccess.soap.php

if this adapter is used the return value has to be string

parameters:

none

You can easily implement your own adapters by extending the ckAbstractResultAdapter class and overriding the abstract ckAbstractResultAdapter::getResult() method.

Using arrays and objects as parameters or result values

In the previous examples only simple types have been used for parameters and result values, but you propably want to use objects, arrays of simple types or arrays of complex types.
To illustrate these features we will stick to the example used earlier.

As you see, we have added a lightweight definition of the ComplexNumber class called ClientComplexNumber, because it is likely that you don't have the same class definition at client and server, only the names and types of the properties will match.

Do not forget to validate objects you get as a parameter!

Often the objects you want to return or pass in as parameter are not as simple as the shown ComplexNumber, e.g. Doctrine or Propel objects or objects which have a JavaBean-style class, so the properties are only accessible through getter and setter methods.

The plugin also offers a solution for this problem. Therefor it introduces so called property strategies, they have two purposes, the first is to determine which properties a class has when the wsdl is generated, the second purpose is to access those properties
at runtime.

There are already four property strategy implementations:

ckDefaultPropertyStrategy:

provides access to all public properties

each property needs to have a proper doc comment so its type can be determined

it is used if there is no other property strategy specified (so it was already used in the background in the ComplexNumber example)

ckBeanPropertyStrategy:

provides access to JavaBean-like properties with getter and setter methods

each getter method needs to have proper doc comment so the property type can be determined

ckDoctrinePropertyStrategy:

provides access to all properties you defined in your schema.yml for Doctrine objects

ckPropelPropertyStrategy:

provides access to all properties you defined in your schema.yml for Propel objects

You can implement your own property strategy by extending ckAbstractPropertyStrategy.

To apply a property strategy to a class you have to add a @PropertyStrategy annotation to the class with the class name of the property strategy as parameter.

If you you want to use those classes as parameter for your methods, you have to map them in your app.yml to ckGenericObjectAdapter_<classname>.
So the app.yml for the Article and UserBean class shown above would look like:

Collections in Doctrine and Propel objects are represented as arrays in the wsdl, so suppose the Article class has many Comment objects and Comment is also a Doctrine class annotated with @PropertyStrategy('ckDoctrinePropertyStrategy').
The app.yml would be:

Passing Doctrine and Propel object graphs with cyclic references as parameters to your methods is currently not supported!

In this section you have learned how to work with arrays and classes, the next section covers the usage of SOAP Headers.

Using SOAP Headers

SOAP Headers provide a way to send additional information, which are not directly or semantically related to the original method call.
An good example for this are authentication information, so the use of a certain method can be restricted to a group of users.

To demonstrate the support for SOAP Headers, we will stick to the simple multiplication example used previously.

The authentication mechanism used here is not secure unless you use https, it is just used for demonstration purpose and to keep the example simple!

When the application receives a SOAP Header a webservice.handle_header event is dispatched (it is a notifyUntil event), it has two attributes, the first is header holding the name of the header and the second is data containing an instance of the header's data class.
To do the authentication stuff in our example we will define an AuthHeaderListener class by creating an AuthHeaderListener.class.php in the application's lib/ folder with the following content:

When adding or accessing a SOAP Header its name has to end with Element.

This section demonstrated the use of SOAP Headers, so now you have seen nearly all features this plugin has to offer.

Throwing SOAP Faults

The equivalent to exceptions in SOAP are so called SOAP Faults. The plugin supports a simple translation of exceptions to SOAP Faults, but also allows you to throw
your own SOAP Faults.

To demonstrate the feature we will extend the authentication example from the last section.

First to demonstrate what happens if an exception is thrown, we will modify the multiply action to throw an exception if the user is not authenticated.
The modified mathActions class will look like this:

How the exception is translated to a SOAP Fault depends on the value of sf_debug. If debugging is disabled every exception will be
translated to a standard SOAP Fault with the message 'Internal Server Error', otherwise if debugging is enabled the message, the type
and the stack trace of the exception will be send to the client.

Functional Testing

The symfony framework promotes the paradigm of test driven development, so it is just natural that this plugin offers you
possibilities to test your webservices. The following two sections show you how to setup a test environment and how to use
ckTestSoapClient for testing.

Test Environment Setup

The setup of a test environment is similar to the configuration described in the section Configuration, only the environment name
changes from soap to soaptest, though you can use any other name you like.

This is the same as the default functional.php script except the environment parameter of ProjectConfiguration::getApplicationConfiguration() can be changed with the $env variable and defaults to soaptest.

You have to run the webservice:generate-wsdl task always twice, once for the soap environment and once for the soaptest environment, do not forget to set the --environment switch to the proper value.

Using ckTestSoapClient

The ckTestSoapClient class lets you dispatch webservice requests to your symfony application without the need of a webserver.
Additionally it offers several evaluation methods for the result of each request.

A good starting point for every test script is the following template:

The headers are cleared after each request, so do not forget to add them again if you need them more then once.

Similar to the evaluation methods for the result, there are three methods to evaluate the response headers. These are isHeader(), isHeaderType() and isHeaderCount().
The parameter list is the same, but the child element selector has to contain at least the header name, e.g.:

The ckTestSoapClient has also methods to check the result for SoapFaults. One method is isFaultEmpty() it is usefull to check that the response contains no SoapFaults, e.g.:

$c->myMethod()
->isFaultEmpty()
->is('', 1);

Another method is hasFault() it checks if a SoapFault with the given message exists.

$c->myMethod()
->hasFault('Internal Server Error');

The method is at all quite similiar to the throwsException() method of sfTestBrowser.

So finally this section has shown you how to write functional tests for your webservices by using the ckTestSoapClient class.

Reference

Supported simple types

All primitive PHP types are supported:

string maps to xsd:string

integer or int maps to xsd:int

float or double maps to xsd:double

boolean or bool maps to xsd:boolean

Tips'n Tricks

Disable wsdl caching during development

If you often regenerate your wsdl file during development, you propably want to disable caching of this file, so changes become usable immediatly.

You can do this by modifying your php.ini:

soap.wsdl_cache_enabled=0

Checking for webservice mode

If you want to check in an action if it is executed in webservice mode, you can use the isSoapRequest() method, e.g.:

<?phpclass FooActions extends sfActions
{/**
* Some description...
*
* @WSMethod(webservice='MyApi')
*/publicfunction executeBar($request){if($this->isSoapRequest()){// do this only in webservice mode...}// do this always...}}

Create multiple webservices for one application

In earlier releases there could only exist one webservice per symfony application.
This has changed with the introduction of the @WSMethod annotation, you can now specify
to which webservice an action belongs with the webservice parameter of the annotation.
But it is important to notice that there has to be one environment per webservice!

Adding an action to multiple webservices

It is possible to add an action to more then one webservice, because the webservice parameter of the
@WSMethod annotation accepts also an array of values. The action shown in the following example will be
available in the webservices MyWebserviceA and MyWebserviceB: