Completed
Push — master ( 830834...005b3d )
by Thomas
10:38
created

Swift   F

Complexity

Total Complexity 113

Size/Duplication

Total Lines 607
Duplicated Lines 3.62 %

Coupling/Cohesion

Components 1
Dependencies 25

Importance

Changes 0
Metric Value
dl 22
loc 607
rs 1.286
c 0
b 0
f 0
wmc 113
lcom 1
cbo 25

21 Methods

Rating   Name   Duplication   Size   Complexity  
A normalizePath() 11 11 2
A file_exists() 0 9 3
B filetype() 0 15 5
B rename() 0 23 5
A getId() 0 3 1
C getConnection() 0 42 8
A getContainer() 0 17 4
A hasUpdated() 0 21 4
A checkDependencies() 0 3 1
B fetchObject() 0 22 5
A doesObjectExist() 0 3 1
D __construct() 0 30 9
B mkdir() 0 26 4
C rmdir() 11 30 7
B opendir() 0 34 5
C stat() 0 42 8
A unlink() 0 20 4
C fopen() 0 61 22
B touch() 0 28 6
B copy() 0 55 7
A writeBack() 0 10 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Swift often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Swift, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Benjamin Liles <[email protected]>
5
 * @author Christian Berendt <[email protected]>
6
 * @author Daniel Tosello <[email protected]>
7
 * @author Felix Moeller <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Martin Mattel <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Philipp Kapfer <[email protected]>
12
 * @author Robin Appelman <[email protected]>
13
 * @author Robin McCorkell <[email protected]>
14
 * @author Thomas Müller <[email protected]>
15
 * @author Tim Dettrick <[email protected]>
16
 * @author Vincent Petry <[email protected]>
17
 *
18
 * @copyright Copyright (c) 2016, ownCloud GmbH.
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OCA\Files_External\Lib\Storage;
36
37
use Guzzle\Http\Url;
38
use Guzzle\Http\Exception\ClientErrorResponseException;
39
use Icewind\Streams\IteratorDirectory;
40
use OpenCloud;
41
use OpenCloud\Common\Exceptions;
42
use OpenCloud\OpenStack;
43
use OpenCloud\Rackspace;
44
use OpenCloud\ObjectStore\Resource\DataObject;
45
use OpenCloud\ObjectStore\Exception;
46
47
class Swift extends \OC\Files\Storage\Common {
48
49
	/**
50
	 * @var \OpenCloud\ObjectStore\Service
51
	 */
52
	private $connection;
53
	/**
54
	 * @var \OpenCloud\ObjectStore\Resource\Container
55
	 */
56
	private $container;
57
	/**
58
	 * @var \OpenCloud\OpenStack
59
	 */
60
	private $anchor;
61
	/**
62
	 * @var string
63
	 */
64
	private $bucket;
65
	/**
66
	 * Connection parameters
67
	 *
68
	 * @var array
69
	 */
70
	private $params;
71
	/**
72
	 * @var array
73
	 */
74
	private static $tmpFiles = array();
75
76
	/**
77
	 * Key value cache mapping path to data object. Maps path to
78
	 * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
79
	 * paths and path to false for not existing paths.
80
	 * @var \OCP\ICache
81
	 */
82
	private $objectCache;
83
84
	/**
85
	 * @param string $path
86
	 */
87 View Code Duplication
	private function normalizePath($path) {
88
		$path = trim($path, '/');
89
90
		if (!$path) {
91
			$path = '.';
92
		}
93
94
		$path = str_replace('#', '%23', $path);
95
96
		return $path;
97
	}
98
99
	const SUBCONTAINER_FILE = '.subcontainers';
100
101
	/**
102
	 * translate directory path to container name
103
	 *
104
	 * @param string $path
105
	 * @return string
106
	 */
107
108
	/**
109
	 * Fetches an object from the API.
110
	 * If the object is cached already or a
111
	 * failed "doesn't exist" response was cached,
112
	 * that one will be returned.
113
	 *
114
	 * @param string $path
115
	 * @return \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject|bool object
116
	 * or false if the object did not exist
117
	 */
118
	private function fetchObject($path) {
119
		if ($this->objectCache->hasKey($path)) {
120
			// might be "false" if object did not exist from last check
121
			return $this->objectCache->get($path);
122
		}
123
		try {
124
			$object = $this->getContainer()->getPartialObject($path);
125
			$this->objectCache->set($path, $object);
126
			return $object;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $object; (OpenCloud\ObjectStore\Resource\DataObject) is incompatible with the return type documented by OCA\Files_External\Lib\Storage\Swift::fetchObject of type OpenCloud\OpenStack\Obje...urce\DataObject|boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
127
		} catch (ClientErrorResponseException $e) {
128
			// this exception happens when the object does not exist, which
129
			// is expected in most cases
130
			$this->objectCache->set($path, false);
131
			return false;
132
		} catch (ClientErrorResponseException $e) {
133
			// Expected response is "404 Not Found", so only log if it isn't
134
			if ($e->getResponse()->getStatusCode() !== 404) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $e->getResponse()->getStatusCode() (string) and 404 (integer) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
135
				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
136
			}
137
			return false;
138
		}
139
	}
140
141
	/**
142
	 * Returns whether the given path exists.
143
	 *
144
	 * @param string $path
145
	 *
146
	 * @return bool true if the object exist, false otherwise
147
	 */
148
	private function doesObjectExist($path) {
149
		return $this->fetchObject($path) !== false;
150
	}
151
152
	public function __construct($params) {
153
		if ((empty($params['key']) and empty($params['password']))
154
			or empty($params['user']) or empty($params['bucket'])
155
			or empty($params['region'])
156
		) {
157
			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
158
		}
159
160
		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
0 ignored issues
show
Bug introduced by
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
161
162
		$bucketUrl = Url::factory($params['bucket']);
163
		if ($bucketUrl->isAbsolute()) {
164
			$this->bucket = end(($bucketUrl->getPathSegments()));
0 ignored issues
show
Bug introduced by
$bucketUrl->getPathSegments() cannot be passed to end() as the parameter $array expects a reference.
Loading history...
165
			$params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
166
		} else {
167
			$this->bucket = $params['bucket'];
168
		}
169
170
		if (empty($params['url'])) {
171
			$params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
172
		}
173
174
		if (empty($params['service_name'])) {
175
			$params['service_name'] = 'cloudFiles';
176
		}
177
178
		$this->params = $params;
179
		// FIXME: private class...
180
		$this->objectCache = new \OC\Cache\CappedMemoryCache();
181
	}
182
183
	public function mkdir($path) {
184
		$path = $this->normalizePath($path);
185
186
		if ($this->is_dir($path)) {
187
			return false;
188
		}
189
190
		if ($path !== '.') {
191
			$path .= '/';
192
		}
193
194
		try {
195
			$customHeaders = array('content-type' => 'httpd/unix-directory');
196
			$metadataHeaders = DataObject::stockHeaders(array());
197
			$allHeaders = $customHeaders + $metadataHeaders;
198
			$this->getContainer()->uploadObject($path, '', $allHeaders);
199
			// invalidate so that the next access gets the real object
200
			// with all properties
201
			$this->objectCache->remove($path);
202
		} catch (Exceptions\CreateUpdateError $e) {
203
			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
204
			return false;
205
		}
206
207
		return true;
208
	}
209
210
	public function file_exists($path) {
211
		$path = $this->normalizePath($path);
212
213
		if ($path !== '.' && $this->is_dir($path)) {
214
			$path .= '/';
215
		}
216
217
		return $this->doesObjectExist($path);
218
	}
219
220
	public function rmdir($path) {
221
		$path = $this->normalizePath($path);
222
223
		if (!$this->is_dir($path) || !$this->isDeletable($path)) {
224
			return false;
225
		}
226
227
		$dh = $this->opendir($path);
228 View Code Duplication
		while ($file = readdir($dh)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229
			if (\OC\Files\Filesystem::isIgnoredDir($file)) {
230
				continue;
231
			}
232
233
			if ($this->is_dir($path . '/' . $file)) {
234
				$this->rmdir($path . '/' . $file);
235
			} else {
236
				$this->unlink($path . '/' . $file);
237
			}
238
		}
239
240
		try {
241
			$this->getContainer()->dataObject()->setName($path . '/')->delete();
242
			$this->objectCache->remove($path . '/');
243
		} catch (Exceptions\DeleteError $e) {
244
			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
245
			return false;
246
		}
247
248
		return true;
249
	}
250
251
	public function opendir($path) {
252
		$path = $this->normalizePath($path);
253
254
		if ($path === '.') {
255
			$path = '';
256
		} else {
257
			$path .= '/';
258
		}
259
260
		$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
261
262
		try {
263
			$files = array();
264
			/** @var OpenCloud\Common\Collection $objects */
265
			$objects = $this->getContainer()->objectList(array(
266
				'prefix' => $path,
267
				'delimiter' => '/'
268
			));
269
270
			/** @var OpenCloud\ObjectStore\Resource\DataObject $object */
271
			foreach ($objects as $object) {
0 ignored issues
show
Bug introduced by
The expression $objects of type object<OpenCloud\Common\Collection> is not traversable.
Loading history...
272
				$file = basename($object->getName());
273
				if ($file !== basename($path)) {
274
					$files[] = $file;
275
				}
276
			}
277
278
			return IteratorDirectory::wrap($files);
279
		} catch (\Exception $e) {
280
			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
281
			return false;
282
		}
283
284
	}
285
286
	public function stat($path) {
287
		$path = $this->normalizePath($path);
288
289
		if ($path === '.') {
290
			$path = '';
291
		} else if ($this->is_dir($path)) {
292
			$path .= '/';
293
		}
294
295
		try {
296
			/** @var DataObject $object */
297
			$object = $this->fetchObject($path);
298
			if (!$object) {
299
				return false;
300
			}
301
		} catch (ClientErrorResponseException $e) {
302
			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
303
			return false;
304
		}
305
306
		$dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
307
		if ($dateTime !== false) {
308
			$mtime = $dateTime->getTimestamp();
309
		} else {
310
			$mtime = null;
311
		}
312
		$objectMetadata = $object->getMetadata();
313
		$metaTimestamp = $objectMetadata->getProperty('timestamp');
314
		if (isset($metaTimestamp)) {
315
			$mtime = $metaTimestamp;
316
		}
317
318
		if (!empty($mtime)) {
319
			$mtime = floor($mtime);
320
		}
321
322
		$stat = array();
323
		$stat['size'] = (int)$object->getContentLength();
324
		$stat['mtime'] = $mtime;
325
		$stat['atime'] = time();
326
		return $stat;
327
	}
328
329
	public function filetype($path) {
330
		$path = $this->normalizePath($path);
331
332
		if ($path !== '.' && $this->doesObjectExist($path)) {
333
			return 'file';
334
		}
335
336
		if ($path !== '.') {
337
			$path .= '/';
338
		}
339
340
		if ($this->doesObjectExist($path)) {
341
			return 'dir';
342
		}
343
	}
344
345
	public function unlink($path) {
346
		$path = $this->normalizePath($path);
347
348
		if ($this->is_dir($path)) {
349
			return $this->rmdir($path);
350
		}
351
352
		try {
353
			$this->getContainer()->dataObject()->setName($path)->delete();
354
			$this->objectCache->remove($path);
355
			$this->objectCache->remove($path . '/');
356
		} catch (ClientErrorResponseException $e) {
357
			if ($e->getResponse()->getStatusCode() !== 404) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $e->getResponse()->getStatusCode() (string) and 404 (integer) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
358
				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
359
			}
360
			return false;
361
		}
362
363
		return true;
364
	}
365
366
	public function fopen($path, $mode) {
367
		$path = $this->normalizePath($path);
368
369
		switch ($mode) {
370
			case 'r':
371
			case 'rb':
372
				try {
373
					$c = $this->getContainer();
374
					$streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
375
					$streamInterface = $streamFactory->fromRequest(
376
						$c->getClient()
377
							->get($c->getUrl($path)));
378
					$streamInterface->rewind();
379
					$stream = $streamInterface->getStream();
380
					stream_context_set_option($stream, 'swift','content', $streamInterface);
381
					if(!strrpos($streamInterface
382
						->getMetaData('wrapper_data')[0], '404 Not Found')) {
383
						return $stream;
384
					}
385
					return false;
386
				} catch (\Guzzle\Http\Exception\BadResponseException $e) {
387
					\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
388
					return false;
389
				}
390
			case 'w':
391
			case 'wb':
392
			case 'a':
393
			case 'ab':
394
			case 'r+':
395
			case 'w+':
396
			case 'wb+':
397
			case 'a+':
398
			case 'x':
399
			case 'x+':
400
			case 'c':
401
			case 'c+':
402
				if (strrpos($path, '.') !== false) {
403
					$ext = substr($path, strrpos($path, '.'));
404
				} else {
405
					$ext = '';
406
				}
407
				$tmpFile = \OCP\Files::tmpFile($ext);
408
				\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
409
				// Fetch existing file if required
410
				if ($mode[0] !== 'w' && $this->file_exists($path)) {
411
					if ($mode[0] === 'x') {
412
						// File cannot already exist
413
						return false;
414
					}
415
					$source = $this->fopen($path, 'r');
416
					file_put_contents($tmpFile, $source);
417
					// Seek to end if required
418
					if ($mode[0] === 'a') {
419
						fseek($tmpFile, 0, SEEK_END);
420
					}
421
				}
422
				self::$tmpFiles[$tmpFile] = $path;
423
424
				return fopen('close://' . $tmpFile, $mode);
425
		}
426
	}
427
428
	public function touch($path, $mtime = null) {
429
		$path = $this->normalizePath($path);
430
		if (is_null($mtime)) {
431
			$mtime = time();
432
		}
433
		$metadata = array('timestamp' => $mtime);
434
		if ($this->file_exists($path)) {
435
			if ($this->is_dir($path) && $path != '.') {
436
				$path .= '/';
437
			}
438
439
			$object = $this->fetchObject($path);
440
			if ($object->saveMetadata($metadata)) {
441
				// invalidate target object to force repopulation on fetch
442
				$this->objectCache->remove($path);
443
			}
444
			return true;
445
		} else {
446
			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
447
			$customHeaders = array('content-type' => $mimeType);
448
			$metadataHeaders = DataObject::stockHeaders($metadata);
449
			$allHeaders = $customHeaders + $metadataHeaders;
450
			$this->getContainer()->uploadObject($path, '', $allHeaders);
451
			// invalidate target object to force repopulation on fetch
452
			$this->objectCache->remove($path);
453
			return true;
454
		}
455
	}
456
457
	public function copy($path1, $path2) {
458
		$path1 = $this->normalizePath($path1);
459
		$path2 = $this->normalizePath($path2);
460
461
		$fileType = $this->filetype($path1);
462
		if ($fileType === 'file') {
463
464
			// make way
465
			$this->unlink($path2);
466
467
			try {
468
				$source = $this->fetchObject($path1);
469
				$source->copy($this->bucket . '/' . $path2);
470
				// invalidate target object to force repopulation on fetch
471
				$this->objectCache->remove($path2);
472
				$this->objectCache->remove($path2 . '/');
473
			} catch (ClientErrorResponseException $e) {
474
				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
475
				return false;
476
			}
477
478
		} else if ($fileType === 'dir') {
479
480
			// make way
481
			$this->unlink($path2);
482
483
			try {
484
				$source = $this->fetchObject($path1 . '/');
485
				$source->copy($this->bucket . '/' . $path2 . '/');
486
				// invalidate target object to force repopulation on fetch
487
				$this->objectCache->remove($path2);
488
				$this->objectCache->remove($path2 . '/');
489
			} catch (ClientErrorResponseException $e) {
490
				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
491
				return false;
492
			}
493
494
			$dh = $this->opendir($path1);
495
			while ($file = readdir($dh)) {
496
				if (\OC\Files\Filesystem::isIgnoredDir($file)) {
497
					continue;
498
				}
499
500
				$source = $path1 . '/' . $file;
501
				$target = $path2 . '/' . $file;
502
				$this->copy($source, $target);
503
			}
504
505
		} else {
506
			//file does not exist
507
			return false;
508
		}
509
510
		return true;
511
	}
512
513
	public function rename($path1, $path2) {
514
		$path1 = $this->normalizePath($path1);
515
		$path2 = $this->normalizePath($path2);
516
517
		$fileType = $this->filetype($path1);
518
519
		if ($fileType === 'dir' || $fileType === 'file') {
520
			// copy
521
			if ($this->copy($path1, $path2) === false) {
522
				return false;
523
			}
524
525
			// cleanup
526
			if ($this->unlink($path1) === false) {
527
				$this->unlink($path2);
528
				return false;
529
			}
530
531
			return true;
532
		}
533
534
		return false;
535
	}
536
537
	public function getId() {
538
		return $this->id;
539
	}
540
541
	/**
542
	 * Returns the connection
543
	 *
544
	 * @return OpenCloud\ObjectStore\Service connected client
545
	 * @throws \Exception if connection could not be made
546
	 */
547
	public function getConnection() {
548
		if (!is_null($this->connection)) {
549
			return $this->connection;
550
		}
551
552
		$settings = array(
553
			'username' => $this->params['user'],
554
		);
555
556
		if (!empty($this->params['password'])) {
557
			$settings['password'] = $this->params['password'];
558
		} else if (!empty($this->params['key'])) {
559
			$settings['apiKey'] = $this->params['key'];
560
		}
561
562
		if (!empty($this->params['tenant'])) {
563
			$settings['tenantName'] = $this->params['tenant'];
564
		}
565
566
		if (!empty($this->params['timeout'])) {
567
			$settings['timeout'] = $this->params['timeout'];
568
		}
569
570
		if (isset($settings['apiKey'])) {
571
			$this->anchor = new Rackspace($this->params['url'], $settings);
572
		} else {
573
			$this->anchor = new OpenStack($this->params['url'], $settings);
574
		}
575
576
		$connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
577
578
		if (!empty($this->params['endpoint_url'])) {
579
			$endpoint = $connection->getEndpoint();
580
			$endpoint->setPublicUrl($this->params['endpoint_url']);
581
			$endpoint->setPrivateUrl($this->params['endpoint_url']);
582
			$connection->setEndpoint($endpoint);
583
		}
584
585
		$this->connection = $connection;
586
587
		return $this->connection;
588
	}
589
590
	/**
591
	 * Returns the initialized object store container.
592
	 *
593
	 * @return OpenCloud\ObjectStore\Resource\Container
594
	 */
595
	public function getContainer() {
596
		if (!is_null($this->container)) {
597
			return $this->container;
598
		}
599
600
		try {
601
			$this->container = $this->getConnection()->getContainer($this->bucket);
602
		} catch (ClientErrorResponseException $e) {
603
			$this->container = $this->getConnection()->createContainer($this->bucket);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getConnection()->...ontainer($this->bucket) can also be of type false. However, the property $container is declared as type object<OpenCloud\ObjectStore\Resource\Container>. 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 $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

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;
}
Loading history...
604
		}
605
606
		if (!$this->file_exists('.')) {
607
			$this->mkdir('.');
608
		}
609
610
		return $this->container;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->container; of type OpenCloud\ObjectStore\Resource\Container|false adds false to the return on line 610 which is incompatible with the return type documented by OCA\Files_External\Lib\Storage\Swift::getContainer of type OpenCloud\ObjectStore\Resource\Container. It seems like you forgot to handle an error condition.
Loading history...
611
	}
612
613
	public function writeBack($tmpFile) {
614
		if (!isset(self::$tmpFiles[$tmpFile])) {
615
			return false;
616
		}
617
		$fileData = fopen($tmpFile, 'r');
618
		$this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData);
619
		// invalidate target object to force repopulation on fetch
620
		$this->objectCache->remove(self::$tmpFiles[$tmpFile]);
621
		unlink($tmpFile);
622
	}
623
624
	public function hasUpdated($path, $time) {
625
		if ($this->is_file($path)) {
626
			return parent::hasUpdated($path, $time);
627
		}
628
		$path = $this->normalizePath($path);
629
		$dh = $this->opendir($path);
630
		$content = array();
631
		while (($file = readdir($dh)) !== false) {
632
			$content[] = $file;
633
		}
634
		if ($path === '.') {
635
			$path = '';
636
		}
637
		$cachedContent = $this->getCache()->getFolderContents($path);
638
		$cachedNames = array_map(function ($content) {
639
			return $content['name'];
640
		}, $cachedContent);
641
		sort($cachedNames);
642
		sort($content);
643
		return $cachedNames != $content;
644
	}
645
646
	/**
647
	 * check if curl is installed
648
	 */
649
	public static function checkDependencies() {
650
		return true;
651
	}
652
653
}
654