1
|
|
|
<?php |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
|
|
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) { |
|
|
|
|
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!'); |
|
|
|
|
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 |
|
|
|
|
658
|
|
|
|
659
|
|
|
(new Browser)->noCache(); // No cache |
660
|
|
|
|
661
|
|
|
$sPrefix = Registry::getInstance()->site_name . '_'; // the prefix |
|
|
|
|
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
|
|
|
|
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.