Completed
Push — master ( 81153d...2a6722 )
by Thomas
52s
created

MetaFileVersionNode::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 5
dl 0
loc 10
rs 9.4285
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\Node\AbstractFile;
25
use OC\Files\Node\File;
26
use OCP\Files\ForbiddenException;
27
use OCP\Files\IProvidesAdditionalHeaders;
28
use OC\Preview;
29
use OCP\Files\IRootFolder;
30
use OCP\Files\IPreviewNode;
31
use OCP\Files\Storage\IVersionedStorage;
32
use OCP\Files\Storage;
33
use OCP\IImage;
34
35
/**
36
 * Class MetaFileVersionNode - this class represents a version of a file in the
37
 * meta endpoint
38
 *
39
 * @package OC\Files\Meta
40
 */
41
class MetaFileVersionNode extends AbstractFile implements IPreviewNode, IProvidesAdditionalHeaders {
42
43
	/** @var string */
44
	private $versionId;
45
	/** @var MetaVersionCollection */
46
	private $parent;
47
	/** @var IVersionedStorage */
48
	private $storage;
49
	/** @var string */
50
	private $internalPath;
51
	/** @var IRootFolder */
52
	private $root;
53
	/** @var array */
54
	private $versionInfo;
55
56
	/**
57
	 * MetaFileVersionNode constructor.
58
	 *
59
	 * @param MetaVersionCollection $parent
60
	 * @param IRootFolder $root
61
	 * @param array $version
62
	 * @param Storage $storage
63
	 * @param string $internalPath
64
	 */
65
	public function __construct(MetaVersionCollection $parent,
66
								IRootFolder $root,
67
								array $version, Storage $storage, $internalPath) {
68
		$this->parent = $parent;
69
		$this->versionId = $version['version'];
70
		$this->versionInfo = $version;
71
		$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...
72
		$this->internalPath = $internalPath;
73
		$this->root = $root;
74
	}
75
76
	/**
77
	 * @inheritdoc
78
	 */
79
	public function getName() {
80
		return $this->versionId;
81
	}
82
83
	/**
84
	 * @inheritdoc
85
	 */
86
	public function getSize() {
87
		return isset($this->versionInfo['size']) ? $this->versionInfo['size'] : null;
88
	}
89
90
	/**
91
	 * @inheritdoc
92
	 */
93
	public function getContent() {
94
		$handle = $this->fopen('r+');
95
		$data = stream_get_contents($handle);
96
		fclose($handle);
97
98
		return $data;
99
	}
100
101
	/**
102
	 * @inheritdoc
103
	 */
104
	public function copy($targetPath) {
105
		$target = $this->root->get($targetPath);
106
		if ($target instanceof File && $target->getId() === $this->parent->getId()) {
107
			if (!$target->isUpdateable()) {
108
				throw new ForbiddenException("Cannot write to $targetPath", false);
109
			}
110
			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...
111
		}
112
113
		// for now we only allow restoring of a version
114
		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...
115
	}
116
117
	public function getMTime() {
118
		return isset($this->versionInfo['timestamp']) ? $this->versionInfo['timestamp'] : null;
119
	}
120
121
	public function getMimetype() {
122
		return isset($this->versionInfo['mimetype']) ? $this->versionInfo['mimetype'] : 'application/octet-stream';
123
	}
124
125
	public function getEtag() {
126
		return isset($this->versionInfo['etag']) ? $this->versionInfo['etag'] : null;
127
	}
128
129
	public function fopen($mode) {
130
		return $this->storage->getContentOfVersion($this->internalPath, $this->versionId);
131
	}
132
133
	/**
134
	 * @return array
135
	 */
136
	public function getHeaders() {
137
		return [];
138
	}
139
140
	/**
141
	 * @inheritdoc
142
	 */
143
	public function getContentDispositionFileName() {
144
		return basename($this->internalPath);
145
	}
146
147
	public function getId() {
148
		return $this->parent->getId();
149
	}
150
151
	public function getPath() {
152
		return $this->parent->getPath() . '/' . $this->getName();
153
	}
154
155
	/**
156
	 * @param array $options
157
	 * @return IImage
158
	 * @since 10.1.0
159
	 */
160 View Code Duplication
	public function getThumbnail($options) {
161
		$maxX = array_key_exists('x', $options) ? (int)$options['x'] : 32;
162
		$maxY = array_key_exists('y', $options) ? (int)$options['y'] : 32;
163
		$scalingUp = array_key_exists('scalingup', $options) ? (bool)$options['scalingup'] : true;
164
		$keepAspect = array_key_exists('a', $options) ? true : false;
165
		$mode = array_key_exists('mode', $options) ? $options['mode'] : 'fill';
166
167
		$preview = new Preview();
168
		$preview->setFile($this, $this->versionId);
169
		$preview->setMaxX($maxX);
170
		$preview->setMaxY($maxY);
171
		$preview->setScalingUp($scalingUp);
172
		$preview->setMode($mode);
173
		$preview->setKeepAspect($keepAspect);
174
		return $preview->getPreview();
175
	}
176
}
177