@@ -296,13 +296,14 @@ public static function _encodeArray($value, $allowParseObjects)
296296 /**
297297 * Parse\Client::_request, internal method for communicating with Parse.
298298 *
299- * @param string $method HTTP Method for this request.
300- * @param string $relativeUrl REST API Path.
301- * @param null $sessionToken Session Token.
302- * @param null $data Data to provide with the request.
303- * @param bool $useMasterKey Whether to use the Master Key.
304- * @param bool $appRequest App request to create or modify a application
305- * @param string $contentType The content type for this request, default is application/json
299+ * @param string $method HTTP Method for this request.
300+ * @param string $relativeUrl REST API Path.
301+ * @param null $sessionToken Session Token.
302+ * @param null $data Data to provide with the request.
303+ * @param bool $useMasterKey Whether to use the Master Key.
304+ * @param bool $appRequest App request to create or modify a application
305+ * @param string $contentType The content type for this request, default is application/json
306+ * @param bool $returnHeaders Allow to return response headers
306307 *
307308 * @throws \Exception
308309 *
@@ -315,7 +316,8 @@ public static function _request(
315316 $ data = null ,
316317 $ useMasterKey = false ,
317318 $ appRequest = false ,
318- $ contentType = 'application/json '
319+ $ contentType = 'application/json ' ,
320+ $ returnHeaders = false
319321 ) {
320322 if ($ data === '[] ' ) {
321323 $ data = '{} ' ;
@@ -329,34 +331,46 @@ public static function _request(
329331 }
330332
331333 $ url = self ::$ serverURL .'/ ' .self ::$ mountPath .ltrim ($ relativeUrl , '/ ' );
334+
332335 if ($ method === 'GET ' && !empty ($ data )) {
333336 $ url .= '? ' .http_build_query ($ data );
334337 }
338+
335339 $ rest = curl_init ();
336340 curl_setopt ($ rest , CURLOPT_URL , $ url );
337341 curl_setopt ($ rest , CURLOPT_RETURNTRANSFER , 1 );
342+
338343 if ($ method === 'POST ' ) {
339344 $ headers [] = 'Content-Type: ' .$ contentType ;
340345 curl_setopt ($ rest , CURLOPT_POST , 1 );
341346 curl_setopt ($ rest , CURLOPT_POSTFIELDS , $ data );
342347 }
348+
343349 if ($ method === 'PUT ' ) {
344350 $ headers [] = 'Content-Type: ' .$ contentType ;
345351 curl_setopt ($ rest , CURLOPT_CUSTOMREQUEST , $ method );
346352 curl_setopt ($ rest , CURLOPT_POSTFIELDS , $ data );
347353 }
354+
348355 if ($ method === 'DELETE ' ) {
349356 curl_setopt ($ rest , CURLOPT_CUSTOMREQUEST , $ method );
350357 }
358+
351359 curl_setopt ($ rest , CURLOPT_HTTPHEADER , $ headers );
352360
353361 if (!is_null (self ::$ connectionTimeout )) {
354362 curl_setopt ($ rest , CURLOPT_CONNECTTIMEOUT , self ::$ connectionTimeout );
355363 }
364+
356365 if (!is_null (self ::$ timeout )) {
357366 curl_setopt ($ rest , CURLOPT_TIMEOUT , self ::$ timeout );
358367 }
359368
369+ if ($ returnHeaders ) {
370+ curl_setopt ($ rest , CURLOPT_HEADER , 1 );
371+ curl_setopt ($ rest , CURLOPT_FOLLOWLOCATION , true );
372+ }
373+
360374 $ response = curl_exec ($ rest );
361375 $ status = curl_getinfo ($ rest , CURLINFO_HTTP_CODE );
362376 $ contentType = curl_getinfo ($ rest , CURLINFO_CONTENT_TYPE );
@@ -367,6 +381,16 @@ public static function _request(
367381 return false ;
368382 }
369383 }
384+
385+ $ headerData = [];
386+
387+ if ($ returnHeaders ) {
388+ $ headerSize = curl_getinfo ($ rest , CURLINFO_HEADER_SIZE );
389+ $ headerContent = substr ($ response , 0 , $ headerSize );
390+ $ headerData = self ::parseCurlHeaders ($ headerContent );
391+ $ response = substr ($ response , $ headerSize );
392+ }
393+
370394 curl_close ($ rest );
371395 if (strpos ($ contentType , 'text/html ' ) !== false ) {
372396 throw new ParseException ('Bad Request ' , -1 );
@@ -383,9 +407,57 @@ public static function _request(
383407 );
384408 }
385409
410+ if ($ returnHeaders ) {
411+ $ decoded ['_headers ' ] = $ headerData ;
412+ }
413+
386414 return $ decoded ;
387415 }
388416
417+ /**
418+ * ParseClient::parseCurlHeaders, will parse headers data and returns it as array.
419+ * @param $headerContent
420+ *
421+ * @return array
422+ */
423+ private static function parseCurlHeaders ($ headerContent )
424+ {
425+ $ headers = [];
426+ $ headersContentSet = explode ("\r\n\r\n" , $ headerContent );
427+ $ withRedirect = count ($ headersContentSet ) > 2 ;
428+
429+ if ($ withRedirect ) {
430+ $ headers ['_previous ' ] = [];
431+ }
432+
433+ foreach ($ headersContentSet as $ headerIndex => $ headersData ) {
434+ if (empty ($ headersData )) {
435+ continue ;
436+ }
437+
438+ if ($ withRedirect && $ headerIndex === 0 ) {
439+ $ storage = &$ headers ['_previous ' ];
440+ } else {
441+ $ storage = &$ headers ;
442+ }
443+
444+ $ exploded = explode ("\r\n" , $ headersData );
445+
446+ foreach ($ exploded as $ i => $ line ) {
447+ if (empty ($ line )) {
448+ continue ;
449+ } elseif ($ i === 0 ) {
450+ $ storage ['http_status ' ] = $ line ;
451+ } else {
452+ list ($ headerName , $ headerValue ) = explode (': ' , $ line );
453+ $ storage [$ headerName ] = $ headerValue ;
454+ }
455+ }
456+ }
457+
458+ return $ headers ;
459+ }
460+
389461 /**
390462 * ParseClient::setStorage, will update the storage object used for
391463 * persistence.
0 commit comments