Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
45.71% |
16 / 35 |
|
20.00% |
1 / 5 |
CRAP | |
0.00% |
0 / 1 |
SwiftFileBackendList | |
47.06% |
16 / 34 |
|
20.00% |
1 / 5 |
28.95 | |
0.00% |
0 / 1 |
__construct | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
3.10 | |||
key | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
next | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
rewind | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
valid | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
pageFromList | n/a |
0 / 0 |
n/a |
0 / 0 |
0 |
1 | <?php |
2 | /** |
3 | * OpenStack Swift based file backend. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup FileBackend |
22 | * @author Russ Nelson |
23 | */ |
24 | |
25 | namespace Wikimedia\FileBackend\FileIteration; |
26 | |
27 | use Iterator; |
28 | use Traversable; |
29 | use Wikimedia\FileBackend\SwiftFileBackend; |
30 | |
31 | /** |
32 | * SwiftFileBackend helper class to page through listings. |
33 | * Swift also has a listing limit of 10,000 objects for performance. |
34 | * Do not use this class from places outside SwiftFileBackend. |
35 | * |
36 | * @ingroup FileBackend |
37 | */ |
38 | abstract class SwiftFileBackendList implements Iterator { |
39 | /** @var string[]|array[] Current page of entries; path list or (path,stat map) list */ |
40 | protected $iterableBuffer = []; |
41 | |
42 | /** @var string|null Continuation marker; the next page starts *after* this path */ |
43 | protected $continueAfter = null; |
44 | |
45 | /** @var int */ |
46 | protected $pos = 0; |
47 | |
48 | /** @var array */ |
49 | protected $params = []; |
50 | |
51 | /** @var SwiftFileBackend */ |
52 | protected $backend; |
53 | |
54 | /** @var string Container name */ |
55 | protected $container; |
56 | |
57 | /** @var string Storage directory */ |
58 | protected $dir; |
59 | |
60 | /** @var int */ |
61 | protected $suffixStart; |
62 | |
63 | private const PAGE_SIZE = 9000; // file listing buffer size |
64 | |
65 | /** |
66 | * @param SwiftFileBackend $backend |
67 | * @param string $fullCont Resolved container name |
68 | * @param string $dir Resolved directory relative to container |
69 | * @param array $params |
70 | * @note This defers I/O by not buffering the first page (useful for AppendIterator use) |
71 | * @note Do not call current()/valid() without calling rewind() first |
72 | */ |
73 | public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) { |
74 | $this->backend = $backend; |
75 | $this->container = $fullCont; |
76 | $this->dir = $dir; |
77 | if ( substr( $this->dir, -1 ) === '/' ) { |
78 | $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash |
79 | } |
80 | if ( $this->dir == '' ) { // whole container |
81 | $this->suffixStart = 0; |
82 | } else { // dir within container |
83 | $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/" |
84 | } |
85 | $this->params = $params; |
86 | } |
87 | |
88 | /** |
89 | * @see Iterator::key() |
90 | * @return int |
91 | */ |
92 | public function key(): int { |
93 | return $this->pos; |
94 | } |
95 | |
96 | /** |
97 | * @inheritDoc |
98 | */ |
99 | public function next(): void { |
100 | ++$this->pos; |
101 | if ( $this->iterableBuffer === null ) { |
102 | // Last page of entries failed to load |
103 | return; |
104 | } |
105 | // Advance to the next entry in the page |
106 | next( $this->iterableBuffer ); |
107 | // Check if there are no entries left in this page and |
108 | // advance to the next page if this page was not empty. |
109 | if ( !$this->valid() && count( $this->iterableBuffer ) ) { |
110 | $this->iterableBuffer = $this->pageFromList( |
111 | $this->container, |
112 | $this->dir, |
113 | $this->continueAfter, |
114 | self::PAGE_SIZE, |
115 | $this->params |
116 | ); |
117 | } |
118 | } |
119 | |
120 | /** |
121 | * @inheritDoc |
122 | */ |
123 | public function rewind(): void { |
124 | $this->pos = 0; |
125 | $this->continueAfter = null; |
126 | $this->iterableBuffer = $this->pageFromList( |
127 | $this->container, |
128 | $this->dir, |
129 | // @phan-suppress-next-line PhanTypeMismatchArgumentPropertyReferenceReal |
130 | $this->continueAfter, |
131 | self::PAGE_SIZE, |
132 | $this->params |
133 | ); |
134 | } |
135 | |
136 | /** |
137 | * @see Iterator::valid() |
138 | * @return bool |
139 | */ |
140 | public function valid(): bool { |
141 | if ( $this->iterableBuffer === null ) { |
142 | // Last page of entries failed to load |
143 | return false; |
144 | } |
145 | // Note that entries (paths/tuples) are never boolean |
146 | return ( current( $this->iterableBuffer ) !== false ); |
147 | } |
148 | |
149 | /** |
150 | * Get the next page of entries |
151 | * |
152 | * @param string $container Resolved container name |
153 | * @param string $dir Resolved path relative to container |
154 | * @param string &$after @phan-output-reference Continuation marker |
155 | * @param int $limit |
156 | * @param array $params |
157 | * @return Traversable|array |
158 | */ |
159 | abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params ); |
160 | } |
161 | |
162 | /** @deprecated class alias since 1.43 */ |
163 | class_alias( SwiftFileBackendList::class, 'SwiftFileBackendList' ); |