PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / framework / base / class-file.php

class-file.php in Defender Security – Malware Scanner, Login Security & Firewall 5.3.1, at framework/base/class-file.php

504 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base file class.
4 *
5 * @package Calotes\Base
6 */
7
8 namespace Calotes\Base;
9
10 use FilesystemIterator;
11 use RecursiveIteratorIterator;
12 use RecursiveDirectoryIterator;
13 use RecursiveCallbackFilterIterator;
14
15 /**
16 * Represents a file object and provides methods for retrieving directory tree.
17 */
18 class File {
19
20 public const ENGINE_SPL = 'spl', ENGINE_SCANDIR = 'scan_dir', ENGINE_OPENDIR = 'open_dir';
21
22 /**
23 * Engine use to create a dir tree.
24 *
25 * @var string
26 */
27 public $engine = '';
28
29 /**
30 * Absolute path to a folder need to create a dir tree.
31 *
32 * @var string
33 */
34 public $path = '';
35
36 /**
37 * Is the result including file?
38 *
39 * @var bool
40 */
41 public $include_file = true;
42
43 /**
44 * Is the result include dir?
45 *
46 * @var bool
47 */
48 public $include_dir = true;
49
50 /**
51 * Whether to include hidden files/directories.
52 *
53 * @var bool
54 */
55 public $include_hidden = false;
56
57 /**
58 * This is where to define the rules for exclude files out of the result:
59 * 'ext'=>array('jpg','gif') file extension you don't want to appear in the result,
60 * 'path'=>array('/tmp/file1.txt','/tmp/file2') absolute path to files,
61 * 'dir'=>array('/tmp/','/dir/') absolute path to the directory you don't want to include files,
62 * 'filename'=>array('abc*') file name you don't want to include, can be regex.
63 *
64 * @var array
65 */
66 public $exclude = array();
67
68 /**
69 * This is where to define the rules for include files:
70 * 'ext'=>array('jpg','gif') file extension you don't want to appear in the result,
71 * 'path'=>array('/tmp/file1.txt','/tmp/file2') absolute path to files,
72 * 'dir'=>array('/tmp/','/dir/') absolute path to the directory you don't want to include files,
73 * 'filename'=>array('abc*') file name you don't want to include, can be regex.
74 * Please note that if $include is provided, the $exclude will get ignored.
75 *
76 * @var array
77 */
78 public $include = array();
79
80 /**
81 * Does this search recursive?
82 *
83 * @var bool
84 */
85 public $is_recursive = true;
86
87 /**
88 * If provided, only search file smaller than this.
89 *
90 * @var bool
91 */
92 public $max_filesize = false;
93
94 /**
95 * Constructor for the File class.
96 *
97 * @param string $path The path to the directory.
98 * @param bool $include_file Whether to include files in the directory.
99 * @param bool $include_dir Whether to include directories in the directory.
100 * @param array $include_rules The rules for including files/directories.
101 * @param array $exclude_rules The rules for excluding files/directories.
102 * @param bool $is_recursive Whether to recursively search the directory.
103 * @param bool $include_hidden Whether to include hidden files/directories.
104 * @param bool $max_filesize The maximum filesize to include.
105 */
106 public function __construct(
107 $path,
108 $include_file = true,
109 $include_dir = false,
110 $include_rules = array(),
111 $exclude_rules = array(),
112 $is_recursive = true,
113 $include_hidden = false,
114 $max_filesize = false
115 ) {
116 $this->path = $path;
117 $this->include_file = $include_file;
118 $this->include_dir = $include_dir;
119 $this->include = $include_rules;
120 $this->exclude = $exclude_rules;
121 $this->is_recursive = $is_recursive;
122 $this->engine = self::ENGINE_SCANDIR;
123 $this->include_hidden = $include_hidden;
124 $this->max_filesize = $max_filesize;
125 }
126
127 /**
128 * Retrieves the directory tree based on the specified engine.
129 *
130 * @return array The directory tree.
131 */
132 public function get_dir_tree() {
133 $result = array();
134 if ( ! is_dir( $this->path ) ) {
135 return $result;
136 }
137
138 if ( self::ENGINE_SPL === $this->engine ) {
139 $result = $this->get_dir_tree_by_spl();
140 } elseif ( self::ENGINE_SCANDIR === $this->engine ) {
141 $result = $this->get_dir_tree_by_scandir();
142 } elseif ( self::ENGINE_OPENDIR === $this->engine ) {
143 $result = $this->get_dir_tree_by_open_dir();
144 }
145
146 return $result;
147 }
148
149 /**
150 * Create a dir tree by SPL library.
151 *
152 * @return array
153 */
154 private function get_dir_tree_by_spl() {
155 $path = $this->path;
156 $data = array();
157 if ( $this->is_recursive ) {
158 $directory_flag = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO
159 | FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS;
160 $directory = new RecursiveDirectoryIterator( $path, $directory_flag );
161
162 if ( ! empty( $this->include ) || ! empty( $this->exclude ) ) {
163 $directory = new RecursiveCallbackFilterIterator(
164 $directory,
165 array(
166 &$this,
167 'filter_directory',
168 )
169 );
170 }
171 $tree = new RecursiveIteratorIterator( $directory, RecursiveIteratorIterator::SELF_FIRST );
172 if ( true !== $this->is_recursive ) {
173 $tree->setMaxDepth( $this->is_recursive );
174 }
175 } else {
176 $tree = new FilesystemIterator( $path );
177 }
178
179 foreach ( $tree as $file ) {
180 $real_path = $file->getRealPath();
181
182 $is_hidden = explode( DIRECTORY_SEPARATOR . '.', $real_path );
183 if ( count( $is_hidden ) > 1 && false === $this->include_hidden ) {
184 continue;
185 }
186 if ( false === $this->is_recursive ) {
187 // Have to filter this, for un recursive.
188 if ( ! empty( $this->include ) || ! empty( $this->exclude ) ) {
189 if ( false === $this->filter_directory( $real_path ) ) {
190 continue;
191 }
192 }
193 }
194
195 if ( false === $this->include_file && $file->isFile() ) {
196 continue;
197 }
198
199 if ( false === $this->include_dir && $file->isDir() ) {
200 continue;
201 }
202
203 if ( $file->isFile() && is_numeric( $this->max_filesize ) ) {
204 // Convert max to bytes.
205 $max_size = $this->max_filesize * ( pow( 1024, 2 ) );
206 if ( $file->getSize() > $max_size ) {
207 continue;
208 }
209 }
210
211 $data[] = $real_path;
212 }
213
214 return $data;
215 }
216
217 /**
218 * Retrieves the directory tree using the scandir() function.
219 *
220 * @param string|null $path The path of the directory to retrieve the tree from. If null, it uses the path
221 * property of the object.
222 *
223 * @return array The array containing the directory tree.
224 */
225 private function get_dir_tree_by_scandir( $path = null ) {
226 if ( is_null( $path ) ) {
227 $path = $this->path;
228 }
229 $path = rtrim( $path, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
230 $rfiles = scandir( $path );
231 $data = array();
232
233 foreach ( $rfiles as $rfile ) {
234 if ( '.' === $rfile || '..' === $rfile ) {
235 continue;
236 }
237 if ( '.' === substr( pathinfo( $rfile, PATHINFO_BASENAME ), 0, 1 )
238 && false === $this->include_hidden ) {
239 // Hidden files, move on.
240 continue;
241 }
242
243 $real_path = $path . $rfile;
244
245 $type = filetype( $real_path );
246 if ( 'dir' === $type ) {
247 $real_path .= DIRECTORY_SEPARATOR;
248 }
249
250 if ( ( ! empty( $this->include ) || ! empty( $this->exclude ) ) && ( false === $this->filter_directory(
251 $real_path,
252 $type
253 ) ) ) {
254 continue;
255 }
256
257 if ( 'file' === $type && true === $this->include_file ) {
258 if ( is_numeric( $this->max_filesize ) ) {
259 $max_size = $this->max_filesize * ( pow( 1024, 2 ) );
260 if ( filesize( $real_path ) > $max_size ) {
261 continue;
262 } else {
263 $data[] = $real_path;
264 }
265 } else {
266 $data[] = $real_path;
267 }
268 }
269
270 if ( 'dir' === $type ) {
271 if ( $this->include_dir ) {
272 $data[] = $real_path;
273 }
274 if ( $this->is_recursive ) {
275 $tdata = $this->get_dir_tree_by_scandir( $real_path );
276 $data = array_merge( $data, $tdata );
277 }
278 }
279 }
280
281 return $data;
282 }
283
284 /**
285 * Query files on path using opendir().
286 *
287 * @param mixed $path The path of the directory to retrieve the tree from.
288 *
289 * @return array
290 * @since 1.0.5
291 */
292 private function get_dir_tree_by_open_dir( $path = null ): array {
293 if ( is_null( $path ) ) {
294 $path = $this->path;
295 }
296 $path = rtrim( $path, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
297 $data = array();
298 $dh = opendir( $path );
299 if ( $dh ) {
300 // Assignment in condition is for comparison.
301 while ( ( $file = readdir( $dh ) ) !== false ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
302 if ( '.' === $file || '..' === $file ) {
303 continue;
304 }
305 $real_path = $path . $file;
306 if ( '.' === substr( pathinfo( $real_path, PATHINFO_BASENAME ), 0, 1 ) ) {
307 // Hidden files, move on.
308 continue;
309 }
310
311 if ( ( ! empty( $this->include ) || ! empty( $this->exclude ) ) && ( false === $this->filter_directory( $real_path ) ) ) {
312 continue;
313 }
314
315 $type = filetype( $real_path );
316
317 if ( 'file' === $type && true === $this->include_file ) {
318 if ( is_numeric( $this->max_filesize ) ) {
319 $max_size = $this->max_filesize * ( pow( 1024, 2 ) );
320 if ( filesize( $real_path ) > $max_size ) {
321 continue;
322 } else {
323 $data[] = $real_path;
324 }
325 } else {
326 $data[] = $real_path;
327 }
328 }
329
330 if ( 'dir' === $type ) {
331 if ( $this->include_dir ) {
332 $data[] = $real_path;
333 }
334 if ( $this->is_recursive ) {
335 $tdata = $this->get_dir_tree_by_open_dir( $real_path );
336 $data = array_merge( $data, $tdata );
337 }
338 }
339 }
340 closedir( $dh );
341 }
342
343 return $data;
344 }
345
346 /**
347 * Filter for recursive directory tree.
348 *
349 * @param mixed $current The path of the directory.
350 * @param mixed $filetype The type of the file.
351 *
352 * @return bool|void
353 */
354 public function filter_directory( $current, $filetype = null ) {
355 if ( ! empty( $this->include ) ) {
356 return $this->filter_include( $current, $filetype );
357 } elseif ( ! empty( $this->exclude ) ) {
358 return $this->filter_exclude( $current, $filetype );
359 }
360 }
361
362 /**
363 * Filter directories based on inclusion rules.
364 *
365 * @param string $path The path of the directory to be filtered.
366 * @param string|null $filetype The type of the file to be filtered. Default is null.
367 *
368 * @return bool Returns true if the directory passes the inclusion rules, false otherwise.
369 */
370 private function filter_include( $path, $filetype = null ) {
371 $include = $this->include;
372 $exclude = $this->exclude;
373 $applied = 0;
374 $dir_include = isset( $include['dir'] ) ? $include['dir'] : array();
375 $dir_exclude = isset( $exclude['dir'] ) ? $exclude['dir'] : array();
376
377 if ( ! is_null( $filetype ) ) {
378 $type = $filetype;
379 } else {
380 $type = filetype( $path );
381 }
382
383 if ( is_array( $dir_include ) && count( $dir_include ) ) {
384 if ( is_array( $dir_exclude ) ) {
385 foreach ( $dir_exclude as $dir ) {
386 if ( 0 === strpos( $path, $dir ) ) {
387 // Exclude matched, we won't list this. Move to next loop.
388 continue;
389 }
390 }
391 }
392
393 foreach ( $dir_include as $dir ) {
394 if ( 0 === strpos( $path, $dir ) ) {
395 return true;
396 }
397 }
398 ++$applied;
399 }
400
401 // Next extension.
402 $ext_include = isset( $include['ext'] ) ? $include['ext'] : array();
403
404 if ( is_array( $ext_include ) && count( $ext_include ) && 'file' === $type ) {
405 // We use foreach() and strcasecmp() instead of regex cause it faster.
406 foreach ( $ext_include as $ext ) {
407 if ( strcasecmp( pathinfo( $path, PATHINFO_EXTENSION ), $ext ) === 0 ) {
408 // Match.
409 return true;
410 }
411 }
412 ++$applied;
413 }
414
415 // Now filename.
416 $filename_include = isset( $include['filename'] ) ? $include['filename'] : array();
417 if ( is_array( $filename_include ) && count( $filename_include ) && 'file' === $type ) {
418 foreach ( $filename_include as $filename ) {
419 if ( preg_match( '/' . $filename . '/', pathinfo( $path, PATHINFO_BASENAME ) ) ) {
420 return true;
421 }
422 }
423 ++$applied;
424 }
425
426 // Now abs path.
427 $path_include = isset( $include['path'] ) ? $include['path'] : array();
428 if ( is_array( $path_include ) && count( $path_include ) && 'file' === $type ) {
429 foreach ( $path_include as $p ) {
430 if ( strcmp( $p, $path ) === 0 ) {
431 return true;
432 }
433 }
434 ++$applied;
435 }
436
437 if ( 0 === $applied ) {
438 return true;
439 }
440
441 return false;
442 }
443
444 /**
445 * Filter out excluded files or directories based on different criteria.
446 *
447 * @param string $path The path of the file or directory to be checked.
448 * @param string|null $filetype The type of the file or directory. If null, it will be determined using the
449 * `filetype` function.
450 *
451 * @return bool Returns true if the file or directory should be included, false otherwise.
452 */
453 private function filter_exclude( $path, $filetype = null ): bool {
454 $exclude = $this->exclude;
455 // First filer dir, or file inside dir.
456 if ( ! is_null( $filetype ) ) {
457 $type = $filetype;
458 } else {
459 $type = filetype( $path );
460 }
461 $dir_exclude = isset( $exclude['dir'] ) ? $exclude['dir'] : array();
462 if ( is_array( $dir_exclude ) && count( $dir_exclude ) ) {
463 foreach ( $dir_exclude as $dir ) {
464 if ( strpos( $path, $dir ) === 0 ) {
465 return false;
466 }
467 }
468 }
469
470 // Next extension.
471 $ext_exclude = isset( $exclude['ext'] ) ? $exclude['ext'] : array();
472 if ( is_array( $ext_exclude ) && count( $ext_exclude ) && 'file' === $type ) {
473 // We use foreach() and strcasecmp() instead of regex cause it faster.
474 foreach ( $ext_exclude as $ext ) {
475 if ( strcasecmp( pathinfo( $path, PATHINFO_EXTENSION ), $ext ) === 0 ) {
476 // Match.
477 return false;
478 }
479 }
480 }
481 // Now filename.
482 $filename_exclude = isset( $exclude['filename'] ) ? $exclude['filename'] : array();
483 if ( is_array( $filename_exclude ) && count( $filename_exclude ) && 'file' === $type ) {
484 foreach ( $filename_exclude as $filename ) {
485 if ( preg_match( '/' . $filename . '/', pathinfo( $path, PATHINFO_BASENAME ) ) ) {
486 return false;
487 }
488 }
489 }
490
491 // Now abs path.
492 $path_exclude = isset( $exclude['path'] ) ? $exclude['path'] : array();
493 if ( is_array( $path_exclude ) && count( $path_exclude ) && 'file' === $type ) {
494 foreach ( $path_exclude as $p ) {
495 if ( strcmp( $p, $path ) === 0 ) {
496 return false;
497 }
498 }
499 }
500
501 return true;
502 }
503 }
504