PluginProbe
User Access Manager / 2.1.11
User Access Manager v2.1.11
2.3.20 2.3.19 2.3.18 2.3.17 2.3.16 2.3.15 2.3.14 2.3.13 trunk 0.6 0.6.1 0.6.2 0.7 0.7 Beta 0.7.0.1 0.8 0.8.0.1 0.8.0.2 0.9 0.9.1 0.9.1.1 0.9.1.2 0.9.1.3 0.9.1.4 1.0 All 136 releases
← All changes | src/File/FileHandler.php +322 -183 trunk → 2.1.11 View file →
@@ -1,75 +1,157 @@
1 1 <?php
2 -
3 -declare(strict_types=1);
4 -
2 +/**
3 + * FileHandler.php
4 + *
5 + * The FileHandler class file.
6 + *
7 + * PHP versions 5
8 + *
9 + * @author Alexander Schneider <[email protected]>
10 + * @copyright 2008-2017 Alexander Schneider
11 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2
12 + * @version SVN: $id$
13 + * @link http://wordpress.org/extend/plugins/user-access-manager/
14 + */
5 15 namespace UserAccessManager\File;
6 16
7 -use JetBrains\PhpStorm\NoReturn;
8 17 use UserAccessManager\Config\MainConfig;
9 18 use UserAccessManager\Config\WordpressConfig;
10 -use UserAccessManager\File\Protection\FileProtectionFactory;
11 -use UserAccessManager\File\Protection\FileProtectionInterface;
12 19 use UserAccessManager\Wrapper\Php;
13 20 use UserAccessManager\Wrapper\Wordpress;
14 21
22 +/**
23 + * Class FileHandler
24 + *
25 + * @package UserAccessManager\FileHandler
26 + */
15 27 class FileHandler
16 28 {
17 - public const X_SEND_FILE_TEST_FILE = 'xSendFileTestFile';
29 + const X_SEND_FILE_TEST_FILE = 'xSendFileTestFile';
18 30
31 + /**
32 + * @var Php
33 + */
34 + private $php;
35 +
36 + /**
37 + * @var Wordpress
38 + */
39 + private $wordpress;
40 +
41 + /**
42 + * @var WordpressConfig
43 + */
44 + private $wordpressConfig;
45 +
46 + /**
47 + * @var MainConfig
48 + */
49 + private $mainConfig;
50 +
51 + /**
52 + * @var FileProtectionFactory
53 + */
54 + private $fileProtectionFactory;
55 +
56 + /**
57 + * FileHandler constructor.
58 + *
59 + * @param Php $php
60 + * @param Wordpress $wordpress
61 + * @param WordpressConfig $wordpressConfig
62 + * @param MainConfig $mainConfig
63 + * @param FileProtectionFactory $fileProtectionFactory
64 + */
19 65 public function __construct(
20 - private Php $php,
21 - private Wordpress $wordpress,
22 - private WordpressConfig $wordpressConfig,
23 - private MainConfig $mainConfig,
24 - private FileProtectionFactory $fileProtectionFactory
66 + Php $php,
67 + Wordpress $wordpress,
68 + WordpressConfig $wordpressConfig,
69 + MainConfig $mainConfig,
70 + FileProtectionFactory $fileProtectionFactory
25 71 ) {
72 + $this->php = $php;
73 + $this->wordpress = $wordpress;
74 + $this->wordpressConfig = $wordpressConfig;
75 + $this->mainConfig = $mainConfig;
76 + $this->fileProtectionFactory = $fileProtectionFactory;
26 77 }
27 78
28 - private function clearBuffer(): void
79 + /**
80 + * Clears the buffer.
81 + */
82 + private function clearBuffer()
29 83 {
30 84 //prevent '\n' / '0A'
31 - if ((int) $this->php->iniGet('output_buffering') === 0
85 + if ((int)$this->php->iniGet('output_buffering') === 0
32 86 && is_numeric(ob_get_length()) === true
33 87 ) {
34 88 ob_clean();
35 89 }
36 90
37 - $this->php->flush();
91 + flush();
38 92 }
39 93
40 - private function getFileMimeType(string $file): string
94 + /**
95 + * Returns the file mine type.
96 + *
97 + * @param string $file
98 + *
99 + * @return string
100 + */
101 + private function getFileMineType($file)
41 102 {
42 - $explodedFileName = explode('.', basename($file));
43 - $fileExtension = strtolower(array_pop($explodedFileName));
103 + $fileName = basename($file);
104 +
105 + /*
106 + * This only for compatibility
107 + * mime_content_type has been deprecated as the PECL extension file info
108 + * provides the same functionality (and more) in a much cleaner way.
109 + */
110 + $explodedFileName = explode('.', $fileName);
111 + $lastElement = array_pop($explodedFileName);
112 + $fileExt = strtolower($lastElement);
113 +
44 114 $mimeTypes = $this->wordpressConfig->getMimeTypes();
45 115
46 - // The deprecated mime_content_type() is only kept as a fallback for installations without fileinfo.
47 116 if ($this->php->functionExists('finfo_open') === true) {
48 - $fileInfo = $this->php->fInfoOpen(FILEINFO_MIME);
49 - $fileMimeType = $this->php->fInfoFile($fileInfo, $file);
50 - $this->php->fInfoClose($fileInfo);
117 + $fileInfo = finfo_open(FILEINFO_MIME);
118 + $fileMimeType = finfo_file($fileInfo, $file);
119 + finfo_close($fileInfo);
51 120 } elseif ($this->php->functionExists('mime_content_type')) {
52 - $fileMimeType = $this->php->mimeContentType($file);
121 + $fileMimeType = mime_content_type($file);
122 + } elseif (isset($mimeTypes[$fileExt]) === true) {
123 + $fileMimeType = $mimeTypes[$fileExt];
53 124 } else {
54 - $fileMimeType = $mimeTypes[$fileExtension] ?? 'application/octet-stream';
125 + $fileMimeType = 'application/octet-stream';
55 126 }
56 127
57 - return (string) $fileMimeType;
128 + return (string)$fileMimeType;
58 129 }
59 130
60 - private function addDefaultHeader(string $file, bool $isInline): void
131 + /**
132 + * Adds the default header.
133 + *
134 + * @param string $file
135 + * @param bool $isInline
136 + */
137 + private function addDefaultHeader($file, $isInline)
61 138 {
62 - $fileMimeType = $this->getFileMimeType($file);
139 + $fileMimeType = $this->getFileMineType($file);
63 140 $contentDisposition = ($isInline === true) ? 'inline' : 'attachment';
64 141 $baseName = str_replace(' ', '_', basename($file));
65 142
66 - $this->php->header('Content-Description: File Transfer');
67 - $this->php->header('Content-Type: ' . $fileMimeType);
68 - $this->php->header("Content-Disposition: $contentDisposition; filename=\"$baseName\"");
143 + header('Content-Description: File Transfer');
144 + header('Content-Type: '.$fileMimeType);
145 + header("Content-Disposition: {$contentDisposition}; filename=\"{$baseName}\"");
69 146 }
70 147
71 - private function deliverFileViaFopen(string $file): void
148 + /**
149 + * Delivers the file via fopen.
150 + *
151 + * @param string $file
152 + */
153 + private function deliverFileViaFopen($file)
72 154 {
73 155 $handler = fopen($file, 'r');
74 156
75 157 while (feof($handler) === false) {
@@ -80,76 +162,75 @@
80 162 echo $this->php->fread($handler, 1024);
81 163 }
82 164 }
83 165
84 - private function addXSendFileHeader(string $file): bool
166 + /**
167 + * Delivers the file.
168 + *
169 + * @param string $file
170 + * @param bool $isInline
171 + */
172 + private function deliverFile($file, $isInline)
85 173 {
86 - if ($this->wordpress->isNginx()) {
87 - // The /uam-files prefix targets a dedicated internal location that bypasses
88 - // UAM's rewrite rules, which would otherwise make the redirect loop.
89 - $uri = '/uam-files' . str_replace(rtrim(ABSPATH, '/'), '', $file);
90 - $this->php->header("X-Accel-Redirect: $uri");
91 -
92 - return true;
93 - }
94 -
95 - if ($this->wordpress->isApacheModuleLoaded('mod_xsendfile')) {
96 - $this->php->header("X-Sendfile: $file");
97 -
98 - return true;
99 - }
100 -
101 - return false;
102 - }
103 -
104 - private function deliverFile(string $file, bool $isInline): void
105 - {
106 - $this->php->header("HTTP/1.1 200 OK");
107 174 $downloadType = $this->mainConfig->getDownloadType();
108 175
109 - if ($downloadType === 'xsendfile' && $this->addXSendFileHeader($file) === false) {
110 - // Without server-side sending support the file still has to be delivered by PHP.
111 - $downloadType = 'fopen';
176 + if ($downloadType === 'xsendfile') {
177 + header("X-Sendfile: {$file}");
112 178 }
113 179
114 180 $this->addDefaultHeader($file, $isInline);
115 181
116 - if ($downloadType === 'xsendfile') {
117 - return;
118 - }
182 + if ($downloadType !== 'xsendfile') {
183 + header('Content-Transfer-Encoding: binary');
184 + header('Content-Length: '.filesize($file));
185 + $this->clearBuffer();
119 186
120 - $this->php->header('Content-Transfer-Encoding: binary');
121 - $this->php->header('Content-Length: ' . filesize($file));
122 - $this->clearBuffer();
123 -
124 - if ($downloadType === 'fopen') {
125 - $this->deliverFileViaFopen($file);
126 - } else {
127 - readfile($file);
187 + if ($downloadType === 'fopen') {
188 + $this->deliverFileViaFopen($file);
189 + } else {
190 + readfile($file);
191 + }
128 192 }
129 193 }
130 194
131 195 /**
132 - * Returns the [start, end] byte offsets of a single HTTP range, or null if the range is invalid.
196 + * Sets the seek start and end.
197 + *
198 + * @param string $range
199 + * @param int $fileSize
200 + * @param int $seekStart
201 + * @param int $seekEnd
202 + *
203 + * @return bool
133 204 */
134 - private function getSeekStartEnd(string $range, int $fileSize): ?array
205 + private function getSeekStartEnd($range, $fileSize, &$seekStart, &$seekEnd)
135 206 {
207 + //Figure out download piece from range (if set)
136 208 $seek = explode('-', $range);
137 - $seekStart = ($seek[0] !== '') ? abs((int) $seek[0]) : null;
138 - $seekEnd = (isset($seek[1]) === true && $seek[1] !== '') ? abs((int) $seek[1]) : null;
209 + $seekStart = ($seek[0] !== '') ? abs((int)$seek[0]) : null;
210 + $seekEnd = (isset($seek[1]) === true && $seek[1] !== '') ? abs((int)$seek[1]) : null;
139 211 $maxSize = $fileSize - 1;
140 212
141 213 if ($seekStart === null) {
142 214 $seekStart = $fileSize - $seekEnd;
143 215 $seekEnd = $maxSize;
216 + } elseif ($seekEnd === null) {
217 + $seekEnd = $maxSize;
144 218 }
145 219
146 - $seekEnd = min($seekEnd ?? $maxSize, $maxSize);
220 + //Start and end based on range (if set), else set defaults also check for invalid ranges.
221 + $seekEnd = min($seekEnd, $maxSize);
147 222
148 - return ($seekStart < $seekEnd) ? [$seekStart, $seekEnd] : null;
223 + return $seekStart < $seekEnd;
149 224 }
150 225
151 - private function readFilePartly($fileHandler, int $bytes): void
226 + /**
227 + * Reads the file partly.
228 + *
229 + * @param resource $fileHandler
230 + * @param int $bytes
231 + */
232 + private function readFilePartly($fileHandler, $bytes)
152 233 {
153 234 $bytesLeft = $bytes;
154 235 $bufferSize = 1024;
155 236
@@ -165,109 +246,136 @@
165 246 }
166 247 }
167 248 }
168 249
169 - private function getRanges(int $fileSize): array
250 + /**
251 + * Returns the http ranges.
252 + *
253 + * @param int $fileSize
254 + *
255 + * @return array
256 + */
257 + private function getRanges($fileSize)
170 258 {
171 259 $httpRange = explode('=', $_SERVER['HTTP_RANGE']);
172 -
173 - if ($httpRange[0] !== 'bytes') {
174 - return [];
175 - }
176 -
260 + $originRanges = isset($httpRange[1]) === true ? $httpRange[1] : '';
261 + $originRanges = explode(',', $originRanges);
262 + $sizeUnit = $httpRange[0];
177 263 $ranges = [];
178 264
179 - foreach (explode(',', $httpRange[1] ?? '') as $originRange) {
180 - $range = $this->getSeekStartEnd($originRange, $fileSize);
265 + if ($sizeUnit === 'bytes') {
266 + foreach ($originRanges as $originRange) {
267 + if ($this->getSeekStartEnd($originRange, $fileSize, $seekStart, $seekEnd) === false) {
268 + $ranges = [];
269 + break;
270 + }
181 271
182 - if ($range === null) {
183 - return [];
272 + $ranges[] = [$seekStart, $seekEnd];
184 273 }
185 -
186 - $ranges[] = $range;
187 274 }
188 275
189 276 return $ranges;
190 277 }
191 278
192 - private function getExtraContents(string $file, array $ranges, ?int &$contentLength, ?string &$boundary): array
279 + /**
280 + * Returns the extra contents.
281 + *
282 + * @param string $file
283 + * @param array $ranges
284 + * @param int $contentLength
285 + * @param string $boundary
286 + *
287 + * @return array
288 + */
289 + private function getExtraContents($file, array $ranges, &$contentLength, &$boundary)
193 290 {
194 291 $contentLength = 0;
195 292 $extraContents = [];
196 293
197 - if (count($ranges) <= 1) {
198 - return $extraContents;
199 - }
294 + //More than one range is requested?
295 + if (count($ranges) > 1) {
296 + $boundary = 'g45d64df96bmdf4sdgh45hf5';
297 + $fullBoundary = "\r\n--{$boundary}--\r\n";
298 + $fileSize = filesize($file);
299 + $mineType = $this->getFileMineType($file);
200 300
201 - $boundary = 'g45d64df96bmdf4sdgh45hf5';
202 - $fullBoundary = "\r\n--$boundary--\r\n";
203 - $fileSize = filesize($file);
204 - $mimeType = $this->getFileMimeType($file);
301 + //compute content length
302 + foreach ($ranges as $index => $range) {
303 + list($seekStart, $seekEnd) = $range;
304 + $extraContent = $fullBoundary;
305 + $extraContent .= "Content-Type: {$mineType}\r\n";
306 + $extraContent .= "Content-Range: bytes $seekStart-$seekEnd/$fileSize\r\n\r\n";
307 + $extraContents[$index] = $extraContent;
308 + $contentLength += strlen($extraContent) + ($seekEnd - $seekStart + 1);
309 + }
205 310
206 - foreach ($ranges as $index => $range) {
207 - [$seekStart, $seekEnd] = $range;
208 - $extraContent = $fullBoundary
209 - . "Content-Type: $mimeType\r\n"
210 - . "Content-Range: bytes $seekStart-$seekEnd/$fileSize\r\n\r\n";
211 - $extraContents[$index] = $extraContent;
212 - $contentLength += strlen($extraContent) + ($seekEnd - $seekStart + 1);
311 + $contentLength += strlen($fullBoundary);
312 + $extraContents[] = $fullBoundary;
213 313 }
214 314
215 - $contentLength += strlen($fullBoundary);
216 - $extraContents[] = $fullBoundary;
217 -
218 315 return $extraContents;
219 316 }
220 317
221 - private function deliverFilePartial(string $file, bool $isInline): void
318 + /**
319 + * Delivers the file partial.
320 + *
321 + * @param string $file
322 + * @param bool $isInline
323 + */
324 + private function deliverFilePartial($file, $isInline)
222 325 {
223 326 $fileSize = filesize($file);
224 327 $ranges = $this->getRanges($fileSize);
225 328
226 - if ($ranges === []) {
227 - $this->php->header('HTTP/1.1 416 Requested Range Not Satisfiable');
228 - $this->php->header("Content-Range: */$fileSize");
329 + if ($ranges !== []) {
330 + $extraContents = $this->getExtraContents($file, $ranges, $contentLength, $boundary);
229 331
230 - return;
231 - }
332 + header('HTTP/1.1 206 Partial Content');
333 + header('Content-Transfer-Encoding: binary');
334 + header('Accept-Ranges: bytes');
232 335
233 - $extraContents = $this->getExtraContents($file, $ranges, $contentLength, $boundary);
336 + if ($extraContents === []) {
337 + $this->addDefaultHeader($file, $isInline);
338 + list($seekStart, $seekEnd) = $ranges[0];
339 + $contentLength = ($seekEnd - $seekStart + 1);
340 + header("Content-Range: bytes {$seekStart}-{$seekEnd}/{$fileSize}");
341 + } else {
342 + header("Content-Type: multipart/x-byteranges; boundary={$boundary}");
343 + }
234 344
235 - $this->php->header('HTTP/1.1 206 Partial Content');
236 - $this->php->header('Content-Transfer-Encoding: binary');
237 - $this->php->header('Accept-Ranges: bytes');
345 + header("Content-Length: {$contentLength}");
346 + $fileHandler = fopen($file, 'r');
238 347
239 - if ($extraContents === []) {
240 - $this->addDefaultHeader($file, $isInline);
241 - [$seekStart, $seekEnd] = $ranges[0];
242 - $contentLength = ($seekEnd - $seekStart + 1);
243 - $this->php->header("Content-Range: bytes $seekStart-$seekEnd/$fileSize");
244 - } else {
245 - $this->php->header("Content-Type: multipart/x-byteranges; boundary=$boundary");
246 - }
348 + foreach ($ranges as $index => $range) {
349 + if (isset($extraContents[$index]) === true) {
350 + echo $extraContents[$index];
351 + }
247 352
248 - $this->php->header("Content-Length: $contentLength");
249 - $fileHandler = fopen($file, 'r');
353 + list($seekStart, $seekEnd) = $ranges[0];
354 + fseek($fileHandler, $seekStart);
355 + $this->readFilePartly($fileHandler, $seekEnd - $seekStart + 1);
356 + }
250 357
251 - foreach ($ranges as $index => $range) {
252 - if (isset($extraContents[$index]) === true) {
253 - echo $extraContents[$index];
358 + if ($extraContents !== []) {
359 + echo end($extraContents);
360 + $this->clearBuffer();
254 361 }
255 -
256 - [$seekStart, $seekEnd] = $range;
257 - $this->php->fseek($fileHandler, $seekStart);
258 - $this->readFilePartly($fileHandler, $seekEnd - $seekStart + 1);
362 + } else {
363 + header('HTTP/1.1 416 Requested Range Not Satisfiable');
364 + header("Content-Range: */$fileSize");
259 365 }
260 -
261 - if ($extraContents !== []) {
262 - echo end($extraContents);
263 - $this->clearBuffer();
264 - }
265 366 }
266 367
267 - private function isInlineFile(string $file): bool
368 + /**
369 + * Checks if the file is an inline file
370 + *
371 + * @param string $file
372 + *
373 + * @return bool
374 + */
375 + private function isInlineFile($file)
268 376 {
269 - $inlineFiles = array_map('trim', explode(',', (string) $this->mainConfig->getInlineFiles()));
377 + $inlineFiles = array_map('trim', explode(',', $this->mainConfig->getInlineFiles()));
270 378 $map = array_flip($inlineFiles);
271 379 $extension = pathinfo($file, PATHINFO_EXTENSION);
272 380
273 381 return isset($map[$extension]);
@@ -272,40 +380,45 @@
272 380
273 381 return isset($map[$extension]);
274 382 }
275 383
276 - private function isRangeRequest(): bool
384 + /**
385 + * Delivers the content of the requested file.
386 + *
387 + * @param string $file
388 + * @param bool $isImage
389 + */
390 + public function getFile($file, $isImage)
277 391 {
278 - return isset($_SERVER['HTTP_RANGE']) === true
279 - && isset($_SERVER['REQUEST_METHOD']) === true
280 - && $_SERVER['REQUEST_METHOD'] === 'GET';
281 - }
392 + //Deliver content
393 + if (file_exists($file) === true) {
394 + $isInline = $isImage === true || $this->isInlineFile($file) === true;
282 395
283 - #[NoReturn]
284 - public function getFile(string $file, bool $isImage): void
285 - {
286 - if (file_exists($file) === false) {
396 + if (isset($_SERVER['HTTP_RANGE']) === true
397 + && isset($_SERVER['REQUEST_METHOD']) === true
398 + && $_SERVER['REQUEST_METHOD'] === 'GET'
399 + ) {
400 + $this->deliverFilePartial($file, $isInline);
401 + } else {
402 + $this->deliverFile($file, $isInline);
403 + }
404 +
405 + $this->php->callExit();
406 + } else {
287 407 $this->wordpress->wpDie(
288 408 TXT_UAM_FILE_NOT_FOUND_ERROR_MESSAGE,
289 409 TXT_UAM_FILE_NOT_FOUND_ERROR_TITLE,
290 410 ['response' => 404]
291 411 );
292 -
293 - return;
294 412 }
295 -
296 - $isInline = $isImage === true || $this->isInlineFile($file) === true;
297 -
298 - if ($this->isRangeRequest() === true) {
299 - $this->deliverFilePartial($file, $isInline);
300 - } else {
301 - $this->deliverFile($file, $isInline);
302 - }
303 -
304 - $this->php->callExit();
305 413 }
306 414
307 - private function getCurrentFileProtectionHandler(): FileProtectionInterface
415 + /**
416 + * Returns the current file protection handler.
417 + *
418 + * @return FileProtectionInterface
419 + */
420 + private function getCurrentFileProtectionHandler()
308 421 {
309 422 if ($this->wordpress->isNginx() === true) {
310 423 return $this->fileProtectionFactory->createNginxFileProtection();
311 424 }
@@ -312,9 +425,14 @@
312 425
313 426 return $this->fileProtectionFactory->createApacheFileProtection();
314 427 }
315 428
316 - public function getFileProtectionFileName(): string
429 + /**
430 + * Returns the file protection file.
431 + *
432 + * @return string
433 + */
434 + public function getFileProtectionFileName()
317 435 {
318 436 return $this->getCurrentFileProtectionHandler()->getFileNameWithPath(
319 437 $this->wordpressConfig->getUploadDirectory()
320 438 );
@@ -319,46 +437,67 @@
319 437 $this->wordpressConfig->getUploadDirectory()
320 438 );
321 439 }
322 440
323 - public function createFileProtection(?string $dir = null, ?string $objectType = null): bool
441 + /**
442 + * Creates a protection file.
443 + *
444 + * @param string $dir The destination directory.
445 + * @param string $objectType The object type.
446 + *
447 + * @return false
448 + */
449 + public function createFileProtection($dir = null, $objectType = null)
324 450 {
325 - $directory = $dir ?? $this->wordpressConfig->getUploadDirectory();
451 + $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
326 452
327 - return $directory !== null
328 - && $this->getCurrentFileProtectionHandler()->create($directory, $objectType);
453 + if ($dir !== null) {
454 + return $this->getCurrentFileProtectionHandler()->create($dir, $objectType);
455 + }
456 +
457 + return false;
329 458 }
330 459
331 - public function deleteFileProtection(?string $dir = null): bool
460 + /**
461 + * Deletes the protection files.
462 + *
463 + * @param string $dir The destination directory.
464 + *
465 + * @return false
466 + */
467 + public function deleteFileProtection($dir = null)
332 468 {
333 - $directory = $dir ?? $this->wordpressConfig->getUploadDirectory();
469 + $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
334 470
335 - return $directory !== null
336 - && $this->getCurrentFileProtectionHandler()->delete($directory);
337 - }
471 + if ($dir !== null) {
472 + return $this->getCurrentFileProtectionHandler()->delete($dir);
473 + }
338 474
339 - private function getXSendFileTestFilePath(): string
340 - {
341 - return $this->wordpressConfig->getUploadDirectory() . DIRECTORY_SEPARATOR . self::X_SEND_FILE_TEST_FILE;
475 + return false;
342 476 }
343 477
344 - #[NoReturn]
345 - public function deliverXSendFileTestFile(): void
478 + /**
479 + * Delivers a xsendfile test file.
480 + */
481 + public function deliverXSendFileTestFile()
346 482 {
347 - $file = $this->getXSendFileTestFilePath();
483 + $file = $this->wordpressConfig->getUploadDirectory().DIRECTORY_SEPARATOR.self::X_SEND_FILE_TEST_FILE;
348 484 file_put_contents($file, 'success');
349 485
350 - $this->php->header("X-Sendfile: $file");
351 - $this->php->header('Content-Type: application/octet-stream');
352 - $this->php->header('Content-Disposition: attachment; filename="' . basename($file) . '"');
486 + header("X-Sendfile: {$file}");
487 + header('Content-Type: application/octet-stream');
488 + header('Content-Disposition: attachment; filename="'.basename($file).'"');
353 489 $this->php->callExit();
354 490 }
355 491
356 - public function removeXSendFileTestFile(): void
492 + /**
493 + * Removes the xsendfile test file if exists.
494 + */
495 + public function removeXSendFileTestFile()
357 496 {
358 - $file = $this->getXSendFileTestFilePath();
497 + $file = $this->wordpressConfig->getUploadDirectory().DIRECTORY_SEPARATOR.self::X_SEND_FILE_TEST_FILE;
359 498
360 - if ($this->php->isFile($file) === true) {
361 - $this->php->unlink($file);
499 + if (file_exists($file) === true) {
500 + unlink($file);
362 501 }
363 502 }
364 503 }