title: PHP Soap client with WCF BadContextToken
body: <p>After few days of google -ing/trying/loosing hair I still can't find solution for this so please help :)
  <br><br>
  <strong>Short info:</strong> <br>
  I need to work with WCF service from PHP (SOAP client). It uses wsHttpBinding (ws-security) and there is no way to set basicHttpBinding. Everything is behind VPN so I can't offer you link to webservice. Also data is considered secret (request from client) so I can't give you full info, only some "common" things. Here is WS config:</p>
  
  <pre><code>&lt;configuration&gt;
  &lt;system.serviceModel&gt;
      &lt;bindings&gt;
          &lt;wsHttpBinding&gt;
              &lt;binding name="WSHttpBinding_IServices" closeTimeout="00:01:00"
                  openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                  bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                  maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                  messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"&gt;
                  &lt;readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /&gt;
                  &lt;reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /&gt;
                  &lt;security mode="TransportWithMessageCredential"&gt;
                      &lt;transport clientCredentialType="None" proxyCredentialType="None" realm="" /&gt;
                      &lt;message clientCredentialType="UserName" negotiateServiceCredential="true" algorithmSuite="Default" /&gt;
                  &lt;/security&gt;
              &lt;/binding&gt;
          &lt;/wsHttpBinding&gt;
      &lt;/bindings&gt;
      &lt;client&gt;
          &lt;endpoint address="https://topSecert.url/Service.svc"
              binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServices"
              contract="IServices" name="WSHttpBinding_IServices" /&gt;
      &lt;/client&gt;
  &lt;/system.serviceModel&gt;
  </code></pre>
  
  <p></p>
  
  <p><strong>My attempts:</strong> <br>
  1) Basic PHP Soap client does not work. It always hangs until max exec time is reached (no error is generated). I found out later that PHP Soap client does not support wsHttpBinding (wanted to cry) <br>
  2) Some SoapClient extension <a href="https://code.google.com/p/wse-php" rel="nofollow">classes</a> but no success, request still hangs. <br>
  3) Trying "self-generated" CURL request with SOAPAction header. Finally I got some error there (I generated request with wse class):</p>
  
  <pre><code>&lt;s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
  xmlns:a="http://www.w3.org/2005/08/addressing"&gt;
  &lt;s:Header&gt;
      &lt;a:Action s:mustUnderstand="1"&gt;http://www.w3.org/2005/08/addressing/soap/fault&lt;/a:Action&gt;
  &lt;/s:Header&gt;
  &lt;s:Body&gt;
      &lt;s:Fault&gt;
          &lt;s:Code&gt;
              &lt;s:Value&gt;s:Sender&lt;/s:Value&gt;
              &lt;s:Subcode&gt;
                  &lt;s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/02/sc"&gt;a:BadContextToken&lt;/s:Value&gt;
              &lt;/s:Subcode&gt;
          &lt;/s:Code&gt;
          &lt;s:Reason&gt;
              &lt;s:Text xml:lang="en-US"&gt;The security context token is expired or is
                  not valid. The message was not processed.&lt;/s:Text&gt;
          &lt;/s:Reason&gt;
      &lt;/s:Fault&gt;
  &lt;/s:Body&gt;
  </code></pre>
  
  <p></p>
  
  <p>I changed my server time to valid zone (same as WCF), tried with nonce, hashed password, plain password and bunch of other things that I can't remember now.
  <br>I also tried to compile <a href="https://github.com/wso2/wsf" rel="nofollow">wso2/wsf</a> however was unable to compile it on PHP 5.4 (I tried to apply provided FIX but it resulted in same error).</p>
  
  <p>Example of test XML:</p>
  
  <pre><code>&lt;env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
  xmlns:ns1="https://topSercret.url/Test"&gt;
  &lt;env:Header&gt;
      &lt;wsse:Security
          xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
          env:mustUnderstand="1"&gt;
          &lt;wsse:UsernameToken&gt;
              &lt;wsse:Username&gt;&lt;!-- Removed --&gt;&lt;/wsse:Username&gt;
              &lt;wsse:Password
                  Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"&gt;&lt;!-- Removed --&gt;&lt;/wsse:Password&gt;
              &lt;wsse:Nonce&gt;&lt;!-- Removed --&gt;&lt;/wsse:Nonce&gt;
              &lt;wsu:Created
                  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"&gt;2014-01-19T15:20:31Z&lt;/wsu:Created&gt;
          &lt;/wsse:UsernameToken&gt;
          &lt;wsu:Timestamp
              xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"&gt;
              &lt;wsu:Created&gt;2014-01-19T15:20:31Z&lt;/wsu:Created&gt;
              &lt;wsu:Expires&gt;2014-01-19T16:20:31Z&lt;/wsu:Expires&gt;
          &lt;/wsu:Timestamp&gt;
      &lt;/wsse:Security&gt;
  &lt;/env:Header&gt;
  &lt;env:Body&gt;
      &lt;ns1:SomeAction /&gt;
  &lt;/env:Body&gt;
  </code></pre>
  
  <p></p>
  
  <p>And this is code of test script (may have errors, I removed large portion of it for this posting):</p>
  
  <pre><code>&lt;?php
  
  date_default_timezone_set( 'UTC' );
  
  include 'WSSESoap.php';
  
  class TestSoap extends SoapClient {
  
      private $_username;
      private $_password;
      private $_digest;
  
      // test vars
      public $r_request;
      public $r_location;
      public $r_action;
  
      function addUserToken($username, $password, $digest = false) {
          $this-&gt;_username = $username;
          $this-&gt;_password = $password;
          $this-&gt;_digest = $digest;
      }
  
      function __doRequest($request, $location, $saction, $version, $one_way = 0) {
          $doc = new DOMDocument('1.0');
          $doc-&gt;loadXML($request);
  
          $objWSSE = new WSSESoap($doc);
          $objWSSE-&gt;signAllHeaders = TRUE;
  
          $objWSSE-&gt;addTimestamp();
          $objWSSE-&gt;addUserToken($this-&gt;_username, $this-&gt;_password, $this-&gt;_digest);
  
          // take data for "my" usage
          $this-&gt;r_request = $objWSSE-&gt;saveXML();
          $this-&gt;r_location = $location;
          $this-&gt;r_action = $saction;
          return '';
      }
  }
  
  function test()
  {
      $soapUrl = "https://topSecret.url/Services.svc";
  
      $context = stream_context_create(array(
              'ssl' =&gt; array(
                      'verify_peer' =&gt; false,
                      'allow_self_signed' =&gt; true
              )
      ));
  
      $client  = new TestSoap('/mypath/wsdl.xml', array(
              'stream_context' =&gt; $context,
              'soap_version' =&gt; SOAP_1_2,
              'trace' =&gt; 1,
              'connection_timeout' =&gt; 10
      ));
      $client-&gt;addUserToken('User', 'Password', TRUE );
  
      $requestParams = array(
              'data1' =&gt; '1',
              'data2' =&gt; '2',
      );
  
      // call to generate request string
      $client-&gt;myAction($requestParams);
      $xml_post_string = $client-&gt;r_request;
  
      $headers = array(
              "Content-type: application/soap+xml; charset=\"utf-8\"",
              "Accept: text/xml,application/soap+xml",
              "Cache-Control: no-cache",
              "Pragma: no-cache",
              "SOAPAction: " . $client-&gt;r_action,
              "Content-length: " . strlen($xml_post_string)
      );
  
      // generate &amp;&amp; run cURL request
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
      curl_setopt($ch, CURLOPT_URL, $soapUrl);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
      curl_setopt($ch, CURLOPT_TIMEOUT, 10);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_post_string); // the SOAP request
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
      curl_setopt($ch, CURLOPT_HEADER, true);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  
      $response = curl_exec($ch);
      curl_close($ch);
  
      echo $response;
  }
  
  test();
  </code></pre>
  
  <p>So finally the question. Can this kind of service be consumed with PHP (and if it can please help to understand how)?</p>