This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | * @author Aaron Schulz |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * @brief Class for an OpenStack Swift (or Ceph RGW) based file backend. |
||
28 | * |
||
29 | * StatusValue messages should avoid mentioning the Swift account name. |
||
30 | * Likewise, error suppression should be used to avoid path disclosure. |
||
31 | * |
||
32 | * @ingroup FileBackend |
||
33 | * @since 1.19 |
||
34 | */ |
||
35 | class SwiftFileBackend extends FileBackendStore { |
||
36 | /** @var MultiHttpClient */ |
||
37 | protected $http; |
||
38 | |||
39 | /** @var int TTL in seconds */ |
||
40 | protected $authTTL; |
||
41 | |||
42 | /** @var string Authentication base URL (without version) */ |
||
43 | protected $swiftAuthUrl; |
||
44 | |||
45 | /** @var string Swift user (account:user) to authenticate as */ |
||
46 | protected $swiftUser; |
||
47 | |||
48 | /** @var string Secret key for user */ |
||
49 | protected $swiftKey; |
||
50 | |||
51 | /** @var string Shared secret value for making temp URLs */ |
||
52 | protected $swiftTempUrlKey; |
||
53 | |||
54 | /** @var string S3 access key (RADOS Gateway) */ |
||
55 | protected $rgwS3AccessKey; |
||
56 | |||
57 | /** @var string S3 authentication key (RADOS Gateway) */ |
||
58 | protected $rgwS3SecretKey; |
||
59 | |||
60 | /** @var BagOStuff */ |
||
61 | protected $srvCache; |
||
62 | |||
63 | /** @var ProcessCacheLRU Container stat cache */ |
||
64 | protected $containerStatCache; |
||
65 | |||
66 | /** @var array */ |
||
67 | protected $authCreds; |
||
68 | |||
69 | /** @var int UNIX timestamp */ |
||
70 | protected $authSessionTimestamp = 0; |
||
71 | |||
72 | /** @var int UNIX timestamp */ |
||
73 | protected $authErrorTimestamp = null; |
||
74 | |||
75 | /** @var bool Whether the server is an Ceph RGW */ |
||
76 | protected $isRGW = false; |
||
77 | |||
78 | /** |
||
79 | * @see FileBackendStore::__construct() |
||
80 | * Additional $config params include: |
||
81 | * - swiftAuthUrl : Swift authentication server URL |
||
82 | * - swiftUser : Swift user used by MediaWiki (account:username) |
||
83 | * - swiftKey : Swift authentication key for the above user |
||
84 | * - swiftAuthTTL : Swift authentication TTL (seconds) |
||
85 | * - swiftTempUrlKey : Swift "X-Account-Meta-Temp-URL-Key" value on the account. |
||
86 | * Do not set this until it has been set in the backend. |
||
87 | * - shardViaHashLevels : Map of container names to sharding config with: |
||
88 | * - base : base of hash characters, 16 or 36 |
||
89 | * - levels : the number of hash levels (and digits) |
||
90 | * - repeat : hash subdirectories are prefixed with all the |
||
91 | * parent hash directory names (e.g. "a/ab/abc") |
||
92 | * - cacheAuthInfo : Whether to cache authentication tokens in APC, XCache, ect. |
||
93 | * If those are not available, then the main cache will be used. |
||
94 | * This is probably insecure in shared hosting environments. |
||
95 | * - rgwS3AccessKey : Rados Gateway S3 "access key" value on the account. |
||
96 | * Do not set this until it has been set in the backend. |
||
97 | * This is used for generating expiring pre-authenticated URLs. |
||
98 | * Only use this when using rgw and to work around |
||
99 | * http://tracker.newdream.net/issues/3454. |
||
100 | * - rgwS3SecretKey : Rados Gateway S3 "secret key" value on the account. |
||
101 | * Do not set this until it has been set in the backend. |
||
102 | * This is used for generating expiring pre-authenticated URLs. |
||
103 | * Only use this when using rgw and to work around |
||
104 | * http://tracker.newdream.net/issues/3454. |
||
105 | */ |
||
106 | public function __construct( array $config ) { |
||
107 | parent::__construct( $config ); |
||
108 | // Required settings |
||
109 | $this->swiftAuthUrl = $config['swiftAuthUrl']; |
||
110 | $this->swiftUser = $config['swiftUser']; |
||
111 | $this->swiftKey = $config['swiftKey']; |
||
112 | // Optional settings |
||
113 | $this->authTTL = isset( $config['swiftAuthTTL'] ) |
||
114 | ? $config['swiftAuthTTL'] |
||
115 | : 15 * 60; // some sane number |
||
116 | $this->swiftTempUrlKey = isset( $config['swiftTempUrlKey'] ) |
||
117 | ? $config['swiftTempUrlKey'] |
||
118 | : ''; |
||
119 | $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] ) |
||
120 | ? $config['shardViaHashLevels'] |
||
121 | : ''; |
||
122 | $this->rgwS3AccessKey = isset( $config['rgwS3AccessKey'] ) |
||
123 | ? $config['rgwS3AccessKey'] |
||
124 | : ''; |
||
125 | $this->rgwS3SecretKey = isset( $config['rgwS3SecretKey'] ) |
||
126 | ? $config['rgwS3SecretKey'] |
||
127 | : ''; |
||
128 | // HTTP helper client |
||
129 | $this->http = new MultiHttpClient( [] ); |
||
130 | // Cache container information to mask latency |
||
131 | if ( isset( $config['wanCache'] ) && $config['wanCache'] instanceof WANObjectCache ) { |
||
132 | $this->memCache = $config['wanCache']; |
||
133 | } |
||
134 | // Process cache for container info |
||
135 | $this->containerStatCache = new ProcessCacheLRU( 300 ); |
||
136 | // Cache auth token information to avoid RTTs |
||
137 | if ( !empty( $config['cacheAuthInfo'] ) && isset( $config['srvCache'] ) ) { |
||
138 | $this->srvCache = $config['srvCache']; |
||
139 | } else { |
||
140 | $this->srvCache = new EmptyBagOStuff(); |
||
141 | } |
||
142 | } |
||
143 | |||
144 | public function getFeatures() { |
||
145 | return ( FileBackend::ATTR_UNICODE_PATHS | |
||
146 | FileBackend::ATTR_HEADERS | FileBackend::ATTR_METADATA ); |
||
147 | } |
||
148 | |||
149 | protected function resolveContainerPath( $container, $relStoragePath ) { |
||
150 | if ( !mb_check_encoding( $relStoragePath, 'UTF-8' ) ) { |
||
151 | return null; // not UTF-8, makes it hard to use CF and the swift HTTP API |
||
152 | } elseif ( strlen( urlencode( $relStoragePath ) ) > 1024 ) { |
||
153 | return null; // too long for Swift |
||
154 | } |
||
155 | |||
156 | return $relStoragePath; |
||
157 | } |
||
158 | |||
159 | public function isPathUsableInternal( $storagePath ) { |
||
160 | list( $container, $rel ) = $this->resolveStoragePathReal( $storagePath ); |
||
161 | if ( $rel === null ) { |
||
162 | return false; // invalid |
||
163 | } |
||
164 | |||
165 | return is_array( $this->getContainerStat( $container ) ); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Sanitize and filter the custom headers from a $params array. |
||
170 | * Only allows certain "standard" Content- and X-Content- headers. |
||
171 | * |
||
172 | * @param array $params |
||
173 | * @return array Sanitized value of 'headers' field in $params |
||
174 | */ |
||
175 | protected function sanitizeHdrs( array $params ) { |
||
176 | return isset( $params['headers'] ) |
||
177 | ? $this->getCustomHeaders( $params['headers'] ) |
||
178 | : []; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * @param array $rawHeaders |
||
183 | * @return array Custom non-metadata HTTP headers |
||
184 | */ |
||
185 | protected function getCustomHeaders( array $rawHeaders ) { |
||
186 | $headers = []; |
||
187 | |||
188 | // Normalize casing, and strip out illegal headers |
||
189 | foreach ( $rawHeaders as $name => $value ) { |
||
190 | $name = strtolower( $name ); |
||
191 | if ( preg_match( '/^content-(type|length)$/', $name ) ) { |
||
192 | continue; // blacklisted |
||
193 | } elseif ( preg_match( '/^(x-)?content-/', $name ) ) { |
||
194 | $headers[$name] = $value; // allowed |
||
195 | } elseif ( preg_match( '/^content-(disposition)/', $name ) ) { |
||
196 | $headers[$name] = $value; // allowed |
||
197 | } |
||
198 | } |
||
199 | // By default, Swift has annoyingly low maximum header value limits |
||
200 | if ( isset( $headers['content-disposition'] ) ) { |
||
201 | $disposition = ''; |
||
202 | // @note: assume FileBackend::makeContentDisposition() already used |
||
203 | foreach ( explode( ';', $headers['content-disposition'] ) as $part ) { |
||
204 | $part = trim( $part ); |
||
205 | $new = ( $disposition === '' ) ? $part : "{$disposition};{$part}"; |
||
206 | if ( strlen( $new ) <= 255 ) { |
||
207 | $disposition = $new; |
||
208 | } else { |
||
209 | break; // too long; sigh |
||
210 | } |
||
211 | } |
||
212 | $headers['content-disposition'] = $disposition; |
||
213 | } |
||
214 | |||
215 | return $headers; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * @param array $rawHeaders |
||
220 | * @return array Custom metadata headers |
||
221 | */ |
||
222 | protected function getMetadataHeaders( array $rawHeaders ) { |
||
223 | $headers = []; |
||
224 | foreach ( $rawHeaders as $name => $value ) { |
||
225 | $name = strtolower( $name ); |
||
226 | if ( strpos( $name, 'x-object-meta-' ) === 0 ) { |
||
227 | $headers[$name] = $value; |
||
228 | } |
||
229 | } |
||
230 | |||
231 | return $headers; |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * @param array $rawHeaders |
||
236 | * @return array Custom metadata headers with prefix removed |
||
237 | */ |
||
238 | protected function getMetadata( array $rawHeaders ) { |
||
239 | $metadata = []; |
||
240 | foreach ( $this->getMetadataHeaders( $rawHeaders ) as $name => $value ) { |
||
241 | $metadata[substr( $name, strlen( 'x-object-meta-' ) )] = $value; |
||
242 | } |
||
243 | |||
244 | return $metadata; |
||
245 | } |
||
246 | |||
247 | protected function doCreateInternal( array $params ) { |
||
248 | $status = $this->newStatus(); |
||
249 | |||
250 | list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] ); |
||
251 | if ( $dstRel === null ) { |
||
252 | $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
||
253 | |||
254 | return $status; |
||
255 | } |
||
256 | |||
257 | $sha1Hash = Wikimedia\base_convert( sha1( $params['content'] ), 16, 36, 31 ); |
||
258 | $contentType = isset( $params['headers']['content-type'] ) |
||
259 | ? $params['headers']['content-type'] |
||
260 | : $this->getContentType( $params['dst'], $params['content'], null ); |
||
261 | |||
262 | $reqs = [ [ |
||
263 | 'method' => 'PUT', |
||
264 | 'url' => [ $dstCont, $dstRel ], |
||
265 | 'headers' => [ |
||
266 | 'content-length' => strlen( $params['content'] ), |
||
267 | 'etag' => md5( $params['content'] ), |
||
268 | 'content-type' => $contentType, |
||
269 | 'x-object-meta-sha1base36' => $sha1Hash |
||
270 | ] + $this->sanitizeHdrs( $params ), |
||
271 | 'body' => $params['content'] |
||
272 | ] ]; |
||
273 | |||
274 | $method = __METHOD__; |
||
275 | View Code Duplication | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
|
276 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
277 | if ( $rcode === 201 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
278 | // good |
||
279 | } elseif ( $rcode === 412 ) { |
||
280 | $status->fatal( 'backend-fail-contenttype', $params['dst'] ); |
||
281 | } else { |
||
282 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
283 | } |
||
284 | }; |
||
285 | |||
286 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
287 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
288 | $status->value = $opHandle; |
||
289 | } else { // actually write the object in Swift |
||
290 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
291 | } |
||
292 | |||
293 | return $status; |
||
294 | } |
||
295 | |||
296 | protected function doStoreInternal( array $params ) { |
||
297 | $status = $this->newStatus(); |
||
298 | |||
299 | list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] ); |
||
300 | if ( $dstRel === null ) { |
||
301 | $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
||
302 | |||
303 | return $status; |
||
304 | } |
||
305 | |||
306 | MediaWiki\suppressWarnings(); |
||
307 | $sha1Hash = sha1_file( $params['src'] ); |
||
308 | MediaWiki\restoreWarnings(); |
||
309 | View Code Duplication | if ( $sha1Hash === false ) { // source doesn't exist? |
|
310 | $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] ); |
||
311 | |||
312 | return $status; |
||
313 | } |
||
314 | $sha1Hash = Wikimedia\base_convert( $sha1Hash, 16, 36, 31 ); |
||
315 | $contentType = isset( $params['headers']['content-type'] ) |
||
316 | ? $params['headers']['content-type'] |
||
317 | : $this->getContentType( $params['dst'], null, $params['src'] ); |
||
318 | |||
319 | $handle = fopen( $params['src'], 'rb' ); |
||
320 | View Code Duplication | if ( $handle === false ) { // source doesn't exist? |
|
321 | $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] ); |
||
322 | |||
323 | return $status; |
||
324 | } |
||
325 | |||
326 | $reqs = [ [ |
||
327 | 'method' => 'PUT', |
||
328 | 'url' => [ $dstCont, $dstRel ], |
||
329 | 'headers' => [ |
||
330 | 'content-length' => filesize( $params['src'] ), |
||
331 | 'etag' => md5_file( $params['src'] ), |
||
332 | 'content-type' => $contentType, |
||
333 | 'x-object-meta-sha1base36' => $sha1Hash |
||
334 | ] + $this->sanitizeHdrs( $params ), |
||
335 | 'body' => $handle // resource |
||
336 | ] ]; |
||
337 | |||
338 | $method = __METHOD__; |
||
339 | View Code Duplication | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
|
340 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
341 | if ( $rcode === 201 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
342 | // good |
||
343 | } elseif ( $rcode === 412 ) { |
||
344 | $status->fatal( 'backend-fail-contenttype', $params['dst'] ); |
||
345 | } else { |
||
346 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
347 | } |
||
348 | }; |
||
349 | |||
350 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
351 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
352 | $status->value = $opHandle; |
||
353 | } else { // actually write the object in Swift |
||
354 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
355 | } |
||
356 | |||
357 | return $status; |
||
358 | } |
||
359 | |||
360 | protected function doCopyInternal( array $params ) { |
||
361 | $status = $this->newStatus(); |
||
362 | |||
363 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
364 | if ( $srcRel === null ) { |
||
365 | $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
||
366 | |||
367 | return $status; |
||
368 | } |
||
369 | |||
370 | list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] ); |
||
371 | if ( $dstRel === null ) { |
||
372 | $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
||
373 | |||
374 | return $status; |
||
375 | } |
||
376 | |||
377 | $reqs = [ [ |
||
378 | 'method' => 'PUT', |
||
379 | 'url' => [ $dstCont, $dstRel ], |
||
380 | 'headers' => [ |
||
381 | 'x-copy-from' => '/' . rawurlencode( $srcCont ) . |
||
382 | '/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) ) |
||
383 | ] + $this->sanitizeHdrs( $params ), // extra headers merged into object |
||
384 | ] ]; |
||
385 | |||
386 | $method = __METHOD__; |
||
387 | View Code Duplication | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
|
388 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
389 | if ( $rcode === 201 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
390 | // good |
||
391 | } elseif ( $rcode === 404 ) { |
||
392 | $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); |
||
393 | } else { |
||
394 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
395 | } |
||
396 | }; |
||
397 | |||
398 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
399 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
400 | $status->value = $opHandle; |
||
401 | } else { // actually write the object in Swift |
||
402 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
403 | } |
||
404 | |||
405 | return $status; |
||
406 | } |
||
407 | |||
408 | protected function doMoveInternal( array $params ) { |
||
409 | $status = $this->newStatus(); |
||
410 | |||
411 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
412 | if ( $srcRel === null ) { |
||
413 | $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
||
414 | |||
415 | return $status; |
||
416 | } |
||
417 | |||
418 | list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] ); |
||
419 | if ( $dstRel === null ) { |
||
420 | $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
||
421 | |||
422 | return $status; |
||
423 | } |
||
424 | |||
425 | $reqs = [ |
||
426 | [ |
||
427 | 'method' => 'PUT', |
||
428 | 'url' => [ $dstCont, $dstRel ], |
||
429 | 'headers' => [ |
||
430 | 'x-copy-from' => '/' . rawurlencode( $srcCont ) . |
||
431 | '/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) ) |
||
432 | ] + $this->sanitizeHdrs( $params ) // extra headers merged into object |
||
433 | ] |
||
434 | ]; |
||
435 | if ( "{$srcCont}/{$srcRel}" !== "{$dstCont}/{$dstRel}" ) { |
||
436 | $reqs[] = [ |
||
437 | 'method' => 'DELETE', |
||
438 | 'url' => [ $srcCont, $srcRel ], |
||
439 | 'headers' => [] |
||
440 | ]; |
||
441 | } |
||
442 | |||
443 | $method = __METHOD__; |
||
444 | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
||
445 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
446 | if ( $request['method'] === 'PUT' && $rcode === 201 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
447 | // good |
||
448 | } elseif ( $request['method'] === 'DELETE' && $rcode === 204 ) { |
||
0 ignored issues
–
show
This
elseif statement is empty, and could be removed.
This check looks for the bodies of These ![]() |
|||
449 | // good |
||
450 | } elseif ( $rcode === 404 ) { |
||
451 | $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] ); |
||
452 | } else { |
||
453 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
454 | } |
||
455 | }; |
||
456 | |||
457 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
458 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
459 | $status->value = $opHandle; |
||
460 | } else { // actually move the object in Swift |
||
461 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
462 | } |
||
463 | |||
464 | return $status; |
||
465 | } |
||
466 | |||
467 | protected function doDeleteInternal( array $params ) { |
||
468 | $status = $this->newStatus(); |
||
469 | |||
470 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
471 | if ( $srcRel === null ) { |
||
472 | $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
||
473 | |||
474 | return $status; |
||
475 | } |
||
476 | |||
477 | $reqs = [ [ |
||
478 | 'method' => 'DELETE', |
||
479 | 'url' => [ $srcCont, $srcRel ], |
||
480 | 'headers' => [] |
||
481 | ] ]; |
||
482 | |||
483 | $method = __METHOD__; |
||
484 | View Code Duplication | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
|
485 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
486 | if ( $rcode === 204 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
487 | // good |
||
488 | } elseif ( $rcode === 404 ) { |
||
489 | if ( empty( $params['ignoreMissingSource'] ) ) { |
||
490 | $status->fatal( 'backend-fail-delete', $params['src'] ); |
||
491 | } |
||
492 | } else { |
||
493 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
494 | } |
||
495 | }; |
||
496 | |||
497 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
498 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
499 | $status->value = $opHandle; |
||
500 | } else { // actually delete the object in Swift |
||
501 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
502 | } |
||
503 | |||
504 | return $status; |
||
505 | } |
||
506 | |||
507 | protected function doDescribeInternal( array $params ) { |
||
508 | $status = $this->newStatus(); |
||
509 | |||
510 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
511 | if ( $srcRel === null ) { |
||
512 | $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
||
513 | |||
514 | return $status; |
||
515 | } |
||
516 | |||
517 | // Fetch the old object headers/metadata...this should be in stat cache by now |
||
518 | $stat = $this->getFileStat( [ 'src' => $params['src'], 'latest' => 1 ] ); |
||
519 | if ( $stat && !isset( $stat['xattr'] ) ) { // older cache entry |
||
520 | $stat = $this->doGetFileStat( [ 'src' => $params['src'], 'latest' => 1 ] ); |
||
521 | } |
||
522 | if ( !$stat ) { |
||
523 | $status->fatal( 'backend-fail-describe', $params['src'] ); |
||
524 | |||
525 | return $status; |
||
526 | } |
||
527 | |||
528 | // POST clears prior headers, so we need to merge the changes in to the old ones |
||
529 | $metaHdrs = []; |
||
530 | foreach ( $stat['xattr']['metadata'] as $name => $value ) { |
||
531 | $metaHdrs["x-object-meta-$name"] = $value; |
||
532 | } |
||
533 | $customHdrs = $this->sanitizeHdrs( $params ) + $stat['xattr']['headers']; |
||
534 | |||
535 | $reqs = [ [ |
||
536 | 'method' => 'POST', |
||
537 | 'url' => [ $srcCont, $srcRel ], |
||
538 | 'headers' => $metaHdrs + $customHdrs |
||
539 | ] ]; |
||
540 | |||
541 | $method = __METHOD__; |
||
542 | View Code Duplication | $handler = function ( array $request, StatusValue $status ) use ( $method, $params ) { |
|
543 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
544 | if ( $rcode === 202 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
545 | // good |
||
546 | } elseif ( $rcode === 404 ) { |
||
547 | $status->fatal( 'backend-fail-describe', $params['src'] ); |
||
548 | } else { |
||
549 | $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc ); |
||
550 | } |
||
551 | }; |
||
552 | |||
553 | $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs ); |
||
554 | View Code Duplication | if ( !empty( $params['async'] ) ) { // deferred |
|
555 | $status->value = $opHandle; |
||
556 | } else { // actually change the object in Swift |
||
557 | $status->merge( current( $this->doExecuteOpHandlesInternal( [ $opHandle ] ) ) ); |
||
0 ignored issues
–
show
|
|||
558 | } |
||
559 | |||
560 | return $status; |
||
561 | } |
||
562 | |||
563 | protected function doPrepareInternal( $fullCont, $dir, array $params ) { |
||
564 | $status = $this->newStatus(); |
||
565 | |||
566 | // (a) Check if container already exists |
||
567 | $stat = $this->getContainerStat( $fullCont ); |
||
568 | View Code Duplication | if ( is_array( $stat ) ) { |
|
569 | return $status; // already there |
||
570 | } elseif ( $stat === null ) { |
||
571 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
572 | $this->logger->error( __METHOD__ . ': cannot get container stat' ); |
||
573 | |||
574 | return $status; |
||
575 | } |
||
576 | |||
577 | // (b) Create container as needed with proper ACLs |
||
578 | if ( $stat === false ) { |
||
579 | $params['op'] = 'prepare'; |
||
580 | $status->merge( $this->createContainer( $fullCont, $params ) ); |
||
581 | } |
||
582 | |||
583 | return $status; |
||
584 | } |
||
585 | |||
586 | View Code Duplication | protected function doSecureInternal( $fullCont, $dir, array $params ) { |
|
587 | $status = $this->newStatus(); |
||
588 | if ( empty( $params['noAccess'] ) ) { |
||
589 | return $status; // nothing to do |
||
590 | } |
||
591 | |||
592 | $stat = $this->getContainerStat( $fullCont ); |
||
593 | if ( is_array( $stat ) ) { |
||
594 | // Make container private to end-users... |
||
595 | $status->merge( $this->setContainerAccess( |
||
596 | $fullCont, |
||
597 | [ $this->swiftUser ], // read |
||
598 | [ $this->swiftUser ] // write |
||
599 | ) ); |
||
600 | } elseif ( $stat === false ) { |
||
601 | $status->fatal( 'backend-fail-usable', $params['dir'] ); |
||
602 | } else { |
||
603 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
604 | $this->logger->error( __METHOD__ . ': cannot get container stat' ); |
||
605 | } |
||
606 | |||
607 | return $status; |
||
608 | } |
||
609 | |||
610 | View Code Duplication | protected function doPublishInternal( $fullCont, $dir, array $params ) { |
|
611 | $status = $this->newStatus(); |
||
612 | |||
613 | $stat = $this->getContainerStat( $fullCont ); |
||
614 | if ( is_array( $stat ) ) { |
||
615 | // Make container public to end-users... |
||
616 | $status->merge( $this->setContainerAccess( |
||
617 | $fullCont, |
||
618 | [ $this->swiftUser, '.r:*' ], // read |
||
619 | [ $this->swiftUser ] // write |
||
620 | ) ); |
||
621 | } elseif ( $stat === false ) { |
||
622 | $status->fatal( 'backend-fail-usable', $params['dir'] ); |
||
623 | } else { |
||
624 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
625 | $this->logger->error( __METHOD__ . ': cannot get container stat' ); |
||
626 | } |
||
627 | |||
628 | return $status; |
||
629 | } |
||
630 | |||
631 | protected function doCleanInternal( $fullCont, $dir, array $params ) { |
||
632 | $status = $this->newStatus(); |
||
633 | |||
634 | // Only containers themselves can be removed, all else is virtual |
||
635 | if ( $dir != '' ) { |
||
636 | return $status; // nothing to do |
||
637 | } |
||
638 | |||
639 | // (a) Check the container |
||
640 | $stat = $this->getContainerStat( $fullCont, true ); |
||
641 | View Code Duplication | if ( $stat === false ) { |
|
642 | return $status; // ok, nothing to do |
||
643 | } elseif ( !is_array( $stat ) ) { |
||
644 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
645 | $this->logger->error( __METHOD__ . ': cannot get container stat' ); |
||
646 | |||
647 | return $status; |
||
648 | } |
||
649 | |||
650 | // (b) Delete the container if empty |
||
651 | if ( $stat['count'] == 0 ) { |
||
652 | $params['op'] = 'clean'; |
||
653 | $status->merge( $this->deleteContainer( $fullCont, $params ) ); |
||
654 | } |
||
655 | |||
656 | return $status; |
||
657 | } |
||
658 | |||
659 | protected function doGetFileStat( array $params ) { |
||
660 | $params = [ 'srcs' => [ $params['src'] ], 'concurrency' => 1 ] + $params; |
||
661 | unset( $params['src'] ); |
||
662 | $stats = $this->doGetFileStatMulti( $params ); |
||
663 | |||
664 | return reset( $stats ); |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT"/"2013-05-11T07:37:27.678360Z". |
||
669 | * Dates might also come in like "2013-05-11T07:37:27.678360" from Swift listings, |
||
670 | * missing the timezone suffix (though Ceph RGW does not appear to have this bug). |
||
671 | * |
||
672 | * @param string $ts |
||
673 | * @param int $format Output format (TS_* constant) |
||
674 | * @return string |
||
675 | * @throws FileBackendError |
||
676 | */ |
||
677 | protected function convertSwiftDate( $ts, $format = TS_MW ) { |
||
678 | try { |
||
679 | $timestamp = new MWTimestamp( $ts ); |
||
680 | |||
681 | return $timestamp->getTimestamp( $format ); |
||
682 | } catch ( Exception $e ) { |
||
683 | throw new FileBackendError( $e->getMessage() ); |
||
684 | } |
||
685 | } |
||
686 | |||
687 | /** |
||
688 | * Fill in any missing object metadata and save it to Swift |
||
689 | * |
||
690 | * @param array $objHdrs Object response headers |
||
691 | * @param string $path Storage path to object |
||
692 | * @return array New headers |
||
693 | */ |
||
694 | protected function addMissingMetadata( array $objHdrs, $path ) { |
||
695 | if ( isset( $objHdrs['x-object-meta-sha1base36'] ) ) { |
||
696 | return $objHdrs; // nothing to do |
||
697 | } |
||
698 | |||
699 | /** @noinspection PhpUnusedLocalVariableInspection */ |
||
700 | $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" ); |
||
0 ignored issues
–
show
$ps is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
701 | $this->logger->error( __METHOD__ . ": $path was not stored with SHA-1 metadata." ); |
||
702 | |||
703 | $objHdrs['x-object-meta-sha1base36'] = false; |
||
704 | |||
705 | $auth = $this->getAuthentication(); |
||
706 | if ( !$auth ) { |
||
707 | return $objHdrs; // failed |
||
708 | } |
||
709 | |||
710 | // Find prior custom HTTP headers |
||
711 | $postHeaders = $this->getCustomHeaders( $objHdrs ); |
||
712 | // Find prior metadata headers |
||
713 | $postHeaders += $this->getMetadataHeaders( $objHdrs ); |
||
714 | |||
715 | $status = $this->newStatus(); |
||
716 | /** @noinspection PhpUnusedLocalVariableInspection */ |
||
717 | $scopeLockS = $this->getScopedFileLocks( [ $path ], LockManager::LOCK_UW, $status ); |
||
0 ignored issues
–
show
$scopeLockS is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
718 | if ( $status->isOK() ) { |
||
719 | $tmpFile = $this->getLocalCopy( [ 'src' => $path, 'latest' => 1 ] ); |
||
720 | if ( $tmpFile ) { |
||
721 | $hash = $tmpFile->getSha1Base36(); |
||
722 | if ( $hash !== false ) { |
||
723 | $objHdrs['x-object-meta-sha1base36'] = $hash; |
||
724 | // Merge new SHA1 header into the old ones |
||
725 | $postHeaders['x-object-meta-sha1base36'] = $hash; |
||
726 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $path ); |
||
727 | list( $rcode ) = $this->http->run( [ |
||
728 | 'method' => 'POST', |
||
729 | 'url' => $this->storageUrl( $auth, $srcCont, $srcRel ), |
||
730 | 'headers' => $this->authTokenHeaders( $auth ) + $postHeaders |
||
731 | ] ); |
||
732 | if ( $rcode >= 200 && $rcode <= 299 ) { |
||
733 | $this->deleteFileCache( $path ); |
||
734 | |||
735 | return $objHdrs; // success |
||
736 | } |
||
737 | } |
||
738 | } |
||
739 | } |
||
740 | |||
741 | $this->logger->error( __METHOD__ . ": unable to set SHA-1 metadata for $path" ); |
||
742 | |||
743 | return $objHdrs; // failed |
||
744 | } |
||
745 | |||
746 | protected function doGetFileContentsMulti( array $params ) { |
||
747 | $contents = []; |
||
748 | |||
749 | $auth = $this->getAuthentication(); |
||
750 | |||
751 | $ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging |
||
752 | // Blindly create tmp files and stream to them, catching any exception if the file does |
||
753 | // not exist. Doing stats here is useless and will loop infinitely in addMissingMetadata(). |
||
754 | $reqs = []; // (path => op) |
||
755 | |||
756 | foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch |
||
757 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $path ); |
||
758 | if ( $srcRel === null || !$auth ) { |
||
759 | $contents[$path] = false; |
||
760 | continue; |
||
761 | } |
||
762 | // Create a new temporary memory file... |
||
763 | $handle = fopen( 'php://temp', 'wb' ); |
||
764 | View Code Duplication | if ( $handle ) { |
|
765 | $reqs[$path] = [ |
||
766 | 'method' => 'GET', |
||
767 | 'url' => $this->storageUrl( $auth, $srcCont, $srcRel ), |
||
768 | 'headers' => $this->authTokenHeaders( $auth ) |
||
769 | + $this->headersFromParams( $params ), |
||
770 | 'stream' => $handle, |
||
771 | ]; |
||
772 | } |
||
773 | $contents[$path] = false; |
||
774 | } |
||
775 | |||
776 | $opts = [ 'maxConnsPerHost' => $params['concurrency'] ]; |
||
777 | $reqs = $this->http->runMulti( $reqs, $opts ); |
||
778 | foreach ( $reqs as $path => $op ) { |
||
779 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $op['response']; |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
780 | if ( $rcode >= 200 && $rcode <= 299 ) { |
||
781 | rewind( $op['stream'] ); // start from the beginning |
||
782 | $contents[$path] = stream_get_contents( $op['stream'] ); |
||
783 | } elseif ( $rcode === 404 ) { |
||
784 | $contents[$path] = false; |
||
785 | } else { |
||
786 | $this->onError( null, __METHOD__, |
||
787 | [ 'src' => $path ] + $ep, $rerr, $rcode, $rdesc ); |
||
788 | } |
||
789 | fclose( $op['stream'] ); // close open handle |
||
790 | } |
||
791 | |||
792 | return $contents; |
||
793 | } |
||
794 | |||
795 | protected function doDirectoryExists( $fullCont, $dir, array $params ) { |
||
796 | $prefix = ( $dir == '' ) ? null : "{$dir}/"; |
||
797 | $status = $this->objectListing( $fullCont, 'names', 1, null, $prefix ); |
||
798 | if ( $status->isOK() ) { |
||
799 | return ( count( $status->value ) ) > 0; |
||
800 | } |
||
801 | |||
802 | return null; // error |
||
803 | } |
||
804 | |||
805 | /** |
||
806 | * @see FileBackendStore::getDirectoryListInternal() |
||
807 | * @param string $fullCont |
||
808 | * @param string $dir |
||
809 | * @param array $params |
||
810 | * @return SwiftFileBackendDirList |
||
811 | */ |
||
812 | public function getDirectoryListInternal( $fullCont, $dir, array $params ) { |
||
813 | return new SwiftFileBackendDirList( $this, $fullCont, $dir, $params ); |
||
814 | } |
||
815 | |||
816 | /** |
||
817 | * @see FileBackendStore::getFileListInternal() |
||
818 | * @param string $fullCont |
||
819 | * @param string $dir |
||
820 | * @param array $params |
||
821 | * @return SwiftFileBackendFileList |
||
822 | */ |
||
823 | public function getFileListInternal( $fullCont, $dir, array $params ) { |
||
824 | return new SwiftFileBackendFileList( $this, $fullCont, $dir, $params ); |
||
825 | } |
||
826 | |||
827 | /** |
||
828 | * Do not call this function outside of SwiftFileBackendFileList |
||
829 | * |
||
830 | * @param string $fullCont Resolved container name |
||
831 | * @param string $dir Resolved storage directory with no trailing slash |
||
832 | * @param string|null $after Resolved container relative path to list items after |
||
833 | * @param int $limit Max number of items to list |
||
834 | * @param array $params Parameters for getDirectoryList() |
||
835 | * @return array List of container relative resolved paths of directories directly under $dir |
||
836 | * @throws FileBackendError |
||
837 | */ |
||
838 | public function getDirListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) { |
||
839 | $dirs = []; |
||
840 | if ( $after === INF ) { |
||
841 | return $dirs; // nothing more |
||
842 | } |
||
843 | |||
844 | $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" ); |
||
0 ignored issues
–
show
$ps is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
845 | |||
846 | $prefix = ( $dir == '' ) ? null : "{$dir}/"; |
||
847 | // Non-recursive: only list dirs right under $dir |
||
848 | if ( !empty( $params['topOnly'] ) ) { |
||
849 | $status = $this->objectListing( $fullCont, 'names', $limit, $after, $prefix, '/' ); |
||
850 | if ( !$status->isOK() ) { |
||
851 | throw new FileBackendError( "Iterator page I/O error." ); |
||
852 | } |
||
853 | $objects = $status->value; |
||
854 | foreach ( $objects as $object ) { // files and directories |
||
855 | if ( substr( $object, -1 ) === '/' ) { |
||
856 | $dirs[] = $object; // directories end in '/' |
||
857 | } |
||
858 | } |
||
859 | } else { |
||
860 | // Recursive: list all dirs under $dir and its subdirs |
||
861 | $getParentDir = function ( $path ) { |
||
862 | return ( strpos( $path, '/' ) !== false ) ? dirname( $path ) : false; |
||
863 | }; |
||
864 | |||
865 | // Get directory from last item of prior page |
||
866 | $lastDir = $getParentDir( $after ); // must be first page |
||
867 | $status = $this->objectListing( $fullCont, 'names', $limit, $after, $prefix ); |
||
868 | |||
869 | if ( !$status->isOK() ) { |
||
870 | throw new FileBackendError( "Iterator page I/O error." ); |
||
871 | } |
||
872 | |||
873 | $objects = $status->value; |
||
874 | |||
875 | foreach ( $objects as $object ) { // files |
||
876 | $objectDir = $getParentDir( $object ); // directory of object |
||
877 | |||
878 | if ( $objectDir !== false && $objectDir !== $dir ) { |
||
879 | // Swift stores paths in UTF-8, using binary sorting. |
||
880 | // See function "create_container_table" in common/db.py. |
||
881 | // If a directory is not "greater" than the last one, |
||
882 | // then it was already listed by the calling iterator. |
||
883 | if ( strcmp( $objectDir, $lastDir ) > 0 ) { |
||
884 | $pDir = $objectDir; |
||
885 | do { // add dir and all its parent dirs |
||
886 | $dirs[] = "{$pDir}/"; |
||
887 | $pDir = $getParentDir( $pDir ); |
||
888 | } while ( $pDir !== false // sanity |
||
889 | && strcmp( $pDir, $lastDir ) > 0 // not done already |
||
890 | && strlen( $pDir ) > strlen( $dir ) // within $dir |
||
891 | ); |
||
892 | } |
||
893 | $lastDir = $objectDir; |
||
894 | } |
||
895 | } |
||
896 | } |
||
897 | // Page on the unfiltered directory listing (what is returned may be filtered) |
||
898 | if ( count( $objects ) < $limit ) { |
||
899 | $after = INF; // avoid a second RTT |
||
900 | } else { |
||
901 | $after = end( $objects ); // update last item |
||
902 | } |
||
903 | |||
904 | return $dirs; |
||
905 | } |
||
906 | |||
907 | /** |
||
908 | * Do not call this function outside of SwiftFileBackendFileList |
||
909 | * |
||
910 | * @param string $fullCont Resolved container name |
||
911 | * @param string $dir Resolved storage directory with no trailing slash |
||
912 | * @param string|null $after Resolved container relative path of file to list items after |
||
913 | * @param int $limit Max number of items to list |
||
914 | * @param array $params Parameters for getDirectoryList() |
||
915 | * @return array List of resolved container relative paths of files under $dir |
||
916 | * @throws FileBackendError |
||
917 | */ |
||
918 | public function getFileListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) { |
||
919 | $files = []; // list of (path, stat array or null) entries |
||
920 | if ( $after === INF ) { |
||
921 | return $files; // nothing more |
||
922 | } |
||
923 | |||
924 | $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" ); |
||
0 ignored issues
–
show
$ps is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
925 | |||
926 | $prefix = ( $dir == '' ) ? null : "{$dir}/"; |
||
927 | // $objects will contain a list of unfiltered names or CF_Object items |
||
928 | // Non-recursive: only list files right under $dir |
||
929 | if ( !empty( $params['topOnly'] ) ) { |
||
930 | View Code Duplication | if ( !empty( $params['adviseStat'] ) ) { |
|
931 | $status = $this->objectListing( $fullCont, 'info', $limit, $after, $prefix, '/' ); |
||
932 | } else { |
||
933 | $status = $this->objectListing( $fullCont, 'names', $limit, $after, $prefix, '/' ); |
||
934 | } |
||
935 | View Code Duplication | } else { |
|
936 | // Recursive: list all files under $dir and its subdirs |
||
937 | if ( !empty( $params['adviseStat'] ) ) { |
||
938 | $status = $this->objectListing( $fullCont, 'info', $limit, $after, $prefix ); |
||
939 | } else { |
||
940 | $status = $this->objectListing( $fullCont, 'names', $limit, $after, $prefix ); |
||
941 | } |
||
942 | } |
||
943 | |||
944 | // Reformat this list into a list of (name, stat array or null) entries |
||
945 | if ( !$status->isOK() ) { |
||
946 | throw new FileBackendError( "Iterator page I/O error." ); |
||
947 | } |
||
948 | |||
949 | $objects = $status->value; |
||
950 | $files = $this->buildFileObjectListing( $params, $dir, $objects ); |
||
951 | |||
952 | // Page on the unfiltered object listing (what is returned may be filtered) |
||
953 | if ( count( $objects ) < $limit ) { |
||
954 | $after = INF; // avoid a second RTT |
||
955 | } else { |
||
956 | $after = end( $objects ); // update last item |
||
957 | $after = is_object( $after ) ? $after->name : $after; |
||
958 | } |
||
959 | |||
960 | return $files; |
||
961 | } |
||
962 | |||
963 | /** |
||
964 | * Build a list of file objects, filtering out any directories |
||
965 | * and extracting any stat info if provided in $objects (for CF_Objects) |
||
966 | * |
||
967 | * @param array $params Parameters for getDirectoryList() |
||
968 | * @param string $dir Resolved container directory path |
||
969 | * @param array $objects List of CF_Object items or object names |
||
970 | * @return array List of (names,stat array or null) entries |
||
971 | */ |
||
972 | private function buildFileObjectListing( array $params, $dir, array $objects ) { |
||
973 | $names = []; |
||
974 | foreach ( $objects as $object ) { |
||
975 | if ( is_object( $object ) ) { |
||
976 | if ( isset( $object->subdir ) || !isset( $object->name ) ) { |
||
977 | continue; // virtual directory entry; ignore |
||
978 | } |
||
979 | $stat = [ |
||
980 | // Convert various random Swift dates to TS_MW |
||
981 | 'mtime' => $this->convertSwiftDate( $object->last_modified, TS_MW ), |
||
982 | 'size' => (int)$object->bytes, |
||
983 | 'sha1' => null, |
||
984 | // Note: manifiest ETags are not an MD5 of the file |
||
985 | 'md5' => ctype_xdigit( $object->hash ) ? $object->hash : null, |
||
986 | 'latest' => false // eventually consistent |
||
987 | ]; |
||
988 | $names[] = [ $object->name, $stat ]; |
||
989 | } elseif ( substr( $object, -1 ) !== '/' ) { |
||
990 | // Omit directories, which end in '/' in listings |
||
991 | $names[] = [ $object, null ]; |
||
992 | } |
||
993 | } |
||
994 | |||
995 | return $names; |
||
996 | } |
||
997 | |||
998 | /** |
||
999 | * Do not call this function outside of SwiftFileBackendFileList |
||
1000 | * |
||
1001 | * @param string $path Storage path |
||
1002 | * @param array $val Stat value |
||
1003 | */ |
||
1004 | public function loadListingStatInternal( $path, array $val ) { |
||
1005 | $this->cheapCache->set( $path, 'stat', $val ); |
||
1006 | } |
||
1007 | |||
1008 | View Code Duplication | protected function doGetFileXAttributes( array $params ) { |
|
1009 | $stat = $this->getFileStat( $params ); |
||
1010 | if ( $stat ) { |
||
1011 | if ( !isset( $stat['xattr'] ) ) { |
||
1012 | // Stat entries filled by file listings don't include metadata/headers |
||
1013 | $this->clearCache( [ $params['src'] ] ); |
||
1014 | $stat = $this->getFileStat( $params ); |
||
1015 | } |
||
1016 | |||
1017 | return $stat['xattr']; |
||
1018 | } else { |
||
1019 | return false; |
||
1020 | } |
||
1021 | } |
||
1022 | |||
1023 | View Code Duplication | protected function doGetFileSha1base36( array $params ) { |
|
1024 | $stat = $this->getFileStat( $params ); |
||
1025 | if ( $stat ) { |
||
1026 | if ( !isset( $stat['sha1'] ) ) { |
||
1027 | // Stat entries filled by file listings don't include SHA1 |
||
1028 | $this->clearCache( [ $params['src'] ] ); |
||
1029 | $stat = $this->getFileStat( $params ); |
||
1030 | } |
||
1031 | |||
1032 | return $stat['sha1']; |
||
1033 | } else { |
||
1034 | return false; |
||
1035 | } |
||
1036 | } |
||
1037 | |||
1038 | protected function doStreamFile( array $params ) { |
||
1039 | $status = $this->newStatus(); |
||
1040 | |||
1041 | $flags = !empty( $params['headless'] ) ? StreamFile::STREAM_HEADLESS : 0; |
||
1042 | |||
1043 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
1044 | if ( $srcRel === null ) { |
||
1045 | StreamFile::send404Message( $params['src'], $flags ); |
||
1046 | $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
||
1047 | |||
1048 | return $status; |
||
1049 | } |
||
1050 | |||
1051 | $auth = $this->getAuthentication(); |
||
1052 | if ( !$auth || !is_array( $this->getContainerStat( $srcCont ) ) ) { |
||
1053 | StreamFile::send404Message( $params['src'], $flags ); |
||
1054 | $status->fatal( 'backend-fail-stream', $params['src'] ); |
||
1055 | |||
1056 | return $status; |
||
1057 | } |
||
1058 | |||
1059 | // If "headers" is set, we only want to send them if the file is there. |
||
1060 | // Do not bother checking if the file exists if headers are not set though. |
||
1061 | if ( $params['headers'] && !$this->fileExists( $params ) ) { |
||
0 ignored issues
–
show
The expression
$this->fileExists($params) of type boolean|null is loosely compared to false ; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.
If an expression can have both $a = canBeFalseAndNull();
// Instead of
if ( ! $a) { }
// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
![]() |
|||
1062 | StreamFile::send404Message( $params['src'], $flags ); |
||
1063 | $status->fatal( 'backend-fail-stream', $params['src'] ); |
||
1064 | |||
1065 | return $status; |
||
1066 | } |
||
1067 | |||
1068 | // Send the requested additional headers |
||
1069 | foreach ( $params['headers'] as $header ) { |
||
1070 | header( $header ); // aways send |
||
1071 | } |
||
1072 | |||
1073 | if ( empty( $params['allowOB'] ) ) { |
||
1074 | // Cancel output buffering and gzipping if set |
||
1075 | call_user_func( $this->obResetFunc ); |
||
1076 | } |
||
1077 | |||
1078 | $handle = fopen( 'php://output', 'wb' ); |
||
1079 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1080 | 'method' => 'GET', |
||
1081 | 'url' => $this->storageUrl( $auth, $srcCont, $srcRel ), |
||
1082 | 'headers' => $this->authTokenHeaders( $auth ) |
||
1083 | + $this->headersFromParams( $params ) + $params['options'], |
||
1084 | 'stream' => $handle, |
||
1085 | 'flags' => [ 'relayResponseHeaders' => empty( $params['headless'] ) ] |
||
1086 | ] ); |
||
1087 | |||
1088 | if ( $rcode >= 200 && $rcode <= 299 ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
1089 | // good |
||
1090 | } elseif ( $rcode === 404 ) { |
||
1091 | $status->fatal( 'backend-fail-stream', $params['src'] ); |
||
1092 | // Per bug 41113, nasty things can happen if bad cache entries get |
||
1093 | // stuck in cache. It's also possible that this error can come up |
||
1094 | // with simple race conditions. Clear out the stat cache to be safe. |
||
1095 | $this->clearCache( [ $params['src'] ] ); |
||
1096 | $this->deleteFileCache( $params['src'] ); |
||
1097 | } else { |
||
1098 | $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc ); |
||
1099 | } |
||
1100 | |||
1101 | return $status; |
||
1102 | } |
||
1103 | |||
1104 | protected function doGetLocalCopyMulti( array $params ) { |
||
1105 | /** @var TempFSFile[] $tmpFiles */ |
||
1106 | $tmpFiles = []; |
||
1107 | |||
1108 | $auth = $this->getAuthentication(); |
||
1109 | |||
1110 | $ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging |
||
1111 | // Blindly create tmp files and stream to them, catching any exception if the file does |
||
1112 | // not exist. Doing a stat here is useless causes infinite loops in addMissingMetadata(). |
||
1113 | $reqs = []; // (path => op) |
||
1114 | |||
1115 | foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch |
||
1116 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $path ); |
||
1117 | if ( $srcRel === null || !$auth ) { |
||
1118 | $tmpFiles[$path] = null; |
||
1119 | continue; |
||
1120 | } |
||
1121 | // Get source file extension |
||
1122 | $ext = FileBackend::extensionFromPath( $path ); |
||
1123 | // Create a new temporary file... |
||
1124 | $tmpFile = TempFSFile::factory( 'localcopy_', $ext, $this->tmpDirectory ); |
||
1125 | if ( $tmpFile ) { |
||
1126 | $handle = fopen( $tmpFile->getPath(), 'wb' ); |
||
1127 | View Code Duplication | if ( $handle ) { |
|
1128 | $reqs[$path] = [ |
||
1129 | 'method' => 'GET', |
||
1130 | 'url' => $this->storageUrl( $auth, $srcCont, $srcRel ), |
||
1131 | 'headers' => $this->authTokenHeaders( $auth ) |
||
1132 | + $this->headersFromParams( $params ), |
||
1133 | 'stream' => $handle, |
||
1134 | ]; |
||
1135 | } else { |
||
1136 | $tmpFile = null; |
||
1137 | } |
||
1138 | } |
||
1139 | $tmpFiles[$path] = $tmpFile; |
||
1140 | } |
||
1141 | |||
1142 | $isLatest = ( $this->isRGW || !empty( $params['latest'] ) ); |
||
1143 | $opts = [ 'maxConnsPerHost' => $params['concurrency'] ]; |
||
1144 | $reqs = $this->http->runMulti( $reqs, $opts ); |
||
1145 | foreach ( $reqs as $path => $op ) { |
||
1146 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $op['response']; |
||
0 ignored issues
–
show
The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1147 | fclose( $op['stream'] ); // close open handle |
||
1148 | if ( $rcode >= 200 && $rcode <= 299 ) { |
||
1149 | $size = $tmpFiles[$path] ? $tmpFiles[$path]->getSize() : 0; |
||
1150 | // Double check that the disk is not full/broken |
||
1151 | if ( $size != $rhdrs['content-length'] ) { |
||
1152 | $tmpFiles[$path] = null; |
||
1153 | $rerr = "Got {$size}/{$rhdrs['content-length']} bytes"; |
||
1154 | $this->onError( null, __METHOD__, |
||
1155 | [ 'src' => $path ] + $ep, $rerr, $rcode, $rdesc ); |
||
1156 | } |
||
1157 | // Set the file stat process cache in passing |
||
1158 | $stat = $this->getStatFromHeaders( $rhdrs ); |
||
1159 | $stat['latest'] = $isLatest; |
||
1160 | $this->cheapCache->set( $path, 'stat', $stat ); |
||
1161 | } elseif ( $rcode === 404 ) { |
||
1162 | $tmpFiles[$path] = false; |
||
1163 | } else { |
||
1164 | $tmpFiles[$path] = null; |
||
1165 | $this->onError( null, __METHOD__, |
||
1166 | [ 'src' => $path ] + $ep, $rerr, $rcode, $rdesc ); |
||
1167 | } |
||
1168 | } |
||
1169 | |||
1170 | return $tmpFiles; |
||
1171 | } |
||
1172 | |||
1173 | public function getFileHttpUrl( array $params ) { |
||
1174 | if ( $this->swiftTempUrlKey != '' || |
||
1175 | ( $this->rgwS3AccessKey != '' && $this->rgwS3SecretKey != '' ) |
||
1176 | ) { |
||
1177 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
||
1178 | if ( $srcRel === null ) { |
||
1179 | return null; // invalid path |
||
1180 | } |
||
1181 | |||
1182 | $auth = $this->getAuthentication(); |
||
1183 | if ( !$auth ) { |
||
1184 | return null; |
||
1185 | } |
||
1186 | |||
1187 | $ttl = isset( $params['ttl'] ) ? $params['ttl'] : 86400; |
||
1188 | $expires = time() + $ttl; |
||
1189 | |||
1190 | if ( $this->swiftTempUrlKey != '' ) { |
||
1191 | $url = $this->storageUrl( $auth, $srcCont, $srcRel ); |
||
1192 | // Swift wants the signature based on the unencoded object name |
||
1193 | $contPath = parse_url( $this->storageUrl( $auth, $srcCont ), PHP_URL_PATH ); |
||
1194 | $signature = hash_hmac( 'sha1', |
||
1195 | "GET\n{$expires}\n{$contPath}/{$srcRel}", |
||
1196 | $this->swiftTempUrlKey |
||
1197 | ); |
||
1198 | |||
1199 | return "{$url}?temp_url_sig={$signature}&temp_url_expires={$expires}"; |
||
1200 | } else { // give S3 API URL for rgw |
||
1201 | // Path for signature starts with the bucket |
||
1202 | $spath = '/' . rawurlencode( $srcCont ) . '/' . |
||
1203 | str_replace( '%2F', '/', rawurlencode( $srcRel ) ); |
||
1204 | // Calculate the hash |
||
1205 | $signature = base64_encode( hash_hmac( |
||
1206 | 'sha1', |
||
1207 | "GET\n\n\n{$expires}\n{$spath}", |
||
1208 | $this->rgwS3SecretKey, |
||
1209 | true // raw |
||
1210 | ) ); |
||
1211 | // See https://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html. |
||
1212 | // Note: adding a newline for empty CanonicalizedAmzHeaders does not work. |
||
1213 | // Note: S3 API is the rgw default; remove the /swift/ URL bit. |
||
1214 | return str_replace( '/swift/v1', '', $this->storageUrl( $auth ) . $spath ) . |
||
1215 | '?' . |
||
1216 | http_build_query( [ |
||
1217 | 'Signature' => $signature, |
||
1218 | 'Expires' => $expires, |
||
1219 | 'AWSAccessKeyId' => $this->rgwS3AccessKey |
||
1220 | ] ); |
||
1221 | } |
||
1222 | } |
||
1223 | |||
1224 | return null; |
||
1225 | } |
||
1226 | |||
1227 | protected function directoriesAreVirtual() { |
||
1228 | return true; |
||
1229 | } |
||
1230 | |||
1231 | /** |
||
1232 | * Get headers to send to Swift when reading a file based |
||
1233 | * on a FileBackend params array, e.g. that of getLocalCopy(). |
||
1234 | * $params is currently only checked for a 'latest' flag. |
||
1235 | * |
||
1236 | * @param array $params |
||
1237 | * @return array |
||
1238 | */ |
||
1239 | protected function headersFromParams( array $params ) { |
||
1240 | $hdrs = []; |
||
1241 | if ( !empty( $params['latest'] ) ) { |
||
1242 | $hdrs['x-newest'] = 'true'; |
||
1243 | } |
||
1244 | |||
1245 | return $hdrs; |
||
1246 | } |
||
1247 | |||
1248 | /** |
||
1249 | * @param FileBackendStoreOpHandle[] $fileOpHandles |
||
1250 | * |
||
1251 | * @return StatusValue[] |
||
1252 | */ |
||
1253 | protected function doExecuteOpHandlesInternal( array $fileOpHandles ) { |
||
1254 | /** @var $statuses StatusValue[] */ |
||
1255 | $statuses = []; |
||
1256 | |||
1257 | $auth = $this->getAuthentication(); |
||
1258 | if ( !$auth ) { |
||
1259 | foreach ( $fileOpHandles as $index => $fileOpHandle ) { |
||
1260 | $statuses[$index] = $this->newStatus( 'backend-fail-connect', $this->name ); |
||
1261 | } |
||
1262 | |||
1263 | return $statuses; |
||
1264 | } |
||
1265 | |||
1266 | // Split the HTTP requests into stages that can be done concurrently |
||
1267 | $httpReqsByStage = []; // map of (stage => index => HTTP request) |
||
1268 | foreach ( $fileOpHandles as $index => $fileOpHandle ) { |
||
1269 | /** @var SwiftFileOpHandle $fileOpHandle */ |
||
1270 | $reqs = $fileOpHandle->httpOp; |
||
1271 | // Convert the 'url' parameter to an actual URL using $auth |
||
1272 | foreach ( $reqs as $stage => &$req ) { |
||
1273 | list( $container, $relPath ) = $req['url']; |
||
1274 | $req['url'] = $this->storageUrl( $auth, $container, $relPath ); |
||
1275 | $req['headers'] = isset( $req['headers'] ) ? $req['headers'] : []; |
||
1276 | $req['headers'] = $this->authTokenHeaders( $auth ) + $req['headers']; |
||
1277 | $httpReqsByStage[$stage][$index] = $req; |
||
1278 | } |
||
1279 | $statuses[$index] = $this->newStatus(); |
||
1280 | } |
||
1281 | |||
1282 | // Run all requests for the first stage, then the next, and so on |
||
1283 | $reqCount = count( $httpReqsByStage ); |
||
1284 | for ( $stage = 0; $stage < $reqCount; ++$stage ) { |
||
1285 | $httpReqs = $this->http->runMulti( $httpReqsByStage[$stage] ); |
||
1286 | foreach ( $httpReqs as $index => $httpReq ) { |
||
1287 | // Run the callback for each request of this operation |
||
1288 | $callback = $fileOpHandles[$index]->callback; |
||
0 ignored issues
–
show
The property
callback does not seem to exist in FileBackendStoreOpHandle .
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
1289 | call_user_func_array( $callback, [ $httpReq, $statuses[$index] ] ); |
||
1290 | // On failure, abort all remaining requests for this operation |
||
1291 | // (e.g. abort the DELETE request if the COPY request fails for a move) |
||
1292 | if ( !$statuses[$index]->isOK() ) { |
||
1293 | $stages = count( $fileOpHandles[$index]->httpOp ); |
||
0 ignored issues
–
show
The property
httpOp does not seem to exist in FileBackendStoreOpHandle .
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
1294 | for ( $s = ( $stage + 1 ); $s < $stages; ++$s ) { |
||
1295 | unset( $httpReqsByStage[$s][$index] ); |
||
1296 | } |
||
1297 | } |
||
1298 | } |
||
1299 | } |
||
1300 | |||
1301 | return $statuses; |
||
1302 | } |
||
1303 | |||
1304 | /** |
||
1305 | * Set read/write permissions for a Swift container. |
||
1306 | * |
||
1307 | * @see http://docs.openstack.org/developer/swift/misc.html#acls |
||
1308 | * |
||
1309 | * In general, we don't allow listings to end-users. It's not useful, isn't well-defined |
||
1310 | * (lists are truncated to 10000 item with no way to page), and is just a performance risk. |
||
1311 | * |
||
1312 | * @param string $container Resolved Swift container |
||
1313 | * @param array $readGrps List of the possible criteria for a request to have |
||
1314 | * access to read a container. Each item is one of the following formats: |
||
1315 | * - account:user : Grants access if the request is by the given user |
||
1316 | * - ".r:<regex>" : Grants access if the request is from a referrer host that |
||
1317 | * matches the expression and the request is not for a listing. |
||
1318 | * Setting this to '*' effectively makes a container public. |
||
1319 | * -".rlistings:<regex>" : Grants access if the request is from a referrer host that |
||
1320 | * matches the expression and the request is for a listing. |
||
1321 | * @param array $writeGrps A list of the possible criteria for a request to have |
||
1322 | * access to write to a container. Each item is of the following format: |
||
1323 | * - account:user : Grants access if the request is by the given user |
||
1324 | * @return StatusValue |
||
1325 | */ |
||
1326 | protected function setContainerAccess( $container, array $readGrps, array $writeGrps ) { |
||
1327 | $status = $this->newStatus(); |
||
1328 | $auth = $this->getAuthentication(); |
||
1329 | |||
1330 | if ( !$auth ) { |
||
1331 | $status->fatal( 'backend-fail-connect', $this->name ); |
||
1332 | |||
1333 | return $status; |
||
1334 | } |
||
1335 | |||
1336 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rdesc is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rerr is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1337 | 'method' => 'POST', |
||
1338 | 'url' => $this->storageUrl( $auth, $container ), |
||
1339 | 'headers' => $this->authTokenHeaders( $auth ) + [ |
||
1340 | 'x-container-read' => implode( ',', $readGrps ), |
||
1341 | 'x-container-write' => implode( ',', $writeGrps ) |
||
1342 | ] |
||
1343 | ] ); |
||
1344 | |||
1345 | if ( $rcode != 204 && $rcode !== 202 ) { |
||
1346 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
1347 | $this->logger->error( __METHOD__ . ': unexpected rcode value (' . $rcode . ')' ); |
||
1348 | } |
||
1349 | |||
1350 | return $status; |
||
1351 | } |
||
1352 | |||
1353 | /** |
||
1354 | * Get a Swift container stat array, possibly from process cache. |
||
1355 | * Use $reCache if the file count or byte count is needed. |
||
1356 | * |
||
1357 | * @param string $container Container name |
||
1358 | * @param bool $bypassCache Bypass all caches and load from Swift |
||
1359 | * @return array|bool|null False on 404, null on failure |
||
1360 | */ |
||
1361 | protected function getContainerStat( $container, $bypassCache = false ) { |
||
1362 | $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" ); |
||
0 ignored issues
–
show
$ps is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
1363 | |||
1364 | if ( $bypassCache ) { // purge cache |
||
1365 | $this->containerStatCache->clear( $container ); |
||
1366 | } elseif ( !$this->containerStatCache->has( $container, 'stat' ) ) { |
||
1367 | $this->primeContainerCache( [ $container ] ); // check persistent cache |
||
1368 | } |
||
1369 | if ( !$this->containerStatCache->has( $container, 'stat' ) ) { |
||
1370 | $auth = $this->getAuthentication(); |
||
1371 | if ( !$auth ) { |
||
1372 | return null; |
||
1373 | } |
||
1374 | |||
1375 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1376 | 'method' => 'HEAD', |
||
1377 | 'url' => $this->storageUrl( $auth, $container ), |
||
1378 | 'headers' => $this->authTokenHeaders( $auth ) |
||
1379 | ] ); |
||
1380 | |||
1381 | if ( $rcode === 204 ) { |
||
1382 | $stat = [ |
||
1383 | 'count' => $rhdrs['x-container-object-count'], |
||
1384 | 'bytes' => $rhdrs['x-container-bytes-used'] |
||
1385 | ]; |
||
1386 | if ( $bypassCache ) { |
||
1387 | return $stat; |
||
1388 | } else { |
||
1389 | $this->containerStatCache->set( $container, 'stat', $stat ); // cache it |
||
1390 | $this->setContainerCache( $container, $stat ); // update persistent cache |
||
1391 | } |
||
1392 | } elseif ( $rcode === 404 ) { |
||
1393 | return false; |
||
1394 | } else { |
||
1395 | $this->onError( null, __METHOD__, |
||
1396 | [ 'cont' => $container ], $rerr, $rcode, $rdesc ); |
||
1397 | |||
1398 | return null; |
||
1399 | } |
||
1400 | } |
||
1401 | |||
1402 | return $this->containerStatCache->get( $container, 'stat' ); |
||
1403 | } |
||
1404 | |||
1405 | /** |
||
1406 | * Create a Swift container |
||
1407 | * |
||
1408 | * @param string $container Container name |
||
1409 | * @param array $params |
||
1410 | * @return StatusValue |
||
1411 | */ |
||
1412 | protected function createContainer( $container, array $params ) { |
||
1413 | $status = $this->newStatus(); |
||
1414 | |||
1415 | $auth = $this->getAuthentication(); |
||
1416 | if ( !$auth ) { |
||
1417 | $status->fatal( 'backend-fail-connect', $this->name ); |
||
1418 | |||
1419 | return $status; |
||
1420 | } |
||
1421 | |||
1422 | // @see SwiftFileBackend::setContainerAccess() |
||
1423 | if ( empty( $params['noAccess'] ) ) { |
||
1424 | $readGrps = [ '.r:*', $this->swiftUser ]; // public |
||
1425 | } else { |
||
1426 | $readGrps = [ $this->swiftUser ]; // private |
||
1427 | } |
||
1428 | $writeGrps = [ $this->swiftUser ]; // sanity |
||
1429 | |||
1430 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1431 | 'method' => 'PUT', |
||
1432 | 'url' => $this->storageUrl( $auth, $container ), |
||
1433 | 'headers' => $this->authTokenHeaders( $auth ) + [ |
||
1434 | 'x-container-read' => implode( ',', $readGrps ), |
||
1435 | 'x-container-write' => implode( ',', $writeGrps ) |
||
1436 | ] |
||
1437 | ] ); |
||
1438 | |||
1439 | if ( $rcode === 201 ) { // new |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
|||
1440 | // good |
||
1441 | } elseif ( $rcode === 202 ) { // already there |
||
0 ignored issues
–
show
This
elseif statement is empty, and could be removed.
This check looks for the bodies of These ![]() |
|||
1442 | // this shouldn't really happen, but is OK |
||
1443 | } else { |
||
1444 | $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc ); |
||
1445 | } |
||
1446 | |||
1447 | return $status; |
||
1448 | } |
||
1449 | |||
1450 | /** |
||
1451 | * Delete a Swift container |
||
1452 | * |
||
1453 | * @param string $container Container name |
||
1454 | * @param array $params |
||
1455 | * @return StatusValue |
||
1456 | */ |
||
1457 | protected function deleteContainer( $container, array $params ) { |
||
1458 | $status = $this->newStatus(); |
||
1459 | |||
1460 | $auth = $this->getAuthentication(); |
||
1461 | if ( !$auth ) { |
||
1462 | $status->fatal( 'backend-fail-connect', $this->name ); |
||
1463 | |||
1464 | return $status; |
||
1465 | } |
||
1466 | |||
1467 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1468 | 'method' => 'DELETE', |
||
1469 | 'url' => $this->storageUrl( $auth, $container ), |
||
1470 | 'headers' => $this->authTokenHeaders( $auth ) |
||
1471 | ] ); |
||
1472 | |||
1473 | if ( $rcode >= 200 && $rcode <= 299 ) { // deleted |
||
1474 | $this->containerStatCache->clear( $container ); // purge |
||
1475 | } elseif ( $rcode === 404 ) { // not there |
||
0 ignored issues
–
show
This
elseif statement is empty, and could be removed.
This check looks for the bodies of These ![]() |
|||
1476 | // this shouldn't really happen, but is OK |
||
1477 | } elseif ( $rcode === 409 ) { // not empty |
||
1478 | $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc ); // race? |
||
1479 | } else { |
||
1480 | $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc ); |
||
1481 | } |
||
1482 | |||
1483 | return $status; |
||
1484 | } |
||
1485 | |||
1486 | /** |
||
1487 | * Get a list of objects under a container. |
||
1488 | * Either just the names or a list of stdClass objects with details can be returned. |
||
1489 | * |
||
1490 | * @param string $fullCont |
||
1491 | * @param string $type ('info' for a list of object detail maps, 'names' for names only) |
||
1492 | * @param int $limit |
||
1493 | * @param string|null $after |
||
1494 | * @param string|null $prefix |
||
1495 | * @param string|null $delim |
||
1496 | * @return StatusValue With the list as value |
||
1497 | */ |
||
1498 | private function objectListing( |
||
1499 | $fullCont, $type, $limit, $after = null, $prefix = null, $delim = null |
||
1500 | ) { |
||
1501 | $status = $this->newStatus(); |
||
1502 | |||
1503 | $auth = $this->getAuthentication(); |
||
1504 | if ( !$auth ) { |
||
1505 | $status->fatal( 'backend-fail-connect', $this->name ); |
||
1506 | |||
1507 | return $status; |
||
1508 | } |
||
1509 | |||
1510 | $query = [ 'limit' => $limit ]; |
||
1511 | if ( $type === 'info' ) { |
||
1512 | $query['format'] = 'json'; |
||
1513 | } |
||
1514 | if ( $after !== null ) { |
||
1515 | $query['marker'] = $after; |
||
1516 | } |
||
1517 | if ( $prefix !== null ) { |
||
1518 | $query['prefix'] = $prefix; |
||
1519 | } |
||
1520 | if ( $delim !== null ) { |
||
1521 | $query['delimiter'] = $delim; |
||
1522 | } |
||
1523 | |||
1524 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rhdrs is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1525 | 'method' => 'GET', |
||
1526 | 'url' => $this->storageUrl( $auth, $fullCont ), |
||
1527 | 'query' => $query, |
||
1528 | 'headers' => $this->authTokenHeaders( $auth ) |
||
1529 | ] ); |
||
1530 | |||
1531 | $params = [ 'cont' => $fullCont, 'prefix' => $prefix, 'delim' => $delim ]; |
||
1532 | if ( $rcode === 200 ) { // good |
||
1533 | if ( $type === 'info' ) { |
||
1534 | $status->value = FormatJson::decode( trim( $rbody ) ); |
||
1535 | } else { |
||
1536 | $status->value = explode( "\n", trim( $rbody ) ); |
||
1537 | } |
||
1538 | } elseif ( $rcode === 204 ) { |
||
1539 | $status->value = []; // empty container |
||
1540 | } elseif ( $rcode === 404 ) { |
||
1541 | $status->value = []; // no container |
||
1542 | } else { |
||
1543 | $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc ); |
||
1544 | } |
||
1545 | |||
1546 | return $status; |
||
1547 | } |
||
1548 | |||
1549 | protected function doPrimeContainerCache( array $containerInfo ) { |
||
1550 | foreach ( $containerInfo as $container => $info ) { |
||
1551 | $this->containerStatCache->set( $container, 'stat', $info ); |
||
1552 | } |
||
1553 | } |
||
1554 | |||
1555 | protected function doGetFileStatMulti( array $params ) { |
||
1556 | $stats = []; |
||
1557 | |||
1558 | $auth = $this->getAuthentication(); |
||
1559 | |||
1560 | $reqs = []; |
||
1561 | foreach ( $params['srcs'] as $path ) { |
||
1562 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $path ); |
||
1563 | if ( $srcRel === null ) { |
||
1564 | $stats[$path] = false; |
||
1565 | continue; // invalid storage path |
||
1566 | } elseif ( !$auth ) { |
||
1567 | $stats[$path] = null; |
||
1568 | continue; |
||
1569 | } |
||
1570 | |||
1571 | // (a) Check the container |
||
1572 | $cstat = $this->getContainerStat( $srcCont ); |
||
1573 | if ( $cstat === false ) { |
||
1574 | $stats[$path] = false; |
||
1575 | continue; // ok, nothing to do |
||
1576 | } elseif ( !is_array( $cstat ) ) { |
||
1577 | $stats[$path] = null; |
||
1578 | continue; |
||
1579 | } |
||
1580 | |||
1581 | $reqs[$path] = [ |
||
1582 | 'method' => 'HEAD', |
||
1583 | 'url' => $this->storageUrl( $auth, $srcCont, $srcRel ), |
||
1584 | 'headers' => $this->authTokenHeaders( $auth ) + $this->headersFromParams( $params ) |
||
1585 | ]; |
||
1586 | } |
||
1587 | |||
1588 | $opts = [ 'maxConnsPerHost' => $params['concurrency'] ]; |
||
1589 | $reqs = $this->http->runMulti( $reqs, $opts ); |
||
1590 | |||
1591 | foreach ( $params['srcs'] as $path ) { |
||
1592 | if ( array_key_exists( $path, $stats ) ) { |
||
1593 | continue; // some sort of failure above |
||
1594 | } |
||
1595 | // (b) Check the file |
||
1596 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $reqs[$path]['response']; |
||
0 ignored issues
–
show
The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1597 | if ( $rcode === 200 || $rcode === 204 ) { |
||
1598 | // Update the object if it is missing some headers |
||
1599 | $rhdrs = $this->addMissingMetadata( $rhdrs, $path ); |
||
1600 | // Load the stat array from the headers |
||
1601 | $stat = $this->getStatFromHeaders( $rhdrs ); |
||
1602 | if ( $this->isRGW ) { |
||
1603 | $stat['latest'] = true; // strong consistency |
||
1604 | } |
||
1605 | } elseif ( $rcode === 404 ) { |
||
1606 | $stat = false; |
||
1607 | } else { |
||
1608 | $stat = null; |
||
1609 | $this->onError( null, __METHOD__, $params, $rerr, $rcode, $rdesc ); |
||
1610 | } |
||
1611 | $stats[$path] = $stat; |
||
1612 | } |
||
1613 | |||
1614 | return $stats; |
||
1615 | } |
||
1616 | |||
1617 | /** |
||
1618 | * @param array $rhdrs |
||
1619 | * @return array |
||
1620 | */ |
||
1621 | protected function getStatFromHeaders( array $rhdrs ) { |
||
1622 | // Fetch all of the custom metadata headers |
||
1623 | $metadata = $this->getMetadata( $rhdrs ); |
||
1624 | // Fetch all of the custom raw HTTP headers |
||
1625 | $headers = $this->sanitizeHdrs( [ 'headers' => $rhdrs ] ); |
||
1626 | |||
1627 | return [ |
||
1628 | // Convert various random Swift dates to TS_MW |
||
1629 | 'mtime' => $this->convertSwiftDate( $rhdrs['last-modified'], TS_MW ), |
||
1630 | // Empty objects actually return no content-length header in Ceph |
||
1631 | 'size' => isset( $rhdrs['content-length'] ) ? (int)$rhdrs['content-length'] : 0, |
||
1632 | 'sha1' => isset( $metadata['sha1base36'] ) ? $metadata['sha1base36'] : null, |
||
1633 | // Note: manifiest ETags are not an MD5 of the file |
||
1634 | 'md5' => ctype_xdigit( $rhdrs['etag'] ) ? $rhdrs['etag'] : null, |
||
1635 | 'xattr' => [ 'metadata' => $metadata, 'headers' => $headers ] |
||
1636 | ]; |
||
1637 | } |
||
1638 | |||
1639 | /** |
||
1640 | * @return array|null Credential map |
||
1641 | */ |
||
1642 | protected function getAuthentication() { |
||
1643 | if ( $this->authErrorTimestamp !== null ) { |
||
1644 | if ( ( time() - $this->authErrorTimestamp ) < 60 ) { |
||
1645 | return null; // failed last attempt; don't bother |
||
1646 | } else { // actually retry this time |
||
1647 | $this->authErrorTimestamp = null; |
||
1648 | } |
||
1649 | } |
||
1650 | // Session keys expire after a while, so we renew them periodically |
||
1651 | $reAuth = ( ( time() - $this->authSessionTimestamp ) > $this->authTTL ); |
||
1652 | // Authenticate with proxy and get a session key... |
||
1653 | if ( !$this->authCreds || $reAuth ) { |
||
1654 | $this->authSessionTimestamp = 0; |
||
1655 | $cacheKey = $this->getCredsCacheKey( $this->swiftUser ); |
||
1656 | $creds = $this->srvCache->get( $cacheKey ); // credentials |
||
1657 | // Try to use the credential cache |
||
1658 | if ( isset( $creds['auth_token'] ) && isset( $creds['storage_url'] ) ) { |
||
1659 | $this->authCreds = $creds; |
||
0 ignored issues
–
show
It seems like
$creds of type * is incompatible with the declared type array of property $authCreds .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
1660 | // Skew the timestamp for worst case to avoid using stale credentials |
||
1661 | $this->authSessionTimestamp = time() - ceil( $this->authTTL / 2 ); |
||
0 ignored issues
–
show
The property
$authSessionTimestamp was declared of type integer , but time() - ceil($this->authTTL / 2) is of type double . 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;
![]() |
|||
1662 | } else { // cache miss |
||
1663 | list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [ |
||
0 ignored issues
–
show
The assignment to
$rdesc is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rbody is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() The assignment to
$rerr is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1664 | 'method' => 'GET', |
||
1665 | 'url' => "{$this->swiftAuthUrl}/v1.0", |
||
1666 | 'headers' => [ |
||
1667 | 'x-auth-user' => $this->swiftUser, |
||
1668 | 'x-auth-key' => $this->swiftKey |
||
1669 | ] |
||
1670 | ] ); |
||
1671 | |||
1672 | if ( $rcode >= 200 && $rcode <= 299 ) { // OK |
||
1673 | $this->authCreds = [ |
||
1674 | 'auth_token' => $rhdrs['x-auth-token'], |
||
1675 | 'storage_url' => $rhdrs['x-storage-url'] |
||
1676 | ]; |
||
1677 | $this->srvCache->set( $cacheKey, $this->authCreds, ceil( $this->authTTL / 2 ) ); |
||
1678 | $this->authSessionTimestamp = time(); |
||
1679 | } elseif ( $rcode === 401 ) { |
||
1680 | $this->onError( null, __METHOD__, [], "Authentication failed.", $rcode ); |
||
1681 | $this->authErrorTimestamp = time(); |
||
1682 | |||
1683 | return null; |
||
1684 | } else { |
||
1685 | $this->onError( null, __METHOD__, [], "HTTP return code: $rcode", $rcode ); |
||
1686 | $this->authErrorTimestamp = time(); |
||
1687 | |||
1688 | return null; |
||
1689 | } |
||
1690 | } |
||
1691 | // Ceph RGW does not use <account> in URLs (OpenStack Swift uses "/v1/<account>") |
||
1692 | if ( substr( $this->authCreds['storage_url'], -3 ) === '/v1' ) { |
||
1693 | $this->isRGW = true; // take advantage of strong consistency in Ceph |
||
1694 | } |
||
1695 | } |
||
1696 | |||
1697 | return $this->authCreds; |
||
1698 | } |
||
1699 | |||
1700 | /** |
||
1701 | * @param array $creds From getAuthentication() |
||
1702 | * @param string $container |
||
1703 | * @param string $object |
||
1704 | * @return array |
||
1705 | */ |
||
1706 | protected function storageUrl( array $creds, $container = null, $object = null ) { |
||
1707 | $parts = [ $creds['storage_url'] ]; |
||
1708 | if ( strlen( $container ) ) { |
||
1709 | $parts[] = rawurlencode( $container ); |
||
1710 | } |
||
1711 | if ( strlen( $object ) ) { |
||
1712 | $parts[] = str_replace( "%2F", "/", rawurlencode( $object ) ); |
||
1713 | } |
||
1714 | |||
1715 | return implode( '/', $parts ); |
||
1716 | } |
||
1717 | |||
1718 | /** |
||
1719 | * @param array $creds From getAuthentication() |
||
1720 | * @return array |
||
1721 | */ |
||
1722 | protected function authTokenHeaders( array $creds ) { |
||
1723 | return [ 'x-auth-token' => $creds['auth_token'] ]; |
||
1724 | } |
||
1725 | |||
1726 | /** |
||
1727 | * Get the cache key for a container |
||
1728 | * |
||
1729 | * @param string $username |
||
1730 | * @return string |
||
1731 | */ |
||
1732 | private function getCredsCacheKey( $username ) { |
||
1733 | return 'swiftcredentials:' . md5( $username . ':' . $this->swiftAuthUrl ); |
||
1734 | } |
||
1735 | |||
1736 | /** |
||
1737 | * Log an unexpected exception for this backend. |
||
1738 | * This also sets the StatusValue object to have a fatal error. |
||
1739 | * |
||
1740 | * @param StatusValue|null $status |
||
1741 | * @param string $func |
||
1742 | * @param array $params |
||
1743 | * @param string $err Error string |
||
1744 | * @param int $code HTTP status |
||
1745 | * @param string $desc HTTP StatusValue description |
||
1746 | */ |
||
1747 | public function onError( $status, $func, array $params, $err = '', $code = 0, $desc = '' ) { |
||
1748 | if ( $status instanceof StatusValue ) { |
||
1749 | $status->fatal( 'backend-fail-internal', $this->name ); |
||
1750 | } |
||
1751 | if ( $code == 401 ) { // possibly a stale token |
||
1752 | $this->srvCache->delete( $this->getCredsCacheKey( $this->swiftUser ) ); |
||
1753 | } |
||
1754 | $this->logger->error( |
||
1755 | "HTTP $code ($desc) in '{$func}' (given '" . FormatJson::encode( $params ) . "')" . |
||
1756 | ( $err ? ": $err" : "" ) |
||
1757 | ); |
||
1758 | } |
||
1759 | } |
||
1760 | |||
1761 | /** |
||
1762 | * @see FileBackendStoreOpHandle |
||
1763 | */ |
||
1764 | class SwiftFileOpHandle extends FileBackendStoreOpHandle { |
||
1765 | /** @var array List of Requests for MultiHttpClient */ |
||
1766 | public $httpOp; |
||
1767 | /** @var Closure */ |
||
1768 | public $callback; |
||
1769 | |||
1770 | /** |
||
1771 | * @param SwiftFileBackend $backend |
||
1772 | * @param Closure $callback Function that takes (HTTP request array, status) |
||
1773 | * @param array $httpOp MultiHttpClient op |
||
1774 | */ |
||
1775 | public function __construct( SwiftFileBackend $backend, Closure $callback, array $httpOp ) { |
||
1776 | $this->backend = $backend; |
||
1777 | $this->callback = $callback; |
||
1778 | $this->httpOp = $httpOp; |
||
1779 | } |
||
1780 | } |
||
1781 | |||
1782 | /** |
||
1783 | * SwiftFileBackend helper class to page through listings. |
||
1784 | * Swift also has a listing limit of 10,000 objects for sanity. |
||
1785 | * Do not use this class from places outside SwiftFileBackend. |
||
1786 | * |
||
1787 | * @ingroup FileBackend |
||
1788 | */ |
||
1789 | abstract class SwiftFileBackendList implements Iterator { |
||
1790 | /** @var array List of path or (path,stat array) entries */ |
||
1791 | protected $bufferIter = []; |
||
1792 | |||
1793 | /** @var string List items *after* this path */ |
||
1794 | protected $bufferAfter = null; |
||
1795 | |||
1796 | /** @var int */ |
||
1797 | protected $pos = 0; |
||
1798 | |||
1799 | /** @var array */ |
||
1800 | protected $params = []; |
||
1801 | |||
1802 | /** @var SwiftFileBackend */ |
||
1803 | protected $backend; |
||
1804 | |||
1805 | /** @var string Container name */ |
||
1806 | protected $container; |
||
1807 | |||
1808 | /** @var string Storage directory */ |
||
1809 | protected $dir; |
||
1810 | |||
1811 | /** @var int */ |
||
1812 | protected $suffixStart; |
||
1813 | |||
1814 | const PAGE_SIZE = 9000; // file listing buffer size |
||
1815 | |||
1816 | /** |
||
1817 | * @param SwiftFileBackend $backend |
||
1818 | * @param string $fullCont Resolved container name |
||
1819 | * @param string $dir Resolved directory relative to container |
||
1820 | * @param array $params |
||
1821 | */ |
||
1822 | public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) { |
||
1823 | $this->backend = $backend; |
||
1824 | $this->container = $fullCont; |
||
1825 | $this->dir = $dir; |
||
1826 | if ( substr( $this->dir, -1 ) === '/' ) { |
||
1827 | $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash |
||
1828 | } |
||
1829 | if ( $this->dir == '' ) { // whole container |
||
1830 | $this->suffixStart = 0; |
||
1831 | } else { // dir within container |
||
1832 | $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/" |
||
1833 | } |
||
1834 | $this->params = $params; |
||
1835 | } |
||
1836 | |||
1837 | /** |
||
1838 | * @see Iterator::key() |
||
1839 | * @return int |
||
1840 | */ |
||
1841 | public function key() { |
||
1842 | return $this->pos; |
||
1843 | } |
||
1844 | |||
1845 | /** |
||
1846 | * @see Iterator::next() |
||
1847 | */ |
||
1848 | public function next() { |
||
1849 | // Advance to the next file in the page |
||
1850 | next( $this->bufferIter ); |
||
1851 | ++$this->pos; |
||
1852 | // Check if there are no files left in this page and |
||
1853 | // advance to the next page if this page was not empty. |
||
1854 | if ( !$this->valid() && count( $this->bufferIter ) ) { |
||
1855 | $this->bufferIter = $this->pageFromList( |
||
0 ignored issues
–
show
It seems like
$this->pageFromList($thi...GE_SIZE, $this->params) can also be of type object<Traversable> . However, the property $bufferIter is declared as type array . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
1856 | $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params |
||
1857 | ); // updates $this->bufferAfter |
||
1858 | } |
||
1859 | } |
||
1860 | |||
1861 | /** |
||
1862 | * @see Iterator::rewind() |
||
1863 | */ |
||
1864 | public function rewind() { |
||
1865 | $this->pos = 0; |
||
1866 | $this->bufferAfter = null; |
||
1867 | $this->bufferIter = $this->pageFromList( |
||
0 ignored issues
–
show
It seems like
$this->pageFromList($thi...GE_SIZE, $this->params) can also be of type object<Traversable> . However, the property $bufferIter is declared as type array . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
1868 | $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params |
||
1869 | ); // updates $this->bufferAfter |
||
1870 | } |
||
1871 | |||
1872 | /** |
||
1873 | * @see Iterator::valid() |
||
1874 | * @return bool |
||
1875 | */ |
||
1876 | public function valid() { |
||
1877 | if ( $this->bufferIter === null ) { |
||
1878 | return false; // some failure? |
||
1879 | } else { |
||
1880 | return ( current( $this->bufferIter ) !== false ); // no paths can have this value |
||
1881 | } |
||
1882 | } |
||
1883 | |||
1884 | /** |
||
1885 | * Get the given list portion (page) |
||
1886 | * |
||
1887 | * @param string $container Resolved container name |
||
1888 | * @param string $dir Resolved path relative to container |
||
1889 | * @param string $after |
||
1890 | * @param int $limit |
||
1891 | * @param array $params |
||
1892 | * @return Traversable|array |
||
1893 | */ |
||
1894 | abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params ); |
||
1895 | } |
||
1896 | |||
1897 | /** |
||
1898 | * Iterator for listing directories |
||
1899 | */ |
||
1900 | class SwiftFileBackendDirList extends SwiftFileBackendList { |
||
1901 | /** |
||
1902 | * @see Iterator::current() |
||
1903 | * @return string|bool String (relative path) or false |
||
1904 | */ |
||
1905 | public function current() { |
||
1906 | return substr( current( $this->bufferIter ), $this->suffixStart, -1 ); |
||
1907 | } |
||
1908 | |||
1909 | protected function pageFromList( $container, $dir, &$after, $limit, array $params ) { |
||
1910 | return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params ); |
||
1911 | } |
||
1912 | } |
||
1913 | |||
1914 | /** |
||
1915 | * Iterator for listing regular files |
||
1916 | */ |
||
1917 | class SwiftFileBackendFileList extends SwiftFileBackendList { |
||
1918 | /** |
||
1919 | * @see Iterator::current() |
||
1920 | * @return string|bool String (relative path) or false |
||
1921 | */ |
||
1922 | public function current() { |
||
1923 | list( $path, $stat ) = current( $this->bufferIter ); |
||
1924 | $relPath = substr( $path, $this->suffixStart ); |
||
1925 | if ( is_array( $stat ) ) { |
||
1926 | $storageDir = rtrim( $this->params['dir'], '/' ); |
||
1927 | $this->backend->loadListingStatInternal( "$storageDir/$relPath", $stat ); |
||
1928 | } |
||
1929 | |||
1930 | return $relPath; |
||
1931 | } |
||
1932 | |||
1933 | protected function pageFromList( $container, $dir, &$after, $limit, array $params ) { |
||
1934 | return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params ); |
||
1935 | } |
||
1936 | } |
||
1937 |
This checks looks for assignemnts to variables using the
list(...)
function, where not all assigned variables are subsequently used.Consider the following code example.
Only the variables
$a
and$c
are used. There was no need to assign$b
.Instead, the list call could have been.