Overview

Namespaces

  • NGS
    • Client
      • Exception
    • Converter
    • Patterns
  • PHP

Classes

  • ApplicationProxy
  • CrudProxy
  • DomainProxy
  • HttpRequest
  • QueryString
  • ReportingProxy
  • RestHttp
  • StandardProxy
  • Overview
  • Namespace
  • Class
  • Tree
  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:  * HTTP client used communication with platform
 29:  * Should not be used directly, instead use domain patterns
 30:  * Requests can be monitored via {@see addSubscriber}
 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:      * @var string
 42:      */
 43:     protected $service;
 44: 
 45:     /**
 46:      * @var string
 47:      */
 48:     protected $username;
 49: 
 50:     /**
 51:      * @var string Authentication string
 52:      */
 53:     protected $auth;
 54:     protected $certPath;
 55: 
 56:     /**
 57:      * @var array
 58:      */
 59:     protected $lastResponse;
 60: 
 61:     /**
 62:      * @var RestHttp Singleton instance
 63:      */
 64:     protected static $instance;
 65: 
 66:     /**
 67:      * Creates new client instance
 68:      *
 69:      * @param string $service Service base url
 70:      * @param string $username
 71:      * @param string $password
 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:      * Set username/password used for http authentication
 83:      *
 84:      * @param string $username
 85:      * @param string $password
 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:      * Gets or sets singleton instance of rest Http
104:      *
105:      * @param RestHttp
106:      * @return RestHttp
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:      * Sends http request
117:      *
118:      * @param string $uriSegment   Appended to REST service uri
119:      * @param string $method       HTTP method
120:      * @param null   $body         Request body
121:      * @param array  $expectedCode Expected http codes, throw exception
122:      * @param string $accept
123:      * @throws Exception\InvalidRequestException|Exception\NotFoundException|Exception\RequestException|Exception\SecurityException
124:      * @throws Exception\RequestException
125:      * @internal param array $headers
126:      * @return mixed
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:             //'Content-length: 0'
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:         // no response received from server or curl errored out
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:      * Subscribe a callable to listen to HTTP request events
262:      *
263:      * Example use for simple logging:<br>
264:      * <code>
265:      * $http = RestHttp::instance();
266:      * $http->addSubscriber(function($event, $context) {
267:      *     if ($event === RestHttp::EVENT_REQUEST_SENT) {
268:      *         echo 'request sent';
269:      *         print_r($context);
270:      *     }
271:      * });
272:      * </code>
273:      *
274:      * @param callable $subscriber
275:      * @throws \InvalidArgumentException
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:      * Dispatches event to all subscribed listeners
287:      *
288:      * @param       $event
289:      * @param array $context
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: 
API documentation generated by ApiGen 2.8.0