Sabre Web Services in PHP

oecdworld200I’ve been on contract for the past couple of months at a lovely company up in Newmarket, called Solutions360. I’m part of a team working on a system for the travel industry. So we’re deep into API’s for hotels, flights, cruises and such. Listing, booking, cancelling have been our constant concerns for weeks now.

My bailiwick is a module called a Service Manager, which translates requests from our system to an outside web service. Then captures the responses and again translates them into a standard format for the system to display.

The first Service Manager I wrote used the cURL library to communicate with the external service. So when it came time to interface with the Sabre services I tried using the same mechanism; but for some reason it was a non-starter. I tweaked it as much as I could; as much as it made sense to. But the only response I got was an error that the service “couldn’t internalize the request.” *sigh*

And while Sabre’s support team has proven to be enormously helpful and responsive, on the phone and via email, if it isn’t Java or .Net, you’re pretty much on your own.

Then I turned to the Interwebs for help, and that’s where I found Moazzam Khan’s blog post PHP and Sabre. In it Moazzam gives us a class which eschews cURL in favour of a low-level approach using fsockopen(), and the ssl:// scheme. Brilliant. And more importantly, it works.

I’ve tweaked the routine a bit, and present it here complete with an invocation for SessionCreateRQ. All you need to add are your credentials.

<?php
	$serverProperties = array (
		 'SERVER'  		=> 'sws-crt.cert.sabre.com' 
		,'USERNAME' 	=> '*USERNAME*'
		,'PASSWORD' 	=> '*PASSWORD*'
		,'PCC'			=> '*PCC*'
	);

	// == Process Request
	$oDate = new DateTime();
	$timestamp = $oDate->format('Y-m-d\Th:i:sP');
	$timetolive = $oDate->add(new DateInterval('PT1H'))->format('Y-m-d\Th:i:sP');
	$xml = <<<SESSIONCREATEXML
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Header>
      <MessageHeader xmlns="http://www.ebxml.org/namespaces/messageHeader">
         <From>
            <PartyId type="urn:x12.org:IO5:01">Sabre WS Example</PartyId>
         </From>
         <To>
            <PartyId type="urn:x12.org:IO5:01">Sabre</PartyId>
         </To>
         <CPAId>{$serverProperties['PCC']}</CPAId>
         <ConversationId>MyConversationID</ConversationId>
         <Service type="string">Cruise</Service>
         <Action>SessionCreateRQ</Action>
         <MessageData>
            <MessageId>1000</MessageId>
            <Timestamp>{$timestamp}</Timestamp>
            <TimeToLive>{$timetolive}</TimeToLive>
         </MessageData>
      </MessageHeader>
      <Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext">
         <UsernameToken>
            <Username>{$serverProperties['USERNAME']}</Username>
            <Password>{$serverProperties['PASSWORD']}</Password>
            <Organization>{$serverProperties['PCC']}</Organization>
            <Domain>DEFAULT</Domain>
         </UsernameToken>
      </Security>
   </soapenv:Header>
   <soapenv:Body>
      <SessionCreateRQ xmlns="http://www.opentravel.org/OTA/2002/11">
         <POS>
            <Source PseudoCityCode="{$serverProperties['PCC']}" />
         </POS>
      </SessionCreateRQ>
   </soapenv:Body>
</soapenv:Envelope>
SESSIONCREATEXML;

	// == Submit Request
	$xmlResponse = callSabre($xml);
	echo '<pre>';
	echo $xmlResponse;
	echo '</pre>';

	function callSabre($xml) {

		$oSabre = new Sabre();
		$oSabre->host = $serverProperties['SERVER'];
		$content = $oSabre->makeRequest($xml);

		return $content;
	}


/**
 * Sabre class (for connecting to Sabre web services)
 * @author Moazzam Khan <moazzam at moazzam-khan dot com>
 * @version $Id$
 * @source http://moazzam-khan.com/blog/?p=495
 */
class Sabre {
	public $host;
	public $port = 443;
	public $timeout = 30;
 
	public function makeRequest($body) {

		$header = "POST /websvc HTTP/1.1
Host: {$this->host}:{$this->port}
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/xml
Content-Length: ".strlen($body)."
Pragma: no-cache
Cache-Control: no-cache";

		$fp = fsockopen("ssl://".$this->host, $this->port, $errno, $errstr, $this->timeout);
		if(!$fp){
			throw new Exception("SOCKET ERROR ({$errno}): {$errstr}\n");
		} else {
				//send the request
				$req = $header."\r\n\r\n".$body;
				fputs($fp, $req, strlen($req));
				stream_set_timeout($fp, $this->timeout);
				stream_set_blocking($fp, 1);
				while (!feof($fp)) {
					$line = fread($fp, 2048);
					// if (trim(strlen($line)) < 10) continue;
					$output .= $line;
				}
				fclose($fp);
		}

		// remove the HTTP response header
		$ret = explode("\r\n\r\n", $output);
		$ret = $ret[1];

		// format XML so we can read it.
		$dom = new DOMDocument;
		$dom->preserveWhiteSpace = FALSE;
		$dom->loadXML($ret);
		$dom->formatOutput = TRUE;
		$ret = $dom->saveXml();
			
		return $ret;
	}
}

I hope you find this helpful.

7 thoughts on “Sabre Web Services in PHP

  1. Hi Alfred,

    This looks nice. I am glad my blog post was helpful. What I also did (but did not publish) is create a session manager along with “Sabre” class. Session manager, checks for the right parameters, etc and instantiates a connection object (an instance of Sabre class).

    I have fallen in love with dependency injection so I have been refactoring everything to use that. You should check it out too!

    • Yes, I’ve looked at Dependency Injection, but I haven’t yet seen the light on that one. From a testing perspective I see it makes it easier. When I jump into testing with both feet I expect it’ll make more sense to me.

      Thanks for taking the time to look at my post.

  2. Peter, I disagree.

    Just because a system has a convoluted history does not mean that it should be scrapped. Using that logic, banks would be redoing their Point of Sale and ATM systems every couple of years. There is no way any non IT company can afford to replace a systenm simply because it is old….

  3. Good afternoon, thank you for your example, everything turned out. Many examples of how to find flights, book, etc. Ready to take part in the development examples, only direct.

Leave a Reply