File   F
last analyzed

Complexity

Total Complexity 118

Size/Duplication

Total Lines 872
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 872
rs 1.728
c 0
b 0
f 0
wmc 118
lcom 1
cbo 7

41 Methods

Rating   Name   Duplication   Size   Complexity  
A getMimeType() 0 4 2
A getFileExt() 0 4 1
A getFileWithoutExt() 0 6 1
A getFile() 0 4 1
A putFile() 0 4 1
A existFile() 0 16 4
A existDir() 0 16 4
A getDirList() 0 17 6
A size() 0 4 1
B getFileList() 0 31 10
A checkExtDir() 0 14 6
A createDir() 0 18 5
A copy() 0 8 2
A copyDir() 0 4 1
A systemCopy() 0 8 2
A rename() 0 8 2
A renameDir() 0 4 1
A systemRename() 0 8 2
A deleteFile() 0 12 4
A deleteDir() 0 4 3
A remove() 0 10 3
A removeWildcards() 0 4 1
A getModifTime() 0 4 2
A version() 0 4 1
A sleep() 0 4 1
A chmod() 0 9 3
A getOctalAccess() 0 5 1
A pack() 0 4 1
B getDirSize() 0 26 7
A getDirFreeSpace() 0 4 1
A unpack() 0 4 1
A download() 0 39 3
A writeHeader() 0 12 3
A save() 0 15 2
A readFiles() 0 19 6
A readDirs() 0 19 6
A getUrlContents() 0 13 1
A zipExtract() 0 13 2
B isBinary() 0 27 7
A getDirIterator() 0 4 1
B recursiveDirIterator() 0 28 6

How to fix   Complexity   

Complex Class

Complex classes like File often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use File, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 29 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @title            File Class
4
 * @desc             Useful methods for handling files.
5
 *
6
 * @author           Pierre-Henry Soria <[email protected]>
7
 * @copyright        (c) 2012-2019, Pierre-Henry Soria. All Rights Reserved.
8
 * @license          GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
9
 * @package          PH7 / Framework / File
10
 */
11
12
namespace PH7\Framework\File;
13
14
defined('PH7') or exit('Restricted access');
15
16
use PH7\Framework\Error\CException\PH7InvalidArgumentException;
17
use PH7\Framework\File\Permission\Chmod;
18
use PH7\Framework\File\Permission\PermissionException;
19
use PH7\Framework\Navigation\Browser;
20
use PH7\Framework\Parse\Url as UrlParser;
21
use PH7\Framework\Registry\Registry;
22
use PH7\Framework\Server\Server;
23
use PH7\Framework\Url\Url;
24
use RecursiveDirectoryIterator;
25
use RecursiveIteratorIterator;
26
use SplFileObject;
27
use ZipArchive;
28
29
class File
30
{
31
    const REGEX_BINARY_FILE = '/^(.*?)\.(gif|jpg|jpeg|png|webp|ico|mp3|mp4|mov|avi|flv|mpg|mpeg|wmv|ogg|ogv|webm|pdf|ttf|eot|woff|svg|swf)$/i';
32
33
    const RENAME_FUNC_NAME = 'rename';
34
    const COPY_FUNC_NAME = 'copy';
35
36
    const DIR_HANDLE_FUNC_NAMES = [
37
        self::RENAME_FUNC_NAME,
38
        self::COPY_FUNC_NAME
39
    ];
40
41
    const WILDCARD_SYMBOL = '*';
42
43
    // End Of Line relative to the operating system
44
    const EOL = PHP_EOL;
45
46
    /**
47
     * Mime Types list.
48
     *
49
     * @var array $aMimeTypes
50
     */
51
    private static $aMimeTypes = [
52
        'pdf' => 'application/pdf',
53
        'txt' => 'text/plain',
54
        'html' => 'text/html',
55
        'htm' => 'text/html',
56
        'exe' => 'application/octet-stream',
57
        'zip' => 'application/zip',
58
        'doc' => 'application/msword',
59
        'xls' => 'application/vnd.ms-excel',
60
        'ppt' => 'application/vnd.ms-powerpoint',
61
        'gif' => 'image/gif',
62
        'png' => 'image/png',
63
        'jpeg' => 'image/jpg',
64
        'jpg' => 'image/jpg',
65
        'webp' => 'image/webp',
66
        'ico' => 'image/x-icon',
67
        'eot' => 'application/vnd.ms-fontobject',
68
        'otf' => 'application/octet-stream',
69
        'ttf' => 'application/octet-stream',
70
        'woff' => 'application/octet-stream',
71
        'svg' => 'application/octet-stream',
72
        'swf' => 'application/x-shockwave-flash',
73
        'mp3' => 'audio/mpeg',
74
        'mp4' => 'video/mp4',
75
        'webm' => 'video/webm',
76
        'mov' => 'video/quicktime',
77
        'avi' => 'video/x-msvideo',
78
        'php' => 'text/plain',
79
    ];
80
81
    /**
82
     * @param string $sExt Extension File.
83
     *
84
     * @return string (string | null) Returns the "mime type" if it is found, otherwise "null"
85
     */
86
    public function getMimeType($sExt)
87
    {
88
        return array_key_exists($sExt, self::$aMimeTypes) ? self::$aMimeTypes[$sExt] : null;
89
    }
90
91
    /**
92
     * Get Extension file without the dot.
93
     *
94
     * @param string $sFile The File Name.
95
     *
96
     * @return string
97
     */
98
    public function getFileExt($sFile)
99
    {
100
        return strtolower(substr(strrchr($sFile, PH7_DOT), 1));
101
    }
102
103
    /**
104
     * Get File without Extension and dot.
105
     * This function is smarter than just a code like this, substr($sFile,0,strpos($sFile,'.'))
106
     * Just look at the example below for you to realize that the function removes only the extension and nothing else!
107
     * Example 1 "my_file.pl" The return value is "my_file"
108
     * Example 2 "my_file.inc.pl" The return value is "my_file.inc"
109
     * Example 3 "my_file.class.html.php" The return value is "my_file.class.html"
110
     *
111
     * @see File::getFileExt() To see the method that retrieves the file extension.
112
     *
113
     * @param string $sFile
114
     *
115
     * @return string
116
     */
117
    public function getFileWithoutExt($sFile)
118
    {
119
        $sExt = $this->getFileExt($sFile);
120
121
        return str_replace(PH7_DOT . $sExt, '', $sFile);
122
    }
123
124
    /**
125
     * Get File Contents.
126
     *
127
     * @param string $sFile File name.
128
     * @param bool $bIncPath Default FALSE
129
     *
130
     * @return string|bool Returns the read data or FALSE on failure.
131
     */
132
    public function getFile($sFile, $bIncPath = false)
133
    {
134
        return @file_get_contents($sFile, $bIncPath);
135
    }
136
137
    /**
138
     * Put File Contents.
139
     *
140
     * @param string $sFile File name.
141
     * @param string $sContents Contents file.
142
     * @param int $iFlag Constant (see http://php.net/manual/function.file-put-contents.php).
143
     *
144
     * @return int|bool Returns the number of bytes that were written to the file, or FALSE on failure.
145
     */
146
    public function putFile($sFile, $sContents, $iFlag = 0)
147
    {
148
        return @file_put_contents($sFile, $sContents, $iFlag);
149
    }
150
151
    /**
152
     * Check if file exists.
153
     *
154
     * @param array|string $mFile
155
     *
156
     * @return bool TRUE if file exists, FALSE otherwise.
157
     */
158
    public function existFile($mFile)
159
    {
160
        $bExists = false; // Default value
161
162
        if (is_array($mFile)) {
163
            foreach ($mFile as $sFile) {
164
                if (!$bExists = $this->existFile($sFile)) {
165
                    return false;
166
                }
167
            }
168
        } else {
169
            $bExists = is_file($mFile);
170
        }
171
172
        return $bExists;
173
    }
174
175
    /**
176
     * Check if directory exists.
177
     *
178
     * @param array|string $mDir
179
     *
180
     * @return bool TRUE if file exists, FALSE otherwise.
181
     */
182
    public function existDir($mDir)
183
    {
184
        $bExists = false; // Default value
185
186
        if (is_array($mDir)) {
187
            foreach ($mDir as $sDir) {
188
                if (!$bExists = $this->existDir($sDir)) {
189
                    return false;
190
                }
191
            }
192
        } else {
193
            $bExists = is_dir($mDir);
194
        }
195
196
        return $bExists;
197
    }
198
199
    /**
200
     * @param string $sDir The directory.
201
     *
202
     * @return array The list of the folder that is in the directory.
203
     */
204
    public function getDirList($sDir)
205
    {
206
        $aDirList = [];
207
208
        if ($rHandle = opendir($sDir)) {
209
            while (false !== ($sFile = readdir($rHandle))) {
210
                if ($sFile != '.' && $sFile != '..' && is_dir($sDir . PH7_DS . $sFile)) {
211
                    $aDirList[] = $sFile;
212
                }
213
            }
214
            asort($aDirList);
215
            reset($aDirList);
216
        }
217
        closedir($rHandle);
218
219
        return $aDirList;
220
    }
221
222
    /**
223
     * Get file size.
224
     *
225
     * @param string $sFile
226
     *
227
     * @return int The size of the file in bytes.
228
     */
229
    public function size($sFile)
230
    {
231
        return (int)@filesize($sFile);
232
    }
233
234
    /**
235
     * @param string $sDir
236
     * @param string|array|null $mExt Retrieves only files with specific extensions.
237
     *
238
     * @return array List of files sorted alphabetically.
239
     */
240
    public function getFileList($sDir, $mExt = null)
241
    {
242
        $aTree = [];
243
        $sDir = $this->checkExtDir($sDir);
244
245
        if (is_dir($sDir) && $rHandle = opendir($sDir)) {
246
            while (false !== ($sFile = readdir($rHandle))) {
247
                if ($sFile !== '.' && $sFile !== '..') {
248
                    if (is_dir($sDir . $sFile)) {
249
                        $aTree = array_merge($aTree, $this->getFileList($sDir . $sFile, $mExt));
250
                    } else {
251
                        if ($mExt !== null) {
252
                            $aExt = (array)$mExt;
253
254
                            foreach ($aExt as $sExt) {
255
                                if (substr($sFile, -strlen($sExt)) === $sExt) {
256
                                    $aTree[] = $sDir . $sFile;
257
                                }
258
                            }
259
                        } else {
260
                            $aTree[] = $sDir . $sFile;
261
                        }
262
                    }
263
                }
264
            }
265
            sort($aTree);
266
        }
267
        closedir($rHandle);
0 ignored issues
show
Bug introduced by
The variable $rHandle does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
268
269
        return $aTree;
270
    }
271
272
    /**
273
     * Make sure that folder names have a trailing.
274
     *
275
     * @param string $sDir The directory.
276
     * @param bool $bStart for check extension directory start. Default FALSE
277
     * @param bool $bEnd for check extension end. Default TRUE
278
     *
279
     * @return string $sDir Directory
280
     */
281
    public function checkExtDir($sDir, $bStart = false, $bEnd = true)
282
    {
283
        $bIsWindows = Server::isWindows();
284
285
        if (!$bIsWindows && $bStart === true && substr($sDir, 0, 1) !== PH7_DS) {
286
            $sDir = PH7_DS . $sDir;
287
        }
288
289
        if ($bEnd === true && substr($sDir, -1) !== PH7_DS) {
290
            $sDir .= PH7_DS;
291
        }
292
293
        return $sDir;
294
    }
295
296
    /**
297
     * Creates a directory if they are in an array. If it does not exist and
298
     * allows the creation of nested directories specified in the pathname.
299
     *
300
     * @param string|array $mDir
301
     * @param int (octal) $iMode Default: 0777
302
     *
303
     * @return void
304
     *
305
     * @throws PermissionException If the file cannot be created.
306
     */
307
    public function createDir($mDir, $iMode = Chmod::MODE_ALL_EXEC)
308
    {
309
        if (is_array($mDir)) {
310
            foreach ($mDir as $sDir) {
311
                $this->createDir($sDir);
312
            }
313
        } else {
314
            if (!is_dir($mDir)) {
315
                if (!@mkdir($mDir, $iMode, true)) {
316
                    $sExceptMessage = 'Cannot create "%s" directory.<br /> Please verify that the directory permission is in writing mode.';
317
318
                    throw new PermissionException(
319
                        sprintf($sExceptMessage, $mDir)
320
                    );
321
                }
322
            }
323
        }
324
    }
325
326
    /**
327
     * Copy files and checks if the "from file" exists.
328
     *
329
     * @param string $sFrom File.
330
     * @param string $sTo File.
331
     *
332
     * @return bool
333
     */
334
    public function copy($sFrom, $sTo)
335
    {
336
        if (!is_file($sFrom)) {
337
            return false;
338
        }
339
340
        return @copy($sFrom, $sTo);
341
    }
342
343
    /**
344
     * Copy the contents of a directory into another.
345
     *
346
     * @param string $sFrom Old directory.
347
     * @param string $sTo New directory.
348
     *
349
     * @return bool TRUE if everything went well, otherwise FALSE if the "from directory" couldn't be found or if it couldn't be copied.
350
     *
351
     * @throws PH7InvalidArgumentException
352
     */
353
    public function copyDir($sFrom, $sTo)
354
    {
355
        return $this->recursiveDirIterator($sFrom, $sTo, self::COPY_FUNC_NAME);
356
    }
357
358
    /**
359
     * Copy a file or directory with the Unix cp command.
360
     *
361
     * @param string $sFrom File or directory.
362
     * @param string $sTo File or directory.
363
     *
364
     * @return int|bool Returns the last line on success, and FALSE on failure.
365
     */
366
    public function systemCopy($sFrom, $sTo)
367
    {
368
        if (file_exists($this->removeWildcards($sFrom))) {
369
            return system("cp -r $sFrom $sTo");
370
        }
371
372
        return false;
373
    }
374
375
    /**
376
     * Rename a file or directory and checks if the "from file" or directory exists with file_exists() function
377
     * since it checks the existence of a file or directory (because, as in the Unix OS, a directory is a file).
378
     *
379
     * @param string $sFrom File or directory.
380
     * @param string $sTo File or directory.
381
     *
382
     * @return bool
383
     */
384
    public function rename($sFrom, $sTo)
385
    {
386
        if (!file_exists($sFrom)) {
387
            return false;
388
        }
389
390
        return @rename($sFrom, $sTo);
391
    }
392
393
    /**
394
     * Rename the contents of a directory into another.
395
     *
396
     * @param string $sFrom Old directory.
397
     * @param string $sTo New directory.
398
     *
399
     * @return bool TRUE if everything went well, otherwise FALSE if the "from directory" couldn't be found or if it couldn't be renamed.
400
     *
401
     * @throws PH7InvalidArgumentException
402
     */
403
    public function renameDir($sFrom, $sTo)
404
    {
405
        return $this->recursiveDirIterator($sFrom, $sTo, self::RENAME_FUNC_NAME);
406
    }
407
408
    /**
409
     * Rename a file or directory with the Unix mv command.
410
     *
411
     * @param string $sFrom File or directory.
412
     * @param string $sTo File or directory.
413
     *
414
     * @return int|bool Returns the last line on success, and FALSE on failure.
415
     */
416
    public function systemRename($sFrom, $sTo)
417
    {
418
        if (file_exists($this->removeWildcards($sFrom))) {
419
            return system("mv $sFrom $sTo");
420
        }
421
422
        return false;
423
    }
424
425
    /**
426
     * Deletes a file or files if they are in an array.
427
     * If the file does not exist, the function does nothing.
428
     *
429
     * @param string|array $mFile
430
     *
431
     * @return void
432
     */
433
    public function deleteFile($mFile)
434
    {
435
        if (is_array($mFile)) {
436
            foreach ($mFile as $sF) {
437
                $this->deleteFile($sF);
438
            }
439
        } else {
440
            if (is_file($mFile)) {
441
                @unlink($mFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
442
            }
443
        }
444
    }
445
446
    /**
447
     * For deleting Directory and files!
448
     * A "rmdir" function improved PHP which also delete files in a directory.
449
     *
450
     * @param string $sPath The path
451
     *
452
     * @return bool
453
     */
454
    public function deleteDir($sPath)
455
    {
456
        return (is_file($sPath) ? unlink($sPath) : (is_dir($sPath) ? array_map([$this, 'deleteDir'], glob($sPath . '/*')) === @rmdir($sPath) : false));
457
    }
458
459
    /**
460
     * Remove the contents of a directory.
461
     *
462
     * @param string $sDir
463
     *
464
     * @return void
465
     */
466
    public function remove($sDir)
467
    {
468
        $oIterator = new RecursiveIteratorIterator($this->getDirIterator($sDir), RecursiveIteratorIterator::CHILD_FIRST);
469
470
        foreach ($oIterator as $sPath) {
471
            $sPath->isFile() ? unlink($sPath) : @rmdir($sPath);
472
        }
473
474
        @rmdir($sDir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
475
    }
476
477
    /**
478
     * Clean paths if wildcard is found in order to get valid paths.
479
     *
480
     * @param string $sPath
481
     *
482
     * @return string
483
     */
484
    public function removeWildcards($sPath)
485
    {
486
        return str_replace(self::WILDCARD_SYMBOL, '', $sPath);
487
    }
488
489
    /**
490
     * Get the creation/modification time of a file in the Unix timestamp.
491
     *
492
     * @param string $sFile Full path of the file.
493
     *
494
     * @return int|bool Returns the time the file was last modified, or FALSE if it not found.
495
     */
496
    public function getModifTime($sFile)
497
    {
498
        return is_file($sFile) ? filemtime($sFile) : false;
499
    }
500
501
    /**
502
     * Get the version of a file based on the its latest modification.
503
     * Shortened form of self::getModifTime()
504
     *
505
     * @param string $sFile Full path of the file.
506
     *
507
     * @return int Returns the latest modification time of the file in Unix timestamp.
508
     */
509
    public static function version($sFile)
510
    {
511
        return @filemtime($sFile);
512
    }
513
514
    /**
515
     * Delay script execution.
516
     *
517
     * @param int $iSleep Halt time in seconds.
518
     *
519
     * @return int|bool Returns 0 on success, or FALSE on error.
520
     */
521
    public function sleep($iSleep = 5)
522
    {
523
        return sleep($iSleep);
524
    }
525
526
    /**
527
     * Changes permission on a file or directory.
528
     *
529
     * @param string $sFile
530
     * @param int $iMode Octal Permission for the file.
531
     *
532
     * @return bool
533
     */
534
    public function chmod($sFile, $iMode)
535
    {
536
        // file_exists function verify the existence of a "file" or "folder"!
537
        if (file_exists($sFile) && $this->getOctalAccess($sFile) !== $iMode) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $this->getOctalAccess($sFile) (string) and $iMode (integer) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
538
            return @chmod($sFile, $iMode);
539
        }
540
541
        return false;
542
    }
543
544
    /**
545
     * @param string $sFile
546
     *
547
     * @return string Octal Permissions.
548
     */
549
    public function getOctalAccess($sFile)
550
    {
551
        clearstatcache();
552
        return substr(sprintf('%o', fileperms($sFile)), -4);
553
    }
554
555
    /**
556
     * @param string $sData
557
     *
558
     * @return string
559
     */
560
    public function pack($sData)
561
    {
562
        return urlencode(serialize($sData));
563
    }
564
565
    /**
566
     * Get the size of a directory.
567
     *
568
     * @param string $sPath
569
     *
570
     * @return int The size of the file in bytes.
571
     */
572
    public function getDirSize($sPath)
573
    {
574
        if (!is_dir($sPath)) {
575
            return 0;
576
        }
577
578
        if (!($rHandle = opendir($sPath))) {
579
            return 0;
580
        }
581
582
        $iSize = 0;
583
        while (false !== ($sFile = readdir($rHandle))) {
584
            if ($sFile != '.' && $sFile != '..') {
585
                $sFullPath = $sPath . PH7_DS . $sFile;
586
587
                if (is_dir($sFullPath)) {
588
                    $iSize = $this->getDirSize($sFullPath);
589
                } else {
590
                    $iSize += $this->size($sFullPath);
591
                }
592
            }
593
        }
594
        closedir($rHandle);
595
596
        return $iSize;
597
    }
598
599
    /**
600
     * Get free space of a directory.
601
     *
602
     * @param string $sPath
603
     *
604
     * @return float The number of available bytes as a float.
605
     */
606
    public function getDirFreeSpace($sPath)
607
    {
608
        return disk_free_space($sPath);
609
    }
610
611
    /**
612
     * @param string $sData
613
     *
614
     * @return bool|int|float|string|array|object
615
     */
616
    public function unpack($sData)
617
    {
618
        return unserialize(urldecode($sData));
619
    }
620
621
    /**
622
     * For download file.
623
     *
624
     * @param string $sFile File to download.
625
     * @param string $sName A name for the file to download.
626
     * @param string|null $sMimeType
627
     *
628
     * @return void
629
     */
630
    public function download($sFile, $sName, $sMimeType = null)
631
    {
632
        /*
633
          This function takes a path to a file to output ($sFile),
634
          the filename that the browser will see ($sName) and
635
          the MIME type of the file ($sMimeType, optional).
636
637
          If you want to do something on download abort/finish,
638
          register_shutdown_function('function_name');
639
         */
640
641
        //if (!is_readable($sFile)) exit('File not found or inaccessible!');
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
642
643
        $sName = Url::decode($sName); // Clean the name file
644
645
        /* Figure out the MIME type (if not specified) */
646
        if (empty($sMimeType)) {
647
            $sFileExtension = $this->getFileExt($sFile);
648
649
            $mGetMimeType = $this->getMimeType($sFileExtension);
650
651
            $sMimeType = 'application/force-download';
652
            if (!empty($mGetMimeType)) {
653
                $sMimeType = $mGetMimeType;
654
            }
655
        }
656
657
        @ob_end_clean(); // Turn off output buffering to decrease CPU usage
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
658
659
        (new Browser)->noCache(); // No cache
660
661
        $sPrefix = Registry::getInstance()->site_name . '_'; // the prefix
0 ignored issues
show
Documentation introduced by
The property site_name does not exist on object<PH7\Framework\Registry\Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
662
        header('Content-Type: ' . $sMimeType);
663
        header('Content-Disposition: attachment; filename=' . UrlParser::clean($sPrefix) . $sName);
664
        header('Content-Transfer-Encoding: binary');
665
        header('Accept-Ranges: bytes');
666
        header('Content-Length: ' . $this->size($sFile));
667
        readfile($sFile);
668
    }
669
670
    /**
671
     * Write Header Contents.
672
     *
673
     * @param string $sHeader Text to be shown in the headers
674
     * @param array $aFile
675
     *
676
     * @return void
677
     */
678
    public function writeHeader($sHeader, array $aFile = [])
679
    {
680
        for ($i = 0, $iCountFiles = count($aFile); $i < $iCountFiles; $i++) {
681
            $rHandle = fopen($aFile[$i], 'wb+');
682
683
            if ($this->size($aFile[$i]) > 0) {
684
                $sData = fread($rHandle, $this->size($aFile[$i]));
685
                fwrite($rHandle, $sHeader . static::EOL . $sData);
686
            }
687
            fclose($rHandle);
688
        }
689
    }
690
691
    /**
692
     * Writes and saves the contents to a file.
693
     * It also creates a temporary file to not delete the original file if something goes wrong during the recording file.
694
     *
695
     * @param string $sFile
696
     * @param string $sData
697
     *
698
     * @return int Returns the number of bytes written, or NULL on error.
699
     */
700
    public function save($sFile, $sData)
701
    {
702
        $sTmpFile = $this->getFileWithoutExt($sFile) . '.tmp.' . $this->getFileExt($sFile);
703
        $iWritten = (new SplFileObject($sTmpFile, 'wb'))->fwrite($sData);
704
705
        if ($iWritten !== null) {
706
            // Copy of the temporary file to the original file if no problem occurred.
707
            copy($sTmpFile, $sFile);
708
        }
709
710
        // Deletes the temporary file.
711
        $this->deleteFile($sTmpFile);
712
713
        return $iWritten;
714
    }
715
716
    /**
717
     * @param string $sPath
718
     * @param array|string $mFiles
719
     *
720
     * @return array|string The Files.
721
     */
722
    public function readFiles($sPath = './', &$mFiles)
723
    {
724
        if (!($rHandle = opendir($sPath))) {
725
            return false;
726
        }
727
728
        while (false !== ($sFile = readdir($rHandle))) {
729
            if ($sFile != '.' && $sFile != '..') {
730
                if (strpos($sFile, '.') === false) {
731
                    $this->readFiles($sPath . PH7_DS . $sFile, $mFiles);
732
                } else {
733
                    $mFiles[] = $sPath . PH7_DS . $sFile;
734
                }
735
            }
736
        }
737
        closedir($rHandle);
738
739
        return $mFiles;
740
    }
741
742
    /**
743
     * Reading Directories.
744
     *
745
     * @param string $sPath
746
     *
747
     * @return array|bool Returns an ARRAY with the folders or FALSE if the folder could not be opened.
748
     */
749
    public function readDirs($sPath = './')
750
    {
751
        if (!($rHandle = opendir($sPath))) {
752
            return false; // TODO: Return when yield is used will be OK with PHP 7
753
        }
754
755
        $aRet = []; // TODO: Remove it once yield is used
756
        while (false !== ($sFolder = readdir($rHandle))) {
757
            if ('.' == $sFolder || '..' == $sFolder || !is_dir($sPath . $sFolder)) {
758
                continue;
759
            }
760
761
            //yield $sFolder; // TODO: For PHP 7
762
            $aRet[] = $sFolder; // TODO: Remove it for yield
763
        }
764
        closedir($rHandle);
765
766
        return $aRet; // TODO: Remove it for yield
767
    }
768
769
    /**
770
     * Get the URL contents (For URLs, it is better to use CURL because it is faster than file_get_contents function).
771
     *
772
     * @param string $sUrl URL to be read contents.
773
     *
774
     * @return string|bool Return the result content on success, FALSE on failure.
775
     */
776
    public function getUrlContents($sUrl)
777
    {
778
        $rCh = curl_init();
779
        curl_setopt($rCh, CURLOPT_URL, $sUrl);
780
        curl_setopt($rCh, CURLOPT_HEADER, 0);
781
        curl_setopt($rCh, CURLOPT_RETURNTRANSFER, 1);
782
        curl_setopt($rCh, CURLOPT_FOLLOWLOCATION, 1);
783
        $mRes = curl_exec($rCh);
784
        curl_close($rCh);
785
        unset($rCh);
786
787
        return $mRes;
788
    }
789
790
    /**
791
     * Extract Zip archive.
792
     *
793
     * @param string $sFile Zip file.
794
     * @param string $sDir Destination to extract the file.
795
     *
796
     * @return bool
797
     */
798
    public function zipExtract($sFile, $sDir)
799
    {
800
        $oZip = new ZipArchive;
801
        $mRes = $oZip->open($sFile);
802
803
        if ($mRes === true) {
804
            $oZip->extractTo($sDir);
805
            $oZip->close();
806
            return true;
807
        }
808
809
        return false; // Return error value
810
    }
811
812
    /**
813
     * Check if the file is binary.
814
     *
815
     * @param string $sFile
816
     *
817
     * @return bool
818
     */
819
    public function isBinary($sFile)
820
    {
821
        if (file_exists($sFile)) {
822
            if (!is_file($sFile)) {
823
                return false;
824
            }
825
826
            if (preg_match(self::REGEX_BINARY_FILE, $sFile)) {
827
                return true;
828
            }
829
830
            $rHandle = fopen($sFile, 'r');
831
            $sContents = fread($rHandle, 512); // Get 512 bytes of the file.
832
            fclose($rHandle);
833
            clearstatcache();
834
835
            if (!function_exists('is_binary')) // PHP 6
836
                return is_binary($sContents);
837
838
            return (
839
                0 or substr_count($sContents, "^ -~", "^\r\n") / 512 > 0.3
840
                or substr_count($sContents, "\x00") > 0
841
            );
842
        }
843
844
        return false;
845
    }
846
847
    /**
848
     * Create a recurive directory iterator for a given directory.
849
     *
850
     * @param string $sPath
851
     *
852
     * @return RecursiveDirectoryIterator
853
     */
854
    private function getDirIterator($sPath)
855
    {
856
        return new RecursiveDirectoryIterator($sPath);
857
    }
858
859
    /**
860
     * Recursive Directory Iterator.
861
     *
862
     * @param string $sFrom Directory.
863
     * @param string $sTo Directory.
864
     * @param string $sFuncName The function name. Choose between 'copy' and 'rename'.
865
     *
866
     * @return bool
867
     *
868
     * @throws PH7InvalidArgumentException If the function name is invalid.
869
     * @throws PermissionException If the directory cannot be created
870
     *
871
     */
872
    private function recursiveDirIterator($sFrom, $sTo, $sFuncName)
873
    {
874
        if (!in_array($sFuncName, self::DIR_HANDLE_FUNC_NAMES, true)) {
875
            throw new PH7InvalidArgumentException('Wrong function name: ' . $sFuncName);
876
        }
877
878
        if (!is_dir($sFrom)) {
879
            return false;
880
        }
881
882
        $bRet = false; // Default value
883
        $oIterator = new RecursiveIteratorIterator($this->getDirIterator($sFrom), RecursiveIteratorIterator::SELF_FIRST);
884
885
        foreach ($oIterator as $sFromFile) {
886
            // http://php.net/manual/en/recursivedirectoryiterator.getsubpathname.php#example-4559
887
            $sDest = $sTo . PH7_DS . $oIterator->getSubPathName();
888
889
            if ($sFromFile->isDir()) {
890
                $this->createDir($sDest);
891
            } else {
892
                if (!$bRet = $this->$sFuncName($sFromFile, $sDest)) {
893
                    return false;
894
                }
895
            }
896
        }
897
898
        return $bRet;
899
    }
900
}
901