]> git.mxchange.org Git - friendica.git/blob - src/Network/CurlResult.php
Merge pull request #6235 from annando/curl-notice
[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          */
81         public static function createErrorCurl($url = '')
82         {
83                 return new CurlResult($url, '', ['http_code' => 0]);
84         }
85
86         /**
87          * Curl constructor.
88          * @param string $url the URL which was called
89          * @param string $result the result of the curl execution
90          * @param array $info an additional info array
91          * @param int $errorNumber the error number or 0 (zero) if no error
92          * @param string $error the error message or '' (the empty string) if no
93          *
94          * @throws InternalServerErrorException when HTTP code of the CURL response is missing
95          */
96         public function __construct($url, $result, $info, $errorNumber = 0, $error = '')
97         {
98                 if (!array_key_exists('http_code', $info)) {
99                         throw new InternalServerErrorException('CURL response doesn\'t contains a response HTTP code');
100                 }
101
102                 $this->returnCode = $info['http_code'];
103                 $this->url = $url;
104                 $this->info = $info;
105                 $this->errorNumber = $errorNumber;
106                 $this->error = $error;
107
108                 Logger::log($url . ': ' . $this->returnCode . " " . $result, Logger::DATA);
109
110                 $this->parseBodyHeader($result);
111                 $this->checkSuccess();
112                 $this->checkRedirect();
113                 $this->checkInfo();
114         }
115
116         private function parseBodyHeader($result)
117         {
118                 // Pull out multiple headers, e.g. proxy and continuation headers
119                 // allow for HTTP/2.x without fixing code
120
121                 $header = '';
122                 $base = $result;
123                 while (preg_match('/^HTTP\/.+? \d+/', $base)) {
124                         $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4);
125                         $header .= $chunk;
126                         $base = substr($base, strlen($chunk));
127                 }
128
129                 $this->body = substr($result, strlen($header));
130                 $this->header = $header;
131         }
132
133         private function checkSuccess()
134         {
135                 $this->isSuccess = ($this->returnCode >= 200 && $this->returnCode <= 299) || $this->errorNumber == 0;
136
137                 // Everything higher or equal 400 is not a success
138                 if ($this->returnCode >= 400) {
139                         $this->isSuccess = false;
140                 }
141
142                 if (!$this->isSuccess) {
143                         Logger::log('error: ' . $this->url . ': ' . $this->returnCode . ' - ' . $this->error, Logger::INFO);
144                         Logger::log('debug: ' . print_r($this->info, true), Logger::DATA);
145                 }
146
147                 if (!$this->isSuccess && $this->errorNumber == CURLE_OPERATION_TIMEDOUT) {
148                         $this->isTimeout = true;
149                 } else {
150                         $this->isTimeout = false;
151                 }
152         }
153
154         private function checkRedirect()
155         {
156                 if (!array_key_exists('url', $this->info)) {
157                         $this->redirectUrl = '';
158                 } else {
159                         $this->redirectUrl = $this->info['url'];
160                 }
161
162                 if ($this->returnCode == 301 || $this->returnCode == 302 || $this->returnCode == 303 || $this->returnCode== 307) {
163                         $redirect_parts = parse_url(defaults($this->info, 'redirect_url', ''));
164                         if (empty($redirect_parts)) {
165                                 $redirect_parts = [];
166                         }
167
168                         if (preg_match('/(Location:|URI:)(.*?)\n/i', $this->header, $matches)) {
169                                 $redirect_parts2 = parse_url(trim(array_pop($matches)));
170                                 if (!empty($redirect_parts2)) {
171                                         $redirect_parts = array_merge($redirect_parts, $redirect_parts2);
172                                 }
173                         }
174
175                         $parts = parse_url(defaults($this->info, 'url', ''));
176                         if (empty($parts)) {
177                                 $parts = [];
178                         }
179
180                         /// @todo Checking the corresponding RFC which parts of a redirect can be ommitted.
181                         $components = ['scheme', 'host', 'path', 'query', 'fragment'];
182                         foreach ($components as $component) {
183                                 if (empty($redirect_parts[$component]) && !empty($parts[$component])) {
184                                         $redirect_parts[$component] = $parts[$component];
185                                 }
186                         }
187
188                         $this->redirectUrl = Network::unparseURL($redirect_parts);
189
190                         $this->isRedirectUrl = true;
191                 } else {
192                         $this->isRedirectUrl = false;
193                 }
194         }
195
196         private function checkInfo()
197         {
198                 if (isset($this->info['content_type'])) {
199                         $this->contentType = $this->info['content_type'];
200                 } else {
201                         $this->contentType = '';
202                 }
203         }
204
205         /**
206          * Gets the Curl Code
207          *
208          * @return string The Curl Code
209          */
210         public function getReturnCode()
211         {
212                 return $this->returnCode;
213         }
214
215         /**
216          * Returns the Curl Content Type
217          *
218          * @return string the Curl Content Type
219          */
220         public function getContentType()
221         {
222                 return $this->contentType;
223         }
224
225         /**
226          * Returns the Curl headers
227          *
228          * @return string the Curl headers
229          */
230         public function getHeader()
231         {
232                 return $this->header;
233         }
234
235         /**
236          * @return bool
237          */
238         public function isSuccess()
239         {
240                 return $this->isSuccess;
241         }
242
243         /**
244          * @return string
245          */
246         public function getUrl()
247         {
248                 return $this->url;
249         }
250
251         /**
252          * @return string
253          */
254         public function getRedirectUrl()
255         {
256                 return $this->redirectUrl;
257         }
258
259         /**
260          * @return string
261          */
262         public function getBody()
263         {
264                 return $this->body;
265         }
266
267         /**
268          * @return array
269          */
270         public function getInfo()
271         {
272                 return $this->info;
273         }
274
275         /**
276          * @return bool
277          */
278         public function isRedirectUrl()
279         {
280                 return $this->isRedirectUrl;
281         }
282
283         /**
284          * @return int
285          */
286         public function getErrorNumber()
287         {
288                 return $this->errorNumber;
289         }
290
291         /**
292          * @return string
293          */
294         public function getError()
295         {
296                 return $this->error;
297         }
298
299         /**
300          * @return bool
301          */
302         public function isTimeout()
303         {
304                 return $this->isTimeout;
305         }
306 }