Using WCF Services With PHP 5

August 20, 2009

Introduction

Most of the time when I need to consume a web service I do so using a .NET client built upon the Windows Communication Foundation (WCF). In most cases the web service in question has also been developed using .NET’s WCF.

About 8 years ago I used PHP as my primary tool for building web sites. At the time I used versions 3 & 4. Recently I had the opportunity to brush up my PHP skills by building a couple of small sites using PHP 5. Besides lending itself more towards object orientend programming I noticed that they added support for SOAP by adding the SoapClient class.

As PHP is still widely used it is not unimaginable, but likely very probably that PHP developers out there need to consume a web service that has been developed with WCF. This article explains how to create WCF services that offer support for PHP clients and how such clients can consume these services.

Let’s start coding…

Table Of Contents

WCF Service

Before we can start consuming a WCF service we obviously need to create one. Let’s build a web service that offers exactly one method, namely for uploading a file to the server which hosts it.

Start an instance of Visual Studio and create a new blank solution called “WcfWithPhp”. Next add a WCF Service Library project and call it CGeers.Wcf.Services. A bunch of automatically generated files will be added to this project. Rename the IService1.cs file to IFileUploadService.cs and replace the generated code with the code shown in the following listing.

Listing 1 – IFileUploadService

[ServiceContract]
public interface IFileUploadService
{
    [OperationContract]
    bool Upload(Stream stream);
}

This is by far one of the simplest possible setups for a service that enables you to upload a file. The only parameter which you have to supply is the file itself (Stream parameter). Afterwards you’ll receive a boolean indicating if the upload succeeded or failed.

Now let’s quickly provide an implementation for this service contract. Rename the Service1.cs file to FileUploadService.cs and delete all of the boilerplate code. Your code should end up resembling the following structure:

Listing 2 – FileUploadService

public class FileUploadService : IFileUploadService
{
    public bool Upload(Stream stream)
    {
        try
        {
            // Generate a random filename
            // ...

            // Read the incoming stream and save it to file
            // ...
            return true;
        }
        catch (Exception)
        {
            // Gulp
            return false;
        }
    }
}

The listing above doesn’t show you the code for the actual implementation. Don’t fret about it, you can consult it in the source code accompanying this article. The code isn’t optimal, but this “Hello, World”-ish example it is sufficient.

The service generates a random filename based on a Guid and then reads the incoming stream and saves it to file. Afterwards it return true. If any exceptions should occur they are swallowed and the service will return false indicating that the upload failed. It’s not good practice to just swallow every possible exception, but once again this is just “illustrative code”.

As a final step delete the App.config file from the WCF Service Library project. In the next step we’ll setup a console application that acts as a host for our service.

Top of page

Hosting The Service

Add a new Console Application project to your solution named ConsoleHost. Add a reference to the System.ServiceModel assembly (WCF) and to the CGeers.Wcf.Services project. Then add the following code to the Main(…) method.

Listing 3 – Hosting The Service

static void Main()
{
    ServiceHost host = new ServiceHost(typeof(FileUploadService));
    host.Open();
    Console.WriteLine("FileUpload Service Host");
    Console.WriteLine("Service Started!");
    foreach (Uri address in host.BaseAddresses)
    {
        Console.WriteLine("Listening on " + address);
    }
    Console.WriteLine("Press any key to close the host...");
    Console.ReadLine();
    host.Close();
}

A service host for the FileUpload service type is created and opened. The service’s configuration is included in the application’s configuration file. So add an App.config file to the project and configure the service as shown below.

Listing 4 – Configuring The Service (App.config)

<system.serviceModel>
  <services>
    <service behaviorConfiguration="MyServiceBehavior" 
             name="CGeers.Wcf.Services.FileUploadService">
      <endpoint address="" 
                binding="basicHttpBinding" 
                contract="CGeers.Wcf.Services.IFileUploadService">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" 
                contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8731/FileUploadService" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="MyServiceBehavior">
        <serviceMetadata httpGetEnabled="True"/>
        <serviceDebug includeExceptionDetailInFaults="False" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

As you can see there is nothing special about this particular configuration. Two endpoints are made available, one for the service itself and another one for exposing it’s metadata (mex). The transport medium is the HTTP protocol so we can choose one of the interoperable http-enabled bindings.

However since the client application will be build using PHP built-in SoapClient class we have to opt for the basicHttpBinding as it conform to the WS-I Basic Profile 1.1 and PHP offers no additional support for any other standards.

Run the console application to make sure it builds and executes correctly. When the application is running you should be able to enter the service’s address in your favorite browser. The result should be the familiar web service test page.

Figure 1 – FileUploadService Test Page
File Upload Service Test Page

Top of page

PHP Client

The PHP script which consumes the service is built using PHP 5.2.0, served by Apache 2.2.3 and running on Windows XP. Your setup might differ, but make sure that your PHP 5.x version supports the SoapClient class.

Make sure that the ConsoleHost application is running while you execute your script. The actual script is surprisingly simple.

Listing 5 – PHP Client Script

<?php

// Function to read the contents of a file
function GetFileContents( $filename ) {
  $handle = fopen($filename, "r");
  $contents = fread($handle, filesize($filename));
  fclose($handle); 
  return $contents;
}

// Create a new soap client based on the service's metadata (WSDL)
$client = new SoapClient("http://localhost:8731/FileUploadService?wsdl");

// Specify the file to upload
$filename = "test.txt";

// Specify the parameters for the Upload(...) method as an associative array
$parameters = array("stream" => GetFileContents($filename));

// Upload the file
$result = $client->Upload($parameters);

// Check if the upload succeeded
if ($result) {
    echo $filename . " uploaded";
}
else {
    echo $filename . " upload failed";
}

?>

The comments in the code adequately explains what it performs. To recap, a soap client is created using the service’s metadata (WSDL), the contents of the file you want to upload are read and put in an associative array. Then the client calls the Upload method and passes in this array to actually upload the file. Finally the return value is checked to determine whether the upload succeeded.

If you execute the PHP script a couple of times you will see a number of GUID-named .dat files appearing in the same folder that hosts your ConsoleHost.exe application.

Figure 2 – Uploaded files
Uploaded Files

While testing my script I choose to upload a simple text file (test.txt), but any file will do. Just make sure to adjust your PHP script accordingly. When uploading large files your WCF service might time out and throw an exception. You need to adjust your service’s timeout settings in this case.

Top of page

MessageContract

The “Hello, World” example I choose for this article enables you to upload a file. I particularly choose this functionality as it lends itself quite easily to implement the MessageContract attribute. As you can see in Listing 1 the Upload(…) method only takes only one parameter, namely a stream of the file itself. When using a stream to upload a file (or any arbitrary data) it can be the only parameter passed in the body of the request.

Since we are using the HTTP transport and SOAP it can be the only parameter in the body of the soap message / envelope. If we want to send extra parameters along, then we have to insert these in the header of the request / SOAP message. This is where the MessageContract attribute comes to the rescue. It helps us shape the SOAP envelope and determine what is placed in the header and what is placed in the body of the SOAP message.

Suppose we want to send the original filename and filesize (expressed in bytes) along with the actual file itself (Stream parameter). To do this add a new class FileUploadInputParameter to the CGeers.Wcf.Services project and add the following code to it:

Listing 6 – FileUploadInputParameter

[MessageContract]
public class FileUploadInputParameter
{
    [MessageHeader(MustUnderstand = true)]
    public string FileName { get; set; }

    [MessageHeader(MustUnderstand = true)]
    public int FileSize { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Stream { get; set; }
}

By decorating the FileUploadInputParameter class with the MessageContract attribute you define a strongly-typed class that corresponds to a SOAP message. Using the MessageHeader and MessageBodyMember attribute you can specify which properties of the class are respectively placed in the header and which are to be inserted in the body.

Remark: WCF states that when using the MessageContract attribute in your service operation that you cannot mix it with regular serializable parameters. Either only use custom message types (= class types decorated with the MessageContract attribute) or only use regular serializable parameters for the input parameters and return value of your service operation.

This means we have to use a custom message type for the return value of our service operation as well. Since this is only a boolean let’s quickly wrap it in a new class type and decorate it with the necessary attributes. Add a new class FileUploadReturnValue to the CGeers.Wcf.Services project and add the code shown in Listing 7 to it.

Listing 7 – FileUploadReturnValue

[MessageContract]
public class FileUploadReturnValue
{
    [MessageBodyMember(Order = 1)]
    public bool UploadSucceeded { get; set; }
}

This message type does not include any headers and places one single boolean named UploadSucceeded in the body of the envelope.

Voila, the message contracts are in place. Let’s add a new service operation to our service (IFileUploadService.cs). Very originally I’ve named it UploadWithMessageContract(…).

Listing 8 – UploadWithMessageContract(…) Service Operation

[OperationContract]
FileUploadReturnValue UploadWithMessageContract(FileUploadInputParameter file);

The implementation of this new service operation in the FileUploadService class is very simular to that of our regular Upload(…) method. The only difference is that instead of generating a random filename based on a Guid we use the filename provided to us. Also note that the return value (true / false) is being wrapped in our custom message type (FileUploadReturnValue).

Listing 9 – UploadWithMessageContract Implementation

public FileUploadReturnValue UploadWithMessageContract(FileUploadInputParameter file)
{
    try
    {
        // Read the incoming stream and save it to file
        SaveFile(file.FileName, file.Stream);
        return new FileUploadReturnValue {UploadSucceeded = true};
    }
    catch (Exception)
    {
        // Gulp
        return new FileUploadReturnValue {UploadSucceeded = false};
    }
}

Since the Upload(…) and UploadWithMessageContract(…) methods both use the same code for saving the incoming file I’ve moved this code to a separate helper method named SaveFile(string filename, Stream stream). Check the accompanying source code to see the code for this method.

Top of page

Revised PHP Client

We have to revise our PHP client a bit before we can call the new UploadWithMessageContract(…) method. Here’s the adjusted code:

Listing 10 – Revised PHP Client

<?php

// Function to read the contents of a file
function GetFileContents( $filename ) {
  $handle = fopen($filename, "r");
  $contents = fread($handle, filesize($filename));
  fclose($handle); 
  return $contents;
}

// Create a new soap client based on the service's metadata (WSDL)
$client = new SoapClient("http://localhost:8731/FileUploadService?wsdl");

// Specify the file to upload
$filename = "test.txt";

// Twee headers (FileName & Length) wordt in de SOAP envelope geplaatst
$headers[] = new SoapHeader('http://tempuri.org/', 
                            'FileName',
                            "test.txt");

$headers[] = new SoapHeader('http://tempuri.org/', 
                            'FileSize',
                            filesize($filename));

$client->__setSoapHeaders($headers);

// Specify the parameters for the Upload(...) method as an associative array
$parameters = array("Stream" => GetFileContents($filename));

// Upload the file
$result = $client->UploadWithMessageContract($parameters);

// Check if the upload succeeded
if ($result->UploadSucceeded) {
    echo $filename . " uploaded";
}
else {
    echo $filename . " upload failed";
}

?>

The only adjustments made here all the creation of the soap headers and their injection into the SOAP message. The rest is simular to that of the example shown in Listing 5. Because the response message has also been wrapped inside a custom message type the check to read the response is also slightly different.

Top of page

Summary

I hope you enjoyed this article which demonstrated the interoperability between .NET and PHP 5 using web services. Exchanging data over the HTTP transport using SOAP (v1.1) messages is a breeze between these two technologies.

Setting up your WCF service and exposing it through an endpoint which uses the httpBasicBinding enables PHP clients to consume these services through the SoapClient class. Service operations which require data to be included through the use of SOAP headers (MessageContract attribute) can also be called using PHP 5 built-in SOAP supported provided ofcourse that you include this information in the headers.

Top of page

Download

You can find the source code for this article on the Download page of this blog.

The source code only contains the C# code for the WCF service. The entire PHP code for creating the clients can be found in Listing 5 & 10.

Top of page

About these ads

33 Responses to “Using WCF Services With PHP 5”


  1. […] just copied the example from Using WCF Services with PHP – Gunnar Peipman's ASP.NET blog and Using WCF Services With PHP 5 Geers’ Blog PHP Code: […]

  2. Martin Evans Says:

    Thank you Christophe.
    That was really useful.

  3. PhpCatalog Says:

    Thank you… it works..

  4. Richard Noya Says:

    Bedankt Cristoffe, scheelt me 3 dagen uitzoekwerk!

  5. Shaun Plumb Says:

    Excellent article, just what I was looking for.

    The same technique can also be used to call ASP.NET Web Service functions, you just need to include the ServiceContract and OperationContract tags in your service class.

  6. Thurupathan Says:

    really nice article, thanks and keep up the good work.

  7. Mayu Says:

    Realy awsme article to quickly understand the interoperability between .net and php
    thanks a lot………continue to do the same work

  8. Steven Says:

    Thank you for such a well-structured and clear walkthrough on this!

  9. Joan Trần Says:

    Thank you. However, I couldn’t download source.

    • Christophe Says:

      Yes, I know. A while back the host on which the files were stored moved my files and dropped them (The @#{#[). Three of which I forgot to store in my Google Docs (Oops). I re-created 2 of them, but still have to do this one. Please check back later…

  10. Joan Trần Says:

    You know about WCF and client PHP with SSL Certificate. You can guide for me…? Thanks so much.

    • Christophe Says:

      So consuming a WCF service over HTTPS from PHP? If it’s custom authentication you might need to add an extra SOAP header:

      For example:

      * http://stackoverflow.com/questions/6652227/how-to-consume-a-wcf-web-service-that-uses-custom-username-validation-with-a-php

      • Joan Trần Says:

        I have secure WCF with mode=”message” and clientcredentialType=”certificate”. but php client can’t connected wcf with secure WCF.
        This is error: An error occurred when verifying security for the message

        Code PHP:

        Demo WCF

        <?php
        try
        {
        $str = "http://localhost:2751/Service1.svc?wsdl&quot;;
        print $str . "”;
        $file = ‘Wcfclient.pem';
        $local_cert = (file_get_contents($file));

        $params = array(
        “local_cert” => $file,
        “verify_peer” => false,
        “allow_seft-signed certificates” => true,
        “passphrase” => ‘123456’,
        “style” => SOAP_DOCUMENT,
        “use” => SOAP_LITERAL,
        “encoding” => ‘utf-8′,
        “soap_version” => SOAP_1_1,
        “trace” => true,
        “exceptions” => true,
        “connection_timeout” => 60
        );

        $client = new SoapClient($str, $params);

        $argument = array(‘value’=>3);

        $webService = $client->GetData($argument);
        $wsResult = $webService->GetDataResult;

        print “Return value: ” . $wsResult . “”;
        //echo “”;
        }
        catch (Exception $e)
        {
        print ‘Caught exception: ‘. $e->getMessage(). “\n”;
        }
        ?>

        And link secure WCF Service (I have attack certificate file and command line create key):

        http://www.mediafire.com/?2gjfos09d3fer33

  11. kais Says:

    Excellent article.
    Unfortunately source code is not available. Can you please fix the link?

    • Christophe Says:

      Lost the source code of this article, because of my (ex)-host fault. Was the only article of which I forgot to take a backup. I’ll see if I can re-create it next week. Have some time off then.

  12. Lary Page Says:

    If you are doing a stream sample, then do it right. Where TransferMode=Streamed? Or U’ll upload only 3kb size files?

    • Christophe Says:

      The article focuses a bit too much on the file upload (stream sample). It’s main goal is to show how to use a WCF service using PHP.

      More information about the TransferMode property can be found on MSDN:

      http://msdn.microsoft.com/en-us/library/ms789010.aspx

      By default a buffered mode is used which causes the entire stream to be buffered before handing it to the transport layer. For larger files it is indeed better to set the mode to Streamed as you suggest.

      Thank you for pointing it out.

  13. Guillaume Says:

    Thanks you very much !
    I’ve tried lot of things to get my specific parameters in my SOAP request and finally… I had only one thing to change: [DataContract] and [DataMember] to
    [MessageContract] and [MessageBodyMember]

    Now it work fine.
    Thanks again.

  14. eugenio Says:

    Hi,
    is there a way to use wsHttpBinding WS with a php soapClient?

    thanks

    • Christophe Says:

      To quote myself:

      “However since the client application will be build using PHP built-in SoapClient class we have to opt for the basicHttpBinding as it conform to the WS-I Basic Profile 1.1 and PHP offers no additional support for any other standards.”

      Well, not at the time of writing. I don’t know if more recent versions of PHP support other standards. The vanilla SoapClient does not support it AFAIK:

      http://be2.php.net/manual/en/class.soapclient.php

      You might have more lucky with a third party PHP SOAP implementation.

  15. Ankit Jain Says:

    Hi i am trying to do the same implementation but wach time i got the error that
    “Object reference not set to an instance of an object.”

    • Christophe Says:

      “Object reference not set to an instance of an object” is way to general. It just means that you are using an object which is not intialized. It is currently null. Best to set breakpoints and debug your code…see where it throws the exception.

  16. rmg1501 Says:

    Christophe, Thanks for the useful and simple tutorial.
    I’m trying to write a PHP SOAP client to the kareo web service too, and getting the same error Ankit is getting.

    Here is my stackoverflow post: http://stackoverflow.com/questions/11381457/exception-in-my-first-soap-client

    Any help is greatly appreciated.

  17. Ankit Jain Says:

    $wsdl = ‘https://webservice.kareo.com/services/soap/2.1/KareoServices.svc?wsdl';

    $client = new SoapClient($wsdl, array(‘trace’ => 1, ‘exceptions’ => 1));

    // Store data in the object of stdClass`enter code here`
    $requestHeader = new stdClass;
    $requestHeader->CustomerKey = ‘123456’;
    $requestHeader->User = ‘username';
    $requestHeader->Password = ‘pass';

    $filter = new stdClass;
    $filter->PracticeName = “a”;
    $filter->FullName = “a”;

    $fields = new stdClass;
    $fields->ID = true;
    $fields->PracticeName = true;
    $fields->PatientFullName = true;

    $request = new stdClass;
    $request->RequestHeader = $requestHeader;
    $request->Filter = $filter;
    $request->Fields = $fields;

    // Call a SOAP function
    $response = $client->GetPatients(array(‘request’ => $request));

    echo ‘

    ';
        print_r($response);
        
        // Check the response for an error
        if ($response->ErrorResponse->IsError) {
            echo 'Step 1';
            echo $response.ErrorResponse.ErrorMessage;
        }
        else if (!$response->SecurityResponse->SecurityResultSuccess) {
            echo 'Step 2';
            echo $response->SecurityResponse->SecurityResult;
        }
        else {
            echo 'Step 3';
            foreach ($response->Patients->PatientData as $patient) {
                echo '####';
                echo $patient->PatientFullName;
                echo '';
            }
        }
        
        echo("
    Dumping request headers:\n" . $client->__getLastRequestHeaders() . "

    “);

    echo (“

    Dumping request:\n" . $client->__getLastRequest() . "

    “);

    echo(“

    Dumping response headers:\n" . $client->__getLastResponseHeaders() . "

    “);

    echo (“

    Dumping response:\n" . $client->__getLastResponse() . "

    “);

  18. Jimmy James Says:

    Just use the SoapClient class in PHP, way better

  19. siva Says:

    Christophe, Its wonderful article. I have one question. could you clarify this?

    http://stackoverflow.com/questions/17571250/wcf-how-to-change-the-settings-of-generatemessagecontractstrue-generatemessa

    I am looking forward reply from you. Thanks in Advance


  20. implemented exactly as you stated but only the stream is getting on to the server not the header values..they are all null..any idea?

  21. kalpanaprasad Says:

    used the example as you stated but only stream getting passed to the server side..headers are all null..any idea what could be the mistake?

  22. angelo leone Says:

    Christophe, Its very interesting and clear.
    I am interested in this issue:
    How to send a file to attach through a php client to a web service written in java that you know only its wsdl ?.

    A help in this regard would be greatly appreciated.

    the tests I’ve done lead to assume that there are problems such as those highlighted in the article.
    XML error parsing SOAP payload on line 1: Unsupported encoding ………… + others


Comments are closed.

%d bloggers like this: