]> git.mxchange.org Git - friendica.git/blob - src/Network/CurlResult.php
New function to check for key existance
[friendica.git] / src / Network / CurlResult.php
1 <?php
2
3 namespace Friendica\Network;
4
5 use Friendica\Core\Logger;
6 use Friendica\Network\HTTPException\InternalServerErrorException;
7 use Friendica\Util\Network;
8
9 /**
10  * A content class for Curl call results
11  */
12 class CurlResult
13 {
14         /**
15          * @var int HTTP return code or 0 if timeout or failure
16          */
17         private $returnCode;
18
19         /**
20          * @var string the content type of the Curl call
21          */
22         private $contentType;
23
24         /**
25          * @var string the HTTP headers of the Curl call
26          */
27         private $header;
28
29         /**
30          * @var boolean true (if HTTP 2xx result) or false
31          */
32         private $isSuccess;
33
34         /**
35          * @var string the URL which was called
36          */
37         private $url;
38
39         /**
40          * @var string in case of redirect, content was finally retrieved from this URL
41          */
42         private $redirectUrl;
43
44         /**
45          * @var string fetched content
46          */
47         private $body;
48
49         /**
50          * @var array some informations about the fetched data
51          */
52         private $info;
53
54         /**
55          * @var boolean true if the URL has a redirect
56          */
57         private $isRedirectUrl;
58
59         /**
60          * @var boolean true if the curl request timed out
61          */
62         private $isTimeout;
63
64         /**
65          * @var int the error number or 0 (zero) if no error
66          */
67         private $errorNumber;
68
69         /**
70          * @var string the error message or '' (the empty string) if no
71          */
72         private $error;
73
74         /**
75          * Creates an errored CURL response
76          *
77          * @param string $url optional URL
78          *
79          * @return CurlResult a CURL with error response
80          * @throws InternalServerErrorException
81          */
82         public static function createErrorCurl($url = '')
83         {
84                 return new CurlResult($url, '', ['http_code' => 0]);
85         }
86
87         /**
88          * Curl constructor.
89          * @param string $url the URL which was called
90          * @param string $result the result of the curl execution
91          * @param array $info an additional info array
92          * @param int $errorNumber the error number or 0 (zero) if no error
93          * @param string $error the error message or '' (the empty string) if no
94          *
95          * @throws InternalServerErrorException when HTTP code of the CURL response is missing
96          */
97         public function __construct($url, $result, $info, $errorNumber = 0, $error = '')
98         {
99                 if (!array_key_exists('http_code', $info)) {
100                         throw new InternalServerErrorException('CURL response doesn\'t contains a response HTTP code');
101                 }
102
103                 $this->returnCode = $info['http_code'];
104                 $this->url = $url;
105                 $this->info = $info;
106                 $this->errorNumber = $errorNumber;
107                 $this->error = $error;
108
109                 Logger::log($url . ': ' . $this->returnCode . " " . $result, Logger::DATA);
110
111                 $this->parseBodyHeader($result);
112                 $this->checkSuccess();
113                 $this->checkRedirect();
114                 $this->checkInfo();
115         }
116
117         private function parseBodyHeader($result)
118         {
119                 // Pull out multiple headers, e.g. proxy and continuation headers
120                 // allow for HTTP/2.x without fixing code
121
122                 $header = '';
123                 $base = $result;
124                 while (preg_match('/^HTTP\/.+? \d+/', $base)) {
125                         $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4);
126                         $header .= $chunk;
127                         $base = substr($base, strlen($chunk));
128                 }
129
130                 $this->body = substr($result, strlen($header));
131                 $this->header = $header;
132         }
133
134         private function checkSuccess()
135         {
136                 $this->isSuccess = ($this->returnCode >= 200 && $this->returnCode <= 299) || $this->errorNumber == 0;
137
138                 // Everything higher or equal 400 is not a success
139                 if ($this->returnCode >= 400) {
140                         $this->isSuccess = false;
141                 }
142
143                 if (!$this->isSuccess) {
144                         Logger::log('error: ' . $this->url . ': ' . $this->returnCode . ' - ' . $this->error, Logger::INFO);
145                         Logger::log('debug: ' . print_r($this->info, true), Logger::DATA);
146                 }
147
148                 if (!$this->isSuccess && $this->errorNumber == CURLE_OPERATION_TIMEDOUT) {
149                         $this->isTimeout = true;
150                 } else {
151                         $this->isTimeout = false;
152                 }
153         }
154
155         private function checkRedirect()
156         {
157                 if (!array_key_exists('url', $this->info)) {
158                         $this->redirectUrl = '';
159                 } else {
160                         $this->redirectUrl = $this->info['url'];
161                 }
162
163                 if ($this->returnCode == 301 || $this->returnCode == 302 || $this->returnCode == 303 || $this->returnCode== 307) {
164                         $redirect_parts = parse_url(defaults($this->info, 'redirect_url', ''));
165                         if (empty($redirect_parts)) {
166                                 $redirect_parts = [];
167                         }
168
169                         if (preg_match('/(Location:|URI:)(.*?)\n/i', $this->header, $matches)) {
170                                 $redirect_parts2 = parse_url(trim(array_pop($matches)));
171                                 if (!empty($redirect_parts2)) {
172                                         $redirect_parts = array_merge($redirect_parts, $redirect_parts2);
173                                 }
174                         }
175
176                         $parts = parse_url(defaults($this->info, 'url', ''));
177                         if (empty($parts)) {
178                                 $parts = [];
179                         }
180
181                         /// @todo Checking the corresponding RFC which parts of a redirect can be ommitted.
182                         $components = ['scheme', 'host', 'path', 'query', 'fragment'];
183                         foreach ($components as $component) {
184                                 if (empty($redirect_parts[$component]) && !empty($parts[$component])) {
185                                         $redirect_parts[$component] = $parts[$component];
186                                 }
187                         }
188
189                         $this->redirectUrl = Network::unparseURL($redirect_parts);
190
191                         $this->isRedirectUrl = true;
192                 } else {
193                         $this->isRedirectUrl = false;
194                 }
195         }
196
197         private function checkInfo()
198         {
199                 if (isset($this->info['content_type'])) {
200                         $this->contentType = $this->info['content_type'];
201                 } else {
202                         $this->contentType = '';
203                 }
204         }
205
206         /**
207          * Gets the Curl Code
208          *
209          * @return string The Curl Code
210          */
211         public function getReturnCode()
212         {
213                 return $this->returnCode;
214         }
215
216         /**
217          * Returns the Curl Content Type
218          *
219          * @return string the Curl Content Type
220          */
221         public function getContentType()
222         {
223                 return $this->contentType;
224         }
225
226         /**
227          * Returns the Curl headers
228          *
229          * @param string $field optional header field. Return all fields if empty
230          *
231          * @return string the Curl headers or the specified content of the header variable
232          */
233         public function getHeader(string $field = '')
234         {
235                 if (empty($field)) {
236                         return $this->header;
237                 }
238
239                 $field = strtolower(trim($field));
240
241                 $headers = self::getHeaderArray();
242
243                 if (isset($headers[$field])) {
244                         return $headers[$field];
245                 }
246         }
247
248         /**
249          * Check if a specified header exists
250          *
251          * @param string $field header field
252          *
253          * @return boolean "true" if header exists
254          */
255         public function headerExists(string $field)
256         {
257                 $field = strtolower(trim($field));
258
259                 $headers = self::getHeaderArray();
260
261                 return array_key_exists($field, $headers);
262         }
263
264         /**
265          * Returns the Curl headers as an associated array
266          *
267          * @return array associated header array
268          */
269         public function getHeaderArray()
270         {
271                 $headers = [];
272
273                 $lines = explode("\n", $this->header);
274                 foreach ($lines as $line) {
275                         $parts = explode(':', $line);
276                         $headerfield = strtolower(trim(array_shift($parts)));
277                         $headerdata = trim(implode(':', $parts));
278
279                         if (!empty($headerdata)) {
280                                 $headers[$headerfield] = $headerdata;
281                         }
282                 }
283
284                 return $headers;
285         }
286
287         /**
288          * @return bool
289          */
290         public function isSuccess()
291         {
292                 return $this->isSuccess;
293         }
294
295         /**
296          * @return string
297          */
298         public function getUrl()
299         {
300                 return $this->url;
301         }
302
303         /**
304          * @return string
305          */
306         public function getRedirectUrl()
307         {
308                 return $this->redirectUrl;
309         }
310
311         /**
312          * @return string
313          */
314         public function getBody()
315         {
316                 return $this->body;
317         }
318
319         /**
320          * @return array
321          */
322         public function getInfo()
323         {
324                 return $this->info;
325         }
326
327         /**
328          * @return bool
329          */
330         public function isRedirectUrl()
331         {
332                 return $this->isRedirectUrl;
333         }
334
335         /**
336          * @return int
337          */
338         public function getErrorNumber()
339         {
340                 return $this->errorNumber;
341         }
342
343         /**
344          * @return string
345          */
346         public function getError()
347         {
348                 return $this->error;
349         }
350
351         /**
352          * @return bool
353          */
354         public function isTimeout()
355         {
356                 return $this->isTimeout;
357         }
358 }