Created
October 9, 2012 19:46
-
-
Save dedalozzo/3860996 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//! @file Client.class.php | |
//! @brief This file contains the Client class. | |
//! @details | |
//! @author Filippo F. Fadda | |
//! @brief TODO | |
namespace Rest; | |
// If PHP is not properly recognizing the line endings when reading files either on or created by a Macintosh computer, | |
// enabling the auto_detect_line_endings run-time configuration option may help resolve the problem. | |
ini_set("auto_detect_line_endings", true); | |
//! @brief TODO ajkhdakjhda | |
class Client { | |
//! HTTP protocol version used by the Client. | |
const HTTP_PROTOCOL_VERSION = "HTTP/1.1"; | |
//! CR+LF (0x0D 0x0A). A Carriage Return followed by a Line Feed. We don't use PHP_EOL because HTTP wants CR+LF. | |
const CRLF = "\r\n"; | |
//! Maximum period to wait before the response is sent. | |
const DEFAULT_TIMEOUT = 60000; | |
const CURL_TRANSPORT = "cURL"; | |
const SOCKET_TRANSPORT = "socket"; | |
private $userAgent; | |
private $host; | |
private $port; | |
private $userName; | |
private $password; | |
// URI specifying address of proxy server. (e.g. tcp://proxy.example.com:5100). | |
protected $proxy; | |
// When set to TRUE, the entire URI will be used when constructing the request. While this is a non-standard request | |
// format, some proxy servers require it. | |
// TODO not used actually. | |
private $requestFullUri; | |
// Socket connection timeout in seconds, specified by a float. By default the default_socket_timeout php.ini setting | |
// is used. | |
private $timeout; | |
// Stores the transport mode. This library can use cURL or sockets. | |
private $transport; | |
//! @brief TODO | |
//! @param[in] string $server TODO | |
//! @param[in] string $userName TODO | |
//! @param[in] string $password TODO | |
public function __construct($server, $userName = "", $password = "") { | |
$this->setServer($server); | |
$this->authorization = NULL; | |
$this->userName = $userName; | |
$this->password = $password; | |
// Proxy parameters. | |
$this->proxy = NULL; | |
$this->requestFullUri = FALSE; | |
// Uses the default socket's timeout. | |
$this->timeout = ini_get("default_socket_timeout"); | |
$this->useSocket(); | |
} | |
//! @brief Sets server host and port. | |
private function setServer($server) { | |
if (preg_match( | |
'/^ | |
(?P<host>[a-z0-9\-._~%]+ # Host | |
|\[[a-f0-9:.]+\] | |
|\[v[a-f0-9][a-z0-9\-._~%!$&\'()*+,;=:]+) | |
(?P<port>:[0-9]+)? | |
$/ix', $server, $matches)) { | |
$this->host = $matches['host']; // Sets host. | |
$this->port = substr($matches['port'], 1); // Sets port. | |
} | |
else // Match attempt failed. | |
throw new \Exception("'$server' is not a valid host."); | |
} | |
//! @brief Set the user agent name. | |
//! @todo regex | |
protected function setUserAgent($name) { | |
$this->userAgent = $name; | |
} | |
//! @brief This method executes the provided request, using sockets. | |
private function socketSendRequest(Request $request) { | |
$command = $request->getMethod()." ".$request->getPath().$request->getQueryStr()." ".self::HTTP_PROTOCOL_VERSION.self::CRLF; | |
echo PHP_EOL."=============================================================================================================="; | |
echo PHP_EOL.$command.PHP_EOL; | |
$request->setHeaderField(Request::HOST_HF, $this->host.":".$this->port); | |
if (!empty($this->userName)) | |
$request->setBasicAuthorization($this->userName, $this->password); | |
// Sets the Content-Lenght header only when the given request has a message body. | |
if ($request->hasBody()) | |
$request->setHeaderField(Message::CONTENT_LENGTH_HF, $request->getBodyLength()); | |
$header = $request->getHeaderAsString(); | |
$socket = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout); | |
if (!is_resource($socket)) | |
throw new \ErrorException($errstr, $errno); | |
echo $header.PHP_EOL.PHP_EOL; | |
if ($request->hasBody()) | |
echo $request->getBody().PHP_EOL.PHP_EOL; | |
// Writes the request over the socket. | |
fputs($socket, $command); | |
fputs($socket, $header.self::CRLF.self::CRLF); | |
fputs($socket, $request->getBody()); | |
fputs($socket, self::CRLF); | |
// Reads the response. | |
$buffer = ""; | |
while (!feof($socket)) { | |
$buffer .= fgets($socket); | |
} | |
@fclose($socket); | |
return new Response($buffer); | |
} | |
//! This method executes the provided request, using cURL library. To use it, cURL must be installed on server. | |
private function curlSendRequest(Request $request) { | |
$url = "http://".$this->host.":".$this->port.$request->getPath().$request->getQueryStr(); | |
$curl = curl_init(); | |
// TODO debug only | |
$command = $request->getMethod()." ".$request->getPath().$request->getQueryStr()." ".self::HTTP_PROTOCOL_VERSION.self::CRLF; | |
echo PHP_EOL."=============================================================================================================="; | |
echo PHP_EOL.$command.PHP_EOL; | |
// Sets the request Uniform Resource Locator. | |
curl_setopt($curl, CURLOPT_URL, $url); | |
// Sets the request HTTP header. | |
curl_setopt($curl, CURLOPT_HTTPHEADER, $request->getHeaderAsArray()); | |
// I'm not sure we need this. TODO | |
if (!empty($this->userName)) { | |
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) ; | |
curl_setopt($curl, CURLOPT_USERPWD, $this->userName.":".$this->password); | |
} | |
//curl_setopt($curl, CURLOPT_VERBOSE, TRUE); | |
// TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly. | |
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); | |
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | |
// Includes the header in the output. We need this because our Response object will parse them. | |
curl_setopt($curl, CURLOPT_HEADER, TRUE); | |
//curl_setopt($curl, CURLINFO_HEADER_OUT, TRUE); | |
// SSL options. | |
// curl_setopt($curl, CURLOPT_SSLVERSION, 3); | |
// curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); | |
// curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); | |
switch ($request->getMethod()) { | |
case Request::GET_METHOD: | |
curl_setopt($curl, CURLOPT_HTTPGET, TRUE); | |
break; | |
case Request::POST_METHOD: | |
// @bug The following instruction doesn't work; it's probably a cURL bug. | |
//curl_setopt($curl, CURLOPT_POST, TRUE); | |
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, Request::POST_METHOD); | |
// The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with @ and use the full | |
// path. This can either be passed as a urlencoded string like 'para1=val1¶2=val2&...' or as an array with | |
// the field name as key and field data as value. If value is an array, the Content-Type header will be set to | |
// multipart/form-data. | |
curl_setopt($curl, CURLOPT_POSTFIELDS, ltrim($request->getQueryStr(), "?")); | |
break; | |
case Request::PUT_METHOD: | |
curl_setopt($curl, CURLOPT_PUT, TRUE); | |
// Often a request contains data in the form of a JSON object. Since cURL is just able to read data from a file, | |
// but we can't create a temporary file because it's just too much expensive, the code below uses a faster and | |
// efficient memory stream. | |
if ($request->hasBody()) { | |
if ($fd = fopen("php://memory", "r+")) { // Try to create a temporary file in memory. | |
fputs($fd, $request->getBody()); // Writes the message body. | |
rewind($fd); // Sets the pointer to the beginning of the file stream. | |
curl_setopt($curl, CURLOPT_INFILE, $fd); | |
curl_setopt($curl, CURLOPT_INFILESIZE, $request->getBodyLength()); | |
} | |
else | |
throw new \Exception("Cannot create the temporary file in memory."); | |
} | |
break; | |
case Request::DELETE_METHOD: | |
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, Request::DELETE_METHOD); | |
break; | |
default: | |
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $request->getMethod()); | |
} | |
//echo curl_getinfo($curl, CURLINFO_HEADER_OUT); | |
echo $request->getHeaderAsString().PHP_EOL.PHP_EOL; | |
if ($request->hasBody()) | |
echo $request->getBody().PHP_EOL.PHP_EOL; | |
if ($result = curl_exec($curl)) { | |
curl_close($curl); | |
return new Response($result); | |
} | |
else { | |
$error = curl_error($curl); | |
curl_close($curl); | |
throw new \Exception($error); | |
} | |
} | |
//! @name Request Factory and Execution Methods | |
//@{ | |
//! @brief This is a factory method to create a new Request. | |
//! @details This method is used to create a Request object. You can still create a Request instance using the appropriate | |
//! constructor, but I recommend you to use this factory method, because it does a lot of dirty work. You should use | |
//! this method combined with sendRequest. | |
public function newRequest($method, $path) { | |
$request = new Request($method, $path); | |
$request->setHeaderField(Request::ACCEPT_HF, "application/json"); // default accept header value | |
$request->setHeaderField(Request::USER_AGENT_HF, $this->userAgent); | |
return $request; | |
} | |
//! @brief This method is used to send a Request to the server. See details for more informations. | |
//! @details You can use this method in conjunction with newRequest factory method to build and execute a new request. | |
public function sendRequest(Request $request) { | |
$request->setHeaderField(Message::CONNECTION_HF, "close"); | |
if ($this->transport == self::SOCKET_TRANSPORT) | |
return $this->socketSendRequest($request); | |
else | |
return $this->curlSendRequest($request); | |
} | |
//! @} | |
//! @name Transport Mode Selection Methods | |
//@{ | |
//! @brief Selects the cURL transport method. | |
public function useCurl() { | |
if (extension_loaded("curl")) | |
$this->transport = self::CURL_TRANSPORT; | |
else | |
throw new \Exception("The cURL extension is not loaded."); | |
} | |
//! @brief Selects socket transport method. | |
public function useSocket() { | |
$this->transport = self::SOCKET_TRANSPORT; | |
} | |
//! @} | |
//! @brief Uses the specified proxy. | |
public function setProxy($proxyAddress) { | |
if (!empty($proxyAddress)) // TODO regex | |
$this->proxy = $proxyAddress; | |
else | |
throw new \Exception("The \$proxy is not valid."); | |
} | |
public function unsetProxy() { | |
$this->proxy = NULL; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment