Completed
Push — master ( eeb4fd...61f010 )
by Thomas
52:00 queued 38:32
created

MetaFileVersionNode::fopen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Thomas Müller <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OC\Files\Meta;
23
24
use OC\Files\Filesystem;
25
use OC\Files\Node\AbstractFile;
26
use OC\Files\Node\File;
27
use OCP\Files\ForbiddenException;
28
use OCP\Files\IProvidesAdditionalHeaders;
29
use OC\Preview;
30
use OCP\Files\IRootFolder;
31
use OCP\Files\IPreviewNode;
32
use OCP\Files\Storage\IVersionedStorage;
33
use OCP\Files\Storage;
34
use OCP\IImage;
35
36
/**
37
 * Class MetaFileVersionNode - this class represents a version of a file in the
38
 * meta endpoint
39
 *
40
 * @package OC\Files\Meta
41
 */
42
class MetaFileVersionNode extends AbstractFile implements IPreviewNode, IProvidesAdditionalHeaders {
43
44
	/** @var string */
45
	private $versionId;
46
	/** @var MetaVersionCollection */
47
	private $parent;
48
	/** @var IVersionedStorage */
49
	private $storage;
50
	/** @var string */
51
	private $internalPath;
52
	/** @var IRootFolder */
53
	private $root;
54
	/** @var array */
55
	private $versionInfo;
56
57
	/**
58
	 * MetaFileVersionNode constructor.
59
	 *
60
	 * @param MetaVersionCollection $parent
61
	 * @param IRootFolder $root
62
	 * @param array $version
63
	 * @param Storage $storage
64
	 * @param string $internalPath
65
	 */
66
	public function __construct(MetaVersionCollection $parent,
67
								IRootFolder $root,
68
								array $version, Storage $storage, $internalPath) {
69
		$this->parent = $parent;
70
		$this->versionId = $version['version'];
71
		$this->versionInfo = $version;
72
		$this->storage = $storage;
0 ignored issues
show
Documentation Bug introduced by
It seems like $storage of type object<OCP\Files\Storage> is incompatible with the declared type object<OCP\Files\Storage\IVersionedStorage> of property $storage.

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..

Loading history...
73
		$this->internalPath = $internalPath;
74
		$this->root = $root;
75
	}
76
77
	/**
78
	 * @inheritdoc
79
	 */
80
	public function getName() {
81
		return $this->versionId;
82
	}
83
84
	/**
85
	 * @inheritdoc
86
	 */
87
	public function getSize() {
88
		return isset($this->versionInfo['size']) ? $this->versionInfo['size'] : null;
89
	}
90
91
	/**
92
	 * @inheritdoc
93
	 */
94
	public function getContent() {
95
		$handle = $this->fopen('r+');
96
		$data = \stream_get_contents($handle);
97
		\fclose($handle);
98
99
		return $data;
100
	}
101
102
	/**
103
	 * @inheritdoc
104
	 */
105
	public function copy($targetPath) {
106
		$target = $this->root->get($targetPath);
107
		if ($target instanceof File && $target->getId() === $this->parent->getId()) {
108
			if (!$target->isUpdateable()) {
109
				throw new ForbiddenException("Cannot write to $targetPath", false);
110
			}
111
			return $this->storage->restoreVersion($this->internalPath, $this->versionId);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->storage->r...ath, $this->versionId); (boolean) is incompatible with the return type declared by the interface OCP\Files\Node::copy of type OCP\Files\Node.

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...
112
		}
113
114
		// for now we only allow restoring of a version
115
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface OCP\Files\Node::copy of type OCP\Files\Node.

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...
116
	}
117
118
	public function getMTime() {
119
		return isset($this->versionInfo['timestamp']) ? $this->versionInfo['timestamp'] : null;
120
	}
121
122
	public function getMimetype() {
123
		return isset($this->versionInfo['mimetype']) ? $this->versionInfo['mimetype'] : 'application/octet-stream';
124
	}
125
126
	public function getEtag() {
127
		return isset($this->versionInfo['etag']) ? $this->versionInfo['etag'] : null;
128
	}
129
130
	public function fopen($mode) {
131
		return $this->storage->getContentOfVersion($this->internalPath, $this->versionId);
132
	}
133
134
	/**
135
	 * @return array
136
	 */
137
	public function getHeaders() {
138
		return [];
139
	}
140
141
	/**
142
	 * @inheritdoc
143
	 */
144
	public function getContentDispositionFileName() {
145
		return \basename($this->internalPath);
146
	}
147
148
	public function getId() {
149
		return $this->parent->getId();
150
	}
151
152
	public function getPath() {
153
		return $this->parent->getPath() . '/' . $this->getName();
154
	}
155
156
	public function getMountPoint() {
157
		return Filesystem::getMountManager()->find($this->versionInfo['path']);
158
	}
159
160
	/**
161
	 * @param array $options
162
	 * @return IImage
163
	 * @since 10.1.0
164
	 */
165
	public function getThumbnail($options) {
166
		$maxX = \array_key_exists('x', $options) ? (int)$options['x'] : 32;
167
		$maxY = \array_key_exists('y', $options) ? (int)$options['y'] : 32;
168
		$scalingUp = \array_key_exists('scalingup', $options) ? (bool)$options['scalingup'] : true;
169
		$keepAspect = \array_key_exists('a', $options) ? true : false;
170
		$mode = \array_key_exists('mode', $options) ? $options['mode'] : 'fill';
171
172
		$preview = new Preview();
173
		$preview->setFile($this, $this->versionId);
174
		$preview->setMaxX($maxX);
175
		$preview->setMaxY($maxY);
176
		$preview->setScalingUp($scalingUp);
177
		$preview->setMode($mode);
178
		$preview->setKeepAspect($keepAspect);
179
		return $preview->getPreview();
180
	}
181
}
182