Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class Manager |
||
39 | { |
||
40 | /** |
||
41 | * @return self |
||
42 | 235 | */ |
|
43 | public static function getInstance() |
||
44 | 235 | { |
|
45 | return StaticContainer::get('Piwik\Plugin\Manager'); |
||
46 | } |
||
47 | |||
48 | protected $pluginsToLoad = array(); |
||
49 | |||
50 | protected $doLoadPlugins = true; |
||
51 | |||
52 | private $pluginsLoadedAndActivated; |
||
53 | |||
54 | /** |
||
55 | * @var Plugin[] |
||
56 | */ |
||
57 | protected $loadedPlugins = array(); |
||
58 | /** |
||
59 | * Default theme used in Piwik. |
||
60 | */ |
||
61 | const DEFAULT_THEME = "Morpheus"; |
||
62 | |||
63 | protected $doLoadAlwaysActivatedPlugins = true; |
||
64 | |||
65 | // These are always activated and cannot be deactivated |
||
66 | protected $pluginToAlwaysActivate = array( |
||
67 | 'CoreHome', |
||
68 | 'Diagnostics', |
||
69 | 'CoreUpdater', |
||
70 | 'CoreAdminHome', |
||
71 | 'CoreConsole', |
||
72 | 'CorePluginsAdmin', |
||
73 | 'CoreVisualizations', |
||
74 | 'Installation', |
||
75 | 'SitesManager', |
||
76 | 'UsersManager', |
||
77 | 'Intl', |
||
78 | 'API', |
||
79 | 'Proxy', |
||
80 | 'LanguagesManager', |
||
81 | 'WebsiteMeasurable', |
||
82 | |||
83 | // default Piwik theme, always enabled |
||
84 | self::DEFAULT_THEME, |
||
85 | ); |
||
86 | |||
87 | private $trackerPluginsNotToLoad = array(); |
||
88 | |||
89 | /** |
||
90 | * @var PluginList |
||
91 | */ |
||
92 | private $pluginList; |
||
93 | 85 | ||
94 | public function __construct(PluginList $pluginList) |
||
95 | 85 | { |
|
96 | 85 | $this->pluginList = $pluginList; |
|
97 | } |
||
98 | |||
99 | /** |
||
100 | * Loads plugin that are enabled |
||
101 | */ |
||
102 | public function loadActivatedPlugins() |
||
103 | { |
||
104 | $pluginsToLoad = $this->getActivatedPluginsFromConfig(); |
||
105 | $this->loadPlugins($pluginsToLoad); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Called during Tracker |
||
110 | */ |
||
111 | public function loadCorePluginsDuringTracker() |
||
112 | { |
||
113 | $pluginsToLoad = $this->pluginList->getActivatedPlugins(); |
||
114 | $pluginsToLoad = array_diff($pluginsToLoad, $this->getTrackerPluginsNotToLoad()); |
||
115 | $this->loadPlugins($pluginsToLoad); |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * @return array names of plugins that have been loaded |
||
120 | */ |
||
121 | public function loadTrackerPlugins() |
||
122 | { |
||
123 | $cacheId = 'PluginsTracker'; |
||
124 | $cache = Cache::getEagerCache(); |
||
125 | |||
126 | if ($cache->contains($cacheId)) { |
||
127 | $pluginsTracker = $cache->fetch($cacheId); |
||
128 | } else { |
||
129 | $this->unloadPlugins(); |
||
130 | $this->loadActivatedPlugins(); |
||
131 | |||
132 | $pluginsTracker = array(); |
||
133 | |||
134 | foreach ($this->loadedPlugins as $pluginName => $plugin) { |
||
135 | if ($this->isTrackerPlugin($plugin)) { |
||
136 | $pluginsTracker[] = $pluginName; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | if (!empty($pluginsTracker)) { |
||
141 | $cache->save($cacheId, $pluginsTracker); |
||
142 | } |
||
143 | } |
||
144 | |||
145 | if (empty($pluginsTracker)) { |
||
146 | $this->unloadPlugins(); |
||
147 | return array(); |
||
148 | } |
||
149 | |||
150 | $pluginsTracker = array_diff($pluginsTracker, $this->getTrackerPluginsNotToLoad()); |
||
151 | $this->doNotLoadAlwaysActivatedPlugins(); |
||
152 | $this->loadPlugins($pluginsTracker); |
||
153 | |||
154 | // we could simply unload all plugins first before loading plugins but this way it is much faster |
||
155 | // since we won't have to create each plugin again and we won't have to parse each plugin metadata file |
||
156 | // again etc |
||
157 | $this->makeSureOnlyActivatedPluginsAreLoaded(); |
||
158 | |||
159 | return $pluginsTracker; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Do not load the specified plugins (used during testing, to disable Provider plugin) |
||
164 | * @param array $plugins |
||
165 | */ |
||
166 | public function setTrackerPluginsNotToLoad($plugins) |
||
167 | { |
||
168 | $this->trackerPluginsNotToLoad = $plugins; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Get list of plugins to not load |
||
173 | * |
||
174 | * @return array |
||
175 | */ |
||
176 | public function getTrackerPluginsNotToLoad() |
||
177 | { |
||
178 | return $this->trackerPluginsNotToLoad; |
||
179 | } |
||
180 | |||
181 | // If a plugin hooks onto at least an event starting with "Tracker.", we load the plugin during tracker |
||
182 | const TRACKER_EVENT_PREFIX = 'Tracker.'; |
||
183 | |||
184 | /** |
||
185 | * @param $pluginName |
||
186 | * @return bool |
||
187 | 85 | */ |
|
188 | public function isPluginOfficialAndNotBundledWithCore($pluginName) |
||
189 | 85 | { |
|
190 | 85 | static $gitModules; |
|
191 | if (empty($gitModules)) { |
||
192 | $gitModules = file_get_contents(PIWIK_INCLUDE_PATH . '/.gitmodules'); |
||
193 | } |
||
194 | 85 | // All submodules are officially maintained plugins |
|
195 | 85 | $isSubmodule = false !== strpos($gitModules, "plugins/" . $pluginName . "\n"); |
|
196 | return $isSubmodule; |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Update Plugins config |
||
201 | * |
||
202 | * @param array $pluginsToLoad Plugins |
||
203 | */ |
||
204 | private function updatePluginsConfig($pluginsToLoad) |
||
205 | { |
||
206 | $pluginsToLoad = $this->pluginList->sortPluginsAndRespectDependencies($pluginsToLoad); |
||
207 | $section = PiwikConfig::getInstance()->Plugins; |
||
|
|||
208 | $section['Plugins'] = $pluginsToLoad; |
||
209 | PiwikConfig::getInstance()->Plugins = $section; |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Update PluginsInstalled config |
||
214 | * |
||
215 | * @param array $plugins Plugins |
||
216 | */ |
||
217 | private function updatePluginsInstalledConfig($plugins) |
||
218 | { |
||
219 | $section = PiwikConfig::getInstance()->PluginsInstalled; |
||
220 | $section['PluginsInstalled'] = $plugins; |
||
221 | PiwikConfig::getInstance()->PluginsInstalled = $section; |
||
222 | } |
||
223 | |||
224 | public function clearPluginsInstalledConfig() |
||
225 | { |
||
226 | $this->updatePluginsInstalledConfig(array()); |
||
227 | PiwikConfig::getInstance()->forceSave(); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Returns true if plugin is always activated |
||
232 | * |
||
233 | * @param string $name Name of plugin |
||
234 | * @return bool |
||
235 | 4 | */ |
|
236 | private function isPluginAlwaysActivated($name) |
||
237 | 4 | { |
|
238 | return in_array($name, $this->pluginToAlwaysActivate); |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Returns true if the plugin can be uninstalled. Any non-core plugin can be uninstalled. |
||
243 | * |
||
244 | * @param $name |
||
245 | * @return bool |
||
246 | */ |
||
247 | private function isPluginUninstallable($name) |
||
248 | { |
||
249 | return !$this->isPluginBundledWithCore($name); |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Returns `true` if a plugin has been activated. |
||
254 | * |
||
255 | * @param string $name Name of plugin, eg, `'Actions'`. |
||
256 | * @return bool |
||
257 | * @api |
||
258 | 9 | */ |
|
259 | public function isPluginActivated($name) |
||
260 | 9 | { |
|
261 | 9 | return in_array($name, $this->pluginsToLoad) |
|
262 | || ($this->doLoadAlwaysActivatedPlugins && $this->isPluginAlwaysActivated($name)); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Checks whether the given plugin is activated, if not triggers an exception. |
||
267 | * |
||
268 | * @param string $pluginName |
||
269 | * @throws PluginDeactivatedException |
||
270 | */ |
||
271 | public function checkIsPluginActivated($pluginName) |
||
272 | { |
||
273 | if (!$this->isPluginActivated($pluginName)) { |
||
274 | throw new PluginDeactivatedException($pluginName); |
||
275 | } |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Returns `true` if plugin is loaded (in memory). |
||
280 | * |
||
281 | * @param string $name Name of plugin, eg, `'Acions'`. |
||
282 | * @return bool |
||
283 | * @api |
||
284 | 36 | */ |
|
285 | public function isPluginLoaded($name) |
||
286 | 36 | { |
|
287 | return isset($this->loadedPlugins[$name]); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Reads the directories inside the plugins/ directory and returns their names in an array |
||
292 | * |
||
293 | * @return array |
||
294 | 283 | */ |
|
295 | public function readPluginsDirectory() |
||
296 | 283 | { |
|
297 | 283 | $pluginsName = _glob(self::getPluginsDirectory() . '*', GLOB_ONLYDIR); |
|
298 | 283 | $result = array(); |
|
299 | 283 | if ($pluginsName != false) { |
|
300 | 283 | foreach ($pluginsName as $path) { |
|
301 | 283 | if (self::pluginStructureLooksValid($path)) { |
|
302 | 283 | $result[] = basename($path); |
|
303 | 283 | } |
|
304 | 283 | } |
|
305 | 283 | } |
|
306 | return $result; |
||
307 | } |
||
308 | 288 | ||
309 | public static function getPluginsDirectory() |
||
310 | 288 | { |
|
311 | return PIWIK_INCLUDE_PATH . '/plugins/'; |
||
312 | } |
||
313 | |||
314 | /** |
||
315 | * Deactivate plugin |
||
316 | * |
||
317 | * @param string $pluginName Name of plugin |
||
318 | */ |
||
319 | public function deactivatePlugin($pluginName) |
||
320 | { |
||
321 | $this->clearCache($pluginName); |
||
322 | |||
323 | // execute deactivate() to let the plugin do cleanups |
||
324 | $this->executePluginDeactivate($pluginName); |
||
325 | |||
326 | $this->unloadPluginFromMemory($pluginName); |
||
327 | |||
328 | $this->removePluginFromConfig($pluginName); |
||
329 | |||
330 | /** |
||
331 | * Event triggered after a plugin has been deactivated. |
||
332 | * |
||
333 | * @param string $pluginName The plugin that has been deactivated. |
||
334 | */ |
||
335 | Piwik::postEvent('PluginManager.pluginDeactivated', array($pluginName)); |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * Tries to find the given components such as a Menu or Tasks implemented by plugins. |
||
340 | * This method won't cache the found components. If you need to find the same component multiple times you might |
||
341 | * want to cache the result to save a tiny bit of time. |
||
342 | * |
||
343 | * @param string $componentName The name of the component you want to look for. In case you request a |
||
344 | * component named 'Menu' it'll look for a file named 'Menu.php' within the |
||
345 | * root of all plugin folders that implement a class named |
||
346 | * Piwik\Plugin\$PluginName\Menu. |
||
347 | * @param string $expectedSubclass If not empty, a check will be performed whether a found file extends the |
||
348 | * given subclass. If the requested file exists but does not extend this class |
||
349 | * a warning will be shown to advice a developer to extend this certain class. |
||
350 | * |
||
351 | * @return \stdClass[] |
||
352 | */ |
||
353 | View Code Duplication | public function findComponents($componentName, $expectedSubclass) |
|
354 | { |
||
355 | $plugins = $this->getPluginsLoadedAndActivated(); |
||
356 | $components = array(); |
||
357 | |||
358 | foreach ($plugins as $plugin) { |
||
359 | $component = $plugin->findComponent($componentName, $expectedSubclass); |
||
360 | |||
361 | if (!empty($component)) { |
||
362 | $components[] = $component; |
||
363 | } |
||
364 | } |
||
365 | |||
366 | return $components; |
||
367 | } |
||
368 | 28 | ||
369 | View Code Duplication | public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass) |
|
370 | 28 | { |
|
371 | 28 | $plugins = $this->getPluginsLoadedAndActivated(); |
|
372 | $found = array(); |
||
373 | 28 | ||
374 | 28 | foreach ($plugins as $plugin) { |
|
375 | $components = $plugin->findMultipleComponents($directoryWithinPlugin, $expectedSubclass); |
||
376 | 28 | ||
377 | 28 | if (!empty($components)) { |
|
378 | 28 | $found = array_merge($found, $components); |
|
379 | 28 | } |
|
380 | } |
||
381 | 28 | ||
382 | return $found; |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Uninstalls a Plugin (deletes plugin files from the disk) |
||
387 | * Only deactivated plugins can be uninstalled |
||
388 | * |
||
389 | * @param $pluginName |
||
390 | * @throws \Exception |
||
391 | * @return bool |
||
392 | */ |
||
393 | public function uninstallPlugin($pluginName) |
||
394 | { |
||
395 | if ($this->isPluginLoaded($pluginName)) { |
||
396 | throw new \Exception("To uninstall the plugin $pluginName, first disable it in Piwik > Settings > Plugins"); |
||
397 | } |
||
398 | $this->loadAllPluginsAndGetTheirInfo(); |
||
399 | |||
400 | SettingsStorage\Backend\PluginSettingsTable::removeAllSettingsForPlugin($pluginName); |
||
401 | SettingsStorage\Backend\MeasurableSettingsTable::removeAllSettingsForPlugin($pluginName); |
||
402 | |||
403 | $this->executePluginDeactivate($pluginName); |
||
404 | $this->executePluginUninstall($pluginName); |
||
405 | |||
406 | $this->removePluginFromPluginsInstalledConfig($pluginName); |
||
407 | |||
408 | $this->unloadPluginFromMemory($pluginName); |
||
409 | |||
410 | $this->removePluginFromConfig($pluginName); |
||
411 | $this->removeInstalledVersionFromOptionTable($pluginName); |
||
412 | $this->clearCache($pluginName); |
||
413 | |||
414 | self::deletePluginFromFilesystem($pluginName); |
||
415 | if ($this->isPluginInFilesystem($pluginName)) { |
||
416 | return false; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Event triggered after a plugin has been uninstalled. |
||
421 | * |
||
422 | * @param string $pluginName The plugin that has been uninstalled. |
||
423 | */ |
||
424 | Piwik::postEvent('PluginManager.pluginUninstalled', array($pluginName)); |
||
425 | |||
426 | return true; |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * @param string $pluginName |
||
431 | */ |
||
432 | private function clearCache($pluginName) |
||
437 | |||
438 | public static function deletePluginFromFilesystem($plugin) |
||
442 | |||
443 | /** |
||
444 | * Install loaded plugins |
||
445 | * |
||
446 | * @throws |
||
447 | * @return array Error messages of plugin install fails |
||
448 | */ |
||
449 | public function installLoadedPlugins() |
||
457 | |||
458 | /** |
||
459 | * Activate the specified plugin and install (if needed) |
||
460 | * |
||
461 | * @param string $pluginName Name of plugin |
||
462 | * @throws \Exception |
||
463 | */ |
||
464 | public function activatePlugin($pluginName) |
||
501 | |||
502 | public function isPluginInFilesystem($pluginName) |
||
509 | 1 | ||
510 | /** |
||
511 | 1 | * Returns the currently enabled theme. |
|
512 | 1 | * |
|
513 | * If no theme is enabled, the **Morpheus** plugin is returned (this is the base and default theme). |
||
514 | 1 | * |
|
515 | 1 | * @return Plugin |
|
516 | 1 | * @api |
|
517 | 1 | */ |
|
518 | public function getThemeEnabled() |
||
519 | { |
||
520 | 1 | $plugins = $this->getLoadedPlugins(); |
|
521 | 1 | ||
522 | 1 | $theme = false; |
|
523 | 1 | foreach ($plugins as $plugin) { |
|
524 | /* @var $plugin Plugin */ |
||
525 | if ($plugin->isTheme() |
||
526 | && $this->isPluginActivated($plugin->getPluginName()) |
||
527 | ) { |
||
528 | if ($plugin->getPluginName() != self::DEFAULT_THEME) { |
||
529 | return $plugin; // enabled theme (not default) |
||
530 | } |
||
531 | $theme = $plugin; // default theme |
||
532 | } |
||
533 | } |
||
534 | return $theme; |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * @param string $themeName |
||
539 | * @throws \Exception |
||
540 | * @return Theme |
||
541 | */ |
||
542 | public function getTheme($themeName) |
||
543 | { |
||
544 | $plugins = $this->getLoadedPlugins(); |
||
545 | |||
546 | foreach ($plugins as $plugin) { |
||
547 | if ($plugin->isTheme() && $plugin->getPluginName() == $themeName) { |
||
548 | return new Theme($plugin); |
||
549 | } |
||
550 | } |
||
551 | throw new \Exception('Theme not found : ' . $themeName); |
||
552 | } |
||
553 | |||
554 | public function getNumberOfActivatedPluginsExcludingAlwaysActivated() |
||
568 | |||
569 | /** |
||
570 | * Returns info regarding all plugins. Loads plugins that can be loaded. |
||
571 | * |
||
572 | * @return array An array that maps plugin names with arrays of plugin information. Plugin |
||
573 | * information consists of the following entries: |
||
574 | * |
||
575 | * - **activated**: Whether the plugin is activated. |
||
576 | * - **alwaysActivated**: Whether the plugin should always be activated, |
||
577 | * or not. |
||
578 | * - **uninstallable**: Whether the plugin is uninstallable or not. |
||
579 | * - **invalid**: If the plugin is invalid, this property will be set to true. |
||
580 | * If the plugin is not invalid, this property will not exist. |
||
581 | * - **info**: If the plugin was loaded, will hold the plugin information. |
||
582 | * See {@link Piwik\Plugin::getInformation()}. |
||
583 | * @api |
||
584 | */ |
||
585 | public function loadAllPluginsAndGetTheirInfo() |
||
586 | { |
||
587 | /** @var Translator $translator */ |
||
588 | $translator = StaticContainer::get('Piwik\Translation\Translator'); |
||
589 | |||
590 | $plugins = array(); |
||
591 | |||
592 | $listPlugins = array_merge( |
||
593 | $this->readPluginsDirectory(), |
||
594 | $this->pluginList->getActivatedPlugins() |
||
595 | ); |
||
596 | $listPlugins = array_unique($listPlugins); |
||
597 | foreach ($listPlugins as $pluginName) { |
||
598 | // Hide plugins that are never going to be used |
||
599 | if ($this->isPluginBogus($pluginName)) { |
||
600 | continue; |
||
601 | } |
||
602 | |||
603 | // If the plugin is not core and looks bogus, do not load |
||
604 | if ($this->isPluginThirdPartyAndBogus($pluginName)) { |
||
605 | $info = array( |
||
606 | 'invalid' => true, |
||
607 | 'activated' => false, |
||
608 | 'alwaysActivated' => false, |
||
609 | 'uninstallable' => true, |
||
610 | ); |
||
611 | } else { |
||
612 | $translator->addDirectory(self::getPluginsDirectory() . $pluginName . '/lang'); |
||
613 | $this->loadPlugin($pluginName); |
||
614 | $info = array( |
||
615 | 'activated' => $this->isPluginActivated($pluginName), |
||
616 | 'alwaysActivated' => $this->isPluginAlwaysActivated($pluginName), |
||
617 | 'uninstallable' => $this->isPluginUninstallable($pluginName), |
||
618 | ); |
||
619 | } |
||
620 | |||
621 | $plugins[$pluginName] = $info; |
||
622 | } |
||
623 | |||
624 | $loadedPlugins = $this->getLoadedPlugins(); |
||
625 | foreach ($loadedPlugins as $oPlugin) { |
||
626 | $pluginName = $oPlugin->getPluginName(); |
||
627 | |||
628 | $info = array( |
||
629 | 283 | 'info' => $oPlugin->getInformation(), |
|
630 | 'activated' => $this->isPluginActivated($pluginName), |
||
631 | 283 | 'alwaysActivated' => $this->isPluginAlwaysActivated($pluginName), |
|
632 | 'missingRequirements' => $oPlugin->getMissingDependenciesAsString(), |
||
633 | 'uninstallable' => $this->isPluginUninstallable($pluginName), |
||
634 | ); |
||
635 | $plugins[$pluginName] = $info; |
||
636 | } |
||
637 | |||
638 | return $plugins; |
||
639 | } |
||
640 | 88 | ||
641 | protected static function isManifestFileFound($path) |
||
645 | |||
646 | /** |
||
647 | 3 | * Returns `true` if the plugin is bundled with core or `false` if it is third party. |
|
648 | * |
||
649 | 3 | * @param string $name The name of the plugin, eg, `'Actions'`. |
|
650 | 3 | * @return bool |
|
651 | */ |
||
652 | public function isPluginBundledWithCore($name) |
||
653 | { |
||
654 | return $this->isPluginEnabledByDefault($name) |
||
655 | || in_array($name, $this->pluginList->getCorePluginsDisabledByDefault()) |
||
656 | || $name == self::DEFAULT_THEME; |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * @param $pluginName |
||
661 | * @return bool |
||
662 | * @ignore |
||
663 | */ |
||
664 | public function isPluginThirdPartyAndBogus($pluginName) |
||
679 | |||
680 | /** |
||
681 | * Load AND activates the specified plugins. It will also overwrite all previously loaded plugins, so it acts |
||
682 | * as a setter. |
||
683 | * |
||
684 | * @param array $pluginsToLoad Array of plugins to load. |
||
685 | */ |
||
686 | public function loadPlugins(array $pluginsToLoad) |
||
692 | |||
693 | /** |
||
694 | * Disable plugin loading. |
||
695 | */ |
||
696 | public function doNotLoadPlugins() |
||
700 | |||
701 | /** |
||
702 | * Disable loading of "always activated" plugins. |
||
703 | */ |
||
704 | public function doNotLoadAlwaysActivatedPlugins() |
||
708 | 1 | ||
709 | /** |
||
710 | 1 | * Execute postLoad() hook for loaded plugins |
|
711 | */ |
||
712 | public function postLoadPlugins() |
||
719 | |||
720 | /** |
||
721 | * Returns an array containing the plugins class names (eg. 'UserCountry' and NOT 'UserCountry') |
||
722 | * |
||
723 | 29 | * @return array |
|
724 | */ |
||
725 | 29 | public function getLoadedPluginsName() |
|
729 | |||
730 | /** |
||
731 | * Returns an array mapping loaded plugin names with their plugin objects, eg, |
||
732 | * |
||
733 | * array( |
||
734 | * 'UserCountry' => Plugin $pluginObject, |
||
735 | * 'UserLanguage' => Plugin $pluginObject, |
||
736 | * ); |
||
737 | * |
||
738 | * @return Plugin[] |
||
739 | */ |
||
740 | public function getLoadedPlugins() |
||
744 | |||
745 | /** |
||
746 | * @param string $piwikVersion |
||
747 | * @return Plugin[] |
||
748 | */ |
||
749 | public function getIncompatiblePlugins($piwikVersion) |
||
762 | 121 | ||
763 | 93 | /** |
|
764 | * Returns an array of plugins that are currently loaded and activated, |
||
765 | * mapping loaded plugin names with their plugin objects, eg, |
||
766 | 28 | * |
|
767 | 28 | * array( |
|
768 | 28 | * 'UserCountry' => Plugin $pluginObject, |
|
769 | * 'UserLanguage' => Plugin $pluginObject, |
||
770 | 28 | * ); |
|
771 | 28 | * |
|
772 | * @return Plugin[] |
||
773 | 30 | */ |
|
774 | public function getPluginsLoadedAndActivated() |
||
775 | { |
||
776 | if (is_null($this->pluginsLoadedAndActivated)) { |
||
777 | $enabled = $this->getActivatedPlugins(); |
||
778 | |||
779 | if (empty($enabled)) { |
||
780 | return array(); |
||
781 | } |
||
782 | |||
783 | $plugins = $this->getLoadedPlugins(); |
||
784 | $enabled = array_combine($enabled, $enabled); |
||
785 | $plugins = array_intersect_key($plugins, $enabled); |
||
786 | 121 | ||
787 | $this->pluginsLoadedAndActivated = $plugins; |
||
788 | 121 | } |
|
789 | |||
790 | return $this->pluginsLoadedAndActivated; |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Returns a list of all names of currently activated plugin eg, |
||
795 | * |
||
796 | * array( |
||
797 | * 'UserCountry' |
||
798 | * 'UserLanguage' |
||
799 | * ); |
||
800 | * |
||
801 | * @return string[] |
||
802 | */ |
||
803 | public function getActivatedPlugins() |
||
807 | 5 | ||
808 | public function getActivatedPluginsFromConfig() |
||
809 | { |
||
810 | 5 | $plugins = $this->pluginList->getActivatedPlugins(); |
|
811 | |||
812 | return $this->makePluginsToLoad($plugins); |
||
813 | } |
||
814 | |||
815 | /** |
||
816 | * Returns a Plugin object by name. |
||
817 | 36 | * |
|
818 | * @param string $name The name of the plugin, eg, `'Actions'`. |
||
819 | 36 | * @throws \Exception If the plugin has not been loaded. |
|
820 | 36 | * @return Plugin |
|
821 | 36 | */ |
|
822 | 36 | public function getLoadedPlugin($name) |
|
823 | 36 | { |
|
824 | 3 | if (!isset($this->loadedPlugins[$name]) || is_null($this->loadedPlugins[$name])) { |
|
825 | 3 | throw new \Exception("The plugin '$name' has not been loaded."); |
|
826 | } |
||
827 | return $this->loadedPlugins[$name]; |
||
828 | } |
||
829 | 3 | ||
830 | /** |
||
831 | * Load the plugins classes installed. |
||
832 | * Register the observers for every plugin. |
||
833 | */ |
||
834 | 3 | private function reloadActivatedPlugins() |
|
846 | |||
847 | private function reloadActivatedPlugin($pluginName, $pluginsToPostPendingEventsTo) |
||
848 | { |
||
849 | if ($this->isPluginLoaded($pluginName) || $this->isPluginThirdPartyAndBogus($pluginName)) { |
||
850 | return $pluginsToPostPendingEventsTo; |
||
851 | } |
||
852 | |||
853 | $newPlugin = $this->loadPlugin($pluginName); |
||
854 | |||
855 | if ($newPlugin === null) { |
||
856 | return $pluginsToPostPendingEventsTo; |
||
857 | } |
||
858 | |||
859 | $requirements = $newPlugin->getMissingDependencies(); |
||
860 | |||
861 | 192 | if (!empty($requirements)) { |
|
862 | foreach ($requirements as $requirement) { |
||
863 | 192 | $possiblePluginName = $requirement['requirement']; |
|
864 | if (in_array($possiblePluginName, $this->pluginsToLoad, $strict = true)) { |
||
865 | 192 | $pluginsToPostPendingEventsTo = $this->reloadActivatedPlugin($possiblePluginName, $pluginsToPostPendingEventsTo); |
|
866 | 192 | } |
|
867 | 192 | } |
|
868 | 192 | } |
|
869 | 192 | ||
870 | 192 | if ($newPlugin->hasMissingDependencies()) { |
|
871 | $this->unloadPluginFromMemory($pluginName); |
||
872 | |||
873 | // at this state we do not know yet whether current user has super user access. We do not even know |
||
874 | // if someone is actually logged in. |
||
875 | $message = Piwik::translate('CorePluginsAdmin_WeCouldNotLoadThePluginAsItHasMissingDependencies', array($pluginName, $newPlugin->getMissingDependenciesAsString())); |
||
876 | $message .= ' '; |
||
877 | $message .= Piwik::translate('General_PleaseContactYourPiwikAdministrator'); |
||
878 | |||
879 | $notification = new Notification($message); |
||
880 | $notification->context = Notification::CONTEXT_ERROR; |
||
881 | 5 | Notification\Manager::notify('PluginManager_PluginUnloaded', $notification); |
|
882 | return $pluginsToPostPendingEventsTo; |
||
883 | 5 | } |
|
884 | 2 | ||
885 | $pluginsToPostPendingEventsTo[] = $newPlugin; |
||
886 | 3 | ||
887 | return $pluginsToPostPendingEventsTo; |
||
888 | 3 | } |
|
889 | 3 | ||
890 | public function getIgnoredBogusPlugins() |
||
891 | { |
||
892 | 3 | $ignored = array(); |
|
893 | foreach ($this->pluginsToLoad as $pluginName) { |
||
894 | 3 | if ($this->isPluginThirdPartyAndBogus($pluginName)) { |
|
895 | $ignored[] = $pluginName; |
||
896 | } |
||
897 | } |
||
898 | return $ignored; |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | 3 | * Returns the name of all plugins found in this Piwik instance |
|
903 | * (including those not enabled and themes) |
||
904 | 3 | * |
|
905 | 3 | * @return array |
|
906 | */ |
||
907 | 3 | public static function getAllPluginsNames() |
|
918 | |||
919 | 3 | /** |
|
920 | * Loads the plugin filename and instantiates the plugin with the given name, eg. UserCountry. |
||
921 | 3 | * Contrary to loadPlugins() it does not activate the plugin, it only loads it. |
|
922 | 3 | * |
|
923 | * @param string $pluginName |
||
924 | * @throws \Exception |
||
925 | 3 | * @return Plugin|null |
|
926 | */ |
||
927 | 3 | public function loadPlugin($pluginName) |
|
928 | { |
||
929 | if (isset($this->loadedPlugins[$pluginName])) { |
||
930 | 3 | return $this->loadedPlugins[$pluginName]; |
|
931 | } |
||
932 | $newPlugin = $this->makePluginClass($pluginName); |
||
933 | 3 | ||
934 | $this->addLoadedPlugin($pluginName, $newPlugin); |
||
935 | 3 | return $newPlugin; |
|
936 | 3 | } |
|
937 | 1 | ||
938 | 1 | public function isValidPluginName($pluginName) |
|
939 | 3 | { |
|
940 | return (bool) preg_match('/^[a-zA-Z]([a-zA-Z0-9_]*)$/D', $pluginName); |
||
941 | } |
||
942 | 36 | ||
943 | /** |
||
944 | 36 | * @param $pluginName |
|
945 | 36 | * @return Plugin |
|
946 | * @throws \Exception |
||
947 | */ |
||
948 | protected function makePluginClass($pluginName) |
||
949 | { |
||
950 | $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName); |
||
951 | $pluginClassName = $pluginName; |
||
952 | |||
953 | if (!$this->isValidPluginName($pluginName)) { |
||
954 | throw new \Exception(sprintf("The plugin filename '%s' is not a valid plugin name", $pluginFileName)); |
||
955 | } |
||
956 | |||
957 | $path = self::getPluginsDirectory() . $pluginFileName; |
||
958 | |||
959 | if (!file_exists($path)) { |
||
960 | // Create the smallest minimal Piwik Plugin |
||
961 | // Eg. Used for Morpheus default theme which does not have a Morpheus.php file |
||
962 | return new Plugin($pluginName); |
||
963 | } |
||
964 | |||
965 | require_once $path; |
||
966 | |||
967 | $namespacedClass = $this->getClassNamePlugin($pluginName); |
||
968 | if (!class_exists($namespacedClass, false)) { |
||
969 | throw new \Exception("The class $pluginClassName couldn't be found in the file '$path'"); |
||
970 | } |
||
971 | $newPlugin = new $namespacedClass; |
||
972 | |||
973 | if (!($newPlugin instanceof Plugin)) { |
||
974 | throw new \Exception("The plugin $pluginClassName in the file $path must inherit from Plugin."); |
||
975 | } |
||
976 | return $newPlugin; |
||
977 | } |
||
978 | |||
979 | protected function getClassNamePlugin($pluginName) |
||
980 | { |
||
981 | $className = $pluginName; |
||
982 | if ($pluginName == 'API') { |
||
983 | $className = 'Plugin'; |
||
984 | } |
||
985 | return "\\Piwik\\Plugins\\$pluginName\\$className"; |
||
986 | } |
||
987 | |||
988 | private function resetTransientCache() |
||
992 | |||
993 | /** |
||
994 | * Unload plugin |
||
995 | * |
||
996 | * @param Plugin|string $plugin |
||
997 | * @throws \Exception |
||
998 | */ |
||
999 | public function unloadPlugin($plugin) |
||
1015 | |||
1016 | /** |
||
1017 | * Unload all loaded plugins |
||
1018 | */ |
||
1019 | public function unloadPlugins() |
||
1020 | { |
||
1021 | $this->resetTransientCache(); |
||
1022 | |||
1023 | $pluginsLoaded = $this->getLoadedPlugins(); |
||
1024 | foreach ($pluginsLoaded as $plugin) { |
||
1025 | $this->unloadPlugin($plugin); |
||
1026 | } |
||
1027 | } |
||
1028 | |||
1029 | /** |
||
1030 | * Install a specific plugin |
||
1031 | * |
||
1032 | * @param Plugin $plugin |
||
1033 | * @throws \Piwik\Plugin\PluginException if installation fails |
||
1034 | */ |
||
1035 | private function executePluginInstall(Plugin $plugin) |
||
1036 | { |
||
1037 | try { |
||
1038 | $plugin->install(); |
||
1039 | } catch (\Exception $e) { |
||
1040 | throw new \Piwik\Plugin\PluginException($plugin->getPluginName(), $e->getMessage()); |
||
1041 | } |
||
1042 | } |
||
1043 | |||
1044 | /** |
||
1045 | * Add a plugin in the loaded plugins array |
||
1046 | * |
||
1047 | * @param string $pluginName plugin name without prefix (eg. 'UserCountry') |
||
1048 | * @param Plugin $newPlugin |
||
1049 | * @internal |
||
1050 | */ |
||
1051 | public function addLoadedPlugin($pluginName, Plugin $newPlugin) |
||
1052 | { |
||
1053 | $this->resetTransientCache(); |
||
1054 | |||
1055 | $this->loadedPlugins[$pluginName] = $newPlugin; |
||
1056 | } |
||
1057 | |||
1058 | /** |
||
1059 | * Return names of all installed plugins. |
||
1060 | * |
||
1061 | * @return array |
||
1062 | * @api |
||
1063 | */ |
||
1064 | public function getInstalledPluginsName() |
||
1069 | |||
1070 | /** |
||
1071 | * Returns names of plugins that should be loaded, but cannot be since their |
||
1072 | * files cannot be found. |
||
1073 | * |
||
1074 | * @return array |
||
1075 | * @api |
||
1076 | */ |
||
1077 | public function getMissingPlugins() |
||
1092 | |||
1093 | /** |
||
1094 | * Install a plugin, if necessary |
||
1095 | * |
||
1096 | * @param Plugin $plugin |
||
1097 | */ |
||
1098 | private function installPluginIfNecessary(Plugin $plugin) |
||
1129 | |||
1130 | public function isTrackerPlugin(Plugin $plugin) |
||
1131 | { |
||
1132 | if (!$this->isPluginInstalled($plugin->getPluginName())) { |
||
1133 | return false; |
||
1134 | } |
||
1135 | |||
1136 | if ($plugin->isTrackerPlugin()) { |
||
1137 | return true; |
||
1138 | } |
||
1139 | |||
1140 | $dimensions = VisitDimension::getDimensions($plugin); |
||
1141 | if (!empty($dimensions)) { |
||
1142 | return true; |
||
1143 | } |
||
1144 | |||
1145 | $dimensions = ActionDimension::getDimensions($plugin); |
||
1146 | if (!empty($dimensions)) { |
||
1147 | return true; |
||
1148 | } |
||
1149 | |||
1150 | $hooks = $plugin->getListHooksRegistered(); |
||
1151 | $hookNames = array_keys($hooks); |
||
1152 | foreach ($hookNames as $name) { |
||
1153 | if (strpos($name, self::TRACKER_EVENT_PREFIX) === 0) { |
||
1154 | return true; |
||
1155 | } |
||
1156 | if ($name === 'Request.initAuthenticationObject') { |
||
1157 | return true; |
||
1158 | } |
||
1159 | } |
||
1160 | |||
1161 | $dimensions = ConversionDimension::getDimensions($plugin); |
||
1162 | if (!empty($dimensions)) { |
||
1163 | return true; |
||
1164 | } |
||
1165 | |||
1166 | return false; |
||
1167 | } |
||
1168 | |||
1169 | private static function pluginStructureLooksValid($path) |
||
1175 | |||
1176 | /** |
||
1177 | * @param $pluginName |
||
1178 | */ |
||
1179 | private function removePluginFromPluginsInstalledConfig($pluginName) |
||
1180 | { |
||
1181 | $pluginsInstalled = Config::getInstance()->PluginsInstalled['PluginsInstalled']; |
||
1182 | $key = array_search($pluginName, $pluginsInstalled); |
||
1183 | if ($key !== false) { |
||
1184 | unset($pluginsInstalled[$key]); |
||
1185 | } |
||
1186 | |||
1187 | $this->updatePluginsInstalledConfig($pluginsInstalled); |
||
1188 | } |
||
1189 | |||
1190 | /** |
||
1191 | * @param $pluginName |
||
1192 | */ |
||
1193 | private function removePluginFromPluginsConfig($pluginName) |
||
1194 | { |
||
1195 | $pluginsEnabled = $this->pluginList->getActivatedPlugins(); |
||
1196 | $key = array_search($pluginName, $pluginsEnabled); |
||
1197 | if ($key !== false) { |
||
1198 | unset($pluginsEnabled[$key]); |
||
1199 | } |
||
1200 | $this->updatePluginsConfig($pluginsEnabled); |
||
1201 | } |
||
1202 | |||
1203 | /** |
||
1204 | * @param $pluginName |
||
1205 | * @return bool |
||
1206 | */ |
||
1207 | private function isPluginBogus($pluginName) |
||
1216 | |||
1217 | private function deactivateThemeIfTheme($pluginName) |
||
1218 | { |
||
1219 | // Only one theme enabled at a time |
||
1220 | $themeEnabled = $this->getThemeEnabled(); |
||
1221 | if ($themeEnabled |
||
1222 | && $themeEnabled->getPluginName() != self::DEFAULT_THEME) { |
||
1223 | $themeAlreadyEnabled = $themeEnabled->getPluginName(); |
||
1224 | |||
1225 | $plugin = $this->loadPlugin($pluginName); |
||
1226 | if ($plugin->isTheme()) { |
||
1227 | $this->deactivatePlugin($themeAlreadyEnabled); |
||
1228 | } |
||
1229 | } |
||
1230 | } |
||
1231 | |||
1232 | /** |
||
1233 | * @param $pluginName |
||
1234 | */ |
||
1235 | private function executePluginDeactivate($pluginName) |
||
1244 | |||
1245 | /** |
||
1246 | * @param $pluginName |
||
1247 | */ |
||
1248 | private function unloadPluginFromMemory($pluginName) |
||
1249 | { |
||
1250 | $this->unloadPlugin($pluginName); |
||
1251 | |||
1252 | $key = array_search($pluginName, $this->pluginsToLoad); |
||
1253 | if ($key !== false) { |
||
1254 | unset($this->pluginsToLoad[$key]); |
||
1255 | } |
||
1256 | } |
||
1257 | |||
1258 | /** |
||
1259 | * @param $pluginName |
||
1260 | */ |
||
1261 | private function removePluginFromConfig($pluginName) |
||
1266 | |||
1267 | /** |
||
1268 | * @param $pluginName |
||
1269 | */ |
||
1270 | private function executePluginUninstall($pluginName) |
||
1271 | { |
||
1272 | try { |
||
1273 | $plugin = $this->getLoadedPlugin($pluginName); |
||
1274 | $plugin->uninstall(); |
||
1275 | } catch (\Exception $e) { |
||
1276 | } |
||
1277 | |||
1278 | if (empty($plugin)) { |
||
1279 | return; |
||
1280 | } |
||
1281 | |||
1282 | try { |
||
1283 | $visitDimensions = VisitDimension::getAllDimensions(); |
||
1284 | |||
1285 | foreach (VisitDimension::getDimensions($plugin) as $dimension) { |
||
1286 | $this->uninstallDimension(VisitDimension::INSTALLER_PREFIX, $dimension, $visitDimensions); |
||
1287 | } |
||
1288 | } catch (\Exception $e) { |
||
1289 | } |
||
1290 | |||
1291 | try { |
||
1292 | $actionDimensions = ActionDimension::getAllDimensions(); |
||
1293 | |||
1294 | foreach (ActionDimension::getDimensions($plugin) as $dimension) { |
||
1295 | $this->uninstallDimension(ActionDimension::INSTALLER_PREFIX, $dimension, $actionDimensions); |
||
1296 | } |
||
1297 | } catch (\Exception $e) { |
||
1298 | } |
||
1299 | |||
1300 | try { |
||
1301 | $conversionDimensions = ConversionDimension::getAllDimensions(); |
||
1302 | |||
1303 | foreach (ConversionDimension::getDimensions($plugin) as $dimension) { |
||
1304 | $this->uninstallDimension(ConversionDimension::INSTALLER_PREFIX, $dimension, $conversionDimensions); |
||
1305 | } |
||
1306 | } catch (\Exception $e) { |
||
1307 | } |
||
1308 | } |
||
1309 | |||
1310 | /** |
||
1311 | * @param VisitDimension|ActionDimension|ConversionDimension $dimension |
||
1312 | * @param VisitDimension[]|ActionDimension[]|ConversionDimension[] $allDimensions |
||
1313 | * @return bool |
||
1314 | */ |
||
1315 | private function doesAnotherPluginDefineSameColumnWithDbEntry($dimension, $allDimensions) |
||
1316 | { |
||
1317 | $module = $dimension->getModule(); |
||
1318 | $columnName = $dimension->getColumnName(); |
||
1319 | |||
1320 | foreach ($allDimensions as $dim) { |
||
1321 | 88 | if ($dim->getColumnName() === $columnName && |
|
1322 | $dim->hasColumnType() && |
||
1323 | 88 | $dim->getModule() !== $module) { |
|
1324 | return true; |
||
1325 | } |
||
1326 | } |
||
1327 | |||
1328 | return false; |
||
1329 | } |
||
1330 | 88 | ||
1331 | /** |
||
1332 | 88 | * @param string $prefix column installer prefix |
|
1333 | 88 | * @param ConversionDimension|VisitDimension|ActionDimension $dimension |
|
1334 | * @param VisitDimension[]|ActionDimension[]|ConversionDimension[] $allDimensions |
||
1335 | */ |
||
1336 | 88 | private function uninstallDimension($prefix, Dimension $dimension, $allDimensions) |
|
1337 | { |
||
1338 | if (!$this->doesAnotherPluginDefineSameColumnWithDbEntry($dimension, $allDimensions)) { |
||
1339 | $dimension->uninstall(); |
||
1340 | |||
1341 | $this->removeInstalledVersionFromOptionTable($prefix . $dimension->getColumnName()); |
||
1342 | } |
||
1343 | 36 | } |
|
1344 | |||
1345 | 36 | /** |
|
1346 | 36 | * @param $pluginName |
|
1347 | 36 | * @return bool |
|
1348 | 36 | */ |
|
1349 | 36 | public function isPluginInstalled($pluginName) |
|
1350 | 36 | { |
|
1351 | 36 | $pluginsInstalled = $this->getInstalledPluginsName(); |
|
1352 | return in_array($pluginName, $pluginsInstalled); |
||
1353 | } |
||
1354 | 192 | ||
1355 | private function removeInstalledVersionFromOptionTable($name) |
||
1360 | 192 | ||
1361 | 192 | private function makeSureOnlyActivatedPluginsAreLoaded() |
|
1362 | { |
||
1363 | foreach ($this->getLoadedPlugins() as $pluginName => $plugin) { |
||
1364 | if (!in_array($pluginName, $this->pluginsToLoad)) { |
||
1365 | $this->unloadPlugin($plugin); |
||
1366 | } |
||
1367 | } |
||
1368 | } |
||
1369 | |||
1370 | /** |
||
1371 | * Reading the plugins from the global.ini.php config file |
||
1372 | * |
||
1373 | * @return array |
||
1374 | */ |
||
1375 | protected function getPluginsFromGlobalIniConfigFile() |
||
1379 | |||
1380 | /** |
||
1381 | * @param $name |
||
1382 | * @return bool |
||
1383 | */ |
||
1384 | protected function isPluginEnabledByDefault($name) |
||
1392 | |||
1393 | /** |
||
1394 | * @param array $pluginsToLoad |
||
1395 | * @return array |
||
1396 | */ |
||
1397 | private function makePluginsToLoad(array $pluginsToLoad) |
||
1407 | |||
1408 | public function loadPluginTranslations() |
||
1416 | } |
||
1417 |
Since your code implements the magic setter
_set
, this function will be called for any write access on an undefined variable. You can add the@property
annotation to your class or interface to document the existence of this variable.Since the property has write access only, you can use the @property-write annotation instead.
Of course, you may also just have mistyped another name, in which case you should fix the error.
See also the PhpDoc documentation for @property.