Filesystem::exec()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php 
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Filesystem;
24
25
use ErrorException;
26
use FilesystemIterator;
27
use Syscodes\Filesystem\Exceptions\FileException;
28
use Syscodes\Filesystem\Exceptions\FileNotFoundException;
29
use Syscodes\Filesystem\Exceptions\FileUnableToMoveException;
30
31
/**
32
 * Provides basic utility to manipulate the file system.
33
 * 
34
 * @author Alexander Campo <[email protected]>
35
 */
36
class Filesystem 
37
{
38
	/**
39
	 * Enable locking for file reading and writing.
40
	 *
41
	 * @var null|bool $lock
42
	 */
43
	public $lock = null;
44
45
	/**
46
	 * Holds the file handler resource if the file is opened.
47
	 *
48
	 * @var resource $handler
49
	 */
50
	protected $handler;
51
52
	/**
53
	 * The files size in bytes.
54
	 *
55
	 * @var float $size
56
	 */
57
	protected $size;
58
59
	/**
60
	 * Append given data string to this file.
61
	 *
62
	 * @param  string  $path
63
	 * @param  string  $data
64
	 *
65
	 * @return bool
66
	 */
67
	public function append($path, $data)
68
	{
69
		return file_put_contents($path, $data, FILE_APPEND);
0 ignored issues
show
Bug Best Practice introduced by
The expression return file_put_contents...Filesystem\FILE_APPEND) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
70
	}
71
72
	/**
73
	 * Copy a file to a new location.
74
	 *
75
	 * @param  string  $path
76
	 * @param  string  $target
77
	 * 
78
	 * @return bool
79
	 */
80
	public function copy($path, $target)
81
	{
82
		return copy($path, $target);
83
	}
84
85
	/**
86
	 * Get the contents of a file.
87
	 *
88
	 * @param  string  $path
89
	 * @param  bool  $lock  
90
	 * @param  bool  $force  
91
	 *
92
	 * @return string
93
	 *
94
	 * @throws FileNotFoundException
95
	 */
96
	public function get($path, $lock = false, $force = false)
97
	{
98
		if ($this->isFile($path)) {
99
			return $lock ? $this->read($path, $force) : file_get_contents($path);
100
		}
101
102
		throw new FileNotFoundException($path);
103
	}
104
105
	/**
106
	 * Get contents of a file with shared access.
107
	 *
108
	 * @param  string  $path
109
	 * @param  bool  $force  
110
	 *
111
	 * @return string
112
	 */
113
	protected function read($path, $force = false)
114
	{
115
		$contents = '';
116
117
		$this->open($path, 'rb', $force);
118
		
119
		if ($this->handler) {
120
			try {
121
				if (flock($this->handler, LOCK_SH)) {
122
					$this->clearStatCache($path);
123
124
					$contents = fread($this->handler, $this->getSize($path) ?: 1);
125
					
126
					while ( ! feof($this->handler)) {
127
						$contents .= fgets($this->handler, 4096);
128
					}
129
130
					flock($this->handler, LOCK_UN);
131
				}
132
			} finally {
133
				$this->close();
134
			}
135
		}
136
137
		return trim($contents);
138
	}
139
140
	/**
141
	 * Opens the current file with a given $mode.
142
	 *
143
	 * @param  string  $path
144
	 * @param  string  $mode  A valid 'fopen' mode string (r|w|a ...)
145
	 * @param  bool  $force  
146
	 *
147
	 * @return bool
148
	 */
149
	public function open($path, $mode, $force = false)
150
	{
151
		if ( ! $force && is_resource($this->handler)) {
152
			return true;
153
		}
154
155
		if ($this->exists($path) === false) {
156
			if ($this->create($path) === false) {
157
				return false;
158
			}
159
		}
160
161
		$this->handler = fopen($path, $mode);
162
163
		return is_resource($this->handler);
164
	}
165
166
	/**
167
	 * Creates the file.
168
	 * 
169
	 * @param  string  $path
170
	 * 
171
	 * @return bool
172
	 */
173
	public function create($path)
174
	{
175
		if (($this->isDirectory($path)) && ($this->isWritable($path)) || ( ! $this->exists($path))) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->isDirectory($pat... ! $this->exists($path), Probably Intended Meaning: $this->isDirectory($path...! $this->exists($path))
Loading history...
176
			if (touch($path)) {
177
				return true;
178
			}
179
		}
180
181
		return false;
182
	}
183
184
	/**
185
	 * Determine if a file exists.
186
	 *
187
	 * @param  string  $path
188
	 *
189
	 * @return bool
190
	 */
191
	public function exists($path)
192
	{
193
		$this->clearStatCache($path);
194
195
		return file_exists($path);
196
	}
197
198
	/**
199
	 * Clear PHP's internal stat cache.
200
	 *
201
	 * @param  string  $path
202
	 * @param  bool  $all  Clear all cache or not
203
	 *
204
	 * @return void
205
	 */
206
	public function clearStatCache($path, $all = false)
207
	{
208
		if ($all === false) {
209
			clearstatcache(true, $path);
210
		}
211
212
		clearstatcache();
213
	}
214
215
	/**
216
	 * Get the returned value of a file.
217
	 * 
218
	 * @param  string  $path
219
	 * @param  array  $data
220
	 * 
221
	 * @return mixed
222
	 * 
223
	 * @throws \Syscodes\Filesystem\Exceptions\FileNotFoundException
224
	 */
225
	public function getRequire($path, array $data = [])
226
	{
227
		if ($this->isFile($path)) {
228
			$__path = $path;
229
			$__data = $data;
230
231
			return (static function () use ($__path, $__data) {
232
				extract($__data, EXTR_SKIP);
233
234
				return require $__path;
235
			})();
236
		}
237
238
		throw new FileNotFoundException($path);
239
	}
240
241
	/**
242
	 * Require the given file once.
243
	 * 
244
	 * @param  string  $path
245
	 * @param  array  $data
246
	 * 
247
	 * @return mixed
248
	 * 
249
	 * @throws \Syscodes\Filesystem\Exceptions\FileNotFoundException
250
	 */
251
	public function getRequireOnce($path, array $data = [])
252
	{
253
		if ($this->isFile($path)) {
254
			$__path = $path;
255
			$__data = $data;
256
257
			return (static function () use ($__path, $__data) {
258
				extract($__data, EXTR_SKIP);
259
260
				return require_once $__path;
261
			})();
262
		}
263
264
		throw new FileNotFoundException($path);
265
	}
266
267
	/**
268
	 * Retrieve the file size.
269
	 *
270
	 * Implementations SHOULD return the value stored in the "size" key of
271
	 * the file in the $_FILES array if available, as PHP calculates this
272
	 * based on the actual size transmitted.
273
	 *
274
	 * @param  string  $path
275
	 * @param  string  $unit  ('b' by default)
276
	 * 
277
	 * @return int|null  The file size in bytes or null if unknown
278
	 */
279
	public function getSize($path, $unit = 'b')
280
	{
281
		if ($this->exists($path)) {
282
			if (is_null($this->size)) {
0 ignored issues
show
introduced by
The condition is_null($this->size) is always false.
Loading history...
283
				$this->size = filesize($path);
284
			}
285
286
			switch (strtolower($unit)) {
287
				case 'kb':
288
					return number_format($this->size / 1024, 3);
0 ignored issues
show
Bug Best Practice introduced by
The expression return number_format($this->size / 1024, 3) returns the type string which is incompatible with the documented return type integer|null.
Loading history...
289
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
290
				case 'mb':
291
					return number_format(($this->size / 1024) / 1024, 3);     
0 ignored issues
show
Bug Best Practice introduced by
The expression return number_format($th...>size / 1024 / 1024, 3) returns the type string which is incompatible with the documented return type integer|null.
Loading history...
292
					break;
293
			}
294
295
			return $this->size;
296
		}
297
	}
298
	
299
	/**
300
	 * Returns the file's group.
301
	 *
302
	 * @param  string  $path
303
	 * 
304
	 * @return int|bool  The file group, or false in case of an error
305
	 */
306
	public function group($path)
307
	{
308
		if ($this->exists($path)) {
309
			return filegroup($path);
310
		}
311
312
		return false;
313
	}
314
	
315
	/**
316
	 * Returns true if the file is executable.
317
	 *
318
	 * @param  string  $path
319
	 * 
320
	 * @return bool  True if file is executable, false otherwise
321
	 */
322
	public function exec($path)
323
	{
324
		return is_executable($path);
325
	}
326
327
	/**
328
	 * Determine if the given path is a directory.
329
	 *
330
	 * @param  string  $directory
331
	 *
332
	 * @return bool
333
	 */
334
	public function isDirectory($directory)
335
	{
336
		return is_dir($directory);
337
	}
338
339
	/**
340
	 * Determine if the given path is a file.
341
	 *
342
	 * @param  string  $file
343
	 *
344
	 * @return bool
345
	 */
346
	public function isFile($file)
347
	{
348
		return is_file($file);
349
	}
350
351
	/**
352
	 * Determine if the given path is writable.
353
	 * 
354
	 * @param  string  $path
355
	 * 
356
	 * @return bool
357
	 */
358
	public function isWritable($path)
359
	{
360
		return is_writable($path);
361
	}
362
363
	/**
364
	 * Returns if true the file is readable.
365
	 *
366
	 * @param  string  $path
367
	 * 
368
	 * @return bool  True if file is readable, false otherwise
369
	 */
370
	public function isReadable($path)
371
	{
372
		return is_readable($path);
373
	}
374
375
	/**
376
	 * Returns last access time.
377
	 *
378
	 * @param  string  $path
379
	 * 
380
	 * @return int|bool  Timestamp of last access time, or false in case of an error
381
	 */
382
	public function lastAccess($path)
383
	{
384
		if ($this->exists($path)) {
385
			return fileatime($path);
386
		}
387
388
		return false;
389
	}
390
391
	/**
392
	 * Returns last modified time.
393
	 *
394
	 * @param  string  $path
395
	 * 
396
	 * @return int|bool  Timestamp of last modified time, or false in case of an error
397
	 */
398
	public function lastModified($path)
399
	{
400
		if ($this->exists($path)) {
401
			return filemtime($path);
402
		}
403
404
		return false;
405
	}
406
407
	/**
408
	 * Get all of the directories within a given directory.
409
	 * 
410
	 * @param  string  $directory
411
	 * 
412
	 * @return array
413
	 */
414
	public function directories($directory)
415
	{
416
		$directories = [];
417
418
		$iterators = new FilesystemIterator($directory);
419
420
		foreach ($iterators as $iterator) {
421
			$directories[] = trim($iterator->getPathname(), '/').'/';
422
		}
423
424
		return $directories;
425
	}
426
427
	/**
428
	 * Delete the file at a given path.
429
	 * 
430
	 * @param  string  $paths
431
	 * 
432
	 * @return bool
433
	 */
434
	public function delete($paths)
435
	{
436
		if (is_resource($this->handler)) {
437
			fclose($this->handler);
438
			$this->handler = null;
439
		}
440
441
		$paths = is_array($paths) ? $paths : func_get_args();
442
443
		$success = true;
444
445
		foreach ($paths as $path) {
446
			try {
447
				if ( ! @unlink($path)) {
448
					return $success = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $success is dead and can be removed.
Loading history...
449
				}
450
			} catch (ErrorException $e) {
451
				return $success = false;
452
			}
453
		}
454
455
		return $success;
456
	}
457
458
	/**
459
	 * Create a directory.
460
	 *
461
	 * @param  string  $path
462
	 * @param  int  $mode
463
	 * @param  bool  $recursive
464
	 * @param  bool  $force
465
	 *
466
	 * @return bool
467
	 * 
468
	 * @throws FileException
469
	 */
470
	public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false)
471
	{
472
		if ($force) {
473
			return @mkdir($path, $mode, $recursive);
474
		}
475
476
		mkdir($path, $mode, $recursive);
477
	}
478
479
	/**
480
	 * Copy a directory from one location to another.
481
	 * 
482
	 * @param  string  $directory
483
	 * @param  string  $destination
484
	 * @param  int  $options  
485
	 * 
486
	 * @return bool
487
	 */
488
	public function copyDirectory($directory, $destination, $options = null)
489
	{
490
		if ( ! $this->isDirectory($directory)) return false;
491
492
		$options = $options ?: FilesystemIterator::SKIP_DOTS;
493
		
494
		// If the destination directory does not actually exist, we will go ahead and
495
		// create it recursively, which just gets the destination prepared to copy
496
		// the files over. Once we make the directory we'll proceed the copying.
497
		if ( ! $this->isdirectory($destination)) {
498
			$this->makeDirectory($destination, 0777, true);
499
		}
500
501
		$iterators = new FilesystemIterator($directory, $options);
502
503
		foreach ($iterators as $iterator) {
504
			$target = $destination.DIRECTORY_SEPARATOR.$iterator->getBasename();
505
			
506
			// As we spin through items, we will check to see if the current file is actually
507
            // a directory or a file. When it is actually a directory we will need to call
508
            // back into this function recursively to keep copying these nested folders.
509
			if ($iterator->isDir()) {
510
				if ( ! $this->copyDirectory($iterator->getPathname(), $target, $options)) return false;
511
			}
512
			// If the current items is just a regular file, we will just copy this to the new
513
			// location and keep looping. If for some reason the copy fails we'll bail out
514
			// and return false, so the developer is aware that the copy process failed.
515
			else {
516
				if ( ! $this->copy($iterator->getPathname(), $target)) return false;
517
			}
518
		}
519
520
		return true;
521
	}
522
523
	/**
524
	 * Recursively delete a directory and optionally you can keep 
525
	 * the directory if you wish.
526
	 * 
527
	 * @param  string  $directory
528
	 * @param  bool  $keep
529
	 * 
530
	 * @return bool
531
	 */
532
	public function deleteDirectory($directory, $keep = false)
533
	{
534
		if ( ! $this->isDirectory($directory)) return false;
535
536
		$iterators = new filesystemIterator($directory);
537
538
		foreach ($iterators as $iterator) {
539
			// If the item is a directory, we can just recurse into the function and delete 
540
			// that sub-directory otherwise we'll just delete the file and keep iterating 
541
			// through each file until the directory is cleaned.
542
			if ($iterator->isDir() && ! $iterator->isLink()) {
543
				$this->deleteDirectory($iterator->getPathname());
544
			}
545
			// If the item is just a file, we can go ahead and delete it since we're
546
			// just looping through and waxing all of the files in this directory
547
			// and calling directories recursively, so we delete the real path.
548
			else {
549
				$this->delete($iterator->getPathname());
550
			}
551
		}
552
553
		if ( ! $keep) @rmdir($directory);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

553
		if ( ! $keep) /** @scrutinizer ignore-unhandled */ @rmdir($directory);

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...
554
555
		return true;
556
	}
557
558
	/**
559
	 * Empty the specified directory of all files and folders.
560
	 * 
561
	 * 
562
	 * @param  string  $directory
563
	 * 
564
	 * @return bool
565
	 */
566
	public function cleanDirectory($directory)
567
	{
568
		return $this->deleteDirectory($directory, true);
569
	}
570
571
	/**
572
	 * Moves a file to a new location.
573
	 * 
574
	 * @param  string  $from
575
	 * @param  string  $to
576
	 * @param  bool  $overwrite  
577
	 * 
578
	 * @return bool
579
	 */
580
	public function moveDirectory($from, $to, $overwrite = false)
581
	{
582
		if ($overwrite && $this->isDirectory($to) && ! $this->deleteDirectory($to)) return false;
583
584
		if (false === @rename($from, $to)) {
585
			$error = error_get_last();
586
587
			throw new FileUnableToMoveException($from, $to, strip_tags($error['message']));
588
		}
589
590
		$this->perms($to, 0777 & ~umask());
591
	}
592
593
	/**
594
	 * Attempts to determine the file extension based on the trusted
595
	 * getType() method. If the mime type is unknown, will return null.
596
	 * 
597
	 * @param  string  $path
598
	 * 
599
	 * @return string|null
600
	 */
601
	public function guessExtension($path)
602
	{
603
		return FileMimeType::guessExtensionFromType($this->getMimeType($path));
604
	}
605
606
	/**
607
	 * Retrieve the media type of the file. 
608
	 * 
609
	 * @param  string  $path
610
	 * 
611
	 * @return string|null
612
	 */
613
	public function getMimeType($path)
614
	{
615
		$finfo    = finfo_open(FILEINFO_MIME_TYPE);
616
		$mimeType = finfo_file($finfo, $path);
617
618
		finfo_close($finfo);
619
620
		return $mimeType;
621
	}
622
623
	/**
624
	 * Move a file to a new location.
625
	 *
626
	 * @param  string  $path
627
	 * @param  string  $target
628
	 *
629
	 * @return bool
630
	 */
631
	public function move($path, $target)
632
	{
633
		if ($this->exists($path)) {
634
			return rename($path, $target);
635
		}
636
	}
637
638
	/**
639
	 * Extract the file name from a file path.
640
	 * 
641
	 * @param  string  $path
642
	 * 
643
	 * @return string
644
	 */
645
	public function name($path)
646
	{
647
		return pathinfo($path, PATHINFO_FILENAME);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo($path, S...stem\PATHINFO_FILENAME) also could return the type array which is incompatible with the documented return type string.
Loading history...
648
	}
649
650
	/**
651
	 * Extract the trailing name component from a file path.
652
	 * 
653
	 * @param  string  $path
654
	 * 
655
	 * @return string
656
	 */
657
	public function basename($path)
658
	{
659
		return pathinfo($path, PATHINFO_BASENAME);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo($path, S...stem\PATHINFO_BASENAME) also could return the type array which is incompatible with the documented return type string.
Loading history...
660
	}
661
662
	/**
663
	 * Extract the parent directory from a file path.
664
	 * 
665
	 * @param  string  $path
666
	 * 
667
	 * @return string
668
	 */
669
	public function dirname($path)
670
	{
671
		return pathinfo($path, PATHINFO_DIRNAME);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo($path, S...ystem\PATHINFO_DIRNAME) also could return the type array which is incompatible with the documented return type string.
Loading history...
672
	}
673
674
	/**
675
	 * Extract the file extension from a file path.
676
	 * 
677
	 * @param  string  $path
678
	 * 
679
	 * @return string
680
	 */
681
	public function extension($path)
682
	{
683
		return pathinfo($path, PATHINFO_EXTENSION);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo($path, S...tem\PATHINFO_EXTENSION) also could return the type array which is incompatible with the documented return type string.
Loading history...
684
	}
685
686
	/**
687
	 *  Find path names matching a given pattern.
688
	 * 
689
	 * @param  string  $pattern
690
	 * @param  int  $flags  (0 by default)
691
	 * 
692
	 * @return array
693
	 */
694
	public function glob($pattern, $flags = 0)
695
	{
696
		return glob($pattern, $flags);
697
	}
698
699
	/**
700
	 * Returns the file's owner.
701
	 *
702
	 * @param  string  $path
703
	 * 
704
	 * @return int|bool  The file owner, or false in case of an error
705
	 */
706
	public function owner($path)
707
	{
708
		if ($this->exists($path)) {
709
			return fileowner($path);
710
		}
711
712
		return false;
713
	}
714
715
	/**
716
	 * Returns the "chmod" (permissions) of the file.
717
	 *
718
	 * @param  string  $path
719
	 * @param  int|null  $mode  
720
	 * 
721
	 * @return mixed  Permissions for the file, or false in case of an error
722
	 */
723
	public function perms($path, $mode = null)
724
	{
725
		if ($mode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mode of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
726
			chmod($path, $mode);
727
		}
728
729
		return substr(sprintf('%o', fileperms($path)), -4);
730
	}
731
732
	/**
733
	 * Prepend to a file.
734
	 * 
735
	 * @param  string  $path
736
	 * @param  string  $data
737
	 * 
738
	 * @return int
739
	 */
740
	public function prepend($path, $data)
741
	{
742
		if ($this->exists($path)) {
743
			$this->put($path, $data.$this->get($path));
744
		}
745
746
		return $this->put($path, $data);
747
	}
748
749
	/**
750
	 * Write the content of a file.
751
	 *
752
	 * @param  string  $path
753
	 * @param  string  $contents
754
	 * @param  bool  $lock  
755
	 *
756
	 * @return int
757
	 */
758
	public function put($path, $contents, $lock = false)
759
	{
760
		return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
761
	}
762
763
	/**
764
	 * Get the file type of a given file.
765
	 * 
766
	 * @param  string  $path
767
	 * 
768
	 * @return string
769
	 */
770
	public function type($path)
771
	{
772
		return filetype($path);
773
	}
774
	
775
	/**
776
	 * Write the contents of a file, replacing it atomically if it already exists.
777
	 * 
778
	 * @param  string  $path
779
	 * @param  string  $content
780
	 * 
781
	 * @return void
782
	 */
783
	public function replace($path, $content)
784
	{
785
		$this->clearstatcache($path);
786
		
787
		$path = realpath($path) ?: $path;
788
		
789
		$tempPath = tempnam(dirname($path), basename($path));
790
		
791
		$this->perms($tempPath, 0777 - umask());
792
		
793
		$this->put($tempPath, $content);
794
		
795
		$this->move($tempPath, $path);
796
    }
797
798
	/**
799
	 * Searches for a given text and replaces the text if found.
800
	 *
801
	 * @param  string  $path
802
	 * @param  string  $search
803
	 * @param  string  $replace
804
	 *
805
	 * @return bool
806
	 */
807
	public function replaceText($path, $search, $replace)
808
	{
809
		if ( ! $this->open($path, 'r+')) {
810
			return false;
811
		}
812
813
		if ($this->lock !== null) {
814
			if (flock($this->handler, LOCK_EX) === false)
815
			{
816
				return false;
817
			}
818
		}
819
820
		$replaced = $this->write($path, str_replace($search, $replace, $this->get($path)), true);
821
822
		if ($this->lock !== null) {
823
			flock($this->handler, LOCK_UN);
824
		}
825
826
		$this->close();
827
828
		return $replaced;
829
	}	
830
831
	/**
832
	 * Closes the current file if it is opened.
833
	 *
834
	 * @return bool
835
	 */
836
	public function close()
837
	{
838
		if ( ! is_resource($this->handler)) {
839
			return true;
840
		}
841
842
		return fclose($this->handler);
843
	}
844
845
	/**
846
	 * Write given data to this file.
847
	 *
848
	 * @param  string  $path
849
	 * @param  string  $data  Data to write to this File
850
	 * @param  bool  $force  The file to open
851
	 *
852
	 * @return bool
853
	 */
854
	public function write($path, $data, $force = false)
855
	{
856
		$success = false;
857
858
		if ($this->open($path, 'w', $force) === true) {
859
			if ($this->lock !== null) {
860
				if (flock($this->handler, LOCK_EX) === false) {
861
					return false;
862
				}
863
			}
864
865
			if (fwrite($this->handler, $data) !== false) {
866
				$success = true;
867
			}
868
869
			if ($this->lock !== null) {
870
				flock($this->handler, LOCK_UN);
871
			}
872
		}
873
874
		return $success;
875
	}
876
}