Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.38% covered (warning)
78.38%
29 / 37
60.00% covered (warning)
60.00%
6 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
FSFileBackendList
78.38% covered (warning)
78.38%
29 / 37
60.00% covered (warning)
60.00%
6 / 10
19.92
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 initIterator
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 key
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 current
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 next
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
2.50
 rewind
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
2.50
 valid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getLastError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 filterViaNext
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRelPath
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
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. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup FileBackend
20 */
21
22/**
23 * Wrapper around RecursiveDirectoryIterator/DirectoryIterator that
24 * catches exception or does any custom behavior that we may want.
25 * Do not use this class from places outside FSFileBackend.
26 *
27 * @ingroup FileBackend
28 */
29abstract class FSFileBackendList implements Iterator {
30    /** @var Iterator|null */
31    protected $iter;
32    /** @var string */
33    protected $lastError;
34
35    /** @var int */
36    protected $suffixStart;
37
38    /** @var int */
39    protected $pos = 0;
40
41    /** @var array */
42    protected $params = [];
43
44    /**
45     * @param string $dir File system directory
46     * @param array $params
47     */
48    public function __construct( $dir, array $params ) {
49        $path = realpath( $dir ); // normalize
50        if ( $path === false ) {
51            $path = $dir;
52        }
53        $this->suffixStart = strlen( $path ) + 1; // size of "path/to/dir/"
54        $this->params = $params;
55
56        try {
57            $this->iter = $this->initIterator( $path );
58        } catch ( UnexpectedValueException $e ) {
59            $this->iter = null; // bad permissions? deleted?
60            $this->lastError = $e->getMessage();
61        }
62    }
63
64    /**
65     * Return an appropriate iterator object to wrap
66     *
67     * @param string $dir File system directory
68     * @return Iterator
69     * @throws UnexpectedValueException
70     */
71    protected function initIterator( $dir ) {
72        if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
73            # Get an iterator that will get direct sub-nodes
74            return new DirectoryIterator( $dir );
75        } else { // recursive
76            # Get an iterator that will return leaf nodes (non-directories)
77            # RecursiveDirectoryIterator extends FilesystemIterator.
78            # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
79            $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
80
81            return new RecursiveIteratorIterator(
82                new RecursiveDirectoryIterator( $dir, $flags ),
83                RecursiveIteratorIterator::CHILD_FIRST // include dirs
84            );
85        }
86    }
87
88    /**
89     * @see Iterator::key()
90     * @return int
91     */
92    public function key(): int {
93        return $this->pos;
94    }
95
96    /**
97     * @see Iterator::current()
98     * @return string|false
99     */
100    #[\ReturnTypeWillChange]
101    public function current() {
102        return $this->getRelPath( $this->iter->current()->getPathname() );
103    }
104
105    /**
106     * @see Iterator::next()
107     * @throws FileBackendError
108     */
109    public function next(): void {
110        try {
111            $this->iter->next();
112            $this->filterViaNext();
113        } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
114            $this->lastError = $e->getMessage();
115
116            throw new FileBackendError( "File iterator gave UnexpectedValueException." );
117        }
118        ++$this->pos;
119    }
120
121    /**
122     * @see Iterator::rewind()
123     * @throws FileBackendError
124     */
125    public function rewind(): void {
126        $this->pos = 0;
127        try {
128            $this->iter->rewind();
129            $this->filterViaNext();
130        } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
131            $this->lastError = $e->getMessage();
132
133            throw new FileBackendError( "File iterator gave UnexpectedValueException." );
134        }
135    }
136
137    /**
138     * @see Iterator::valid()
139     * @return bool
140     */
141    public function valid(): bool {
142        return $this->iter && $this->iter->valid();
143    }
144
145    /**
146     * @return string|null The last caught exception message
147     */
148    public function getLastError() {
149        return $this->lastError;
150    }
151
152    /**
153     * Filter out items by advancing to the next ones
154     */
155    protected function filterViaNext() {
156    }
157
158    /**
159     * Return only the relative path and normalize slashes to FileBackend-style.
160     * Uses the "real path" since the suffix is based upon that.
161     *
162     * @param string $dir
163     * @return string
164     */
165    protected function getRelPath( $dir ) {
166        $path = realpath( $dir );
167        if ( $path === false ) {
168            $path = $dir;
169        }
170
171        return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
172    }
173}