PluginProbe
User Access Manager / 2.2.3
User Access Manager v2.2.3
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 +188 -59 2.3.162.2.3 View file →
@@ -1,30 +1,88 @@
1 1 <?php
2 +/**
3 + * FileHandler.php
4 + *
5 + * The FileHandler class file.
6 + *
7 + * PHP versions 5
8 + *
9 + * @author Alexander Schneider <alexanderschneider85@gmail.com>
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 + */
2 15
3 16 declare(strict_types=1);
4 17
5 18 namespace UserAccessManager\File;
6 19
7 -use JetBrains\PhpStorm\NoReturn;
8 20 use UserAccessManager\Config\MainConfig;
9 21 use UserAccessManager\Config\WordpressConfig;
10 22 use UserAccessManager\Wrapper\Php;
11 23 use UserAccessManager\Wrapper\Wordpress;
12 24
25 +/**
26 + * Class FileHandler
27 + *
28 + * @package UserAccessManager\FileHandler
29 + */
13 30 class FileHandler
14 31 {
15 - public const X_SEND_FILE_TEST_FILE = 'xSendFileTestFile';
32 + const X_SEND_FILE_TEST_FILE = 'xSendFileTestFile';
16 33
34 + /**
35 + * @var Php
36 + */
37 + private $php;
38 +
39 + /**
40 + * @var Wordpress
41 + */
42 + private $wordpress;
43 +
44 + /**
45 + * @var WordpressConfig
46 + */
47 + private $wordpressConfig;
48 +
49 + /**
50 + * @var MainConfig
51 + */
52 + private $mainConfig;
53 +
54 + /**
55 + * @var FileProtectionFactory
56 + */
57 + private $fileProtectionFactory;
58 +
59 + /**
60 + * FileHandler constructor.
61 + * @param Php $php
62 + * @param Wordpress $wordpress
63 + * @param WordpressConfig $wordpressConfig
64 + * @param MainConfig $mainConfig
65 + * @param FileProtectionFactory $fileProtectionFactory
66 + */
17 67 public function __construct(
18 - private Php $php,
19 - private Wordpress $wordpress,
20 - private WordpressConfig $wordpressConfig,
21 - private MainConfig $mainConfig,
22 - private FileProtectionFactory $fileProtectionFactory
68 + Php $php,
69 + Wordpress $wordpress,
70 + WordpressConfig $wordpressConfig,
71 + MainConfig $mainConfig,
72 + FileProtectionFactory $fileProtectionFactory
23 73 ) {
74 + $this->php = $php;
75 + $this->wordpress = $wordpress;
76 + $this->wordpressConfig = $wordpressConfig;
77 + $this->mainConfig = $mainConfig;
78 + $this->fileProtectionFactory = $fileProtectionFactory;
24 79 }
25 80
26 - private function clearBuffer(): void
81 + /**
82 + * Clears the buffer.
83 + */
84 + private function clearBuffer()
27 85 {
28 86 //prevent '\n' / '0A'
29 87 if ((int) $this->php->iniGet('output_buffering') === 0
30 88 && is_numeric(ob_get_length()) === true
@@ -31,11 +89,16 @@
31 89 ) {
32 90 ob_clean();
33 91 }
34 92
35 - $this->php->flush();
93 + flush();
36 94 }
37 95
96 + /**
97 + * Returns the file mine type.
98 + * @param string $file
99 + * @return string
100 + */
38 101 private function getFileMineType(string $file): string
39 102 {
40 103 $fileName = basename($file);
41 104
@@ -52,9 +115,9 @@
52 115
53 116 if ($this->php->functionExists('finfo_open') === true) {
54 117 $fileInfo = finfo_open(FILEINFO_MIME);
55 118 $fileMimeType = finfo_file($fileInfo, $file);
56 - $this->php->fInfoClose($fileInfo);
119 + finfo_close($fileInfo);
57 120 } elseif ($this->php->functionExists('mime_content_type')) {
58 121 $fileMimeType = mime_content_type($file);
59 122 } elseif (isset($mimeTypes[$fileExt]) === true) {
60 123 $fileMimeType = $mimeTypes[$fileExt];
@@ -64,20 +127,29 @@
64 127
65 128 return (string) $fileMimeType;
66 129 }
67 130
68 - private function addDefaultHeader(string $file, bool $isInline): void
131 + /**
132 + * Adds the default header.
133 + * @param string $file
134 + * @param bool $isInline
135 + */
136 + private function addDefaultHeader(string $file, bool $isInline)
69 137 {
70 138 $fileMimeType = $this->getFileMineType($file);
71 139 $contentDisposition = ($isInline === true) ? 'inline' : 'attachment';
72 140 $baseName = str_replace(' ', '_', basename($file));
73 141
74 - $this->php->header('Content-Description: File Transfer');
75 - $this->php->header('Content-Type: ' . $fileMimeType);
76 - $this->php->header("Content-Disposition: $contentDisposition; filename=\"$baseName\"");
142 + header('Content-Description: File Transfer');
143 + header('Content-Type: ' . $fileMimeType);
144 + header("Content-Disposition: {$contentDisposition}; filename=\"{$baseName}\"");
77 145 }
78 146
79 - private function deliverFileViaFopen(string $file): void
147 + /**
148 + * Delivers the file via fopen.
149 + * @param string $file
150 + */
151 + private function deliverFileViaFopen(string $file)
80 152 {
81 153 $handler = fopen($file, 'r');
82 154
83 155 while (feof($handler) === false) {
@@ -88,33 +160,26 @@
88 160 echo $this->php->fread($handler, 1024);
89 161 }
90 162 }
91 163
92 - private function deliverFile(string $file, bool $isInline): void
164 + /**
165 + * Delivers the file.
166 + * @param string $file
167 + * @param bool $isInline
168 + */
169 + private function deliverFile(string $file, bool $isInline)
93 170 {
94 - $this->php->header("HTTP/1.1 200 OK");
95 171 $downloadType = $this->mainConfig->getDownloadType();
96 172
97 173 if ($downloadType === 'xsendfile') {
98 - if ($this->wordpress->isNginx()) {
99 - // Use /uam-files/ prefix so the internal redirect goes to a dedicated
100 - // internal location that bypasses UAM's rewrite rules, avoiding a loop.
101 - $uri = '/uam-files' . str_replace(rtrim(ABSPATH, '/'), '', $file);
102 - $this->php->header("X-Accel-Redirect: $uri");
103 - } elseif ($this->wordpress->isApacheModuleLoaded('mod_xsendfile')) {
104 - $this->php->header("X-Sendfile: $file");
105 - } else {
106 - // mod_xsendfile is not available — fall back to fopen so the file
107 - // is still delivered rather than sending an empty response.
108 - $downloadType = 'fopen';
109 - }
174 + header("X-Sendfile: {$file}");
110 175 }
111 176
112 177 $this->addDefaultHeader($file, $isInline);
113 178
114 179 if ($downloadType !== 'xsendfile') {
115 - $this->php->header('Content-Transfer-Encoding: binary');
116 - $this->php->header('Content-Length: ' . filesize($file));
180 + header('Content-Transfer-Encoding: binary');
181 + header('Content-Length: ' . filesize($file));
117 182 $this->clearBuffer();
118 183
119 184 if ($downloadType === 'fopen') {
120 185 $this->deliverFileViaFopen($file);
@@ -123,8 +188,16 @@
123 188 }
124 189 }
125 190 }
126 191
192 + /**
193 + * Sets the seek start and end.
194 + * @param string $range
195 + * @param int $fileSize
196 + * @param int|null $seekStart
197 + * @param int|null $seekEnd
198 + * @return bool
199 + */
127 200 private function getSeekStartEnd(string $range, int $fileSize, ?int &$seekStart, ?int &$seekEnd): bool
128 201 {
129 202 //Figure out download piece from range (if set)
130 203 $seek = explode('-', $range);
@@ -144,9 +217,14 @@
144 217
145 218 return $seekStart < $seekEnd;
146 219 }
147 220
148 - private function readFilePartly($fileHandler, int $bytes): void
221 + /**
222 + * Reads the file partly.
223 + * @param resource $fileHandler
224 + * @param int $bytes
225 + */
226 + private function readFilePartly($fileHandler, int $bytes)
149 227 {
150 228 $bytesLeft = $bytes;
151 229 $bufferSize = 1024;
152 230
@@ -162,8 +240,13 @@
162 240 }
163 241 }
164 242 }
165 243
244 + /**
245 + * Returns the http ranges.
246 + * @param int $fileSize
247 + * @return array
248 + */
166 249 private function getRanges(int $fileSize): array
167 250 {
168 251 $httpRange = explode('=', $_SERVER['HTTP_RANGE']);
169 252 $originRanges = isset($httpRange[1]) === true ? $httpRange[1] : '';
@@ -184,8 +267,16 @@
184 267
185 268 return $ranges;
186 269 }
187 270
271 + /**
272 + * Returns the extra contents.
273 + * @param string $file
274 + * @param array $ranges
275 + * @param int|null $contentLength
276 + * @param string|null $boundary
277 + * @return array
278 + */
188 279 private function getExtraContents(string $file, array $ranges, ?int &$contentLength, ?string &$boundary): array
189 280 {
190 281 $contentLength = 0;
191 282 $extraContents = [];
@@ -192,17 +283,17 @@
192 283
193 284 //More than one range is requested?
194 285 if (count($ranges) > 1) {
195 286 $boundary = 'g45d64df96bmdf4sdgh45hf5';
196 - $fullBoundary = "\r\n--$boundary--\r\n";
287 + $fullBoundary = "\r\n--{$boundary}--\r\n";
197 288 $fileSize = filesize($file);
198 289 $mineType = $this->getFileMineType($file);
199 290
200 291 //compute content length
201 292 foreach ($ranges as $index => $range) {
202 - [$seekStart, $seekEnd] = $range;
293 + list($seekStart, $seekEnd) = $range;
203 294 $extraContent = $fullBoundary;
204 - $extraContent .= "Content-Type: $mineType\r\n";
295 + $extraContent .= "Content-Type: {$mineType}\r\n";
205 296 $extraContent .= "Content-Range: bytes $seekStart-$seekEnd/$fileSize\r\n\r\n";
206 297 $extraContents[$index] = $extraContent;
207 298 $contentLength += strlen($extraContent) + ($seekEnd - $seekStart + 1);
208 299 }
@@ -213,9 +304,14 @@
213 304
214 305 return $extraContents;
215 306 }
216 307
217 - private function deliverFilePartial(string $file, bool $isInline): void
308 + /**
309 + * Delivers the file partial.
310 + * @param string $file
311 + * @param bool $isInline
312 + */
313 + private function deliverFilePartial(string $file, bool $isInline)
218 314 {
219 315 $fileSize = filesize($file);
220 316 $ranges = $this->getRanges($fileSize);
221 317
@@ -221,22 +317,22 @@
221 317
222 318 if ($ranges !== []) {
223 319 $extraContents = $this->getExtraContents($file, $ranges, $contentLength, $boundary);
224 320
225 - $this->php->header('HTTP/1.1 206 Partial Content');
226 - $this->php->header('Content-Transfer-Encoding: binary');
227 - $this->php->header('Accept-Ranges: bytes');
321 + header('HTTP/1.1 206 Partial Content');
322 + header('Content-Transfer-Encoding: binary');
323 + header('Accept-Ranges: bytes');
228 324
229 325 if ($extraContents === []) {
230 326 $this->addDefaultHeader($file, $isInline);
231 - [$seekStart, $seekEnd] = $ranges[0];
327 + list($seekStart, $seekEnd) = $ranges[0];
232 328 $contentLength = ($seekEnd - $seekStart + 1);
233 - $this->php->header("Content-Range: bytes $seekStart-$seekEnd/$fileSize");
329 + header("Content-Range: bytes {$seekStart}-{$seekEnd}/{$fileSize}");
234 330 } else {
235 - $this->php->header("Content-Type: multipart/x-byteranges; boundary=$boundary");
331 + header("Content-Type: multipart/x-byteranges; boundary={$boundary}");
236 332 }
237 333
238 - $this->php->header("Content-Length: $contentLength");
334 + header("Content-Length: {$contentLength}");
239 335 $fileHandler = fopen($file, 'r');
240 336
241 337 foreach ($ranges as $index => $range) {
242 338 if (isset($extraContents[$index]) === true) {
@@ -242,10 +338,10 @@
242 338 if (isset($extraContents[$index]) === true) {
243 339 echo $extraContents[$index];
244 340 }
245 341
246 - [$seekStart, $seekEnd] = $ranges[0];
247 - $this->php->fseek($fileHandler, $seekStart);
342 + list($seekStart, $seekEnd) = $ranges[0];
343 + fseek($fileHandler, $seekStart);
248 344 $this->readFilePartly($fileHandler, $seekEnd - $seekStart + 1);
249 345 }
250 346
251 347 if ($extraContents !== []) {
@@ -252,13 +348,18 @@
252 348 echo end($extraContents);
253 349 $this->clearBuffer();
254 350 }
255 351 } else {
256 - $this->php->header('HTTP/1.1 416 Requested Range Not Satisfiable');
257 - $this->php->header("Content-Range: */$fileSize");
352 + header('HTTP/1.1 416 Requested Range Not Satisfiable');
353 + header("Content-Range: */$fileSize");
258 354 }
259 355 }
260 356
357 + /**
358 + * Checks if the file is an inline file
359 + * @param string $file
360 + * @return bool
361 + */
261 362 private function isInlineFile(string $file): bool
262 363 {
263 364 $inlineFiles = array_map('trim', explode(',', (string) $this->mainConfig->getInlineFiles()));
264 365 $map = array_flip($inlineFiles);
@@ -266,10 +367,14 @@
266 367
267 368 return isset($map[$extension]);
268 369 }
269 370
270 - #[NoReturn]
271 - public function getFile(string $file, bool $isImage): void
371 + /**
372 + * Delivers the content of the requested file.
373 + * @param string $file
374 + * @param bool $isImage
375 + */
376 + public function getFile(string $file, bool $isImage)
272 377 {
273 378 //Deliver content
274 379 if (file_exists($file) === true) {
275 380 $isInline = $isImage === true || $this->isInlineFile($file) === true;
@@ -292,8 +397,12 @@
292 397 );
293 398 }
294 399 }
295 400
401 + /**
402 + * Returns the current file protection handler.
403 + * @return FileProtectionInterface
404 + */
296 405 private function getCurrentFileProtectionHandler(): FileProtectionInterface
297 406 {
298 407 if ($this->wordpress->isNginx() === true) {
299 408 return $this->fileProtectionFactory->createNginxFileProtection();
@@ -301,8 +410,12 @@
301 410
302 411 return $this->fileProtectionFactory->createApacheFileProtection();
303 412 }
304 413
414 + /**
415 + * Returns the file protection file.
416 + * @return string
417 + */
305 418 public function getFileProtectionFileName(): string
306 419 {
307 420 return $this->getCurrentFileProtectionHandler()->getFileNameWithPath(
308 421 $this->wordpressConfig->getUploadDirectory()
@@ -308,9 +421,15 @@
308 421 $this->wordpressConfig->getUploadDirectory()
309 422 );
310 423 }
311 424
312 - public function createFileProtection(?string $dir = null, ?string $objectType = null): bool
425 + /**
426 + * Creates a protection file.
427 + * @param string $dir The destination directory.
428 + * @param string $objectType The object type.
429 + * @return false
430 + */
431 + public function createFileProtection($dir = null, $objectType = null): bool
313 432 {
314 433 $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
315 434
316 435 if ($dir !== null) {
@@ -319,9 +438,14 @@
319 438
320 439 return false;
321 440 }
322 441
323 - public function deleteFileProtection(?string $dir = null): bool
442 + /**
443 + * Deletes the protection files.
444 + * @param string $dir The destination directory.
445 + * @return false
446 + */
447 + public function deleteFileProtection($dir = null): bool
324 448 {
325 449 $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
326 450
327 451 if ($dir !== null) {
@@ -330,25 +454,30 @@
330 454
331 455 return false;
332 456 }
333 457
334 - #[NoReturn]
335 - public function deliverXSendFileTestFile(): void
458 + /**
459 + * Delivers a xsendfile test file.
460 + */
461 + public function deliverXSendFileTestFile()
336 462 {
337 463 $file = $this->wordpressConfig->getUploadDirectory() . DIRECTORY_SEPARATOR . self::X_SEND_FILE_TEST_FILE;
338 464 file_put_contents($file, 'success');
339 465
340 - $this->php->header("X-Sendfile: $file");
341 - $this->php->header('Content-Type: application/octet-stream');
342 - $this->php->header('Content-Disposition: attachment; filename="' . basename($file) . '"');
466 + header("X-Sendfile: {$file}");
467 + header('Content-Type: application/octet-stream');
468 + header('Content-Disposition: attachment; filename="' . basename($file) . '"');
343 469 $this->php->callExit();
344 470 }
345 471
346 - public function removeXSendFileTestFile(): void
472 + /**
473 + * Removes the xsendfile test file if exists.
474 + */
475 + public function removeXSendFileTestFile()
347 476 {
348 477 $file = $this->wordpressConfig->getUploadDirectory() . DIRECTORY_SEPARATOR . self::X_SEND_FILE_TEST_FILE;
349 478
350 - if ($this->php->isFile($file) === true) {
351 - $this->php->unlink($file);
479 + if (file_exists($file) === true) {
480 + unlink($file);
352 481 }
353 482 }
354 483 }