* @license PHP License * @package WB * @subpackage rest */ /** * load base class */ WBClass::load('WBRest' , 'WBLog' , 'WBStream' , 'WBClock' ); /** * Rest Client * * Base class for Restful Client * * @version 0.4.5 * @package WB * @subpackage rest */ class WBRest_Client extends WBStdClass { /** * Config parameter * * Default values * @var array */ private $config = array( 'port' => '', 'protocol' => 'https', 'server' => '', 'pathprefix' => '', 'useragent' => '', 'verifycert' => 1 ); /** * Logger * @var WBLog */ protected $log; /** * Header * * Header for next request * @var array */ private $header = array(); /** * @var WBStream_Response_Http */ private $response; /** * Construtor * * @param array configation parameters */ public function __construct($parameter = array()) { $this->log = WBLog::start(__CLASS__); if (empty($parameter['useragent'])) { $this->config['useragent'] = 'Wombat-generic-REST-Client-1.0.0'; } if (empty($parameter['protocol'])) { $parameter['protocol'] = 'https'; } if (empty($parameter['server'])) { WBClass::load('WBString'); $parameter['server'] = WBString::replaceSuperPlaceholders('[[SERVER]]'); } $this->configure($parameter); } public function setHeader($name, $value = null) { if (is_null($value)) { unset($this->header[$name]); return; } $this->header[$name] = $value; } /** * Set Configuration Parameter * * @param array configation parameters */ public function configure($parameter = array()) { // remove slashes if (isset($parameter['pathprefix'])) { $parameter['pathprefix'] = trim($parameter['pathprefix'], '/'); if (!empty($parameter['pathprefix'])) { $parameter['pathprefix'] = '/' . $parameter['pathprefix']; } } if (1 > $this->config['verifycert']) { $this->config['verifycert'] = 0; } $this->config = array_merge($this->config, $parameter); if (empty($this->config['port'])) { switch ($this->config['protocol']) { case 'http': $this->config['port'] = '80'; break; default: $this->config['port'] = '443'; break; } } } /** * Send GET Request * * @param string $path * @return bool */ public function get($path = '/') { $this->response = $this->request($path); return $this->response->isOK(); } /** * Send DELETE Request * * @param string $path * @return bool */ public function delete($path = '/') { $this->response = $this->request($path, null, WBRest::METHOD_DELETE); return $this->response->isOK(); } /** * Send POST Request * * @param string $path * @param string|array body * @return bool */ public function post($path = '/', $body) { return $this->requestWithBody(WBRest::METHOD_POST, $path, $body); } /** * Send PUT Request * * @param string $path * @param string|array body * @return bool */ public function put($path = '/', $body) { return $this->requestWithBody(WBRest::METHOD_PUT, $path, $body); } /** * Send Request With Body * * @param string $path * @param string|array body * @return bool */ private function requestWithBody($method, $path = '/', $body) { if (!empty($this->header['Content-Type'])) { $type = explode(';', $this->header['Content-Type']); switch ($type[0]) { case 'application/json': $body = json_encode($body); break; default: break; } } $this->response = $this->request($path, $body, $method); return $this->response->isOK(); } /** * Get Response Object * * @return WBStream_Response_Http */ public function getRespone() { return $this->response; } /** * Send HTTP Request * * The return value is a simple array. In case of an error (access denied and such) it is empty. * Otherwise it is filled with decoded data from JSON response * * @todo improve debug print * @param string enpoint of HTTP request * @param string request data, if applicaple * @param string HTTP request method like GET * @return WBStream_Response_Http */ private function request($endpoint, $content = null, $method = WBRest::METHOD_GET) { $endpoint = trim($endpoint, '/'); $url = sprintf('%s://%s:%s%s/%s', $this->config['protocol'], $this->config['server'], $this->config['port'], $this->config['pathprefix'], $endpoint); $log = array( 'protocol' => $this->config['protocol'], 'server' => $this->config['server'], 'port' => $this->config['port'], 'prefix' => $this->config['pathprefix'], 'endpoint' => $endpoint, 'method' => $method, 'status' => 'success', 'elapsed' => 0 ); $start = WBClock::now(); $opt = array( 'method' => strtoupper($method), 'protocol_version' => 1.1, 'ignore_errors' => true, 'user_agent' => $this->config['useragent'] ); if (!empty($content)) { $opt['content'] = $content; $this->setHeader('Content-Length', strlen($content)); } $opt['header'] = $this->prepareHeader4Http($method); // insecure mode if (1 > $this->config['verifycert']) { $opt['ssl'] = array( 'verify_peer' => false, 'verify_peer_name' => false, ); } $stream = WBStream::open($url, 'r', false, $opt); /** @var WBStream_Response_Http */ $response = WBClass::create('WBStream_Response_Http'); if (empty($stream)) { $log['status'] = 'error'; $log['error'] = 'fopen failed'; $log['message'] = 'failed to open URL'; $log['elapsed'] = WBClock::stop($start); $this->log->err($log); return $response; } $response->setStream($stream); if ($response->isOK()) { $log['elapsed'] = WBClock::stop($start); $this->log->notice($log); return $response; } // debug print to be improved if (10 <= WBParam::get('wb/debug', 0)) { echo "REQUEST " . $opt['method'] . " $url\n"; echo ' HEADER ' . print_r($opt['header'], true) . "\n"; echo ' BODY ' . $opt['content'] . "\n"; echo "RESPONSE \n"; echo ' STATUS ' . $response->getStatus() . "\n"; echo ' HEADER ' . print_r($response->getHeader(), true) . "\n"; echo ' BODY LENGTH ' . strlen($response->getRawContent()) . "\n"; echo ' BODY ' . print_r($response->getContent(), true) . "\n"; } $log['elapsed'] = WBClock::stop($start); $log['status'] = 'error'; $log['error'] = 'http status ' . $response->getStatus(); if (500 <= $response->getStatus()) { $log['message'] = 'Maybe some server issues'; $this->log->err($log); return $response; } if (400 <= $response->getStatus()) { $log['message'] = 'Maybe access is denied, check credentials.'; $this->log->err($log); return $response; } return $response; } /** * Prepare Header for HTTP Request * * Convert associative array to lines * * @param string http method * @return string */ private function prepareHeader4Http($method) { $line = array(); foreach ($this->header as $k => $v) { switch ($method) { case WBRest::METHOD_GET: case WBRest::METHOD_DELETE: if ('Content-Type' == $k) { continue 2; } break; default: break; } $line[] = sprintf('%s: %s', $k, $v); } return implode("\r\n", $line); } }