1 <?php
2
3 4 5 6 7 8 9
10 abstract class rex_package implements rex_package_interface
11 {
12 const FILE_PACKAGE = 'package.yml';
13 const FILE_BOOT = 'boot.php';
14 const FILE_INSTALL = 'install.php';
15 const FILE_INSTALL_SQL = 'install.sql';
16 const FILE_UNINSTALL = 'uninstall.php';
17 const FILE_UNINSTALL_SQL = 'uninstall.sql';
18 const FILE_UPDATE = 'update.php';
19
20 21 22 23 24
25 private $name;
26
27 28 29 30 31
32 private $properties = [];
33
34 35 36 37 38
39 private $propertiesLoaded = false;
40
41 42 43 44 45
46 public function __construct($name)
47 {
48 $this->name = $name;
49 }
50
51 52 53 54 55 56 57 58 59
60 public static function get($packageId)
61 {
62 if (!is_string($packageId)) {
63 throw new InvalidArgumentException('Expecting $packageId to be string, but ' . gettype($packageId) . ' given!');
64 }
65 $package = explode('/', $packageId);
66 $addon = rex_addon::get($package[0]);
67 if (isset($package[1])) {
68 return $addon->getPlugin($package[1]);
69 }
70 return $addon;
71 }
72
73 74 75 76 77 78 79
80 public static function exists($packageId)
81 {
82 $package = explode('/', $packageId);
83 if (isset($package[1])) {
84 return rex_plugin::exists($package[0], $package[1]);
85 }
86 return rex_addon::exists($package[0]);
87 }
88
89 90 91
92 public function getName()
93 {
94 return $this->name;
95 }
96
97 98 99
100 public function setConfig($key, $value = null)
101 {
102 return rex_config::set($this->getPackageId(), $key, $value);
103 }
104
105 106 107
108 public function getConfig($key = null, $default = null)
109 {
110 return rex_config::get($this->getPackageId(), $key, $default);
111 }
112
113 114 115
116 public function hasConfig($key = null)
117 {
118 return rex_config::has($this->getPackageId(), $key);
119 }
120
121 122 123
124 public function removeConfig($key)
125 {
126 return rex_config::remove($this->getPackageId(), $key);
127 }
128
129 130 131
132 public function setProperty($key, $value)
133 {
134 if (!is_string($key)) {
135 throw new InvalidArgumentException('Expecting $key to be string, but ' . gettype($key) . ' given!');
136 }
137 $this->properties[$key] = $value;
138 }
139
140 141 142
143 public function getProperty($key, $default = null)
144 {
145 if ($this->hasProperty($key)) {
146 return $this->properties[$key];
147 }
148 return $default;
149 }
150
151 152 153
154 public function hasProperty($key)
155 {
156 if (!is_string($key)) {
157 throw new InvalidArgumentException('Expecting $key to be string, but ' . gettype($key) . ' given!');
158 }
159 if (!isset($this->properties[$key]) && !$this->propertiesLoaded) {
160 $this->loadProperties();
161 }
162 return isset($this->properties[$key]);
163 }
164
165 166 167
168 public function removeProperty($key)
169 {
170 if (!is_string($key)) {
171 throw new InvalidArgumentException('Expecting $key to be string, but ' . gettype($key) . ' given!');
172 }
173 unset($this->properties[$key]);
174 }
175
176 177 178
179 public function isAvailable()
180 {
181 return $this->isInstalled() && (bool) $this->getProperty('status', false);
182 }
183
184 185 186
187 public function isInstalled()
188 {
189 return (bool) $this->getProperty('install', false);
190 }
191
192 193 194
195 public function getAuthor($default = null)
196 {
197 return $this->getProperty('author', $default);
198 }
199
200 201 202
203 public function getVersion($format = null)
204 {
205 $version = $this->getProperty('version');
206 if ($format) {
207 return rex_formatter::version($version, $format);
208 }
209 return $version;
210 }
211
212 213 214
215 public function getSupportPage($default = null)
216 {
217 return $this->getProperty('supportpage', $default);
218 }
219
220 221 222
223 public function includeFile($__file, array $__context = [])
224 {
225 extract($__context, EXTR_SKIP);
226
227 if (file_exists($this->getPath($__file))) {
228 return include $this->getPath($__file);
229 }
230
231 return include $__file;
232 }
233
234 235 236
237 public function loadProperties()
238 {
239 $file = $this->getPath(self::FILE_PACKAGE);
240 if (!file_exists($file)) {
241 $this->propertiesLoaded = true;
242 return;
243 }
244
245 static $cache = null;
246 if (null === $cache) {
247 $cache = rex_file::getCache(rex_path::coreCache('packages.cache'));
248 }
249 $id = $this->getPackageId();
250
251 $isCached = isset($cache[$id]);
252 $isBackendAdmin = rex::isBackend() && rex::getUser() && rex::getUser()->isAdmin();
253 if (!$isCached || (rex::getConsole() || $isBackendAdmin) && $cache[$id]['timestamp'] < filemtime($file)) {
254 try {
255 $properties = rex_file::getConfig($file);
256
257 $cache[$id]['timestamp'] = filemtime($file);
258 $cache[$id]['data'] = $properties;
259
260 static $registeredShutdown = false;
261 if (!$registeredShutdown) {
262 $registeredShutdown = true;
263 register_shutdown_function(function () use (&$cache) {
264 foreach ($cache as $package => $_) {
265 if (!self::exists($package)) {
266 unset($cache[$package]);
267 }
268 }
269 rex_file::putCache(rex_path::coreCache('packages.cache'), $cache);
270 });
271 }
272 } catch (rex_yaml_parse_exception $exception) {
273 if ($this->isInstalled()) {
274 throw $exception;
275 }
276
277 $properties = [];
278 }
279 } else {
280 $properties = $cache[$id]['data'];
281 }
282
283 $this->properties = array_intersect_key($this->properties, ['install' => null, 'status' => null]);
284 if ($properties) {
285 foreach ($properties as $key => $value) {
286 if (isset($this->properties[$key])) {
287 continue;
288 }
289 if ('supportpage' !== $key) {
290 $value = rex_i18n::translateArray($value, false, [$this, 'i18n']);
291 } elseif (!preg_match('@^https?://@i', $value)) {
292 $value = 'https://'.$value;
293 }
294 $this->properties[$key] = $value;
295 }
296 }
297 $this->propertiesLoaded = true;
298 }
299
300 301 302 303 304
305 public function clearCache()
306 {
307 $cache_dir = $this->getCachePath();
308 if (is_dir($cache_dir) && !rex_dir::delete($cache_dir)) {
309 throw new rex_functional_exception($this->i18n('cache_not_writable', $cache_dir));
310 }
311 }
312
313 public function enlist()
314 {
315 $folder = $this->getPath();
316
317
318 if (is_readable($folder . 'lang')) {
319 rex_i18n::addDirectory($folder . 'lang');
320 }
321
322 if (is_readable($folder . 'fragments')) {
323 rex_fragment::addDirectory($folder . 'fragments' . DIRECTORY_SEPARATOR);
324 }
325
326 if (is_readable($folder . 'lib')) {
327 rex_autoload::addDirectory($folder . 'lib');
328 }
329 if (is_readable($folder . 'vendor')) {
330 rex_autoload::addDirectory($folder . 'vendor');
331 }
332 $autoload = $this->getProperty('autoload');
333 if (is_array($autoload) && isset($autoload['classes']) && is_array($autoload['classes'])) {
334 foreach ($autoload['classes'] as $dir) {
335 $dir = $this->getPath($dir);
336 if (is_readable($dir)) {
337 rex_autoload::addDirectory($dir);
338 }
339 }
340 }
341 }
342
343 public function boot()
344 {
345 if (is_readable($this->getPath(self::FILE_BOOT))) {
346 $this->includeFile(self::FILE_BOOT);
347 }
348 }
349
350 351 352 353 354
355 public static function getRegisteredPackages()
356 {
357 return self::getPackages('Registered');
358 }
359
360 361 362 363 364
365 public static function getInstalledPackages()
366 {
367 return self::getPackages('Installed');
368 }
369
370 371 372 373 374
375 public static function getAvailablePackages()
376 {
377 return self::getPackages('Available');
378 }
379
380 381 382 383 384
385 public static function getSetupPackages()
386 {
387 return self::getPackages('Setup', 'System');
388 }
389
390 391 392 393 394
395 public static function getSystemPackages()
396 {
397 return self::getPackages('System');
398 }
399
400 401 402 403 404 405 406 407
408 private static function getPackages($method, $pluginMethod = null)
409 {
410 $packages = [];
411 $addonMethod = 'get' . $method . 'Addons';
412 $pluginMethod = 'get' . ($pluginMethod ?: $method) . 'Plugins';
413 foreach (rex_addon::$addonMethod() as $addon) {
414 $packages[$addon->getPackageId()] = $addon;
415 foreach ($addon->$pluginMethod() as $plugin) {
416 $packages[$plugin->getPackageId()] = $plugin;
417 }
418 }
419 return $packages;
420 }
421 }
422