1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Manages plugin packages under mod. |
4
|
|
|
* |
5
|
|
|
* @todo This should eventually be merged into \ElggPlugin. |
6
|
|
|
* Currently \ElggPlugin objects are only used to get and save |
7
|
|
|
* plugin settings and user settings, so not every plugin |
8
|
|
|
* has an \ElggPlugin object. It's not implemented in \ElggPlugin |
9
|
|
|
* right now because of conflicts with at least the constructor, |
10
|
|
|
* enable(), disable(), and private settings. |
11
|
|
|
* |
12
|
|
|
* Around 1.9 or so we should each plugin over to using |
13
|
|
|
* \ElggPlugin and merge \ElggPluginPackage and \ElggPlugin. |
14
|
|
|
* |
15
|
|
|
* @package Elgg.Core |
16
|
|
|
* @subpackage Plugins |
17
|
|
|
* @since 1.8 |
18
|
|
|
*/ |
19
|
|
|
class ElggPluginPackage { |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* The required files in the package |
23
|
|
|
* |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
private $requiredFiles = array( |
27
|
|
|
'start.php', 'manifest.xml' |
28
|
|
|
); |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The optional files that can be read and served through the markdown page handler |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
private $textFiles = array( |
35
|
|
|
'README.txt', 'CHANGES.txt', |
36
|
|
|
'INSTALL.txt', 'COPYRIGHT.txt', 'LICENSE.txt', |
37
|
|
|
|
38
|
|
|
'README', 'README.md', 'README.markdown' |
39
|
|
|
); |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Valid types for provides. |
43
|
|
|
* |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
private $providesSupportedTypes = array( |
47
|
|
|
'plugin', 'php_extension' |
48
|
|
|
); |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* The type of requires/conflicts supported |
52
|
|
|
* |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
private $depsSupportedTypes = array( |
56
|
|
|
'elgg_version', 'elgg_release', 'php_version', 'php_extension', 'php_ini', 'plugin', 'priority', |
57
|
|
|
); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* An invalid plugin error. |
61
|
|
|
*/ |
62
|
|
|
private $errorMsg = ''; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* The plugin's manifest object |
66
|
|
|
* |
67
|
|
|
* @var \ElggPluginManifest |
68
|
|
|
*/ |
69
|
|
|
protected $manifest; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The plugin's full path |
73
|
|
|
* |
74
|
|
|
* @var string |
75
|
|
|
*/ |
76
|
|
|
protected $path; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Is the plugin valid? |
80
|
|
|
* |
81
|
|
|
* @var mixed Bool after validation check, null before. |
82
|
|
|
*/ |
83
|
|
|
protected $valid = null; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* The plugin ID (dir name) |
87
|
|
|
* |
88
|
|
|
* @var string |
89
|
|
|
*/ |
90
|
|
|
protected $id; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Load a plugin package from mod/$id or by full path. |
94
|
|
|
* |
95
|
|
|
* @param string $plugin The ID (directory name) or full path of the plugin. |
96
|
|
|
* @param bool $validate Automatically run isValid()? |
97
|
|
|
* |
98
|
|
|
* @throws PluginException |
99
|
|
|
*/ |
100
|
|
|
public function __construct($plugin, $validate = true) { |
101
|
|
|
$plugin_path = _elgg_services()->config->getPluginsPath(); |
102
|
|
|
// @todo wanted to avoid another is_dir() call here. |
103
|
|
|
// should do some profiling to see how much it affects |
104
|
|
|
if (strpos($plugin, $plugin_path) === 0 || is_dir($plugin)) { |
105
|
|
|
// this is a path |
106
|
|
|
$path = sanitise_filepath($plugin); |
107
|
|
|
|
108
|
|
|
// the id is the last element of the array |
109
|
|
|
$path_array = explode('/', trim($path, '/')); |
110
|
|
|
$id = array_pop($path_array); |
111
|
|
|
} else { |
112
|
|
|
// this is a plugin id |
113
|
|
|
// strict plugin names |
114
|
|
|
if (preg_match('/[^a-z0-9\.\-_]/i', $plugin)) { |
115
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidID', array($plugin))); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
$path = "{$plugin_path}$plugin/"; |
119
|
|
|
$id = $plugin; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
if (!is_dir($path)) { |
123
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPath', array($path))); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$this->path = $path; |
127
|
|
|
$this->id = $id; |
128
|
|
|
|
129
|
|
|
if ($validate && !$this->isValid()) { |
130
|
|
|
if ($this->errorMsg) { |
131
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPlugin:Details', |
132
|
|
|
array($plugin, $this->errorMsg))); |
133
|
|
|
} else { |
134
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPlugin', array($plugin))); |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/******************************** |
140
|
|
|
* Validation and sanity checks * |
141
|
|
|
********************************/ |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Checks if this is a valid Elgg plugin. |
145
|
|
|
* |
146
|
|
|
* Checks for requires files as defined at the start of this |
147
|
|
|
* class. Will check require manifest fields via \ElggPluginManifest |
148
|
|
|
* for Elgg 1.8 plugins. |
149
|
|
|
* |
150
|
|
|
* @note This doesn't check dependencies or conflicts. |
151
|
|
|
* Use {@link \ElggPluginPackage::canActivate()} or |
152
|
|
|
* {@link \ElggPluginPackage::checkDependencies()} for that. |
153
|
|
|
* |
154
|
|
|
* @return bool |
155
|
|
|
*/ |
156
|
|
|
public function isValid() { |
157
|
|
|
if (!isset($this->valid)) { |
158
|
|
|
$this->valid = $this->validate(); |
159
|
|
|
} |
160
|
|
|
return $this->valid; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @return bool |
165
|
|
|
*/ |
166
|
|
|
private function validate() { |
167
|
|
|
// check required files. |
168
|
|
|
$have_req_files = true; |
169
|
|
|
foreach ($this->requiredFiles as $file) { |
170
|
|
|
if (!is_readable($this->path . $file)) { |
171
|
|
|
$have_req_files = false; |
172
|
|
|
$this->errorMsg = |
173
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:MissingFile', array($file)); |
174
|
|
|
return false; |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
// check required files |
179
|
|
|
if (!$have_req_files) { |
180
|
|
|
return $this->valid = false; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
// check for valid manifest. |
184
|
|
|
if (!$this->loadManifest()) { |
185
|
|
|
return false; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if (!$this->isNamedCorrectly()) { |
189
|
|
|
return false; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
// can't require or conflict with yourself or something you provide. |
193
|
|
|
// make sure provides are all valid. |
194
|
|
|
if (!$this->hasSaneDependencies()) { |
195
|
|
|
return false; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
return true; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Check that the plugin is installed in the directory with name specified |
203
|
|
|
* in the manifest's "id" element. |
204
|
|
|
* |
205
|
|
|
* @return bool |
206
|
|
|
*/ |
207
|
|
|
private function isNamedCorrectly() { |
208
|
|
|
$manifest = $this->getManifest(); |
209
|
|
|
if ($manifest) { |
210
|
|
|
$required_id = $manifest->getID(); |
211
|
|
|
if (!empty($required_id) && ($required_id !== $this->id)) { |
212
|
|
|
$this->errorMsg = |
213
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidId', array($required_id)); |
214
|
|
|
return false; |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
return true; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Check the plugin doesn't require or conflict with itself |
222
|
|
|
* or something provides. Also check that it only list |
223
|
|
|
* valid provides. Deps are checked in checkDependencies() |
224
|
|
|
* |
225
|
|
|
* @note Plugins always provide themselves. |
226
|
|
|
* |
227
|
|
|
* @todo Don't let them require and conflict the same thing |
228
|
|
|
* |
229
|
|
|
* @return bool |
230
|
|
|
*/ |
231
|
|
|
private function hasSaneDependencies() { |
232
|
|
|
// protection against plugins with no manifest file |
233
|
|
|
if (!$this->getManifest()) { |
234
|
|
|
return false; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
// Note: $conflicts and $requires are not unused. They're called dynamically |
238
|
|
|
$conflicts = $this->getManifest()->getConflicts(); |
239
|
|
|
$requires = $this->getManifest()->getRequires(); |
240
|
|
|
$provides = $this->getManifest()->getProvides(); |
241
|
|
|
|
242
|
|
|
foreach ($provides as $provide) { |
243
|
|
|
// only valid provide types |
244
|
|
View Code Duplication |
if (!in_array($provide['type'], $this->providesSupportedTypes)) { |
245
|
|
|
$this->errorMsg = |
246
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidProvides', array($provide['type'])); |
247
|
|
|
return false; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
// doesn't conflict or require any of its provides |
251
|
|
|
$name = $provide['name']; |
252
|
|
|
foreach (array('conflicts', 'requires') as $dep_type) { |
253
|
|
|
foreach (${$dep_type} as $dep) { |
254
|
|
View Code Duplication |
if (!in_array($dep['type'], $this->depsSupportedTypes)) { |
255
|
|
|
$this->errorMsg = |
256
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidDependency', array($dep['type'])); |
257
|
|
|
return false; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
// make sure nothing is providing something it conflicts or requires. |
261
|
|
|
if (isset($dep['name']) && $dep['name'] == $name) { |
262
|
|
|
$version_compare = version_compare($provide['version'], $dep['version'], $dep['comparison']); |
263
|
|
|
|
264
|
|
View Code Duplication |
if ($version_compare) { |
265
|
|
|
$this->errorMsg = |
266
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:CircularDep', |
267
|
|
|
array($dep['type'], $dep['name'], $this->id)); |
268
|
|
|
|
269
|
|
|
return false; |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
return true; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
|
280
|
|
|
/************ |
281
|
|
|
* Manifest * |
282
|
|
|
************/ |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Returns a parsed manifest file. |
286
|
|
|
* |
287
|
|
|
* @return \ElggPluginManifest |
288
|
|
|
*/ |
289
|
|
|
public function getManifest() { |
290
|
|
|
if (!$this->manifest) { |
291
|
|
|
if (!$this->loadManifest()) { |
292
|
|
|
return false; |
|
|
|
|
293
|
|
|
} |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
return $this->manifest; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Loads the manifest into this->manifest as an |
301
|
|
|
* \ElggPluginManifest object. |
302
|
|
|
* |
303
|
|
|
* @return bool |
304
|
|
|
*/ |
305
|
|
|
private function loadManifest() { |
306
|
|
|
$file = $this->path . 'manifest.xml'; |
307
|
|
|
|
308
|
|
|
try { |
309
|
|
|
$this->manifest = new \ElggPluginManifest($file, $this->id); |
310
|
|
|
} catch (Exception $e) { |
311
|
|
|
$this->errorMsg = $e->getMessage(); |
312
|
|
|
return false; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
if ($this->manifest instanceof \ElggPluginManifest) { |
316
|
|
|
return true; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
$this->errorMsg = _elgg_services()->translator->translate('unknown_error'); |
320
|
|
|
return false; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/**************** |
324
|
|
|
* Readme Files * |
325
|
|
|
***************/ |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Returns an array of present and readable text files |
329
|
|
|
* |
330
|
|
|
* @return array |
331
|
|
|
*/ |
332
|
|
|
public function getTextFilenames() { |
333
|
|
|
return $this->textFiles; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/*********************** |
337
|
|
|
* Dependencies system * |
338
|
|
|
***********************/ |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Returns if the Elgg system meets the plugin's dependency |
342
|
|
|
* requirements. This includes both requires and conflicts. |
343
|
|
|
* |
344
|
|
|
* Full reports can be requested. The results are returned |
345
|
|
|
* as an array of arrays in the form array( |
346
|
|
|
* 'type' => requires|conflicts, |
347
|
|
|
* 'dep' => array( dependency array ), |
348
|
|
|
* 'status' => bool if depedency is met, |
349
|
|
|
* 'comment' => optional comment to display to the user. |
350
|
|
|
* ) |
351
|
|
|
* |
352
|
|
|
* @param bool $full_report Return a full report. |
353
|
|
|
* @return bool|array |
354
|
|
|
*/ |
355
|
|
|
public function checkDependencies($full_report = false) { |
356
|
|
|
// Note: $conflicts and $requires are not unused. They're called dynamically |
357
|
|
|
$requires = $this->getManifest()->getRequires(); |
358
|
|
|
$conflicts = $this->getManifest()->getConflicts(); |
359
|
|
|
|
360
|
|
|
$enabled_plugins = elgg_get_plugins('active'); |
361
|
|
|
$this_id = $this->getID(); |
362
|
|
|
$report = array(); |
363
|
|
|
|
364
|
|
|
// first, check if any active plugin conflicts with us. |
365
|
|
|
foreach ($enabled_plugins as $plugin) { |
366
|
|
|
$temp_conflicts = array(); |
367
|
|
|
$temp_manifest = $plugin->getManifest(); |
368
|
|
|
if ($temp_manifest instanceof \ElggPluginManifest) { |
369
|
|
|
$temp_conflicts = $plugin->getManifest()->getConflicts(); |
370
|
|
|
} |
371
|
|
|
foreach ($temp_conflicts as $conflict) { |
372
|
|
|
if ($conflict['type'] == 'plugin' && $conflict['name'] == $this_id) { |
373
|
|
|
$result = $this->checkDepPlugin($conflict, $enabled_plugins, false); |
374
|
|
|
|
375
|
|
|
// rewrite the conflict to show the originating plugin |
376
|
|
|
$conflict['name'] = $plugin->getManifest()->getName(); |
377
|
|
|
|
378
|
|
|
if (!$full_report && !$result['status']) { |
379
|
|
|
$this->errorMsg = "Conflicts with plugin \"{$plugin->getManifest()->getName()}\"."; |
380
|
|
|
return $result['status']; |
381
|
|
|
} else { |
382
|
|
|
$report[] = array( |
383
|
|
|
'type' => 'conflicted', |
384
|
|
|
'dep' => $conflict, |
385
|
|
|
'status' => $result['status'], |
386
|
|
|
'value' => $this->getManifest()->getVersion() |
387
|
|
|
); |
388
|
|
|
} |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
$check_types = array('requires', 'conflicts'); |
394
|
|
|
|
395
|
|
|
if ($full_report) { |
396
|
|
|
// Note: $suggests is not unused. It's called dynamically |
397
|
|
|
$suggests = $this->getManifest()->getSuggests(); |
398
|
|
|
$check_types[] = 'suggests'; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
foreach ($check_types as $dep_type) { |
402
|
|
|
$inverse = ($dep_type == 'conflicts') ? true : false; |
403
|
|
|
|
404
|
|
|
foreach (${$dep_type} as $dep) { |
405
|
|
|
switch ($dep['type']) { |
406
|
|
|
case 'elgg_version': |
407
|
|
|
elgg_deprecated_notice("elgg_version in manifest.xml files is deprecated. Use elgg_release", 1.9); |
408
|
|
|
$result = $this->checkDepElgg($dep, elgg_get_version(), $inverse); |
|
|
|
|
409
|
|
|
break; |
410
|
|
|
|
411
|
|
|
case 'elgg_release': |
412
|
|
|
$result = $this->checkDepElgg($dep, elgg_get_version(true), $inverse); |
|
|
|
|
413
|
|
|
break; |
414
|
|
|
|
415
|
|
|
case 'plugin': |
416
|
|
|
$result = $this->checkDepPlugin($dep, $enabled_plugins, $inverse); |
417
|
|
|
break; |
418
|
|
|
|
419
|
|
|
case 'priority': |
420
|
|
|
$result = $this->checkDepPriority($dep, $enabled_plugins, $inverse); |
421
|
|
|
break; |
422
|
|
|
|
423
|
|
|
case 'php_version': |
424
|
|
|
$result = $this->checkDepPhpVersion($dep, $inverse); |
425
|
|
|
break; |
426
|
|
|
|
427
|
|
|
case 'php_extension': |
428
|
|
|
$result = $this->checkDepPhpExtension($dep, $inverse); |
429
|
|
|
break; |
430
|
|
|
|
431
|
|
|
case 'php_ini': |
432
|
|
|
$result = $this->checkDepPhpIni($dep, $inverse); |
433
|
|
|
break; |
434
|
|
|
|
435
|
|
|
default: |
436
|
|
|
$result = null;//skip further check |
437
|
|
|
break; |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
if ($result !== null) { |
441
|
|
|
// unless we're doing a full report, break as soon as we fail. |
442
|
|
|
if (!$full_report && !$result['status']) { |
443
|
|
|
$this->errorMsg = "Missing dependencies."; |
444
|
|
|
return $result['status']; |
445
|
|
|
} else { |
446
|
|
|
// build report element and comment |
447
|
|
|
$report[] = array( |
448
|
|
|
'type' => $dep_type, |
449
|
|
|
'dep' => $dep, |
450
|
|
|
'status' => $result['status'], |
451
|
|
|
'value' => $result['value'] |
452
|
|
|
); |
453
|
|
|
} |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
if ($full_report) { |
459
|
|
|
// add provides to full report |
460
|
|
|
$provides = $this->getManifest()->getProvides(); |
461
|
|
|
|
462
|
|
|
foreach ($provides as $provide) { |
463
|
|
|
$report[] = array( |
464
|
|
|
'type' => 'provides', |
465
|
|
|
'dep' => $provide, |
466
|
|
|
'status' => true, |
467
|
|
|
'value' => '' |
468
|
|
|
); |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
return $report; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
return true; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* Checks if $plugins meets the requirement by $dep. |
479
|
|
|
* |
480
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
481
|
|
|
* @param array $plugins A list of plugins as returned by elgg_get_plugins(); |
482
|
|
|
* @param bool $inverse Inverse the results to use as a conflicts. |
483
|
|
|
* @return bool |
484
|
|
|
*/ |
485
|
|
|
private function checkDepPlugin(array $dep, array $plugins, $inverse = false) { |
486
|
|
|
$r = _elgg_check_plugins_provides('plugin', $dep['name'], $dep['version'], $dep['comparison']); |
487
|
|
|
|
488
|
|
|
if ($inverse) { |
489
|
|
|
$r['status'] = !$r['status']; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
return $r; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Checks if $plugins meets the requirement by $dep. |
497
|
|
|
* |
498
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
499
|
|
|
* @param array $plugins A list of plugins as returned by elgg_get_plugins(); |
500
|
|
|
* @param bool $inverse Inverse the results to use as a conflicts. |
501
|
|
|
* @return bool |
502
|
|
|
*/ |
503
|
|
|
private function checkDepPriority(array $dep, array $plugins, $inverse = false) { |
504
|
|
|
// grab the \ElggPlugin using this package. |
505
|
|
|
$plugin_package = elgg_get_plugin_from_id($this->getID()); |
506
|
|
|
if (!$plugin_package) { |
507
|
|
|
return array( |
508
|
|
|
'status' => true, |
509
|
|
|
'value' => 'uninstalled' |
510
|
|
|
); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
$test_plugin = elgg_get_plugin_from_id($dep['plugin']); |
514
|
|
|
|
515
|
|
|
// If this isn't a plugin or the plugin isn't installed or active |
516
|
|
|
// priority doesn't matter. Use requires to check if a plugin is active. |
517
|
|
|
if (!$test_plugin || !$test_plugin->isActive()) { |
518
|
|
|
return array( |
519
|
|
|
'status' => true, |
520
|
|
|
'value' => 'uninstalled' |
521
|
|
|
); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
$plugin_priority = $plugin_package->getPriority(); |
525
|
|
|
$test_plugin_priority = $test_plugin->getPriority(); |
526
|
|
|
|
527
|
|
|
switch ($dep['priority']) { |
528
|
|
|
case 'before': |
529
|
|
|
$status = $plugin_priority < $test_plugin_priority; |
530
|
|
|
break; |
531
|
|
|
|
532
|
|
|
case 'after': |
533
|
|
|
$status = $plugin_priority > $test_plugin_priority; |
534
|
|
|
break; |
535
|
|
|
|
536
|
|
|
default; |
537
|
|
|
$status = false; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
// get the current value |
541
|
|
|
if ($plugin_priority < $test_plugin_priority) { |
542
|
|
|
$value = 'before'; |
543
|
|
|
} else { |
544
|
|
|
$value = 'after'; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
if ($inverse) { |
548
|
|
|
$status = !$status; |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
return array( |
552
|
|
|
'status' => $status, |
553
|
|
|
'value' => $value |
554
|
|
|
); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* Checks if $elgg_version meets the requirement by $dep. |
559
|
|
|
* |
560
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
561
|
|
|
* @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z) |
562
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
563
|
|
|
* @return bool |
564
|
|
|
*/ |
565
|
|
View Code Duplication |
private function checkDepElgg(array $dep, $elgg_version, $inverse = false) { |
|
|
|
|
566
|
|
|
$status = version_compare($elgg_version, $dep['version'], $dep['comparison']); |
567
|
|
|
|
568
|
|
|
if ($inverse) { |
569
|
|
|
$status = !$status; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
return array( |
573
|
|
|
'status' => $status, |
574
|
|
|
'value' => $elgg_version |
575
|
|
|
); |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
/** |
579
|
|
|
* Checks if $php_version meets the requirement by $dep. |
580
|
|
|
* |
581
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
582
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
583
|
|
|
* @return bool |
584
|
|
|
*/ |
585
|
|
View Code Duplication |
private function checkDepPhpVersion(array $dep, $inverse = false) { |
|
|
|
|
586
|
|
|
$php_version = phpversion(); |
587
|
|
|
$status = version_compare($php_version, $dep['version'], $dep['comparison']); |
588
|
|
|
|
589
|
|
|
if ($inverse) { |
590
|
|
|
$status = !$status; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
return array( |
594
|
|
|
'status' => $status, |
595
|
|
|
'value' => $php_version |
596
|
|
|
); |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
/** |
600
|
|
|
* Checks if the PHP extension in $dep is loaded. |
601
|
|
|
* |
602
|
|
|
* @todo Can this be merged with the plugin checker? |
603
|
|
|
* |
604
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
605
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
606
|
|
|
* @return array An array in the form array( |
607
|
|
|
* 'status' => bool |
608
|
|
|
* 'value' => string The version provided |
609
|
|
|
* ) |
610
|
|
|
*/ |
611
|
|
|
private function checkDepPhpExtension(array $dep, $inverse = false) { |
612
|
|
|
$name = $dep['name']; |
613
|
|
|
$version = $dep['version']; |
614
|
|
|
$comparison = $dep['comparison']; |
615
|
|
|
|
616
|
|
|
// not enabled. |
617
|
|
|
$status = extension_loaded($name); |
618
|
|
|
|
619
|
|
|
// enabled. check version. |
620
|
|
|
$ext_version = phpversion($name); |
621
|
|
|
|
622
|
|
|
if ($status) { |
623
|
|
|
// some extensions (like gd) don't provide versions. neat. |
624
|
|
|
// don't check version info and return a lie. |
625
|
|
|
if ($ext_version && $version) { |
626
|
|
|
$status = version_compare($ext_version, $version, $comparison); |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
if (!$ext_version) { |
630
|
|
|
$ext_version = '???'; |
631
|
|
|
} |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
// some php extensions can be emulated, so check provides. |
635
|
|
|
if ($status == false) { |
|
|
|
|
636
|
|
|
$provides = _elgg_check_plugins_provides('php_extension', $name, $version, $comparison); |
637
|
|
|
$status = $provides['status']; |
638
|
|
|
$ext_version = $provides['value']; |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
if ($inverse) { |
642
|
|
|
$status = !$status; |
643
|
|
|
} |
644
|
|
|
|
645
|
|
|
return array( |
646
|
|
|
'status' => $status, |
647
|
|
|
'value' => $ext_version |
648
|
|
|
); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Check if the PHP ini setting satisfies $dep. |
653
|
|
|
* |
654
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
655
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
656
|
|
|
* @return bool |
657
|
|
|
*/ |
658
|
|
|
private function checkDepPhpIni($dep, $inverse = false) { |
659
|
|
|
$name = $dep['name']; |
660
|
|
|
$value = $dep['value']; |
661
|
|
|
$comparison = $dep['comparison']; |
662
|
|
|
|
663
|
|
|
// ini_get() normalizes truthy values to 1 but falsey values to 0 or ''. |
664
|
|
|
// version_compare() considers '' < 0, so normalize '' to 0. |
665
|
|
|
// \ElggPluginManifest normalizes all bool values and '' to 1 or 0. |
666
|
|
|
$setting = ini_get($name); |
667
|
|
|
|
668
|
|
|
if ($setting === '') { |
669
|
|
|
$setting = 0; |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
$status = version_compare($setting, $value, $comparison); |
673
|
|
|
|
674
|
|
|
if ($inverse) { |
675
|
|
|
$status = !$status; |
676
|
|
|
} |
677
|
|
|
|
678
|
|
|
return array( |
679
|
|
|
'status' => $status, |
680
|
|
|
'value' => $setting |
681
|
|
|
); |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
/** |
685
|
|
|
* Returns the Plugin ID |
686
|
|
|
* |
687
|
|
|
* @return string |
688
|
|
|
*/ |
689
|
|
|
public function getID() { |
690
|
|
|
return $this->id; |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
/** |
694
|
|
|
* Returns the last error message. |
695
|
|
|
* |
696
|
|
|
* @return string |
697
|
|
|
*/ |
698
|
|
|
public function getError() { |
699
|
|
|
return $this->errorMsg; |
700
|
|
|
} |
701
|
|
|
} |
702
|
|
|
|
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.