Skip to content

Commit

Permalink
Enable writing response directly to file (#45)
Browse files Browse the repository at this point in the history
* Switch the reading of response headers to a dedicated function

* Add example of saving request response directly to file

* Add test for the  function

* Update array initialization format to support PHP < 5.4
  • Loading branch information
daniel-zahariev authored and amouhzi committed Aug 12, 2017
1 parent 4b62439 commit 16e8489
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 14 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ curl_set_opt($curl->curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0;
curl_close($curl->curl);
```

```php
// Example of downloading a file or any other content
$curl = new Curl\Curl();
// open the file where the request response should be written
$file_handle = fopen($target_file, 'w+');
// pass it to the curl resource
$curl->setOpt(CURLOPT_FILE, $file_handle);
// do any type of request
$curl->get('https://github.com');
// disable writing to file
$curl->setOpt(CURLOPT_FILE, null);
// close the file for writing
fclose($file_handle);
```


## Testing

In order to test the library:
Expand Down
46 changes: 33 additions & 13 deletions src/Curl/Curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,18 @@ class Curl
/**
* @var string|array TBD (ensure type) Contains the response header informations
*/
public $response_headers = null;
public $response_headers = array();

/**
* @var string Contains the response from the curl request
*/
public $response = null;

/**
* @var boolean Whether the current section of response headers is after 'HTTP/1.1 100 Continue'
*/
protected $response_header_continue = false;

/**
* Constructor ensures the available curl extension is loaded.
*
Expand Down Expand Up @@ -164,11 +169,35 @@ private function init()
$this->curl = curl_init();
$this->setUserAgent(self::USER_AGENT);
$this->setOpt(CURLINFO_HEADER_OUT, true);
$this->setOpt(CURLOPT_HEADER, true);
$this->setOpt(CURLOPT_HEADER, false);
$this->setOpt(CURLOPT_RETURNTRANSFER, true);
$this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'addResponseHeaderLine'));
return $this;
}

/**
* Handle writing the response headers
*
* @param resource $curl The current curl resource
* @param string $header_line A line from the list of response headers
*
* @return int Returns the length of the $header_line
*/
public function addResponseHeaderLine($curl, $header_line)
{
$trimmed_header = trim($header_line, "\r\n");

if ($trimmed_header === "") {
$this->response_header_continue = false;
} else if (strtolower($trimmed_header) === 'http/1.1 100 continue') {
$this->response_header_continue = true;
} else if (!$this->response_header_continue) {
$this->response_headers[] = $trimmed_header;
}

return strlen($header_line);
}

// protected methods

/**
Expand All @@ -178,6 +207,7 @@ private function init()
*/
protected function exec()
{
$this->response_headers = array();
$this->response = curl_exec($this->curl);
$this->curl_error_code = curl_errno($this->curl);
$this->curl_error_message = curl_error($this->curl);
Expand All @@ -186,17 +216,7 @@ protected function exec()
$this->http_error = in_array(floor($this->http_status_code / 100), array(4, 5));
$this->error = $this->curl_error || $this->http_error;
$this->error_code = $this->error ? ($this->curl_error ? $this->curl_error_code : $this->http_status_code) : 0;

$this->request_headers = preg_split('/\r\n/', curl_getinfo($this->curl, CURLINFO_HEADER_OUT), null, PREG_SPLIT_NO_EMPTY);
$this->response_headers = '';
if (!(strpos($this->response, "\r\n\r\n") === false)) {
list($response_header, $this->response) = explode("\r\n\r\n", $this->response, 2);
while (strtolower(trim($response_header)) === 'http/1.1 100 continue') {
list($response_header, $this->response) = explode("\r\n\r\n", $this->response, 2);
}
$this->response_headers = preg_split('/\r\n/', $response_header, null, PREG_SPLIT_NO_EMPTY);
}

$this->http_error_message = $this->error ? (isset($this->response_headers['0']) ? $this->response_headers['0'] : '') : '';
$this->error_message = $this->curl_error ? $this->curl_error_message : $this->http_error_message;

Expand Down Expand Up @@ -540,7 +560,7 @@ public function reset()
$this->http_status_code = 0;
$this->http_error_message = null;
$this->request_headers = null;
$this->response_headers = null;
$this->response_headers = array();
$this->response = null;
$this->init();
return $this;
Expand Down
15 changes: 14 additions & 1 deletion tests/CurlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ public function testHeaders() {
'key' => 'HTTP_ACCEPT',
)) === 'application/json');
}

public function testHeadersWithContinue() {
$headers = file(dirname(__FILE__) . '/data/response_headers_with_continue.txt');

$this->curl->response_headers = array();
foreach($headers as $header_line) {
$this->curl->addResponseHeaderLine(null, $header_line);
}

$expected_headers = array_values(array_filter(array_map(function($l) { return trim($l, "\r\n"); }, array_slice($headers, 1))));

$this->assertEquals($expected_headers, $this->curl->response_headers);
}

public function testReset()
{
Expand All @@ -236,7 +249,7 @@ public function testReset()
$this->assertSame(0, $curl->http_status_code);
$this->assertNull($curl->http_error_message);
$this->assertNull($curl->request_headers);
$this->assertNull($curl->response_headers);
$this->assertEmpty($curl->response_headers);
$this->assertNull($curl->response);
}

Expand Down
13 changes: 13 additions & 0 deletions tests/data/response_headers_with_continue.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Fri, 11 Aug 2017 13:22:00 GMT
Content-Type: image/jpeg
Content-Length: 62574
Connection: close
Cache-Control: max-age=7257600
Expires: Fri, 03 Nov 2017 13:22:00 GMT
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Option: DENY

0 comments on commit 16e8489

Please sign in to comment.