Passed
Push — master ( b5dddf...91d417 )
by Richard
09:12
created

XoopsFolderHandler::isSlashTerm()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * Folder engine For XOOPS
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2005-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             class
15
 * @subpackage          file
16
 * @since               2.3.0
17
 * @author              Taiwen Jiang <[email protected]>
18
 */
19
20
/**
21
 * Convenience class for handling directories.
22
 *
23
 * PHP versions 4 and 5
24
 *
25
 * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
26
 * Copyright 2005-2008, Cake Software Foundation, Inc.
27
 *                                     1785 E. Sahara Avenue, Suite 490-204
28
 *                                     Las Vegas, Nevada 89104
29
 *
30
 * Licensed under The MIT License
31
 * Redistributions of files must retain the above copyright notice.
32
 *
33
 * @filesource
34
 * @copyright  Copyright 2005-2008, Cake Software Foundation, Inc.
35
 * @link       http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
36
 * @package    cake
37
 * @subpackage cake.cake.libs
38
 * @since      CakePHP(tm) v 0.2.9
39
 * @modifiedby $LastChangedBy: beckmi $
40
 * @lastmodified $Date: 2015-06-06 17:59:41 -0400 (Sat, 06 Jun 2015) $
41
 * @license    http://www.opensource.org/licenses/mit-license.php The MIT License
42
 */
43
44
/**
45
 * Folder structure browser, lists folders and files.
46
 *
47
 * Long description for class
48
 *
49
 * @package    cake
50
 * @subpackage cake.cake.libs
51
 */
52
class XoopsFolderHandler
53
{
54
    /**
55
     * Path to Folder.
56
     *
57
     * @var string
58
     * @access public
59
     */
60
    public $path;
61
62
    /**
63
     * Sortedness.
64
     *
65
     * @var boolean
66
     * @access public
67
     */
68
    public $sort = false;
69
70
    /**
71
     * mode to be used on create.
72
     *
73
     * @var boolean
74
     * @access public
75
     */
76
    public $mode = '0755';
77
78
    /**
79
     * holds messages from last method.
80
     *
81
     * @var array
82
     * @access private
83
     */
84
    public $messages = array();
85
86
    /**
87
     * holds errors from last method.
88
     *
89
     * @var array
90
     * @access private
91
     */
92
    public $errors = false;
93
94
    /**
95
     * holds array of complete directory paths.
96
     *
97
     * @var array
98
     * @access private
99
     */
100
    public $directories;
101
102
    /**
103
     * holds array of complete file paths.
104
     *
105
     * @var array
106
     * @access private
107
     */
108
    public $files;
109
110
    /**
111
     * Constructor.
112
     *
113
     * @param bool|string $path   Path to folder
114
     * @param boolean     $create Create folder if not found
115
     * @param mixed       $mode   Mode (CHMOD) to apply to created folder, false to ignore
116
     */
117
    public function __construct($path = false, $create = true, $mode = false)
118
    {
119
        if (empty($path)) {
120
            $path = XOOPS_VAR_PATH . '/caches/xoops_cache';
121
        }
122
        if ($mode) {
123
            $this->mode = intval($mode, 8);
0 ignored issues
show
Documentation Bug introduced by
The property $mode was declared of type boolean, but intval($mode, 8) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
124
        }
125
        if (!file_exists($path) && $create == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
126
            $this->create($path, $this->mode);
127
        }
128
        if (!$this->isAbsolute($path)) {
129
            $path = realpath($path);
130
        }
131
        $this->cd($path);
132
    }
133
134
    /**
135
     * Return current path.
136
     *
137
     * @return string Current path
138
     * @access public
139
     */
140
    public function pwd()
141
    {
142
        return $this->path;
143
    }
144
145
    /**
146
     * Change directory to $desired_path.
147
     *
148
     * @param string $path Path to the directory to change to
149
     *
150
     * @return string The new path. Returns false on failure
151
     * @access   public
152
     */
153
    public function cd($path)
154
    {
155
        $path = $this->realpath($path);
156
        if (is_dir($path) && file_exists($path)) {
157
            return $this->path = $path;
158
        }
159
160
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
161
    }
162
163
    /**
164
     * Returns an array of the contents of the current directory, or false on failure.
165
     * The returned array holds two arrays: one of dirs and one of files.
166
     *
167
     * @param boolean $sort
168
     * @param mixed   $exceptions either an array or boolean true will no grab dot files
169
     *
170
     * @return mixed Contents of current directory as an array, false on failure
171
     * @access public
172
     */
173
    public function read($sort = true, $exceptions = false)
174
    {
175
        $dirs = $files = array();
176
        $dir  = opendir($this->path);
177
        if ($dir !== false) {
178
            while (false !== ($n = readdir($dir))) {
179
                $item = false;
180
                if (is_array($exceptions)) {
181
                    if (!in_array($n, $exceptions)) {
182
                        $item = $n;
183
                    }
184
                } else {
185
                    if ((!preg_match('/^\\.+$/', $n) && $exceptions === false) || ($exceptions === true && !preg_match('/^\\.(.*)$/', $n))) {
186
                        $item = $n;
187
                    }
188
                }
189
                if ($item !== false) {
190
                    if (is_dir($this->addPathElement($this->path, $item))) {
191
                        $dirs[] = $item;
192
                    } else {
193
                        $files[] = $item;
194
                    }
195
                }
196
            }
197
            if ($sort || $this->sort) {
198
                sort($dirs);
199
                sort($files);
200
            }
201
            closedir($dir);
202
        }
203
204
        return array(
205
            $dirs,
206
            $files);
207
    }
208
209
    /**
210
     * Returns an array of all matching files in current directory.
211
     *
212
     * @param string $regexp_pattern Preg_match pattern (Defaults to: .*)
213
     * @param bool   $sort
214
     *
215
     * @return array Files that match given pattern
216
     * @access   public
217
     */
218
    public function find($regexp_pattern = '.*', $sort = false)
219
    {
220
        $data = $this->read($sort);
221
        if (!is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
222
            return array();
223
        }
224
        list($dirs, $files) = $data;
225
        $found = array();
226
        foreach ($files as $file) {
227
            if (preg_match("/^{$regexp_pattern}$/i", $file)) {
228
                $found[] = $file;
229
            }
230
        }
231
232
        return $found;
233
    }
234
235
    /**
236
     * Returns an array of all matching files in and below current directory.
237
     *
238
     * @param string $pattern Preg_match pattern (Defaults to: .*)
239
     * @param bool   $sort
240
     *
241
     * @return array Files matching $pattern
242
     * @access public
243
     */
244
    public function findRecursive($pattern = '.*', $sort = false)
245
    {
246
        $startsOn = $this->path;
247
        $out      = $this->_findRecursive($pattern, $sort);
248
        $this->cd($startsOn);
249
250
        return $out;
251
    }
252
253
    /**
254
     * Private helper function for findRecursive.
255
     *
256
     * @param string $pattern Pattern to match against
257
     * @param bool   $sort
258
     *
259
     * @return array Files matching pattern
260
     * @access private
261
     */
262
    public function _findRecursive($pattern, $sort = false)
263
    {
264
        list($dirs, $files) = $this->read($sort);
265
        $found = array();
266
        foreach ($files as $file) {
267
            if (preg_match("/^{$pattern}$/i", $file)) {
268
                $found[] = $this->addPathElement($this->path, $file);
269
            }
270
        }
271
        $start = $this->path;
272
        foreach ($dirs as $dir) {
273
            $this->cd($this->addPathElement($start, $dir));
274
            $found = array_merge($found, $this->findRecursive($pattern));
275
        }
276
277
        return $found;
278
    }
279
280
    /**
281
     * Returns true if given $path is a Windows path.
282
     *
283
     * @param string $path Path to check
284
     *
285
     * @return boolean true if windows path, false otherwise
286
     * @access public
287
     * @static
288
     */
289
    public function isWindowsPath($path)
290
    {
291
        if (preg_match('/^[A-Z]:\\\\/i', $path)) {
292
            return true;
293
        }
294
295
        return false;
296
    }
297
298
    /**
299
     * Returns true if given $path is an absolute path.
300
     *
301
     * @param string $path Path to check
302
     *
303
     * @return bool
304
     * @access public
305
     * @static
306
     */
307
    public function isAbsolute($path)
308
    {
309
        $match = preg_match('/^\\//', $path) || preg_match('/^[A-Z]:\\//i', $path);
310
311
        return $match;
312
    }
313
314
    /**
315
     * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
316
     *
317
     * @param string $path Path to check
318
     *
319
     * @return string Set of slashes ("\\" or "/")
320
     * @access public
321
     * @static
322
     */
323
    public function normalizePath($path)
324
    {
325
        if (XoopsFolderHandler::isWindowsPath($path)) {
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsFolderHandler::isWindowsPath() is not static, but was called statically. ( Ignorable by Annotation )

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

325
        if (XoopsFolderHandler::/** @scrutinizer ignore-call */ isWindowsPath($path)) {
Loading history...
326
            return '\\';
327
        }
328
329
        return '/';
330
    }
331
332
    /**
333
     * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
334
     *
335
     * @param string $path Path to check
336
     *
337
     * @return string Set of slashes ("\\" or "/")
338
     * @access public
339
     * @static
340
     */
341
    public function correctSlashFor($path)
342
    {
343
        if (XoopsFolderHandler::isWindowsPath($path)) {
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsFolderHandler::isWindowsPath() is not static, but was called statically. ( Ignorable by Annotation )

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

343
        if (XoopsFolderHandler::/** @scrutinizer ignore-call */ isWindowsPath($path)) {
Loading history...
344
            return '\\';
345
        }
346
347
        return '/';
348
    }
349
350
    /**
351
     * Returns $path with added terminating slash (corrected for Windows or other OS).
352
     *
353
     * @param string $path Path to check
354
     *
355
     * @return string Path with ending slash
356
     * @access public
357
     * @static
358
     */
359
    public function slashTerm($path)
360
    {
361
        if (XoopsFolderHandler::isSlashTerm($path)) {
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsFolderHandler::isSlashTerm() is not static, but was called statically. ( Ignorable by Annotation )

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

361
        if (XoopsFolderHandler::/** @scrutinizer ignore-call */ isSlashTerm($path)) {
Loading history...
362
            return $path;
363
        }
364
365
        return $path . XoopsFolderHandler::correctSlashFor($path);
0 ignored issues
show
Bug Best Practice introduced by
The method XoopsFolderHandler::correctSlashFor() is not static, but was called statically. ( Ignorable by Annotation )

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

365
        return $path . XoopsFolderHandler::/** @scrutinizer ignore-call */ correctSlashFor($path);
Loading history...
366
    }
367
368
    /**
369
     * Returns $path with $element added, with correct slash in-between.
370
     *
371
     * @param string $path    Path
372
     * @param string $element Element to and at end of path
373
     *
374
     * @return string Combined path
375
     * @access public
376
     * @static
377
     */
378
    public function addPathElement($path, $element)
379
    {
380
        return $this->slashTerm($path) . $element;
381
    }
382
383
    /**
384
     * Returns true if the File is in a given XoopsPath.
385
     *
386
     * @param string $path
387
     *
388
     * @return bool
389
     * @access public
390
     */
391
    public function inXoopsPath($path = '')
392
    {
393
        $dir    = substr($this->slashTerm(XOOPS_ROOT_PATH), 0, -1);
394
        $newdir = $dir . $path;
395
396
        return $this->inPath($newdir);
397
    }
398
399
    /**
400
     * Returns true if the File is in given path.
401
     *
402
     * @param string $path
403
     * @param bool   $reverse
404
     *
405
     * @return bool
406
     * @access public
407
     */
408
    public function inPath($path = '', $reverse = false)
409
    {
410
        $dir     = $this->slashTerm($path);
411
        $current = $this->slashTerm($this->pwd());
412
        if (!$reverse) {
413
            $return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
414
        } else {
415
            $return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
416
        }
417
        if ($return == 1) {
418
            return true;
419
        } else {
420
            return false;
421
        }
422
    }
423
424
    /**
425
     * Change the mode on a directory structure recursively.
426
     *
427
     * @param string   $path       The path to chmod
428
     * @param bool|int $mode       octal value 0755
429
     * @param boolean  $recursive  chmod recursively
430
     * @param array    $exceptions array of files, directories to skip
431
     *
432
     * @return boolean Returns TRUE on success, FALSE on failure
433
     * @access public
434
     */
435
    public function chmod($path, $mode = false, $recursive = true, $exceptions = array())
436
    {
437
        if (!$mode) {
438
            $mode = $this->mode;
439
        }
440
        if ($recursive === false && is_dir($path)) {
441
            if (chmod($path, intval($mode, 8))) {
442
                $this->messages[] = sprintf('%s changed to %s', $path, $mode);
443
444
                return true;
445
            } else {
446
                $this->errors[] = sprintf('%s NOT changed to %s', $path, $mode);
447
448
                return false;
449
            }
450
        }
451
        if (is_dir($path)) {
452
            list($paths) = $this->tree($path);
453
            foreach ($paths as $key => $fullpath) {
454
                $check = explode('/', $fullpath);
455
                $count = count($check);
456
457
                if (in_array($check[$count - 1], $exceptions)) {
458
                    continue;
459
                }
460
461
                if (chmod($fullpath, intval($mode, 8))) {
462
                    $this->messages[] = sprintf('%s changed to %s', $fullpath, $mode);
463
                } else {
464
                    $this->errors[] = sprintf('%s NOT changed to %s', $fullpath, $mode);
465
                }
466
            }
467
            if (empty($this->errors)) {
468
                return true;
469
            }
470
        }
471
472
        return false;
473
    }
474
475
    /**
476
     * Returns an array of nested directories and files in each directory
477
     *
478
     * @param string  $path   the directory path to build the tree from
479
     * @param boolean $hidden return hidden files and directories
480
     * @param string  $type   either file or dir. null returns both files and directories
481
     *
482
     * @return mixed array of nested directories and files in each directory
483
     * @access public
484
     */
485
    public function tree($path, $hidden = true, $type = null)
486
    {
487
        $path              = rtrim($path, '/');
488
        $this->files       = array();
489
        $this->directories = array(
490
            $path);
491
        $directories       = array();
492
        while (count($this->directories)) {
493
            $dir = array_pop($this->directories);
494
            $this->_tree($dir, $hidden);
495
            $directories[] =  $dir;
496
        }
497
        if ($type === null) {
498
            return array(
499
                $directories,
500
                $this->files);
501
        }
502
        if ($type === 'dir') {
503
            return $directories;
504
        }
505
506
        return $this->files;
507
    }
508
509
    /**
510
     * Private method to list directories and files in each directory
511
     *
512
     * @param string $path
513
     * @param        $hidden
514
     *
515
     * @internal param $ $ = boolean $hidden
516
     * @access   private
517
     */
518
    public function _tree($path, $hidden)
519
    {
520
        if (is_dir($path)) {
521
            $dirHandle = opendir($path);
522
            while (false !== ($item = readdir($dirHandle))) {
0 ignored issues
show
Bug introduced by
It seems like $dirHandle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

522
            while (false !== ($item = readdir(/** @scrutinizer ignore-type */ $dirHandle))) {
Loading history...
523
                $found = false;
524
                if (($hidden === true && $item !== '.' && $item !== '..') || ($hidden === false && !preg_match('/^\\.(.*)$/', $item))) {
525
                    $found = $path . '/' . $item;
526
                }
527
                if ($found !== false) {
528
                    if (is_dir($found)) {
529
                        $this->directories[] =  $found;
530
                    } else {
531
                        $this->files[] =  $found;
532
                    }
533
                }
534
            }
535
            closedir($dirHandle);
0 ignored issues
show
Bug introduced by
It seems like $dirHandle can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

535
            closedir(/** @scrutinizer ignore-type */ $dirHandle);
Loading history...
536
        }
537
    }
538
539
    /**
540
     * Create a directory structure recursively.
541
     *
542
     * @param string   $pathname The directory structure to create
543
     * @param bool|int $mode     octal value 0755
544
     *
545
     * @return boolean Returns TRUE on success, FALSE on failure
546
     * @access public
547
     */
548
    public function create($pathname, $mode = false)
549
    {
550
        if (is_dir($pathname) || empty($pathname)) {
551
            return true;
552
        }
553
        if (!$mode) {
554
            $mode = $this->mode;
555
        }
556
        if (is_file($pathname)) {
557
            $this->errors[] = sprintf('%s is a file', $pathname);
558
559
            return true;
560
        }
561
        $nextPathname = substr($pathname, 0, strrpos($pathname, '/'));
562
        if ($this->create($nextPathname, $mode)) {
563
            if (!file_exists($pathname)) {
564
                if (mkdir($pathname, intval($mode, 8))) {
565
                    $this->messages[] = sprintf('%s created', $pathname);
566
567
                    return true;
568
                } else {
569
                    $this->errors[] = sprintf('%s NOT created', $pathname);
570
571
                    return false;
572
                }
573
            }
574
        }
575
576
        return true;
577
    }
578
579
    /**
580
     * Returns the size in bytes of this Folder.
581
     *
582
     * @return int $size
583
     * @access   public
584
     */
585
    public function dirsize()
586
    {
587
        $size      = 0;
588
        $directory = $this->slashTerm($this->path);
589
        $stack     = array($directory);
590
        $count     = count($stack);
591
        for ($i = 0, $j = $count; $i < $j; ++$i) {
592
            if (is_file($stack[$i])) {
593
                $size += filesize($stack[$i]);
594
            } else {
595
                if (is_dir($stack[$i])) {
596
                    $dir = dir($stack[$i]);
597
                    if ($dir) {
598
                        while (false !== ($entry = $dir->read())) {
599
                            if ($entry === '.' || $entry === '..') {
600
                                continue;
601
                            }
602
                            $add = $stack[$i] . $entry;
603
                            if (is_dir($stack[$i] . $entry)) {
604
                                $add = $this->slashTerm($add);
605
                            }
606
                            $stack[] = $add;
607
                        }
608
                        $dir->close();
609
                    }
610
                }
611
            }
612
            $j = count($stack);
613
        }
614
615
        return $size;
616
    }
617
618
    /**
619
     * Recursively Remove directories if system allow.
620
     *
621
     * @param string $path Path of directory to delete
622
     *
623
     * @return boolean Success
624
     * @access public
625
     */
626
    public function delete($path)
627
    {
628
        $path = $this->slashTerm($path);
629
        if (is_dir($path) === true) {
630
            $files        = glob($path . '*', GLOB_NOSORT);
0 ignored issues
show
Unused Code introduced by
The assignment to $files is dead and can be removed.
Loading history...
631
            $normal_files = glob($path . '*');
632
            $hidden_files = glob($path . '\.?*');
633
            $files        = array_merge($normal_files, $hidden_files);
634
            if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
635
                foreach ($files as $file) {
636
                    if (preg_match("/(\.|\.\.)$/", $file)) {
637
                        continue;
638
                    }
639
                    if (is_file($file) === true) {
640
                        if (unlink($file)) {
641
                            $this->messages[] = sprintf('%s removed', $path);
642
                        } else {
643
                            $this->errors[] = sprintf('%s NOT removed', $path);
644
                        }
645
                    } else {
646
                        if (is_dir($file) === true) {
647
                            if ($this->delete($file) === false) {
648
                                return false;
649
                            }
650
                        }
651
                    }
652
                }
653
            }
654
            $path = substr($path, 0, strlen($path) - 1);
655
            if (rmdir($path) === false) {
656
                $this->errors[] = sprintf('%s NOT removed', $path);
657
658
                return false;
659
            } else {
660
                $this->messages[] = sprintf('%s removed', $path);
661
            }
662
        }
663
664
        return true;
665
    }
666
667
    /**
668
     * Recursive directory copy.
669
     *
670
     * @param array|string $options (to, from, chmod, skip)
671
     *
672
     * @return bool
673
     * @access public
674
     */
675
    public function copy($options = array())
676
    {
677
        $to = null;
678
        if (is_string($options)) {
679
            $to      = $options;
680
            $options = array();
681
        }
682
        $options = array_merge(array(
683
                                   'to'   => $to,
684
                                   'from' => $this->path,
685
                                   'mode' => $this->mode,
686
                                   'skip' => array()), $options);
687
688
        $fromDir = $options['from'];
689
        $toDir   = $options['to'];
690
        $mode    = $options['mode'];
691
        if (!$this->cd($fromDir)) {
692
            $this->errors[] = sprintf('%s not found', $fromDir);
693
694
            return false;
695
        }
696
        if (!is_dir($toDir)) {
697
            mkdir($toDir, $mode);
698
        }
699
        if (!is_writable($toDir)) {
700
            $this->errors[] = sprintf('%s not writable', $toDir);
701
702
            return false;
703
        }
704
        $exceptions = array_merge(array(
705
                                      '.',
706
                                      '..',
707
                                      '.svn'), $options['skip']);
708
        $handle     = opendir($fromDir);
709
        if ($handle) {
0 ignored issues
show
introduced by
$handle is of type resource|false, thus it always evaluated to false.
Loading history...
710
            while (false !== ($item = readdir($handle))) {
711
                if (!in_array($item, $exceptions)) {
712
                    $from = $this->addPathElement($fromDir, $item);
713
                    $to   = $this->addPathElement($toDir, $item);
714
                    if (is_file($from)) {
715
                        if (copy($from, $to)) {
716
                            chmod($to, intval($mode, 8));
717
                            touch($to, filemtime($from));
718
                            $this->messages[] = sprintf('%s copied to %s', $from, $to);
719
                        } else {
720
                            $this->errors[] = sprintf('%s NOT copied to %s', $from, $to);
721
                        }
722
                    }
723
                    if (is_dir($from) && !file_exists($to)) {
724
                        if (mkdir($to, intval($mode, 8))) {
725
                            chmod($to, intval($mode, 8));
726
                            $this->messages[] = sprintf('%s created', $to);
727
                            $options          = array_merge($options, array(
728
                                                                        'to'   => $to,
729
                                                                        'from' => $from));
730
                            $this->copy($options);
731
                        } else {
732
                            $this->errors[] = sprintf('%s not created', $to);
733
                        }
734
                    }
735
                }
736
            }
737
            closedir($handle);
738
        } else {
739
            return false;
740
        }
741
        if (!empty($this->errors)) {
742
            return false;
743
        }
744
745
        return true;
746
    }
747
748
    /**
749
     * Recursive directory move.
750
     *
751
     * @param array|string $options (to, from, chmod, skip)
752
     *
753
     * @return boolean Success
754
     * @access public
755
     */
756
    public function move($options)
757
    {
758
        $to = null;
759
        if (is_string($options)) {
760
            $to      = $options;
761
            $options = (array)$options;
762
        }
763
        $options = array_merge(array(
764
                                   'to'   => $to,
765
                                   'from' => $this->path,
766
                                   'mode' => $this->mode,
767
                                   'skip' => array()), $options);
768
        if ($this->copy($options)) {
769
            if ($this->delete($options['from'])) {
770
                return $this->cd($options['to']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->cd($options['to']) returns the type string which is incompatible with the documented return type boolean.
Loading history...
771
            }
772
        }
773
774
        return false;
775
    }
776
777
    /**
778
     * get messages from latest method
779
     *
780
     * @return array
781
     * @access public
782
     */
783
    public function messages()
784
    {
785
        return $this->messages;
786
    }
787
788
    /**
789
     * get error from latest method
790
     *
791
     * @return array
792
     * @access public
793
     */
794
    public function errors()
795
    {
796
        return $this->errors;
797
    }
798
799
    /**
800
     * Get the real path (taking ".." and such into account)
801
     *
802
     * @param string $path Path to resolve
803
     *
804
     * @return string The resolved path
805
     */
806
    public function realpath($path)
807
    {
808
        $path = trim($path);
809
        if (strpos($path, '..') === false) {
810
            if (!$this->isAbsolute($path)) {
811
                $path = $this->addPathElement($this->path, $path);
812
            }
813
814
            return $path;
815
        }
816
        $parts    = explode('/', $path);
817
        $newparts = array();
818
        $newpath  = $path{0} === '/' ? '/' : '';
819
        while (($part = array_shift($parts)) !== null) {
820
            if ($part === '.' || $part == '') {
821
                continue;
822
            }
823
            if ($part === '..') {
824
                if (count($newparts) > 0) {
825
                    array_pop($newparts);
826
                    continue;
827
                } else {
828
                    return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
829
                }
830
            }
831
            $newparts[] = $part;
832
        }
833
        $newpath .= implode('/', $newparts);
834
        if (strlen($path > 1) && $path{strlen($path) - 1} === '/') {
835
            $newpath .= '/';
836
        }
837
838
        return $newpath;
839
    }
840
841
    /**
842
     * Returns true if given $path ends in a slash (i.e. is slash-terminated).
843
     *
844
     * @param string $path Path to check
845
     *
846
     * @return boolean true if path ends with slash, false otherwise
847
     * @access public
848
     * @static
849
     */
850
    public function isSlashTerm($path)
851
    {
852
        if (preg_match('/[\/\\\]$/', $path)) {
853
            return true;
854
        }
855
856
        return false;
857
    }
858
}
859