Sweet mother of SOAP

Sweet mother of SOAP

SOAP ... Love it or hate it!

Many projects integrate with SOAP servers. In PHP, this can be implemented in manu different ways. The one way is more maintainable than the other.

In this talk I'll show you the most common errors you can make when implementing a SOAP service and I'll show you some of my worst experiences with SOAP. Finally, I'll show you how a decent SOAP integration is implemented in PHP.

Demo:
https://github.com/veewee/demo-soap-client

Package:
https://github.com/phpro/soap-client

6c18f9874bd8dd408e41d77518987a0e?s=128

Toon Verwerft

February 27, 2018
Tweet

Transcript

  1. Sweet  Mother of  SOAP

  2. • Chemical  composition • Household  used • Soap  making  process

    • Bathing  for  dummies Table  of  contents
  3. Remember  SOAP?

  4. None
  5. Simple  HTTP Documentation  – if  lucky Various  data  formats Faster

    Easier Standardized Protocol Service description XML Slower More complex
  6. Simple  HTTP Documentation  – if  lucky Various  data  formats Faster

    Easier Standardized Protocol Service description XML Slower More complex
  7. Simple  HTTP Documentation  – if  lucky Various  data  formats Faster

    Easier Standardized Protocol Service description XML Slower More complex
  8. Simple  HTTP Documentation  – if  lucky Various  data  formats Faster

    Easier Standardized Protocol Service description XML Slower More complex
  9. Simple  HTTP Documentation  – if  lucky Various  data  formats Faster

    Easier Standardized Protocol Service description XML Slower More complex
  10. S imple O bject A ccess P rotocol

  11. XML  EVERYWHERE!

  12. WSDL  Service  Description

  13. WSDL  Service  Description

  14. Messaging

  15. What  is  your  worst SOAP  experience?

  16. <Request> <where> SOME  RANDOM  SQL  CONDITIONS WITHOUT  KNOWING  THE  DB

     SCHEMA </where> </  Request>
  17. <Response> <Result> &lt;data&gt; PHNvbWUgaHRtb*snap*D0iIiAvPg== &lt;/data&gt; </Results> </  Response>

  18. 5  levels  deep XML  object XML string SOAP response

  19. S imple O bject A ccess P rotocol

  20. Why  do  people  hate  SOAP?

  21. XML  EVERYWHERE!

  22. <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-­‐envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-­‐encoding"> <soap:Header> <wsse:Security xmlns:wsse="http://docs.oasis-­‐open.org/wss/2004/01/oasis-­‐200401-­‐wss-­‐wssecurity-­‐secext-­‐1.0.xsd" soap:mustUnderstand="1"> <wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-­‐open.org/wss/2004/01/oasis-­‐200401-­‐wss-­‐wssecurity-­‐utility-­‐1.0.xsd" EncodingType="http://docs.oasis-­‐open.org/wss/2004/01/oasis-­‐200401-­‐wss-­‐soap-­‐message-­‐security-­‐1.0#Base64Binary"

    wsu:Id="pfx5ba7ce35-­‐b490-­‐f684-­‐a64b-­‐0dd2ffae07c1" ValueType="http://docs.oasis-­‐open.org/wss/2004/01/oasis-­‐200401-­‐wss-­‐x509-­‐token-­‐profile-­‐1.0#X509v3"/> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-­‐exc-­‐c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-­‐sha1"/> <ds:Reference URI="#pfxd3079e1b-­‐c37e-­‐0546-­‐d708-­‐71a6e29bede2"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-­‐exc-­‐c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>W5p2EqlPUbH+mJmT7hUf+0tykXw=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue> t2+F8XtvNTNpDEtHJknhDrJBFKqRCHEzmEHC6kcogKTxfOAmjDSb3IJKqal4aDdN6QcY66rjMV1uVAOXhE6nyxcI/YLr0qzj7O33HILVpFhx READING  SOAP  ISN'T  FUN
  23. Service  descriptions

  24. Platform  Incompatibilities

  25. EXT-­‐SOAP EXT-­‐SOAP

  26. None
  27. $client  = new  SoapClient('some.wsdl'); $client-­‐>SomeFunction($a,  $b,  $c);

  28. $client  =  new  SoapClient('some.wsdl'); $client-­‐>SomeFunction('<rawXml />'); $client-­‐>SomeFunction(['someInfo'  => true]); $client-­‐>SomeFunction(new

     stdClass()); $client-­‐>SomeFunction(new  SoapParam()); $client-­‐>SomeFunction(new  SoapVar()); $client-­‐>SomeFunction(new  MyType());   //  ...
  29. $client-­‐>__getFunctions(); 'array(26)  {  [0]=>  string(70)  "ProductInfo KeywordSearchRequest(KeywordRequest $KeywordSearchRequest)"  [1]=>  string(79)

     "ProductInfo TextStreamSearchRequest(TextStreamRequest $TextStreamSearchRequest)"  [2]=>  string(64)   "ProductInfo PowerSearchRequest(PowerRequest $PowerSearchRequest)"  ...  [23]=>   string(107)  "ShoppingCart RemoveShoppingCartItemsRequest(RemoveShoppingCartItemsRequest $RemoveShoppingCartItemsRequest)"  [24]=>  string(107)  "ShoppingCart ModifyShoppingCartItemsRequest(ModifyShoppingCartItemsRequest $ModifyShoppingCartItemsRequest)"  [25]=>  string(118)  "GetTransactionDetailsResponse GetTransactionDetailsRequest(GetTransactionDetailsRequest $GetTransactionDetailsRequest)"   }';
  30. $client-­‐>__getTypes(); 'array(88)  {  [0]=>  string(30)  "ProductLine ProductLineArray[]"  [1]=>  string(85)  "struct

    ProductLine {  string   Mode;  string  RelevanceRank;  ProductInfo ProductInfo;  }"  [2]=>  string(105)  "struct ProductInfo {  string   TotalResults;  string  TotalPages;  string  ListName;  DetailsArray Details;  }"  ...  [85]=>  string(32)   "ShortSummary ShortSummaryArray[]"  [86]=>  string(121)  "struct GetTransactionDetailsRequest {  string   tag;  string  devtag;  string  key;  OrderIdArray OrderIds;  string  locale;  }"  [87]=>  string(75)  "struct GetTransactionDetailsResponse {  ShortSummaryArray ShortSummaries;  }"  }';
  31. No  extensions  allowed!

  32. class  MySoapClient extends  SoapClient { public  function  __doRequest( $request, $location,

    $action, $version, $one_way =  0 ) { //  your  logic  here   } }
  33. Crappy  error  handling

  34. Basic  Auth on  WSDL HTTP  Chunked  transfer  encoding NTLM  Authentication

    Common  extensions  like  WSA and  WSSE
  35. Crappy  API • __call() • __doRequest()

  36. Dealing  with  SOAP Like  a  SIR!

  37. Value  Objects class  Request implements  RequestInterface {} class  Response implements

     ResponseInterface {} class  MyType {}
  38. Classmap class  MyClientClassMap { public  static  function  getCollection():  ClassMapCollection {

    return  new  ClassMapCollection([ new  ClassMap('GetWeatherRequest',  Type\GetWeatherRequest::class), new  ClassMap('GetWeatherResponse',  Type\GetWeatherResponse::class), ]); } }
  39. Value  objects  generation $  ./bin/soap-­‐client  generate:types $  ./bin/soap-­‐client  generate:classmap

  40. Type  converters class  DateTypeConverter implements  TypeConverterInterface { const NAMESPACE  =

    'http://www.w3.org/2001/XMLSchema'; const TYPE  = 'date'; public  function  convertXmlToPhp(string  $data) { $doc  = new  DOMDocument(); $doc-­‐>loadXML($data); return  new  DateTime($doc-­‐>textContent); } public  function  convertPhpToXml(DateTime $value):  string { return  sprintf('<%1$s>%2$s</%1$s>',  self::TYPE,  $value-­‐>format('Y-­‐m-­‐d')); } }
  41. Client  API class  YourClient { private  $soapClient; function  getWeather(WeatherRequest $request)

     :  WeatherResponse { return  $this-­‐>soapClient-­‐>getWeather($request); } }
  42. Client  generation  tools $  ./bin/soap-­‐client  generate:config $  ./bin/soap-­‐client  generate:clientfactory $

     ./bin/soap-­‐client  generate:client
  43. Event  based  client class  YourClient {       function

     call(string $method,  RequestInterface $request):  ResponseInterface { $this-­‐>dispatcher-­‐>dispatch('soap.request',  []); try  { $response  = $this-­‐>soapClient-­‐>{$method}($request); }  catch  (\Exception  $exception)  { $this-­‐>dispatcher-­‐>dispatch('soap.fault',  []); throw  $exception; } $this-­‐>dispatcher-­‐>dispatch('soap.response',  []); return  $response; } }
  44. Event  Listeners class  Logger implements  EventSubscriber {} class  RequestValidator implements

     EventSubscriber {} class  Cacher implements  EventSubscriber {}
  45. HTTP  SoapClient (PSR-­‐7) class  CustomSoapClient extends  SoapClient { private  $handler;

    public  function  __doRequest($request,  $location,  $action,  $version,  $oneWay = 0) { return  $this-­‐>handler-­‐>request($request,  $location,  $action,  $version,  $oneWay); }         }
  46. HTTP  Middleware  (PSR-­‐15) class  BasicAuth implements  MiddlewareInterface {} class  NtlmAuth

    implements  MiddlewareInterface {} class  HttpLogger implements  MiddlewareInterface {} class  HttpCacher implements  MiddlewareInterface {}
  47. SOAP  Extension  Middleware  (PSR-­‐15) class  Wsa implements  MiddlewareInterface {} class

     Wsse implements  MiddlewareInterface {} class  SwaAttachments implements  MiddlewareInterface {} class  MTOMAttachments implements  MiddlewareInterface {}
  48. Ext-­‐soap  bufixing Middleware  (PSR-­‐15) class  DisableWsdlExtensions implements  MiddlewareInterface {} class

     RenameXsdType implements  MiddlewareInterface {} class  RemoveEmptyNode implements  MiddlewareInterface {} class  AddSoapHeader implements  MiddlewareInterface {}
  49. Client  configuration $builder  = new  ClientBuilder( new  ClientFactory(MyCustomClient::class), 'webservice.wsdl', ['cache_wsdl'

     => $shouldCacheWsdl] ); $builder-­‐>addClassMap(new  MyCustomClientClassmap()); $builder-­‐>withHandler(new  GuzzleHandler()) ; $builder-­‐>addMiddleware(new  MyMiddleware()); $client  = $builder-­‐>getClient();
  50. Testing testGetWheater() testGetWheater()

  51. Testing /** *  @test *  @vcr soap-­‐get-­‐city-­‐weather-­‐by-­‐zip-­‐10013.yml */ function  it_should_be_possible_to_hook_php_vcr_for_testing()

    { $result  = $this-­‐>soapClient-­‐>GetCityWeatherByZIP(['ZIP'  => '10013']); $this-­‐>assertTrue($result-­‐>GetCityWeatherByZIPResult-­‐>Success); }
  52. None
  53. phpro /  soap-­‐client

  54. Toon  out  !  !  !