1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Piwik - free/libre analytics platform |
4
|
|
|
* |
5
|
|
|
* @link http://piwik.org |
6
|
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Piwik\Application\Kernel; |
10
|
|
|
|
11
|
|
|
use Piwik\Plugin\MetadataLoader; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Lists the currently activated plugins. Used when setting up Piwik's environment before |
15
|
|
|
* initializing the DI container. |
16
|
|
|
* |
17
|
|
|
* Uses the [Plugins] section in Piwik's INI config to get the activated plugins. |
18
|
|
|
* |
19
|
|
|
* Depends on GlobalSettingsProvider being used. |
20
|
|
|
* |
21
|
|
|
* TODO: parts of Plugin\Manager edit the plugin list; maybe PluginList implementations should be mutable? |
22
|
|
|
*/ |
23
|
|
|
class PluginList |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @var GlobalSettingsProvider |
27
|
|
|
*/ |
28
|
|
|
private $settings; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Plugins bundled with core package, disabled by default |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
private $corePluginsDisabledByDefault = array( |
35
|
|
|
'DBStats', |
36
|
|
|
'ExampleCommand', |
37
|
|
|
'ExampleSettingsPlugin', |
38
|
|
|
'ExampleUI', |
39
|
|
|
'ExampleVisualization', |
40
|
|
|
'ExamplePluginTemplate', |
41
|
|
|
'ExampleTracker', |
42
|
|
|
'ExampleReport', |
43
|
|
|
'MobileAppMeasurable', |
44
|
|
|
'Provider' |
45
|
|
|
); |
46
|
|
|
|
47
|
|
|
// Themes bundled with core package, disabled by default |
48
|
|
|
private $coreThemesDisabledByDefault = array( |
49
|
|
|
'ExampleTheme' |
50
|
85 |
|
); |
51
|
|
|
|
52
|
85 |
|
public function __construct(GlobalSettingsProvider $settings) |
53
|
85 |
|
{ |
54
|
|
|
$this->settings = $settings; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Returns the list of plugins that should be loaded. Used by the container factory to |
59
|
|
|
* load plugin specific DI overrides. |
60
|
|
|
* |
61
|
85 |
|
* @return string[] |
62
|
|
|
*/ |
63
|
85 |
|
public function getActivatedPlugins() |
64
|
85 |
|
{ |
65
|
|
|
$section = $this->settings->getSection('Plugins'); |
66
|
|
|
return @$section['Plugins'] ?: array(); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Returns the list of plugins that are bundled with Piwik. |
71
|
|
|
* |
72
|
121 |
|
* @return string[] |
73
|
|
|
*/ |
74
|
121 |
|
public function getPluginsBundledWithPiwik() |
75
|
|
|
{ |
76
|
121 |
|
$pathGlobal = $this->settings->getPathGlobal(); |
77
|
121 |
|
|
78
|
|
|
$section = $this->settings->getIniFileChain()->getFrom($pathGlobal, 'Plugins'); |
79
|
|
|
return $section['Plugins']; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Returns the plugins bundled with core package that are disabled by default. |
84
|
|
|
* |
85
|
278 |
|
* @return string[] |
86
|
|
|
*/ |
87
|
278 |
|
public function getCorePluginsDisabledByDefault() |
88
|
|
|
{ |
89
|
|
|
return array_merge($this->corePluginsDisabledByDefault, $this->coreThemesDisabledByDefault); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Sorts an array of plugins in the order they should be loaded. We cannot use DI here as DI is not initialized |
94
|
|
|
* at this stage. |
95
|
|
|
* |
96
|
121 |
|
* @params string[] $plugins |
97
|
|
|
* @return \string[] |
98
|
121 |
|
*/ |
99
|
121 |
|
public function sortPlugins(array $plugins) |
100
|
|
|
{ |
101
|
|
|
$global = $this->getPluginsBundledWithPiwik(); |
102
|
|
|
if (empty($global)) { |
103
|
|
|
return $plugins; |
104
|
121 |
|
} |
105
|
|
|
|
106
|
121 |
|
// we need to make sure a possibly disabled plugin will be still loaded before any 3rd party plugin |
107
|
121 |
|
$global = array_merge($global, $this->corePluginsDisabledByDefault); |
108
|
|
|
|
109
|
121 |
|
$global = array_values($global); |
110
|
|
|
$plugins = array_values($plugins); |
111
|
121 |
|
|
112
|
|
|
$defaultPluginsLoadedFirst = array_intersect($global, $plugins); |
113
|
|
|
|
114
|
121 |
|
$otherPluginsToLoadAfterDefaultPlugins = array_diff($plugins, $defaultPluginsLoadedFirst); |
115
|
|
|
|
116
|
121 |
|
// sort by name to have a predictable order for those extra plugins |
117
|
|
|
natcasesort($otherPluginsToLoadAfterDefaultPlugins); |
118
|
121 |
|
|
119
|
|
|
$sorted = array_merge($defaultPluginsLoadedFirst, $otherPluginsToLoadAfterDefaultPlugins); |
120
|
|
|
|
121
|
|
|
return $sorted; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Sorts an array of plugins in the order they should be saved in config.ini.php. This basically influences |
126
|
|
|
* the order of the plugin config.php and which config will be loaded first. We want to make sure to require the |
127
|
|
|
* config or a required plugin first before loading the plugin that requires it. |
128
|
|
|
* |
129
|
|
|
* We do not sort using this logic on each request since it is much slower than `sortPlugins()`. The order |
130
|
|
|
* of plugins in config.ini.php is only important for the ContainerFactory. During a regular request it is otherwise |
131
|
|
|
* fine to load the plugins in the order of `sortPlugins()` since we will make sure that required plugins will be |
132
|
|
|
* loaded first in plugin manager. |
133
|
|
|
* |
134
|
|
|
* @param string[] $plugins |
135
|
|
|
* @param array[] $pluginJsonCache For internal testing only |
136
|
|
|
* @return \string[] |
137
|
|
|
*/ |
138
|
|
|
public function sortPluginsAndRespectDependencies(array $plugins, $pluginJsonCache = array()) |
139
|
|
|
{ |
140
|
|
|
$global = $this->getPluginsBundledWithPiwik(); |
141
|
|
|
|
142
|
|
|
if (empty($global)) { |
143
|
|
|
return $plugins; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
// we need to make sure a possibly disabled plugin will be still loaded before any 3rd party plugin |
147
|
|
|
$global = array_merge($global, $this->corePluginsDisabledByDefault); |
148
|
|
|
|
149
|
|
|
$global = array_values($global); |
150
|
|
|
$plugins = array_values($plugins); |
151
|
|
|
|
152
|
|
|
$defaultPluginsLoadedFirst = array_intersect($global, $plugins); |
153
|
|
|
|
154
|
|
|
$otherPluginsToLoadAfterDefaultPlugins = array_diff($plugins, $defaultPluginsLoadedFirst); |
155
|
|
|
|
156
|
|
|
// we still want to sort alphabetically by default |
157
|
|
|
natcasesort($otherPluginsToLoadAfterDefaultPlugins); |
158
|
|
|
|
159
|
|
|
$sorted = array(); |
160
|
|
|
foreach ($otherPluginsToLoadAfterDefaultPlugins as $pluginName) { |
161
|
|
|
$sorted = $this->sortRequiredPlugin($pluginName, $pluginJsonCache, $otherPluginsToLoadAfterDefaultPlugins, $sorted); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$sorted = array_merge($defaultPluginsLoadedFirst, $sorted); |
165
|
|
|
|
166
|
|
|
return $sorted; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
private function sortRequiredPlugin($pluginName, &$pluginJsonCache, $toBeSorted, $sorted) |
170
|
|
|
{ |
171
|
|
|
if (!isset($pluginJsonCache[$pluginName])) { |
172
|
|
|
$loader = new MetadataLoader($pluginName); |
173
|
|
|
$pluginJsonCache[$pluginName] = $loader->loadPluginInfoJson(); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (!empty($pluginJsonCache[$pluginName]['require'])) { |
177
|
|
|
$dependencies = $pluginJsonCache[$pluginName]['require']; |
178
|
|
|
foreach ($dependencies as $possiblePluginName => $key) { |
179
|
|
|
if (in_array($possiblePluginName, $toBeSorted, true) && !in_array($possiblePluginName, $sorted, true)) { |
180
|
|
|
$sorted = $this->sortRequiredPlugin($possiblePluginName, $pluginJsonCache, $toBeSorted, $sorted); |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
if (!in_array($pluginName, $sorted, true)) { |
186
|
|
|
$sorted[] = $pluginName; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return $sorted; |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|