1 <?php
2 /**
3 * HTTPRequest.php
4 *
5 * @author Elvyrra SAS
6 * @license http://rem.mit-license.org/ MIT
7 */
8
9 namespace Hawk;
10
11 /**
12 * This class is used to send HTTP request and get the result
13 *
14 * @package Network
15 */
16 class HTTPRequest{
17 use Utils;
18
19 const METHOD_POST = 'POST';
20 const METHOD_GET = 'GET';
21 const METHOD_PUT = 'PUT';
22 const METHOD_DELETE = 'DELETE';
23 const METHOD_PATCH = 'PATCH';
24
25 /**
26 * The URL to call
27 *
28 * @var string
29 */
30 private $url,
31
32 /**
33 * The HTTP method
34 *
35 * @var string
36 */
37 $method = self::METHOD_GET,
38
39 /**
40 * The data to send in the request body
41 *
42 * @var array
43 */
44 $body = array(),
45
46 /**
47 * The files to upload
48 */
49 $files = array(),
50
51 /**
52 * The request headers
53 *
54 * @var array
55 */
56 $headers = array(),
57
58 /**
59 * The response
60 *
61 * @var string
62 */
63 $response = '',
64
65
66 /**
67 * The request content type
68 *
69 * @var sting
70 */
71 $contentType = 'urlencoded',
72
73
74 /**
75 * The response content type
76 *
77 * @var string
78 */
79 $dataType = 'html',
80
81 /**
82 * The response headers
83 *
84 * @var array
85 */
86 $responseHeaders = array(),
87
88 /**
89 * The HTTP status code of the response
90 *
91 * @var int
92 */
93 $status = 0;
94
95
96 /**
97 * Standard data types
98 *
99 * @var array
100 */
101 private static $dataTypes = array(
102 'text' => 'text/plain',
103 'html' => 'text/html',
104 'json' => 'application/json',
105 'xml' => 'application/xml',
106 'urlencoded' => 'application/x-www-form-urlencoded'
107 );
108
109
110 /**
111 * Create a HTTP request
112 *
113 * @param array $options The request parameters. This array can have the following data :
114 * - 'url' (required): The url to call
115 * - 'method' (optionnal, default 'GET') : The HTTP method
116 * - 'body' (optionnal) : The request content
117 * - 'files' (optionnal) : The files to upload (the filenames)
118 * - 'headers' (optionnal) : The request headers
119 * - 'dataType' (optionnal, default 'html') : The wanted response type
120 * - 'contentType' (optionnal) : The request content type
121 */
122 public function __construct($options){
123 $this->map($options);
124
125 $this->setDataType($this->dataType);
126 $this->setContentType($this->contentType);
127 }
128
129
130 /**
131 * Set further headers
132 *
133 * @param array $headers The headers to add
134 */
135 public function setHeaders($headers){
136 $this->headers = array_merge($this->headers, $headers);
137 }
138
139 /**
140 * Set the request body
141 *
142 * @param mixed $body The body to set
143 */
144 public function setBody($body){
145 $this->body = $body;
146 }
147
148 /**
149 * Set the files to upload
150 *
151 * @param array $files The list of filenames to upload
152 */
153 public function setFiles($files){
154 $this->files = $files;
155 }
156
157 /**
158 * Set the expected data type
159 *
160 * @param string $type The expected type of response data. Can be 'text', 'html', 'json', 'xml' or the wanted mime type
161 */
162 public function setDataType($type){
163 if(isset(self::$dataTypes[$type])) {
164 $value = self::$dataTypes[$type];
165 }
166 else{
167 $value = $type;
168 }
169
170 $this->dataType = $type;
171 }
172
173
174 /**
175 * Set the request Content type
176 *
177 * @param string $type The expected type of response data. Can be 'text', 'html', 'json', 'xml' or the wanted mime type
178 */
179 public function setContentType($type){
180 if(isset(self::$dataTypes[$type])) {
181 $value = self::$dataTypes[$type];
182 }
183 else{
184 $value = $type;
185 }
186 $this->setHeaders(
187 array(
188 'Content-Type' => $value
189 )
190 );
191
192 $this->contentType = $type;
193 }
194
195
196 /**
197 * Build the HTTP resquest body
198 */
199 private function build(){
200 if(!empty($this->files)) {
201 // Upload files
202 $data = '';
203 $boundary = '----' . uniqid();
204
205 $this->setContentType('multipart/form-data; boundary=' . $boundary);
206
207 foreach($this->files as $name => $filename){
208 // Add all files
209 $data .= '--' . $boundary . "\r\n" .
210 'Content-Disposition: form-data; name="' . $name . '"; filename="' . basename($filename) . "\"\r\n" .
211 "Content-Type: application/octet-stream\r\n\r\n" .
212 file_get_contents($filename) . "\r\n";
213 }
214
215 foreach($this->body as $key => $value){
216 // Add post data
217 $data .= '--' . $boundary . "\r\n" .
218 'Content-Disposition: form-data; name="' . $key . "\"\r\n\r\n" .
219 $value . "\r\n";
220 }
221
222 $data .= '--' . $boundary . '--';
223
224 $this->setHeaders(
225 array(
226 'Content-Length' => strlen($data)
227 )
228 );
229 return $data;
230 }
231 else{
232 switch($this->contentType){
233 case 'urlencoded' :
234 return http_build_query($this->body);
235
236 case 'json' :
237 return json_encode($this->body);
238
239 default :
240 return $this->body;
241 }
242 }
243 }
244
245
246 /**
247 * Send the request and get the result
248 */
249 public function send(){
250 $data = $this->build();
251
252 $opts = array('http' =>
253 array(
254 'method' => strtoupper($this->method),
255 'ignore_errors' => '1',
256 'header' => implode(
257 PHP_EOL,
258 array_map(
259 function ($key, $value) {
260 return "$key: $value";
261 },
262 array_keys($this->headers),
263 $this->headers
264 )
265 ),
266 'content' => $data
267 )
268 );
269
270 $context = stream_context_create($opts);
271
272 $result = @file_get_contents($this->url, false, $context);
273 if(!empty($http_response_header)) {
274 foreach($http_response_header as $header){
275 if(preg_match('/^(.*?)\:\s+(.*)$/', $header, $match)) {
276 $this->responseHeaders[$match[1]] = $match[2];
277 }
278 elseif(preg_match('/^HTTP\/[^\s]+\s+(\d+)/', $header, $match)) {
279 $this->status = (int) $match[1];
280 }
281 }
282 }
283 else{
284 $this->status = 404;
285 }
286
287 $this->response = $result;
288 }
289
290
291 /**
292 * Get the response headers. If $name is set, then this function will return the specific header value, else it will return an array containing all headers
293 *
294 * @param string $name The name of a specific
295 *
296 * @return mixed The array containing all headers, or the value of the headers specified by $name
297 */
298 public function getResponseHeaders($name = null){
299 if(!$name) {
300 return $this->responseHeaders;
301 }
302 else{
303 return isset($this->responseHeaders[$name]) ? $this->responseHeaders[$name] : null;
304 }
305
306 }
307
308
309 /**
310 * Get the response status Code
311 *
312 * @return int the value of the response status code
313 */
314 public function getStatusCode(){
315 return $this->status;
316 }
317
318
319 /**
320 * Get the response body
321 *
322 * @return mixed The HTTP response body, formatted following the dataType requested
323 */
324 public function getResponse(){
325 switch($this->dataType){
326 case 'json' :
327 return json_decode($this->response, true);
328
329 default :
330 return $this->response;
331 }
332 }
333 }
334