phpxmlsoapwsdl

PHP Array/Object Structure for SOAP (WSDL/wsse)


I have to understand how a structure for this sample WSDL could be generated with PHP. (SoapClient, SoapHeaders)

Lets say the actual output should look like this:

<soapenv:Envelope xmlns="http://schemas.xmlsoap.org/1">
    <soapenv:Header>
        <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/3">
         <wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/4">
          <wsse:Username>name</wsse:Username>
          <wsse:Password Type="wsse:PasswordText">good_password</wsse:Password>
         <wsse:UsernameToken>
        </wsse:Security>    
    </soapenv:Header>
    
    <soapenv:Body>
      <asi:ProcessMsg>
        <req:Payload>
          <req:Request>
            <req:Sub>DATA_1</req:Sub>
            <req:EID>DATA_2</req:EID>
            <req:IID>DATA_3</req:IID>
            <req:Customer FirstName="xxx" LastName="xxx"></req:Customer>
            <req:Lead LeadMaster="xxx" IntendedRepPer="xxx"></req:Lead>
          </req:Request>
        </req:Payload>
      </asi:ProcessMsg>
    </soapenv:Body>
    
   </soapenv:Envelope>

I've tried three different structures which none of them worked

Structure for "new SoapHeader();"

 $Security = new \ArrayObject();
 $Security['UsernameToken'] = new \ArrayObject();
 $Security['UsernameToken']['Username'] = "name";
 $Security['UsernameToken']['Password'] = "good_password";

 // OR

 $Security = new \stdClass();
 $Security->UsernameToken = new \stdClass();
 $Security->UsernameToken->Username = "name";
 $Security->UsernameToken->Password = "good_password";


$header = new SoapHeader('http://schemas.xmlsoap.org/ws/2002/07/secext','Security',$Security,false);
$soapClient->__setSoapHeaders($header);

Structure for "$soap_client->ServerMethod("Payload", $soap_request);":

$soap_request = new \ArrayObject();
$soap_request['Payload'] = new \ArrayObject();
$soap_request['Payload']['Request'] = new \ArrayObject();
$soap_request['Payload']['Request']['Sub'] = "xxx";
$soap_request['Payload']['Request']['EID'] = "xxx";
$soap_request['Payload']['Request']['IID'] = "xxx";
$soap_request['Payload']['Request']['Customer'] = new \ArrayObject();
$soap_request['Payload']['Request']['Customer']['_'] = '';

$soap_request['Payload']['Request']['Lead'] = new \ArrayObject();
$soap_request['Payload']['Request']['Lead']['_'] = '';


foreach ($data['x'] as $key => $value)
{
    $soap_request['Payload']['Request']['Customer'][$key] = $value;
}
foreach ($data['xx'] as $key => $value)
{
    $soap_request['Payload']['Request']['Lead'][$key] = $value;
}

// OR

$soap_request = new \stdClass();
$soap_request->Request = new \stdClass();
$soap_request->Request->Sub = "xxx";
$soap_request->Request->EID = "xxx";
$soap_request->Request->IID = "xxx";
$soap_request->Request->Customer = new \ArrayObject();
$soap_request->Request->Customer['_'] = '';
$soap_request->Request->Lead = new \ArrayObject();
$soap_request->Request->Lead['_'] = '';
foreach ($data['customer'] as $key => $value)
{
    $soap_request->Request->Customer[$key] = $value;
}
foreach ($data['lead'] as $key => $value)
{
    $soap_request->Request->Lead[$key] = $value;
}

things I tried to debug structures:

echo "FNs:\n" . $soapClient->__getFunctions() . "\n";
echo "REQUEST:\n" . $soapClient->__getLastRequest() . "\n";
echo "REQUEST (htmlent):\n" . htmlentities($soapClient->__getLastRequest()) . "\n";

Error messages I'm stuck with:

The actual send execution:

$soap_response = $soapClient->ServerMethod($soap_request);

Well, I'm really stuck at the moment and do not know where to even search for errors/mistakes because I do not have any "good" verbose errors to debug.

It's the first time I have to use SOAP (sending data to a service) and maybe I got it completely wrong. Help is very much appreciated.

The core question:

how does the structure inside php look like with all the namespaces, attributes, nested Elements?


Solution

  • The problem was that the built in PHP function SoapHeader does not provide a WSSE-compliant Soap-Header. Further down you can see the implementation by extending the PHP SoapHeader class.

    I used a stdClass object for the payload. And the actual data fields by passing all attributes to the deepest object layer.

    Security Soap Header:

    namespace AppName\TheBundle\Services;
    
    use SoapHeader;
    use SoapVar;
    
    class AuthSoapHeaderHelper extends SoapHeader
    {
        private $wss_ns = 'http://schemas.xmlsoap.org/.../secext';
        private $username = "username"; 
        private $password = "good_password"; 
    
        function __construct()
        {
    
            $auth = new \stdClass();
            $auth->Username = new SoapVar($this->username, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
            $auth->Password = new SoapVar($this->password, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
    
            $username_token = new \stdClass();
            $username_token->UsernameToken = new SoapVar($auth, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns);
    
            $security_sv = new SoapVar(
                new SoapVar($username_token, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns),
                SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'Security', $this->wss_ns);
            parent::__construct($this->wss_ns, 'Security', $security_sv, true);
        }
    }
    

    SOAP Payload:

    $soap_request = new \stdClass();
    $soap_request->Payload = new \stdClass();
    $soap_request->Payload->Request = new \stdClass();
    $soap_request->Payload->Request->Sub = "DATA_1";
    $soap_request->Payload->Request->EID = "DATA_2";
    $soap_request->Payload->Request->IID = "DATA_3";
    $soap_request->Payload->Request->Customer = new \stdClass();
    $soap_request->Payload->Request->Lead = new \stdClass();
    
    foreach ($data['x'] as $key => $value)
    {
        $soap_request->Payload->Request->Customer->{$key} = $value;
    }
    foreach ($data['xx'] as $key => $value)
    {
        $soap_request->Payload->Request->Lead->{$key} = $value;
    }
    

    which resulted in following wsdl/xml structure:

    <soapenv:Envelope xmlns="http://schemas.xmlsoap.org/1">
        <soapenv:Header>
            <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/3">
             <wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/4">
              <wsse:Username>username</wsse:Username>
              <wsse:Password Type="wsse:PasswordText">good_password</wsse:Password>
             <wsse:UsernameToken>
            </wsse:Security>    
        </soapenv:Header>
    
        <soapenv:Body>
          <asi:ProcessMsg>
            <req:Payload>
              <req:Request>
                <req:Sub>DATA_1</req:Sub>
                <req:EID>DATA_2</req:EID>
                <req:IID>DATA_3</req:IID>
                <req:Customer FirstName="xxx" LastName="xxx"></req:Customer>
                <req:Lead LeadMaster="xxx" IntendedRepPer="xxx"></req:Lead>
              </req:Request>
            </req:Payload>
          </asi:ProcessMsg>
        </soapenv:Body>
    
       </soapenv:Envelope>