Question

I am new to WebServices in General, and so far I have developed a Web Service SoapServer using Zend/Soap. I have been able to use it just fine, the problem is, that i want the client to be able to send the data as and array .

So far this is what i have done:

soap_server.php

<?php
/*
 * url_helper used to check the client Access Ip adrress and Port, in case is from dev or prod enviorement. etc.
 * returns the portion of the URL dynamicly in case its accesded from a public or local location.
 * 
 */
function url_helper(){
    $s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
    $sp = strtolower($_SERVER["SERVER_PROTOCOL"]);
    $protocol = substr($sp, 0, strpos($sp, "/")) . $s;
    $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
    return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port;
}
if(($_SERVER['PHP_AUTH_USER'] == "test") AND ($_SERVER['PHP_AUTH_PW'] == "secret")){
    //autoload from composer, loads all the required files for ZendSoap
    include("vendor/autoload.php");

    $serviceURL = url_helper().'/soap_server.php';

    //The class for the WebService
    class soap_server{
         /**
         *
         * @param string $str1
         * @param string $str2
         * @param string $str3
         * @return stdClass
         */
        public function TEST($str1,$str2,$str3) {
            // do some work here he

            $response = new stdClass();
            $response->message = "vars = ($str1,$str2,$str3)";
            $response->success = true;

            return $response;
        }
    }

    // Generate WSDL relevant to code
    if (isset($_GET['wsdl'])){
        $autodiscover = new Zend\Soap\AutoDiscover();
        $autodiscover->setClass('soap_server')
                     ->setUri($serviceURL)
                     ->setServiceName('soap_server');
        $autodiscover->generate();
        $autodiscover->handle();

      //Soap Server
    } else {
        $server = new Zend\Soap\Server(null,array('uri' => $serviceURL.'?wsdl'));
        $server->setClass('soap_server');
        $server->handle();
    }
}
else
{
        //Send headers to cause a browser to request
        //username and password from user
        header("WWW-Authenticate: " .
            "Basic realm=\"Protected Area\"");
        header("HTTP/1.0 401 Unauthorized");

        //Show failure text, which browsers usually
        //show only after several failed attempts
        print("This page is protected by HTTP " .
            "Authentication.<br>\nUse <b>User</b> " .
            "for the username, and <b>PW</b> " .
            "for the password.<br>\n");
}

And the test client works fine when i send at as String, as intended and defined in the soapServer:

test_client.php

include("vendor/autoload.php");
$client = new Zend\Soap\Client("http://127.0.0.1/soap_server.php?wsdl",array('login'=>'test','password'=>'secret'));
$result1 = $client->TEST('Data1','OtherString','test');
print_r($result1);

All i need now if find a way so that the client can send me data in and Array like:

$data = array('str1'=>'Data1','str2'=>'OtherString','str3'=>'test');

But i don't know how to set this up with Zend Framework Autodiscovery and pair it with a working client. Have tried using type array instead of String, but with no success.

Thanks a lot for any help.

Sincerely, Daniel

EDIT

So I have done further testing, and i do get to set up and Array or and stdClass in the docblock and it works in the following way:

The sample Zend Framework Soap Server:

$serviceURL = url_helper().'/test_ws.php';


    //The class for the WebService
    class test_ws{
        /**
        *
        * @param string $str1
        * @param array $myArray
        * @param stdClass $myObject test
        * @return stdClass
        */

        public function TEST2($str1,$myArray,$myObject) {              
            // do some work here    
            $response = new stdClass();

            $response->string = $str1;
            $response->array = var_export($myArray,TRUE);            
            $response->stdClass = var_export($myObject,TRUE);
            $response->object1 = $myObject->obj1;
            $response->success = true;

            return $response;

        }

    }

    // Generate WSDL relevant to code
    if (isset($_GET['wsdl'])){
        $autodiscover = new Zend\Soap\AutoDiscover();
        $autodiscover->setClass('test_ws')
                     ->setUri($serviceURL)
                     ->setServiceName('test_ws');
        $autodiscover->generate();
        $autodiscover->handle();

      //Soap Server
    } else {
        $server = new Zend\Soap\Server(null,array('uri' => $serviceURL.'?wsdl'));
        $server->setClass('test_ws');
        $server->handle();
    }
}

The Soap Call:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://10.1.11.122/ws_kioskos/test_ws.php">
   <soapenv:Header/>
   <soapenv:Body>
      <test:TEST2 soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <str1 xsi:type="xsd:string">String</str1>
         <myArray xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
            <!--You may enter ANY elements at this point-->
            <elem1 xsi:type="xsd:string">pos1</elem1>
            <elem2 xsi:type="xsd:string">pos2</elem2>
         </myArray>
         <myObject xsi:type="test:stdClass">
            <obj1 xsi:type="xsd:string">HELO</obj1>
            <obj2 xsi:type="xsd:string">WORLD</obj2>
         </myObject>         
      </test:TEST2>
   </soapenv:Body>
</soapenv:Envelope>

And the Soap Response:

<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://10.1.11.122/ws_kioskos/test_ws.php?wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
   <SOAP-ENV:Body>
      <ns1:TEST2Response>
         <return xsi:type="SOAP-ENC:Struct">
            <string xsi:type="xsd:string">String</string>
            <array xsi:type="xsd:string">array (0 => 'pos1', 1 => 'pos2',)</array>
            <stdClass xsi:type="xsd:string">stdClass::__set_state(array('obj1' => 'HELO', 'obj2' => 'WORLD',))</stdClass>
            <object1 xsi:type="xsd:string">HELO</object1>
            <success xsi:type="xsd:boolean">true</success>
         </return>
      </ns1:TEST2Response>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

So with The stdClass I´m close to what i want to implement, as you can see with the: <object1 xsi:type="xsd:string">HELO</object1> the only think missing is If there is a way to tell the Autodiscover Method what are the elements that are inside the stdClass, so that the client know how to interact with the WSDL, or is there another approach that would let me do such think.

I have read some about using Complex Data types, and using a ClassMap to define the WebService, but i could not make anything work with it, as i could not find good documentation for such implementation.

Once again, Thanks a lot for any help.

Daniel

Was it helpful?

Solution

Working example

After some research, here is the solution that would be useful to anyone who builds SOAP server with zend-soap in Laravel.

Acme\Controllers\SoapController class:

...

public function wsdl()
{
    $wsdl = new AutoDiscover(new ArrayOfTypeComplex());
    $this->populateServer($wsdl);

    return response($wsdl->toXml(), 200)
        ->header('Content-Type', 'application/wsdl+xml');
}

private function populateServer($server)
{
    $server->setClass(MyService::class);
    $server->setUri('http://host.com/soap/server');
}

public function server(Request $request)
{
    $server = new Zend\Soap\ServerServer();
    $this->populateServer($server);

    $response = $server->handle();

    return response($response, 200)->header('Content-Type', 'application/soap+xml');
}

...

Acme\Services\MyService class:

namespace Acme\Services;

class MyService
{

    /**
     * Does something.
     * @param Acme\Types\ItemType[] $items
     * @return \StdClass
     */
    public function doSomething(array $items)
    {
        // ...
    }
}

Acme\Types\ItemType class:

namespace Acme\Types;

class ItemType
{

    /**
     * @var string
     */
    public $propertyName;
}

Please note two things:

  1. Use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex strategy; and
  2. Provide fully-qualified class name in a docblock.

Hope that helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top