1: <?php
2: namespace NGS\Client;
3:
4: require_once(__DIR__.'/../Utils.php');
5: require_once(__DIR__.'/../Name.php');
6:
7: require_once(__DIR__.'/HttpRequest.php');
8: require_once(__DIR__.'/Exception/InvalidRequestException.php');
9: require_once(__DIR__.'/Exception/NotFoundException.php');
10: require_once(__DIR__.'/Exception/RequestException.php');
11: require_once(__DIR__.'/Exception/SecurityException.php');
12: require_once(__DIR__.'/Exception/ClientErrorException.php');
13: require_once(__DIR__.'/Exception/ServerErrorException.php');
14: require_once(__DIR__.'/../Converter/PrimitiveConverter.php');
15: require_once(__DIR__.'/../Converter/ObjectConverter.php');
16: require_once(__DIR__.'/QueryString.php');
17:
18: use NGS\Client\Exception\InvalidRequestException;
19: use NGS\Client\Exception\NotFoundException;
20: use NGS\Client\Exception\RequestException;
21: use NGS\Client\Exception\SecurityException;
22: use NGS\Client\Exception\ServerErrorException;
23: use NGS\Client\Exception\ClientErrorException;
24: use NGS\Converter\PrimitiveConverter;
25: use NGS\Converter\ObjectConverter;
26:
27: 28: 29: 30: 31:
32: class RestHttp
33: {
34: const EVENT_REQUEST_BEFORE = 'request.before';
35: const EVENT_REQUEST_SENT = 'request.sent';
36: const EVENT_REQUEST_ERROR = 'request.error';
37:
38: protected $subscribers = array();
39:
40: 41: 42:
43: protected $service;
44:
45: 46: 47:
48: protected $username;
49:
50: 51: 52:
53: protected $auth;
54: protected $certPath;
55:
56: 57: 58:
59: protected $lastResponse;
60:
61: 62: 63:
64: protected static $instance;
65:
66: 67: 68: 69: 70: 71: 72:
73: public function __construct($service, $username=null, $password=null)
74: {
75: $this->service = $service;
76: if ($username!==null && $password!==null) {
77: $this->setAuth($username, $password);
78: }
79: }
80:
81: 82: 83: 84: 85: 86:
87: public function setAuth($username, $password)
88: {
89: $this->username = $username;
90: $this->auth = 'Basic '.base64_encode($username.':'.$password);
91: }
92:
93: public function getUsername()
94: {
95: return $this->username;
96: }
97:
98: public function setCertificate($certPath) {
99: $this->certPath = $certPath;
100: }
101:
102: 103: 104: 105: 106: 107:
108: public static function instance(RestHttp $http = null)
109: {
110: if($http === null)
111: return self::$instance;
112: self::$instance = $http;
113: }
114:
115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127:
128: public function sendRequest(
129: $uriSegment,
130: $method = 'GET',
131: $body = null,
132: array $expectedCode = null,
133: $accept = 'application/json')
134: {
135: $options = array();
136: if (isset($this->certPath)) {
137: $options[CURLOPT_CAINFO] = $this->certPath;
138: }
139:
140: $request = new HttpRequest($this->service.$uriSegment, $method, null, null, $options);
141:
142: $requestHeaders = array(
143: 'Accept: '.$accept,
144: 'Content-type: application/json',
145: 'Authorization: '.$this->auth,
146:
147: );
148: if (($method==='PUT' || $method==='POST') && ($body===null || strlen($body)===0)){
149: $requestHeaders[] = 'Content-length: 0';
150: }
151:
152: $request->headers($requestHeaders);
153:
154: if($body !== null)
155: $request->body($body);
156:
157: if ($this->hasSubscribers())
158: $this->dispatch(self::EVENT_REQUEST_BEFORE, array(
159: 'request' => $request,
160: ));
161:
162: $response = $request->send();
163:
164: $responseHeaders = $request->getResponseHeaders();
165: $this->lastResponse = array(
166: 'info' => $responseHeaders,
167: 'body' => $response
168: );
169:
170:
171: if($response === null && $this->hasSubscribers()) {
172: $this->dispatch(self::EVENT_REQUEST_ERROR, array(
173: 'error' => $request->getError()
174: ));
175: $ex = new RequestException('Failed to send request. '.$request->getError());
176: $ex->setRequest($request);
177: throw $ex;
178: }
179: $httpCode = $request->getResponseCode();
180: $contentType = $request->getResponseContentType();
181:
182: if ($this->hasSubscribers())
183: $this->dispatch(self::EVENT_REQUEST_SENT, array(
184: 'request' => $request,
185: 'response' => array(
186: 'body' => $response,
187: 'code' => $httpCode,
188: ),
189: 'curl_info' => $request->getResponseInfo(),
190: ));
191:
192: if($expectedCode !== null && !in_array($httpCode, $expectedCode)) {
193: switch($contentType) {
194: case 'application/json':
195: $response = json_decode($response);
196: break;
197: case 'text/xml':
198: $xml = new \SimpleXmlIterator($response);
199: $response = (string) $xml;
200: break;
201: }
202: $message = trim($response);
203: if ($message==='') {
204: $message = 'Unexpected http code. Response body was empty. ';
205: }
206: if ($curlError = $request->getError()) {
207: $message .= 'Curl error: '.$curlError;
208: }
209:
210: $ex = false;
211: switch($httpCode) {
212: case 400:
213: $ex = new InvalidRequestException($message, $httpCode);
214: break;
215: case 401:
216: case 403:
217: $ex = new SecurityException($message, $httpCode);
218: break;
219: case 404:
220: $ex = new NotFoundException($message, $httpCode);
221: break;
222: case 413:
223: $ex = new RequestException('Request body was too large. '.$message, $httpCode);
224: break;
225: default:
226: if($httpCode < 300) {
227: $ex = new RequestException('Unexpected http code '.$httpCode.'. '.$message);
228: }
229: if ($httpCode>=400 && $httpCode < 500) {
230: $ex = new ClientErrorException($message, $httpCode);
231: }
232: if ($httpCode>=500 && $httpCode < 600) {
233: $ex = new ServerErrorException($message, $httpCode);
234: }
235: $ex = new RequestException($message, $httpCode);
236: break;
237: }
238: $ex->setRequest($request);
239: throw $ex;
240: } else {
241: return $response;
242: }
243: }
244:
245: public static function parseResult($response, $class = null)
246: {
247: $data = json_decode($response, true);
248: if($class !== null && is_array($data)) {
249: $converter = ObjectConverter::getConverter($class);
250: return $converter::fromJson($response);
251: }
252: return $data;
253: }
254:
255: public function getLastResult()
256: {
257: return $this->lastResponse;
258: }
259:
260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276:
277: public function addSubscriber($subscriber)
278: {
279: if (!is_callable($subscriber)) {
280: throw new \InvalidArgumentException('Subscriber must be callable type!');
281: }
282: $this->subscribers[] = $subscriber;
283: }
284:
285: 286: 287: 288: 289: 290:
291: protected function dispatch($event, array $context)
292: {
293: array_map(
294: function($subscriber) use ($event, $context) {
295: call_user_func_array($subscriber, array($event, $context));
296: },
297: $this->subscribers);
298: }
299:
300: private function hasSubscribers()
301: {
302: return !empty($this->subscribers);
303: }
304: }
305: