Skip to content

Commit d9c9a83

Browse files
committed
Issue #26 Implement custom headers
1 parent 9626f9c commit d9c9a83

File tree

2 files changed

+82
-21
lines changed

2 files changed

+82
-21
lines changed

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A convenience class for temporary files.
1212

1313
* Create temporary file with arbitrary content
1414
* Delete file after use (can be disabled)
15-
* Send file to client, either inline or with save dialog
15+
* Send file to client, either inline or with save dialog, optionally with custom HTTP headers
1616
* Save file locally
1717

1818
## Examples
@@ -25,6 +25,14 @@ $file = new File('some content', '.html');
2525

2626
// send to client for download
2727
$file->send('home.html');
28+
// ... with custom content type (autodetected otherwhise)
29+
$file->send('home.html', 'application/pdf');
30+
// ... for inline display (download dialog otherwhise)
31+
$file->send('home.html', 'application/pdf', true);
32+
// ... with custom headers
33+
$file->send('home.html', 'application/pdf', true, [
34+
'X-Header' => 'Example',
35+
]);
2836

2937
// save to disk
3038
$file->saveAs('/dir/test.html');
@@ -43,3 +51,14 @@ use mikehaertl\tmp\File;
4351
$file = new File('some content', '.html');
4452
$file->delete = false;
4553
```
54+
55+
Default HTTP headers can also be added:
56+
```php
57+
<?php
58+
use mikehaertl\tmp\File;
59+
60+
File::$defaultHeader['X-Header'] = 'My Default';
61+
62+
$file = new File('some content', '.html');
63+
$file->send('home.html');
64+
```

src/File.php

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ class File
1919
*/
2020
public $delete = true;
2121

22+
/**
23+
* @var array the list of static default headers to send when `send()` is
24+
* called as key/value pairs.
25+
*/
26+
public static $defaultHeaders = array(
27+
'Pragma' => 'public',
28+
'Expires' => 0,
29+
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
30+
'Content-Transfer-Encoding' => 'binary',
31+
);
32+
2233
/**
2334
* @var string the name of this file
2435
*/
@@ -73,40 +84,48 @@ public function __destruct()
7384
* 'application/octet-stream' is used.
7485
* @param bool $inline whether to force inline display of the file, even if
7586
* filename is present.
87+
* @param array $headers a list of additional HTTP headers to send in the
88+
* response as an array. The array keys are the header names like
89+
* 'Cache-Control' and the array values the header value strings to send.
90+
* Each array value can also be another array of strings if the same header
91+
* should be sent multiple times. This can also be used to override
92+
* automatically created headers like 'Expires' or 'Content-Length'. To suppress
93+
* automatically created headers, `false` can also be used as header value.
7694
*/
77-
public function send($filename = null, $contentType = null, $inline = false)
95+
public function send($filename = null, $contentType = null, $inline = false, $headers = array())
7896
{
79-
if ($contentType === null) {
97+
$headers = array_merge(self::$defaultHeaders, $headers);
98+
99+
if ($contentType !== null) {
100+
$headers['Content-Type'] = $contentType;
101+
} elseif (!isset($headers['Content-Type'])) {
80102
$contentType = @mime_content_type($this->_filename);
81103
if ($contentType === false) {
82104
$contentType = self::DEFAULT_CONTENT_TYPE;
83105
}
106+
$headers['Content-Type'] = $contentType;
84107
}
85-
header('Pragma: public');
86-
header('Expires: 0');
87-
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
88-
header('Content-Type: ' . $contentType);
89-
header('Content-Transfer-Encoding: binary');
90-
91-
//#11 Undefined index: HTTP_USER_AGENT
92-
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
93-
94-
// #84: Content-Length leads to "network connection was lost" on iOS
95-
$isIOS = preg_match('/i(phone|pad|pod)/i', $userAgent);
96-
if (!$isIOS) {
97-
header('Content-Length: ' . filesize($this->_fileName));
108+
109+
if (!isset($headers['Content-Length'])) {
110+
// #11 Undefined index: HTTP_USER_AGENT
111+
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
112+
113+
// #84: Content-Length leads to "network connection was lost" on iOS
114+
$isIOS = preg_match('/i(phone|pad|pod)/i', $userAgent);
115+
if (!$isIOS) {
116+
$headers['Content-Length'] = filesize($this->_fileName);
117+
}
98118
}
99119

100-
if ($filename !== null || $inline) {
120+
if (($filename !== null || $inline) && !isset($headers['Content-Disposition'])) {
101121
$disposition = $inline ? 'inline' : 'attachment';
102122
$encodedFilename = rawurlencode($filename);
103-
header(
104-
"Content-Disposition: $disposition; " .
123+
$headers['Content-Disposition'] = "$disposition; " .
105124
"filename=\"$filename\"; " .
106-
"filename*=UTF-8''$encodedFilename"
107-
);
125+
"filename*=UTF-8''$encodedFilename";
108126
}
109127

128+
$this->sendHeaders($headers);
110129
readfile($this->_fileName);
111130
}
112131

@@ -152,4 +171,27 @@ public function __toString()
152171
{
153172
return $this->_fileName;
154173
}
174+
175+
/**
176+
* Send the given list of headers
177+
*
178+
* @param array $headers the list of headers to send as key/value pairs.
179+
* Value can either be a string or an array of strings to send the same
180+
* header multiple times.
181+
*/
182+
protected function sendHeaders($headers)
183+
{
184+
foreach ($headers as $name => $value) {
185+
if ($value === false) {
186+
continue;
187+
}
188+
if (is_array($value)) {
189+
foreach ($value as $v) {
190+
header("$name: $v");
191+
}
192+
} else {
193+
header("$name: $value");
194+
}
195+
}
196+
}
155197
}

0 commit comments

Comments
 (0)