phpro / soap-client

A general purpose SOAP client for PHP
MIT License
859 stars 175 forks source link

PHP Notice: Undefined offset: 0 in /client/vendor/phpro/soap-client/src/Phpro/SoapClient/Middleware/WsaMiddleware.php on line 31 #145

Closed orette closed 6 years ago

orette commented 6 years ago

I'm trying to use this client on a WCF service that has WS-Addressing and WS-ReliableMessaging enabled. The plan is to use WsaMiddleware and to manually handle WS-ReliableMessaging as i have not seen a middleware or client that handles it.

I have however ran into this error PHP Notice: Undefined offset: 0 in /client/vendor/phpro/soap-client/src/Phpro/SoapClient/Middleware/WsaMiddleware.php on line 31 and would just like to be sure that it's not as a result of misconfigurartion. After going through the code, it seems the middleware is expecting the header SOAPAction to be an array with at least one value, but it is actually empty. Line 31:$wsa->addAction($request->getHeader('SOAPAction')[0]);. Is this a bug or did i miss some part of the configuration?

I also have two other questions:

  1. What is the best way to set the SOAPAction manually?
  2. Is there a better way to handle WS-ReliableMessaging?

My Config:

use Phpro\SoapClient\CodeGenerator\Config\Config;
use Phpro\SoapClient\CodeGenerator\Rules;
use Phpro\SoapClient\CodeGenerator\Assembler;

return Config::create()
    ->setWsdl('http://10.11.12.13/CrazyBearService.svc?wsdl')
    ->setTypeDestination('src/CrazyBear/ServiceTypes')
    ->setTypeNamespace('CrazyBear\ServiceTypes')
    ->setClientDestination('src/CrazyBear/ServiceClient')
    ->setClientNamespace('CrazyBear\ServiceClient')
    ->setClientName('CrazyBearClient')
//    ->addSoapOption('features', SOAP_SINGLE_ELEMENT_ARRAYS)
    ->addSoapOption('soap_version', SOAP_1_2)
    ->addRule(new Rules\AssembleRule(new Assembler\GetterAssembler(
        (new Assembler\GetterAssemblerOptions())
            ->withReturnType(true)
            ->withBoolGetters(true)
    )))
    ->addRule(new Rules\AssembleRule(new Assembler\ConstructorAssembler()))
    ->addRule(new Rules\AssembleRule(new Assembler\FluentSetterAssembler(
        (new  Assembler\FluentSetterAssemblerOptions())
            ->withReturnType(true)
    )))
    ->addRule(new Rules\TypenameMatchesRule(
        new Rules\AssembleRule(new Assembler\RequestAssembler()),
        '/Action$/'
    ))
    ->addRule(new Rules\TypenameMatchesRule(
        new Rules\AssembleRule(new Assembler\ResultAssembler()),
        '/Response$/'
    ))
    ->setClassMapName('CrazyBearClassMap')
    ->setClassMapNamespace('CrazyBear\ClassMap')
    ->setClassMapDestination('src/CrazyBear/ClassMap')

My Client:

require __DIR__.'/vendor/autoload.php';

use CrazyBear\ServiceClient\CrazyBearClient;
use CrazyBear\ServiceTypes\LoginAction;
use Phpro\SoapClient\ClientFactory;
use Phpro\SoapClient\ClientBuilder;
use Phpro\SoapClient\Soap\Handler\HttPlugHandle;
use Phpro\SoapClient\Middleware\WsaMiddleware;
use Phpro\SoapClient\Soap\TypeConverter\DateTimeTypeConverter;
use Phpro\SoapClient\Soap\ClassMap\ClassMap;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Discovery\StreamFactoryDiscovery;
use Http\Adapter\Guzzle6\Client as GuzzleAdapter;
use GuzzleHttp\Client as GuzzleClient;
//use Http\Client\Curl\Client;
use Http\Client\Socket\Client;

// create a log channel
$logger = new Logger('http');
$logger->pushHandler(new StreamHandler('ws.log', Logger::DEBUG));

$wsdl = 'http://10.11.12.13/CrazyBearService.svc?wsdl';
$clientFactory = new ClientFactory(CrazyBearClient::class);

$options = [];
//$httpClient = new Client(MessageFactoryDiscovery::find(), StreamFactoryDiscovery::find(), $options);
$httpClient = new Client(MessageFactoryDiscovery::find(), $options);
$soapOptions = [
    'cache_wsdl' => WSDL_CACHE_NONE,
    'soap_version' => SOAP_1_2
];

$config = ['timeout' => 5, 'verify' => false];
$guzzle = new GuzzleClient($config);
$adapter = new GuzzleAdapter($guzzle);

$clientBuilder = new ClientBuilder($clientFactory, $wsdl, $soapOptions);
$clientBuilder->withHandler(HttPlugHandle::createForClient($adapter));
$clientBuilder->withLogger($logger);
$clientBuilder->withEventDispatcher(new EventDispatcher());
$clientBuilder->addClassMap(new ClassMap('WsdlType', PhpType::class));
$clientBuilder->addMiddleware(new WsaMiddleware());
//$clientBuilder->setSoapAction('http://tempuri.org/ICrazyBearService/LoginAction');
$client = $clientBuilder->build();
//$header = new SoapHeader('http://www.w3.org/2005/08/addressing', 'SOAPAction', 'http://tempuri.org/ICrazyBearService/LoginAction', true);
//$client->applySoapHeaders($header);

//print_r(new LoginAction('username', 'password'));exit;

$response = $client->loginAction(new LoginAction('username', 'password'));
echo print_r($response);
veewee commented 6 years ago

Hi @orette,

Thanks for reporting!

This is a known issue which has been adressed in https://github.com/phpro/soap-client/pull/131. SOAP 1.2 does not require a SOAPAction header and uses the Content-Type header instead. This PR hasn't been completed yet, but you could use the code in there or use SOAP 1.1 instead if it's available for your service.

Currently there is no middleware for WS-ReliableMessaging. Feel free to add one :)

orette commented 6 years ago

Thanks for the response @veewee.

SOAP 1.1 isn't an option for me sadly. I tested the code from the PR #131 , and the error went away, i'm going to dig a little deeper to see if the resulting request is formatted the way the web service i'm using expects it to be.