Acess the HTTP Request Headers and Body via PHP

Recently I have been updating the PHP end of our Browser based Chat Client and this involved sending a special HTTP Request to the Server from the Browser. The HTTP Request would contain a JSONJavaScript Object Notation string in the HTTP Body and some "presence notifications" in the HTTP Headers. The problem is that I couldn't find a single reference on how to retrieve HTTP Request Headers and the HTTP Request Body Via PHP.

The new Chat Client - Using JSON in the HTTP Body

One of the main changes is to use JavaScript Object Notation (JSON) instead of XML as it is more lightweight. The new approach is to have the Chat client send the JSON to the Server, and have the server reply with JSON. To do this we need to send JSON in the body of our HTTP Request. We also need to send some custom HTTP Headers telling the chat server what the current status of the client was.

After a lot of searching, I decided to write a rudimentary snippet of PHP as an approach to the problem. The first part is how to retrieve the HTTP Request Body.

Retrieving the HTTP Request Body

PHP offers a special "file" that can access the Standard IN/OUT streams. This file is: "php://input" and "php://output". So reading from php://input on a webserver, would retrieve the Body of the HTTP Request. (more: STDIN).
$this->body = @file_get_contents('php://input');

Retrieving HTTP Request Headers with PHP

The next thing would be to retrieve the HTTP Request Headers.
Fortunately, PHP creates a "superglobal" $_SERVER which has infomration on the HTTP Request, and the server (duh).

When examining the $_SERVER associative array, I noticed that most the HTTP Request Headers were prefixed with HTTP in their Array Index. eg: The HTTP Request Header "User-Agent" is $_SERVER['HTTP_USER_AGENT']. So retrieving these Headers from the $_SERVER array is as simple as iterating through each array index, checking if the index starts with "HTTP_", and saving it to a new array if it does.

$this->headers = array();
foreach($_SERVER as $i=>$val) {
	if (strpos($i, 'HTTP_') === 0) {
		$name = str_replace(array('HTTP_', '_'), array('', '-'), $i);
		$this->headers[$name] = $val;
	}
}
This saves the HTTP Request Headers in an array, $this->headers. ($this is the class "http_request")

After a bit of debugging however, I found that two of the HTTP Headers are not prefixed with "HTTP_" in the $_SERVER indices. These two were: 'CONTENT_TYPE' and 'CONTENT_LENGTH'. I suspect there may be others, so I created an array that would allow the programmer to define any headers they wanted to retrieve from $_SERVER that are not prefixed with "HTTP_". ie: var $add_headers = array('CONTENT_TYPE', 'CONTENT_LENGTH');

PHP Class for retrieving HTTP Request

And thus the PHP Class for retrieving HTTP Request info. Unfortunately, due to the way PHP defines variables based on the HTTP Request, getting all the information back together to create the raw HTTP Request proves difficult. (any suggestions welcome).
/**
* Access the HTTP Request
*/
class http_request {

	/** additional HTTP headers not prefixed with HTTP_ in $_SERVER superglobal */
	var $add_headers = array('CONTENT_TYPE', 'CONTENT_LENGTH');

	/**
	* Construtor
	* Retrieve HTTP Body
	* @param Array Additional Headers to retrieve
	*/
	function http_request($add_headers = false) {
	
		$this->retrieve_headers($add_headers);
		$this->body = @file_get_contents('php://input');
	}
	
	/**
	* Retrieve the HTTP request headers from the $_SERVER superglobal
	* @param Array Additional Headers to retrieve
	*/
	function retrieve_headers($add_headers = false) {
		
		if ($add_headers) {
			$this->add_headers = array_merge($this->add_headers, $add_headers);
		}
	
		if (isset($_SERVER['HTTP_METHOD'])) {
			$this->method = $_SERVER['HTTP_METHOD'];
			unset($_SERVER['HTTP_METHOD']);
		} else {
			$this->method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : false;
		}
		$this->protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : false;
		$this->request_method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : false;
		
		$this->headers = array();
		foreach($_SERVER as $i=>$val) {
			if (strpos($i, 'HTTP_') === 0 || in_array($i, $this->add_headers)) {
				$name = str_replace(array('HTTP_', '_'), array('', '-'), $i);
				$this->headers[$name] = $val;
			}
		}
	}
	
	/** 
	* Retrieve HTTP Method
	*/
	function method() {
		return $this->method;
	}
	
	/** 
	* Retrieve HTTP Body
	*/
	function body() {
		return $this->body;
	}
	
	/** 
	* Retrieve an HTTP Header
	* @param string Case-Insensitive HTTP Header Name (eg: "User-Agent")
	*/
	function header($name) {
		$name = strtoupper($name);
		return isset($this->headers[$name]) ? $this->headers[$name] : false;
	}
	
	/**
	* Retrieve all HTTP Headers 
	* @return array HTTP Headers
	*/
	function headers() {
		return $this->headers;
	}
	
	/**
	* Return Raw HTTP Request (note: This is incomplete)
	* @param bool ReBuild the Raw HTTP Request
	*/
	function raw($refresh = false) {
	
		if (isset($this->raw) && !$refresh) {
			return $this->raw; // return cached
		}
	
		$headers = $this->headers();
		$this->raw = "{$this->method}\r\n";
		
		foreach($headers as $i=>$header) {
				$this->raw .= "$i: $header\r\n";
		}
		
		$this->raw .= "\r\n{$this->body}";
		
		return $this->raw;
	}

}

Example Use of the HTTP Request Class

/**
*
* Echos the HTTP Request back the client/browser (in the HTTP Body)
*/
$http_request = new http_request();
$resp = $http_request->raw();
echo nl2br($resp);
The above will print the Raw HTTP Request in the browser window.

Practical Usage

Now the chat client is able to sent a JSON request to the server and let it know to parse the msg as JSON.
$http_request = new http_request();
$content_type = $http_request->header('Content-Type');
$content = $http_request->body();

if ($content_type == 'text/json') {
  // parse as JSON
  $json = new JSON_READER(); 
  $req_obj = $json->parse($content);
} elseif ($conten_type == 'text/xml') {
  // parse as XML
  $xml = new XML_READER();
  $req_obj = $xml->parse($content);
} else {
  // error
  trigger_error('Content-Type unknown.. etc');
}

Another place where this would be useful is with Simple Object Access Protocol (SOAP) and other web services that post XML between servers. I haven't looked into the nusoap.php class to see the better way to do this. The class above is very lightweight though and a simple alternative for more simple applications.