* @license PHP License * @package WB * @subpackage rest */ /** * load base class */ WBClass::load('WBRest'); /** * Rest Client * * Base class for Restful Client * * @version 0.2.0 * @package WB * @subpackage rest */ class WBRest_ClientV1 extends WBStdClass { /** * Protocol * Either http or https * * @var string */ private $protocol = 'https'; /** * Server's Name (FQDN) * @var string */ private $server; /** * Request Path Prefix * @var string */ private $pathPrefix; /** * Server's Port Number * @var int */ private $port; /** * Network Socket * @var resource */ private $socket; /** * Username and password * * @var array */ private $credentials = array(); private $responseState; private $responseHeader; private $responseBody; private $requestHeader; private $header = array( 'User-Agent' => 'wbrestclient 0.1', // 'Accept' => 'application/json' 'Accept' => 'text/xml', 'Content-Type' => 'text/xml' ); private $cookies = array(); /** * Construtor * */ public function __construct($parameter = array()) { if (empty($parameter['protocol'])) { $parameter['protocol'] = 'https'; } if (empty($parameter['server'])) { WBClass::load('WBString'); $parameter['server'] = WBString::replaceSuperPlaceholders('[[SERVER]]'); } if (empty($parameter['port'])) { switch ($parameter['protocol']) { case 'http': $parameter['port'] = '80'; break; default: $parameter['port'] = '443'; break; } } $this->protocol = $parameter['protocol']; $this->server = $parameter['server']; $this->port = $parameter['port']; $this->responseState = array(); $this->responseHeader = array(); $this->responseBody = null; } /** * Set Path Prefix * * Use this prefix on every request * * @param string */ public function setPathPrefix($prefix = '') { $this->pathPrefix = $prefix; } /** * Set Credentials * * Support for HTTP-Basic only. * Pass username and password or nothing to unset credentials * * @param string username * @param string password */ public function setCredentials($user = '', $password = '') { if (empty($user)) { $this->credentials = array(); return; } $this->credentials = array($user, $password); } /** * Get Request Header Value * * @param string head name * @return string|string */ public function getHeader($name = '') { if (empty($name)) { return $this->header; } if (empty($this->header[$name])) { return ''; } return $this->header[$name]; } /** * Set Request Header Value * * @param string header name * @param string value */ public function setHeader($name, $value) { $this->header[$name] = $value; } /** * Get Cookie Value * * @param string cookie name * @return string|array */ public function getCookie($name = '') { if (empty($name)) { return $this->cookies; } if (empty($this->cookies[$name])) { return ''; } return $this->cookies[$name]; } /** * Set Cookie Value * * @param string cookie name * @param string cookie value */ public function setCookie($name, $value) { $this->cookies[$name] = $value; return true; } public function getResponseState() { return $this->responseState; } public function getResponseHeader() { return $this->responseHeader; } public function getResponseBody() { return $this->responseBody; } public function getRequestHeader() { return $this->requestHeader; } /** * Send GET Request * * @param string $path * @return bool */ public function get($path = '/') { return $this->getOrDelete(WBRest::METHOD_GET, $path); } /** * Send DELETE Request * * @param string $path * @return bool */ public function delete($path = '/') { return $this->getOrDelete(WBRest::METHOD_DELETE, $path); } private function getOrDelete($method, $path = '/') { if (!$this->connect()) { return false; } $this->writeHeader($method, $path); $this->parseResponse(); $this->disconnect(); return true; } /** * Send POST Request * * @param string path * @param string body * @return bool */ public function post($path = '/', $body = '') { return $this->putOrPost(WBRest::METHOD_POST, $path, $body); } /** * Send PUT Request * * @param string path * @param string body * @return bool */ public function put($path = '/', $body = '') { return $this->putOrPost(WBRest::METHOD_PUT, $path, $body); } private function putOrPost($method, $path = '/', $body = '') { if (!$this->connect()) { return false; } $header = array( 'Content-Length' => strlen( $body ) ); $this->writeHeader($method, $path, $header); $this->write($body); $this->parseResponse(); $this->disconnect(); return true; } /** * Perse HTTP Response * * */ private function parseResponse() { $this->responseState = array(); $this->responseHeader = array(); $this->responseBody = ''; $header = ''; $body = ''; $response = ''; while (!feof($this->socket)) { $response .= fgets($this->socket, 128); } $split = preg_split('/\r\n/', $response); // check state line $state = explode(' ', array_shift($split)); // ignore "continue" and use next line... if ($state[1] == '100') { $state = explode( ' ', array_shift( $split ) ); } $this->responseState['protocol'] = array_shift($state); $this->responseState['code'] = array_shift($state); $this->responseState['message'] = implode(' ', $state); // parse header while (true) { $line = array_shift($split); if (empty($line)) { break; } preg_match('/^(.*):\s+(.*)$/', $line, $match); // multiple header if (isset($this->responseHeader[$match[1]])) { $this->responseHeader[$match[1]] = array($this->responseHeader[$match[1]]); array_push($this->responseHeader[$match[1]], $match[2]); continue; } $this->responseHeader[$match[1]] = $match[2]; } // keep cookies if (isset($this->responseHeader['Set-Cookie'])) { $cookies = $this->responseHeader['Set-Cookie']; if (!is_array( $cookies)) { $cookies = array($cookies); } foreach ($cookies as $cookie) { $c = explode(';', $cookie); $c = explode('=', $c[0]); $this->cookies[$c[0]] = $c[1]; } } // parse body if (isset($this->responseHeader['Transfer-Encoding'] ) && $this->responseHeader['Transfer-Encoding'] == 'chunked') { while (!empty($split)) { $length = hexdec(array_shift($split)); // reached end of boddy if ($length == 0) { break; } $this->responseBody .= array_shift($split); } } else { $this->responseBody = implode( "\r\n", $split ); } if (!isset($this->responseHeader['Content-Encoding'])) { return true; } if ('gzip' == $this->responseHeader['Content-Encoding']) { $tmp = tempnam(sys_get_temp_dir(), 'httpgzip'); file_put_contents($tmp, $this->responseBody); $stream = gzopen($tmp, 'r'); $this->responseBody = stream_get_contents($stream); gzclose($stream); unlink($tmp); return true; } if ('deflate' == $this->responseHeader['Content-Encoding']) { $this->responseBody = gzinflate($this->responseBody); } return true; } /** * Connect Socket * * @return bool true on success */ private function connect( ) { // direct connect $this->socket = fsockopen($this->server, $this->port); if (!$this->socket) { return false; } return true; } /** * Disconnect Socket * */ private function disconnect( ) { fclose( $this->socket ); } /** * Write HTTP Headers for Request * * @see write() * @param string $method * @param string $path * @param array $header */ private function writeHeader($method, $path, $header = array()) { $method = strtoupper($method); $this->requestHeader = array(); $this->requestHeader[] = sprintf('%s %s://%s%s%s HTTP/1.1', $method, $this->protocol, $this->server, $this->pathPrefix, $path); $this->requestHeader[] = 'Host: ' . $this->server; $this->requestHeader[] = 'Connection: Close'; if (!empty($this->credentials)) { $this->requestHeader[] = 'Authorization: Basic ' . base64_encode(implode(':', $this->credentials)); } // add cookies $cookies = array(); foreach ($this->cookies as $key => $value) { array_push($cookies, $key . '=' . urlencode($value)); } unset($this->header['Cookie']); if (!empty($cookies )) { $this->header['Cookie'] = implode('; ', $cookies); } // additional header $header = array_merge($this->header, $header); foreach ($header as $name => $value) { $this->requestHeader[] = $name . ': ' . $value; } foreach ($this->requestHeader as $hdr) { $this->write($hdr); } $this->write(); } /** * Write 2 Socket * * Write a line to http socket * * @param string $line */ private function write($line = '') { fwrite($this->socket, $line . "\r\n"); } }