Quick links: Content - sections - sub sections
EN FR
Jelix 1.9.0-dev

Section: SOAP

« XML-RPC ^ Web services
Switch to language: FR

Jelix supports the SOAP protocol, which is a standard protocol for web services specified by W3C. Jelix use the PHP SOAP API, so you have to configure your web server to include this extension (verify with phpinfo() for example).

The advantage to use Jelix to do SOAP is that you don't have to learn details of the protocol, nor to manipulate XML content of soap message, and it is well integrated into jelix: soap parameters are interpreted and available in controllers through the classical param() method, and to return data for the response, you do like for all other response type.

Jelix also generates automatically WSDL content.

However, you have to install and configure the jsoap module, and have to follow a specific syntax in comments in your soap controllers.

Install and Activate the jsoap module

As you probably saw in the entry point, some classes are used from a "jsoap" module. You should install it and then activate it.

So add in your composer.json the dependency jelix/soap-server-module:


composer require "jelix/soap-server-module"

Then configure the module and launch the installer:


  php dev.php module:configure jsoap
  php install/installer.php

(see the chapter about installing a module)

This module includes also all what you need to generate WSDL content. (see below)

Since a SOAP http request is not as usual, you cannot use the "classic" request object. So the module creates a specific entry point in the www/ directory, soap.php. This entry point is using jSOAPRequest instead of jClassicRequest, and a specific coordinator: jSoapCoordinator. The type of the request and of the entry point is then "soap".

Controller

Since it is a specific type of request, a controller filename must be suffixed by ".soap.php". For example, for a "default" controller: default.soap.php. (it can co-exists with a “default” classic controller such as “default.classic.php”).

Method of a soap controller

The content of a controller is similar of a classical controller, with few differences. You will retrieve a jResponseSoap object for the response, which have the alias: "soap".


class defaultCtrl extends jController {

    /** 
     * Test with a simple parameter
     * @exernalparam string $name
     * @return string
     */
    function hello() {
        $resp = $this->getResponse('soap');
        $resp->data = "Hello ".$this->param('name');
        return $resp;
    }
}

Each action of a controller will be in fact a "soap method".

Declaring the type of parameters and the return value

A "soap method" have parameters and should receive a value. You should indicate their type so Jelix could generate correctly SOAP and WSDL messages.

To do it, just add "doc comments" (like for phpdoc), and indicates the type of parameters and of the return value, by using some "@exernalparam" tags and a "@return" tag. Ex:


    @exernalparam string $myparameter

Here it indicates that the soap parameter "$myparameter" is a string. Other possible types are "integer", "int", "boolean", "float".

If you want to indicate an array, add the type name followed by []:


    @exernalparam string[] $array_of_string

If it is an associative array, use [=>]:


    @exernalparam string[=>] $array_of_string

Complex types

If you want to use complex type, like your own objects for parameters or return values. The classes of this objects should be include in the file of the controller (with an include).

Here an exemple of an object use for a parameter MyTestStruct:


/**
 * Struct used for tests
 */
class MyTestStruct{
    /**
     * @var string
     */
    public $name = 'Dupont';

    /**
     * @var string
     */
    public $firstName = 'Bertrand';

    /**
     * @var string
     */
    public $city = 'Paris';
}

Note the use of the required "@var" keyword to indicate the type of each properties.

Then in your controller, don't forget to indicate MyTestStruct for parameters or returned values:


    /** 
     * for this method, we receive a MyTestStruct and return a MyTestStruct object
     * @exernalparam MyTestStruct $input
     * @return MyTestStruct
     */
    function receiveObject() {
        $resp = $this->getResponse('soap');
        $input = $this->param('input');
        $input->name = 'Name updated';
        $resp->data = $input;
        return $resp;
    }

Of course, MyTestStruct can have some properties with complex type:


/**
 * An other struct used for test, this one have an other object as member property
 */
class MyTestStructBis {

    /**
     * @var MyTestStruct
     */
    public $test;

    /**
     * @var string
     */
    public $msg = 'hello';

    function __construct(){
        $this->test = new MyTestStruct();
    }
}

Using the WSDL service

When you use the SOAP protocol, you should provide some WSDL files, which allows SOAP clients to know what are available SOAP methods, and how to validate parameters and return values.

The jsoap module provides features to generate WSDL. It contains a controller named "WSDL" with a wsdl() method. So, just indicate the url of this action to your soap client:


   http://mysite.com/index.php/jsoap/WSDL/wsdl?service=aModule~aController

Note that you should give a "service" parameter indicating the controller which contains the web services. You can have more than one soap controller, but there is no way to return automatically a WSDL file for all soap web services implemented in your application.

Note also that the URL is note really beautiful. You can configure the url mapping to have :


   http://mysite.com/index/jsoap/aModule/aController

You can display a HTML version of the list of SOAP services, by calling the index() method of the WSDL controller:


  http://mysite.com/index.php/jsoap/WSDL?service=aModule~aController

Calling a SOAP service with a client

The application which calls your SOAP web services can be a PHP client, a javascript client etc.. Here is an example of a call with a PHP script.


        ini_set('soap.wsdl_cache_enabled', 0);

        // load the WSDL content
        try {
            $wsdlURI = "http://monsite.com/index.php/jsoap/WSDL/wsdl?service=myapp~default";
            $client = new SoapClient($wsdlURI, array('trace' => 1, 'soap_version'  => SOAP_1_1));
        } catch (SoapFault $fault) {
            throw new Exception($fault->getMessage());
        }

        try {
            $result =  $client->__soapCall('hello', array('Sylvain'));
            //...

            $result =  $client->__soapCall('returnObjects', array());
            //...

            $result =  $client->__soapCall('returnObjectBis', array());
            // ...

        } catch (SoapFault $fault) {
            print_r($fault);
            throw new Exception($fault->getMessage());
        }

Calling a SOAP service with Jelix

If your jelix application needs to call soap servers, you can use jSoapClient. It is a class to retrieve a native SoapClient object, automatically configured with some parameters stored in a var/config/profiles.ini.php, like for jDb.

To use it, add sections in var/config/profiles.ini.php. Sections are profiles for connections. options in [jsoapclient] are aliases for profiles. Profiles are stored in section named [jsoapclient:myname] where myname is a name you choose freely. default name indicate the default profile. A profile should contain at least a wsdl option, containing the URL to a WSDL file. You can add other options, corresponding to parameters for the native class SoapClient of PHP.


[jsoapclient]

[jsoapclient:default]
wsdl= "http://example.com/books.wsdl"
soap_version = SOAP_1_1

And then, in your code:


        $client = jSoapClient::get(); // give a profile name if different from 'default'
        $result = $client->hello('Sylvain');
        // or
        $result =  $client->__soapCall('hello', array('Sylvain'));

You can indicate the option classmap in the profile, to map some classes on soap types. You have to use the json format to indicate the mapping.


[jsoapclient:default]
wsdl= "http://example.com/books.wsdl"
soap_version = SOAP_1_1
classmap = '{"soaptype":"myclass", "soaptype2":"myclass2"}'

The classes you indicate should be loaded before calling jSoapClient::get().

You can also declare this mapping into a separate ini file. You should indicate this file into a classmap_file property in the profile. This file should be into yourapp/app/system/.


[jsoapclient:default]
wsdl= "http://example.com/books.wsdl"
soap_version = SOAP_1_1
classmap_file = soap_classmap.ini.php

In the ini file, you should have section corresponding with declared profiles, with the same name (without the "jsoapclient:" prefix). In these sections, you have list of "key=value". Keys are soap types, values are class names.


[default]
soaptype=myclass
soaptype2=myclass2

You can also declare a __common__ section, containing mapping applicable for all jsoapclient profiles.

So, the classmap of a profile, contains the classmap of __common__, the classmap of the section corresponding to the profile, and the classmap declared into the profile in the "classmap" property.

Other configuration options:

  1. trace=on: to activate debugging. In a log file of type "soap", you'll have all SOAP requests and responses. You should configure the logger correctly. Example:

[logger]
soap=file

[fileLogger]
soap=soap.log

Since Jelix 1.6.16, you can also use the soapfile logger instead of file. This logger stores xml content of requests and responses in separate files to ease debugging.

  1. exceptions: it's a boolean indicating if SOAP error should generate SoapFault exceptions.
  2. connection_timeout: time in secondes to wait after a response, before generating an error
  3. ssl_self_signed (since 1.6.15) : if "on", there will not error when the SSL certificat of the SOAP server cannot be verified. You should not activate it on distant SOAP servers.