PluginProbe
User Access Manager / 2.2.21
User Access Manager v2.2.21
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 +189 -59 2.3.152.2.21 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,27 @@
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");
171 + header("HTTP/1.1 200 OK");
95 172 $downloadType = $this->mainConfig->getDownloadType();
96 173
97 174 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 - }
175 + header("X-Sendfile: {$file}");
110 176 }
111 177
112 178 $this->addDefaultHeader($file, $isInline);
113 179
114 180 if ($downloadType !== 'xsendfile') {
115 - $this->php->header('Content-Transfer-Encoding: binary');
116 - $this->php->header('Content-Length: ' . filesize($file));
181 + header('Content-Transfer-Encoding: binary');
182 + header('Content-Length: ' . filesize($file));
117 183 $this->clearBuffer();
118 184
119 185 if ($downloadType === 'fopen') {
120 186 $this->deliverFileViaFopen($file);
@@ -123,8 +189,16 @@
123 189 }
124 190 }
125 191 }
126 192
193 + /**
194 + * Sets the seek start and end.
195 + * @param string $range
196 + * @param int $fileSize
197 + * @param int|null $seekStart
198 + * @param int|null $seekEnd
199 + * @return bool
200 + */
127 201 private function getSeekStartEnd(string $range, int $fileSize, ?int &$seekStart, ?int &$seekEnd): bool
128 202 {
129 203 //Figure out download piece from range (if set)
130 204 $seek = explode('-', $range);
@@ -144,9 +218,14 @@
144 218
145 219 return $seekStart < $seekEnd;
146 220 }
147 221
148 - private function readFilePartly($fileHandler, int $bytes): void
222 + /**
223 + * Reads the file partly.
224 + * @param resource $fileHandler
225 + * @param int $bytes
226 + */
227 + private function readFilePartly($fileHandler, int $bytes)
149 228 {
150 229 $bytesLeft = $bytes;
151 230 $bufferSize = 1024;
152 231
@@ -162,8 +241,13 @@
162 241 }
163 242 }
164 243 }
165 244
245 + /**
246 + * Returns the http ranges.
247 + * @param int $fileSize
248 + * @return array
249 + */
166 250 private function getRanges(int $fileSize): array
167 251 {
168 252 $httpRange = explode('=', $_SERVER['HTTP_RANGE']);
169 253 $originRanges = isset($httpRange[1]) === true ? $httpRange[1] : '';
@@ -184,8 +268,16 @@
184 268
185 269 return $ranges;
186 270 }
187 271
272 + /**
273 + * Returns the extra contents.
274 + * @param string $file
275 + * @param array $ranges
276 + * @param int|null $contentLength
277 + * @param string|null $boundary
278 + * @return array
279 + */
188 280 private function getExtraContents(string $file, array $ranges, ?int &$contentLength, ?string &$boundary): array
189 281 {
190 282 $contentLength = 0;
191 283 $extraContents = [];
@@ -192,17 +284,17 @@
192 284
193 285 //More than one range is requested?
194 286 if (count($ranges) > 1) {
195 287 $boundary = 'g45d64df96bmdf4sdgh45hf5';
196 - $fullBoundary = "\r\n--$boundary--\r\n";
288 + $fullBoundary = "\r\n--{$boundary}--\r\n";
197 289 $fileSize = filesize($file);
198 290 $mineType = $this->getFileMineType($file);
199 291
200 292 //compute content length
201 293 foreach ($ranges as $index => $range) {
202 - [$seekStart, $seekEnd] = $range;
294 + list($seekStart, $seekEnd) = $range;
203 295 $extraContent = $fullBoundary;
204 - $extraContent .= "Content-Type: $mineType\r\n";
296 + $extraContent .= "Content-Type: {$mineType}\r\n";
205 297 $extraContent .= "Content-Range: bytes $seekStart-$seekEnd/$fileSize\r\n\r\n";
206 298 $extraContents[$index] = $extraContent;
207 299 $contentLength += strlen($extraContent) + ($seekEnd - $seekStart + 1);
208 300 }
@@ -213,9 +305,14 @@
213 305
214 306 return $extraContents;
215 307 }
216 308
217 - private function deliverFilePartial(string $file, bool $isInline): void
309 + /**
310 + * Delivers the file partial.
311 + * @param string $file
312 + * @param bool $isInline
313 + */
314 + private function deliverFilePartial(string $file, bool $isInline)
218 315 {
219 316 $fileSize = filesize($file);
220 317 $ranges = $this->getRanges($fileSize);
221 318
@@ -221,22 +318,22 @@
221 318
222 319 if ($ranges !== []) {
223 320 $extraContents = $this->getExtraContents($file, $ranges, $contentLength, $boundary);
224 321
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');
322 + header('HTTP/1.1 206 Partial Content');
323 + header('Content-Transfer-Encoding: binary');
324 + header('Accept-Ranges: bytes');
228 325
229 326 if ($extraContents === []) {
230 327 $this->addDefaultHeader($file, $isInline);
231 - [$seekStart, $seekEnd] = $ranges[0];
328 + list($seekStart, $seekEnd) = $ranges[0];
232 329 $contentLength = ($seekEnd - $seekStart + 1);
233 - $this->php->header("Content-Range: bytes $seekStart-$seekEnd/$fileSize");
330 + header("Content-Range: bytes {$seekStart}-{$seekEnd}/{$fileSize}");
234 331 } else {
235 - $this->php->header("Content-Type: multipart/x-byteranges; boundary=$boundary");
332 + header("Content-Type: multipart/x-byteranges; boundary={$boundary}");
236 333 }
237 334
238 - $this->php->header("Content-Length: $contentLength");
335 + header("Content-Length: {$contentLength}");
239 336 $fileHandler = fopen($file, 'r');
240 337
241 338 foreach ($ranges as $index => $range) {
242 339 if (isset($extraContents[$index]) === true) {
@@ -242,10 +339,10 @@
242 339 if (isset($extraContents[$index]) === true) {
243 340 echo $extraContents[$index];
244 341 }
245 342
246 - [$seekStart, $seekEnd] = $ranges[0];
247 - $this->php->fseek($fileHandler, $seekStart);
343 + list($seekStart, $seekEnd) = $ranges[0];
344 + fseek($fileHandler, $seekStart);
248 345 $this->readFilePartly($fileHandler, $seekEnd - $seekStart + 1);
249 346 }
250 347
251 348 if ($extraContents !== []) {
@@ -252,13 +349,18 @@
252 349 echo end($extraContents);
253 350 $this->clearBuffer();
254 351 }
255 352 } else {
256 - $this->php->header('HTTP/1.1 416 Requested Range Not Satisfiable');
257 - $this->php->header("Content-Range: */$fileSize");
353 + header('HTTP/1.1 416 Requested Range Not Satisfiable');
354 + header("Content-Range: */$fileSize");
258 355 }
259 356 }
260 357
358 + /**
359 + * Checks if the file is an inline file
360 + * @param string $file
361 + * @return bool
362 + */
261 363 private function isInlineFile(string $file): bool
262 364 {
263 365 $inlineFiles = array_map('trim', explode(',', (string) $this->mainConfig->getInlineFiles()));
264 366 $map = array_flip($inlineFiles);
@@ -266,10 +368,14 @@
266 368
267 369 return isset($map[$extension]);
268 370 }
269 371
270 - #[NoReturn]
271 - public function getFile(string $file, bool $isImage): void
372 + /**
373 + * Delivers the content of the requested file.
374 + * @param string $file
375 + * @param bool $isImage
376 + */
377 + public function getFile(string $file, bool $isImage)
272 378 {
273 379 //Deliver content
274 380 if (file_exists($file) === true) {
275 381 $isInline = $isImage === true || $this->isInlineFile($file) === true;
@@ -292,8 +398,12 @@
292 398 );
293 399 }
294 400 }
295 401
402 + /**
403 + * Returns the current file protection handler.
404 + * @return FileProtectionInterface
405 + */
296 406 private function getCurrentFileProtectionHandler(): FileProtectionInterface
297 407 {
298 408 if ($this->wordpress->isNginx() === true) {
299 409 return $this->fileProtectionFactory->createNginxFileProtection();
@@ -301,8 +411,12 @@
301 411
302 412 return $this->fileProtectionFactory->createApacheFileProtection();
303 413 }
304 414
415 + /**
416 + * Returns the file protection file.
417 + * @return string
418 + */
305 419 public function getFileProtectionFileName(): string
306 420 {
307 421 return $this->getCurrentFileProtectionHandler()->getFileNameWithPath(
308 422 $this->wordpressConfig->getUploadDirectory()
@@ -308,9 +422,15 @@
308 422 $this->wordpressConfig->getUploadDirectory()
309 423 );
310 424 }
311 425
312 - public function createFileProtection(?string $dir = null, ?string $objectType = null): bool
426 + /**
427 + * Creates a protection file.
428 + * @param string $dir The destination directory.
429 + * @param string $objectType The object type.
430 + * @return false
431 + */
432 + public function createFileProtection($dir = null, $objectType = null): bool
313 433 {
314 434 $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
315 435
316 436 if ($dir !== null) {
@@ -319,9 +439,14 @@
319 439
320 440 return false;
321 441 }
322 442
323 - public function deleteFileProtection(?string $dir = null): bool
443 + /**
444 + * Deletes the protection files.
445 + * @param string $dir The destination directory.
446 + * @return false
447 + */
448 + public function deleteFileProtection($dir = null): bool
324 449 {
325 450 $dir = ($dir === null) ? $this->wordpressConfig->getUploadDirectory() : $dir;
326 451
327 452 if ($dir !== null) {
@@ -330,25 +455,30 @@
330 455
331 456 return false;
332 457 }
333 458
334 - #[NoReturn]
335 - public function deliverXSendFileTestFile(): void
459 + /**
460 + * Delivers a xsendfile test file.
461 + */
462 + public function deliverXSendFileTestFile()
336 463 {
337 464 $file = $this->wordpressConfig->getUploadDirectory() . DIRECTORY_SEPARATOR . self::X_SEND_FILE_TEST_FILE;
338 465 file_put_contents($file, 'success');
339 466
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) . '"');
467 + header("X-Sendfile: {$file}");
468 + header('Content-Type: application/octet-stream');
469 + header('Content-Disposition: attachment; filename="' . basename($file) . '"');
343 470 $this->php->callExit();
344 471 }
345 472
346 - public function removeXSendFileTestFile(): void
473 + /**
474 + * Removes the xsendfile test file if exists.
475 + */
476 + public function removeXSendFileTestFile()
347 477 {
348 478 $file = $this->wordpressConfig->getUploadDirectory() . DIRECTORY_SEPARATOR . self::X_SEND_FILE_TEST_FILE;
349 479
350 - if ($this->php->isFile($file) === true) {
351 - $this->php->unlink($file);
480 + if (file_exists($file) === true) {
481 + unlink($file);
352 482 }
353 483 }
354 484 }