Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing a live SOAP data stream with PHP

Tags:

php

soap

I have a enterprise application that provides a fairly robust API for gathering data. Currently I am querying each user that I want an update on in a loop every second. However the new API documentation now provides a live stream of all changes on all users. I am wondering how I can parse this live data as it comes with php. Here are some details:

The data is requested via a SOAP request, and I am using PHP to make these requests like this (example of session initiation with a unique ID returned):

//Get a session ID for this user for the shoretel WEBAPI
$soap_url = 'http://11.11.11.11:8070/ShoreTelWebSDK?wsdl';
$client = new SOAPClient($soap_url, array( 'proxy_host' => '11.11.11.11', 'proxy_port' => 8070, 'trace' => 1 ) );
$client = new SoapClient($soap_url);

$header = new SoapHeader('http://www.ShoreTel.com/ProServices/SDK/Web');
$client->__setSoapHeaders($header);
$registered_string = $client->RegisterClient(array(
                'clientName' => '11.11.11.211'
            )); 
$registered_string = get_object_vars($registered_string);   
$session =  $registered_string['RegisterClientResult'];

The new method allows me to specify a time period. For example if I would like to get all events for 1 minute the call will initiate, wait one minute, then return all of the events that occured during that minute.

What I would like to do is grab each event as it occurs, and insert it into a database. Is this something that I can accomplish with PHP, or do I need to look to another language to make this happen?

like image 769
Joel Lewis Avatar asked Sep 04 '15 13:09

Joel Lewis


1 Answers

Ok, the goal is to "stream a SOAP reponse" with a "timeout" and/or in an "interval"?

I suggest to override the SoapClient __doRequest() method and implement a custom connection via fsockopen() and stream the data with stream_get_contents(). Now you get a stream of XML data and what you want is in the middle of it. You would have to extract the XML envelope or parts of it, probably using string functions or by using preg_match to fetch the inner content.

The following code provides a SoapClientTimeout class, where the timeout is set via stream_set_timeout(). This is for use-case, when the server has slow responses and you want to make sure, when to end listening.

I suggest to play around with it and tweak the timeout behavior on the socket. Because, what you want is to stop listening after a while (interval fetch). So, you cloud try to combine timeout with blocking, to stop reading from the stream after a while:

$timeout = 60; // seconds
stream_set_blocking($socket, true);
stream_set_timeout($socket, $timeout); 

When you have a stream, which blocks after 1 minute and closes, you need a loop (with a solid exit condition) triggering the next request.


class SoapClientTimeout extends SoapClient
{    
    public function __construct ($wsdl, $options = null)
    {
        if (!$options) $options = [];

        $this->_connectionTimeout = @$options['connection_timeout'] ?: ini_get ('default_socket_timeout');
        $this->_socketTimeout = @$options['socket_timeout'] ?: ini_get ('default_socket_timeout');
        unset ($options['socket_timeout']);

        parent::__construct($wsdl, $options);
    }

    /**
     * Override parent __doRequest and add "timeout" functionality.
     */
    public function __doRequest ($request, $location, $action, $version, $one_way = 0)
    {
        // fetch host, port, and scheme from url.
        $url_parts = parse_url($location);

        $host = $url_parts['host'];
        $port =  @$url_parts['port'] ?: ($url_parts['scheme'] == 'https' ? 443 : 80);
        $length = strlen ($request);

        // create HTTP SOAP request.
        $http_req = "POST $location HTTP/1.0\r\n";
        $http_req .= "Host: $host\r\n";
        $http_req .= "SoapAction: $action\r\n";
        $http_req .= "Content-Type: text/xml; charset=utf-8\r\n";
        $http_req .= "Content-Length: $length\r\n";
        $http_req .= "\r\n";
        $http_req .= $request;

        // switch to SSL, when requested
        if ($url_parts['scheme'] == 'https') $host = 'ssl://'.$host;

        // connect
        $socket = @fsockopen($host, $port, $errno, $errstr, $this->_connectionTimeout);

        if (!$socket) {
            throw new SoapFault('Client',"Failed to connect to SOAP server ($location): $errstr");
        }

        // send request with socket timeout
        stream_set_timeout($socket, $this->_socketTimeout);
        fwrite ($socket, $http_req);

        // start reading the response.
        $http_response = stream_get_contents($socket);

        // close the socket and throw an exception if we timed out.
        $info = stream_get_meta_data($socket);
        fclose ($socket);
        if ($info['timed_out']) {
            throw new SoapFault ('Client', "HTTP timeout contacting $location");
        }

        // the stream contains XML data
        // lets extract the XML from the HTTP response and return it.
        $response = preg_replace (
            '/
                \A       # Start of string
                .*?      # Match any number of characters (as few as possible)
                ^        # Start of line
                \r       # Carriage Return
                $        # End of line
             /smx',
            '', $http_response
        );
        return $response;
    }

}
like image 173
Jens A. Koch Avatar answered Oct 23 '22 05:10

Jens A. Koch