Passed
Pull Request — master (#77)
by Daniel
51:29
created

Plugin::isCompatible()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 11
ccs 0
cts 10
cp 0
rs 10
cc 3
nc 3
nop 0
crap 12
1
<?php
2
/**
3
 * CMS Pico - Create websites using Pico CMS for Nextcloud.
4
 *
5
 * @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>)
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
declare(strict_types=1);
24
25
namespace OCA\CMSPico\Model;
26
27
use OCA\CMSPico\Exceptions\PluginNotCompatibleException;
28
use OCA\CMSPico\Files\FolderInterface;
29
use OCA\CMSPico\Pico;
30
use OCA\CMSPico\Service\MiscService;
31
use OCP\Files\InvalidPathException;
32
use OCP\Files\NotFoundException;
33
34
class Plugin implements \JsonSerializable
35
{
36
	/** @var int */
37
	const TYPE_SYSTEM = 1;
38
39
	/** @var int */
40
	const TYPE_CUSTOM = 2;
41
42
	/** @var int[] */
43
	const PLUGIN_API_VERSIONS = [
44
		Pico::API_VERSION_1,
45
		Pico::API_VERSION_2,
46
		Pico::API_VERSION_3,
47
	];
48
49
	/** @var MiscService */
50
	private $miscService;
51
52
	/** @var FolderInterface */
53
	private $folder;
54
55
	/** @var int */
56
	private $type;
57
58
	/** @var bool|null */
59
	private $compat;
60
61
	/** @var PluginNotCompatibleException|null */
62
	private $compatException;
63
64
	/**
65
	 * Plugin constructor.
66
	 *
67
	 * @param FolderInterface $folder
68
	 * @param int             $type
69
	 */
70
	public function __construct(FolderInterface $folder, int $type = self::TYPE_SYSTEM)
71
	{
72
		$this->miscService = \OC::$server->query(MiscService::class);
73
74
		$this->folder = $folder;
75
		$this->type = $type;
76
	}
77
78
	/**
79
	 * @return string
80
	 */
81
	public function getName(): string
82
	{
83
		return $this->folder->getName();
84
	}
85
86
	/**
87
	 * @return FolderInterface
88
	 */
89
	public function getFolder(): FolderInterface
90
	{
91
		return $this->folder;
92
	}
93
94
	/**
95
	 * @return int
96
	 */
97
	public function getType(): int
98
	{
99
		return $this->type;
100
	}
101
102
	/**
103
	 * @return bool
104
	 */
105
	public function isCompatible(): bool
106
	{
107
		if ($this->compat !== null) {
108
			return $this->compat;
109
		}
110
111
		try {
112
			$this->checkCompatibility();
113
			return true;
114
		} catch (PluginNotCompatibleException $e) {
115
			return false;
116
		}
117
	}
118
119
	/**
120
	 * @throws PluginNotCompatibleException
121
	 */
122
	public function checkCompatibility()
123
	{
124
		if ($this->compat === false) {
125
			throw $this->compatException;
126
		} elseif ($this->compat) {
127
			return;
128
		}
129
130
		$includeClosure = static function (string $pluginFile) {
131
			/** @noinspection PhpIncludeInspection */
132
			require_once($pluginFile);
133
		};
134
135
		try {
136
			try {
137
				$pluginFile = $this->getFolder()->getFile($this->getName() . '.php');
138
				$includeClosure($pluginFile->getLocalPath());
139
			} catch (\Exception $e) {
140
				/** @noinspection PhpUnhandledExceptionInspection */
141
				$this->miscService->consumeException($e, InvalidPathException::class, NotFoundException::class);
142
143
				throw new PluginNotCompatibleException(
144
					$this->getName(),
145
					'Incompatible plugin: Plugin file "{file}" not found.',
146
					[ 'file' => $this->getName() . '/' . $this->getName() . '.php' ]
147
				);
148
			}
149
150
			$className = $this->getName();
151
			if (!class_exists($className, false)) {
152
				throw new PluginNotCompatibleException(
153
					$this->getName(),
154
					'Incompatible plugin: Plugin class "{class}" not found.',
155
					[ 'class' => $className ]
156
				);
157
			}
158
159
			$apiVersion = Pico::API_VERSION_0;
160
			if (is_a($className, \PicoPluginInterface::class, true)) {
161
				$apiVersion = Pico::API_VERSION_1;
162
				if (defined($className . '::API_VERSION')) {
163
					/** @noinspection PhpUndefinedFieldInspection */
164
					$apiVersion = (int) $className::API_VERSION;
165
				}
166
			}
167
168
			if (!in_array($apiVersion, static::PLUGIN_API_VERSIONS, true)) {
169
				throw new PluginNotCompatibleException(
170
					$this->getName(),
171
					'Incompatible plugin: Plugins for Pico CMS for Nextcloud must use one of the API versions '
172
							. '{compatApiVersions}, but this plugin uses API version {apiVersion}.',
173
					[ 'compatApiVersions' => implode(', ', static::PLUGIN_API_VERSIONS), 'apiVersion' => $apiVersion ]
174
				);
175
			}
176
177
			$this->compat = true;
178
			$this->compatException = null;
179
		} catch (PluginNotCompatibleException $e) {
180
			$this->compat = false;
181
			$this->compatException = $e;
182
183
			throw $e;
184
		}
185
	}
186
187
	/**
188
	 * @return array
189
	 */
190
	public function toArray(): array
191
	{
192
		$data = [
193
			'name' => $this->getName(),
194
			'type' => $this->getType(),
195
			'compat' => $this->isCompatible(),
196
		];
197
198
		if (!$this->isCompatible() && ($this->compatException !== null)) {
199
			$data['compatReason'] = $this->compatException->getRawReason();
200
			$data['compatReasonData'] = $this->compatException->getRawReasonData();
201
		}
202
203
		return $data;
204
	}
205
206
	/**
207
	 * @return array
208
	 */
209
	public function jsonSerialize(): array
210
	{
211
		return $this->toArray();
212
	}
213
}
214