PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.8.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.8.2
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Plugin.php
matomo / app / core Last commit date
API 3 months ago Access 3 months ago Application 3 months ago Archive 3 months ago ArchiveProcessor 3 months ago Archiver 2 years ago AssetManager 3 months ago Auth 7 months ago Category 7 months ago Changes 3 months ago CliMulti 1 year ago Columns 3 months ago Concurrency 3 months ago Config 3 months ago Container 3 months ago CronArchive 3 months ago DataAccess 3 months ago DataFiles 2 years ago DataTable 3 months ago Db 3 months ago DeviceDetector 1 year ago Email 2 years ago Exception 4 months ago Http 4 months ago Intl 3 months ago Log 2 years ago Mail 1 year ago Measurable 7 months ago Menu 3 months ago Metrics 3 months ago Notification 7 months ago Period 3 months ago Plugin 3 months ago Policy 3 months ago ProfessionalServices 1 year ago Report 1 year ago ReportRenderer 3 months ago Request 3 months ago Scheduler 3 months ago Segment 3 months ago Session 3 months ago Settings 3 months ago Tracker 3 months ago Translation 3 months ago Twig 1 year ago UpdateCheck 3 months ago Updater 4 months ago Updates 3 months ago Validators 1 year ago View 7 months ago ViewDataTable 3 months ago Visualization 1 year ago Widget 3 months ago .htaccess 2 years ago Access.php 3 months ago Archive.php 3 months ago ArchiveProcessor.php 4 months ago AssetManager.php 3 months ago Auth.php 7 months ago AuthResult.php 7 months ago BaseFactory.php 2 years ago Cache.php 2 years ago CacheId.php 4 months ago CliMulti.php 3 months ago Common.php 3 months ago Config.php 3 months ago Console.php 3 months ago Context.php 2 years ago Cookie.php 1 year ago CronArchive.php 3 months ago DI.php 3 months ago DataArray.php 5 months ago DataTable.php 3 months ago Date.php 3 months ago Db.php 3 months ago DbHelper.php 3 months ago Development.php 1 year ago ErrorHandler.php 7 months ago EventDispatcher.php 1 year ago ExceptionHandler.php 4 months ago FileIntegrity.php 3 months ago Filechecks.php 1 year ago Filesystem.php 3 months ago FrontController.php 4 months ago Http.php 4 months ago IP.php 1 year ago Log.php 3 months ago LogDeleter.php 1 year ago Mail.php 1 year ago Metrics.php 3 months ago NoAccessException.php 2 years ago Nonce.php 7 months ago Notification.php 7 months ago NumberFormatter.php 5 months ago Option.php 5 months ago Period.php 3 months ago Piwik.php 3 months ago Plugin.php 3 months ago Process.php 1 year ago Profiler.php 7 months ago ProxyHeaders.php 4 months ago ProxyHttp.php 5 months ago QuickForm2.php 3 months ago RankingQuery.php 5 months ago ReportRenderer.php 3 months ago Request.php 3 months ago Segment.php 3 months ago Sequence.php 7 months ago Session.php 3 months ago SettingsPiwik.php 3 months ago SettingsServer.php 1 year ago Singleton.php 2 years ago Site.php 4 months ago SiteContentDetector.php 3 months ago SupportedBrowser.php 2 years ago TCPDF.php 1 year ago Theme.php 1 year ago Timer.php 2 years ago Tracker.php 3 months ago Twig.php 3 months ago Unzip.php 1 year ago UpdateCheck.php 3 months ago Updater.php 3 months ago UpdaterErrorException.php 2 years ago Updates.php 3 months ago Url.php 3 months ago UrlHelper.php 3 months ago Version.php 3 months ago View.php 3 months ago bootstrap.php 1 year ago dispatch.php 2 years ago testMinimumPhpVersion.php 7 months ago
Plugin.php
623 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik;
10
11 use Piwik\Archive\ArchiveInvalidator;
12 use Piwik\Container\StaticContainer;
13 use Piwik\Plugin\Dependency;
14 use Piwik\Plugin\Manager;
15 use Piwik\Plugin\MetadataLoader;
16 if (!class_exists('Piwik\\Plugin')) {
17 /**
18 * Base class of all Plugin Descriptor classes.
19 *
20 * Any plugin that wants to add event observers to one of Piwik's {@hook # hooks},
21 * or has special installation/uninstallation logic must implement this class.
22 * Plugins that can specify everything they need to in the _plugin.json_ files,
23 * such as themes, don't need to implement this class.
24 *
25 * Class implementations should be named after the plugin they are a part of
26 * (eg, `class UserCountry extends Plugin`).
27 *
28 * ### Plugin Metadata
29 *
30 * In addition to providing a place for plugins to install/uninstall themselves
31 * and add event observers, this class is also responsible for loading metadata
32 * found in the plugin.json file.
33 *
34 * The plugin.json file must exist in the root directory of a plugin. It can
35 * contain the following information:
36 *
37 * - **description**: An internationalized string description of what the plugin
38 * does.
39 * - **homepage**: The URL to the plugin's website.
40 * - **authors**: A list of author arrays with keys for 'name', 'email' and 'homepage'
41 * - **license**: The license the code uses (eg, GPL, MIT, etc.).
42 * - **version**: The plugin version (eg, 1.0.1).
43 * - **theme**: `true` or `false`. If `true`, the plugin will be treated as a theme.
44 *
45 * ### Examples
46 *
47 * **How to extend**
48 *
49 * use Piwik\Common;
50 * use Piwik\Plugin;
51 * use Piwik\Db;
52 *
53 * class MyPlugin extends Plugin
54 * {
55 * public function registerEvents()
56 * {
57 * return array(
58 * 'API.getReportMetadata' => 'getReportMetadata',
59 * 'Another.event' => array(
60 * 'function' => 'myOtherPluginFunction',
61 * 'after' => true // executes this callback after others
62 * )
63 * );
64 * }
65 *
66 * public function install()
67 * {
68 * Db::exec("CREATE TABLE " . Common::prefixTable('mytable') . "...");
69 * }
70 *
71 * public function uninstall()
72 * {
73 * Db::exec("DROP TABLE IF EXISTS " . Common::prefixTable('mytable'));
74 * }
75 *
76 * public function getReportMetadata(&$metadata)
77 * {
78 * // ...
79 * }
80 *
81 * public function myOtherPluginFunction()
82 * {
83 * // ...
84 * }
85 * }
86 *
87 * @api
88 */
89 class Plugin
90 {
91 /**
92 * Name of this plugin.
93 *
94 * @var string
95 */
96 protected $pluginName;
97 /**
98 * Holds plugin metadata.
99 *
100 * @var array
101 */
102 private $pluginInformation;
103 /**
104 * As the cache is used quite often we avoid having to create instances all the time. We reuse it which is not
105 * perfect but efficient. If the cache is used we need to make sure to call setId() before usage as there
106 * is maybe a different key set since last usage.
107 *
108 * @var \Matomo\Cache\Eager
109 */
110 private $cache;
111 /**
112 * Constructor.
113 *
114 * @param string|bool $pluginName A plugin name to force. If not supplied, it is set
115 * to the last part of the class name.
116 * @throws \Exception If plugin metadata is defined in both the getInformation() method
117 * and the **plugin.json** file.
118 */
119 public function __construct($pluginName = \false)
120 {
121 if (empty($pluginName)) {
122 $pluginName = explode('\\', get_class($this));
123 $pluginName = end($pluginName);
124 }
125 $this->pluginName = $pluginName;
126 $cacheId = 'Plugin' . $pluginName . 'Metadata';
127 $cache = \Piwik\Cache::getEagerCache();
128 if ($cache->contains($cacheId)) {
129 $this->pluginInformation = $cache->fetch($cacheId);
130 } else {
131 $this->reloadPluginInformation();
132 $cache->save($cacheId, $this->pluginInformation);
133 }
134 }
135 public function reloadPluginInformation()
136 {
137 $metadataLoader = new MetadataLoader($this->pluginName);
138 $this->pluginInformation = $metadataLoader->load();
139 if ($this->hasDefinedPluginInformationInPluginClass() && $metadataLoader->hasPluginJson()) {
140 throw new \Exception('Plugin ' . $this->pluginName . ' has defined the method getInformation() and as well as having a plugin.json file. Please delete the getInformation() method from the plugin class. Alternatively, you may delete the plugin directory from plugins/' . $this->pluginName);
141 }
142 }
143 private function createCacheIfNeeded()
144 {
145 if (is_null($this->cache)) {
146 $this->cache = \Piwik\Cache::getEagerCache();
147 }
148 }
149 private function hasDefinedPluginInformationInPluginClass()
150 {
151 $myClassName = \Piwik\Plugin::class;
152 $pluginClassName = get_class($this);
153 if ($pluginClassName == $myClassName) {
154 // plugin has not defined its own class
155 return \false;
156 }
157 $foo = new \ReflectionMethod(get_class($this), 'getInformation');
158 $declaringClass = $foo->getDeclaringClass()->getName();
159 return $declaringClass != $myClassName;
160 }
161 /**
162 * Returns plugin information, including:
163 *
164 * - 'description' => string // 1-2 sentence description of the plugin
165 * - 'author' => string // plugin author
166 * - 'author_homepage' => string // author homepage URL (or email "mailto:youremail@example.org")
167 * - 'homepage' => string // plugin homepage URL
168 * - 'license' => string // plugin license
169 * - 'version' => string // plugin version number; examples and 3rd party plugins must not use Version::VERSION; 3rd party plugins must increment the version number with each plugin release
170 * - 'theme' => bool // Whether this plugin is a theme (a theme is a plugin, but a plugin is not necessarily a theme)
171 *
172 * @return array
173 */
174 public function getInformation()
175 {
176 return $this->pluginInformation;
177 }
178 public final function isPremiumFeature()
179 {
180 return !empty($this->pluginInformation['price']['base']);
181 }
182 /**
183 * Return true here if you want your plugin's Vue module to be loaded on demand, when it is first
184 * referenced, rather than on page load. This can be used to improve initial page load time,
185 * especially if your plugin includes a lot of Vue code.
186 *
187 * Note: doing this means that any other plugins that depend on yours will no longer
188 * be able to do a normal `import ... from 'MyPlugin';`, they will instead have to
189 * use the `importPluginUmd()` function in `CoreHome` which returns a Promise.
190 *
191 * @return boolean
192 */
193 public function shouldLoadUmdOnDemand()
194 {
195 return \false;
196 }
197 /**
198 * Returns a list of events with associated event observers.
199 *
200 * Derived classes should use this method to associate callbacks with events.
201 *
202 * @return array eg,
203 *
204 * array(
205 * 'API.getReportMetadata' => 'myPluginFunction',
206 * 'Another.event' => array(
207 * 'function' => 'myOtherPluginFunction',
208 * 'after' => true // execute after callbacks w/o ordering
209 * )
210 * 'Yet.Another.event' => array(
211 * 'function' => 'myOtherPluginFunction',
212 * 'before' => true // execute before callbacks w/o ordering
213 * )
214 * )
215 * @since 2.15.0
216 */
217 public function registerEvents()
218 {
219 return array();
220 }
221 /**
222 * This method is executed after a plugin is loaded and translations are registered.
223 * Useful for initialization code that uses translated strings.
224 *
225 * @return void
226 */
227 public function postLoad()
228 {
229 return;
230 }
231 /**
232 * Defines whether the whole plugin requires a working internet connection
233 * If set to true, the plugin will be automatically unloaded if `enable_internet_features` is 0,
234 * even if the plugin is activated
235 *
236 * @return bool
237 */
238 public function requiresInternetConnection()
239 {
240 return \false;
241 }
242 /**
243 * Installs the plugin. Derived classes should implement this class if the plugin
244 * needs to:
245 *
246 * - create tables
247 * - update existing tables
248 * - etc.
249 *
250 * @return void
251 * @throws \Exception if installation of fails for some reason.
252 */
253 public function install()
254 {
255 return;
256 }
257 /**
258 * Uninstalls the plugins. Derived classes should implement this method if the changes
259 * made in {@link install()} need to be undone during uninstallation.
260 *
261 * In most cases, if you have an {@link install()} method, you should provide
262 * an {@link uninstall()} method.
263 *
264 * @return void
265 * @throws \Exception if uninstallation of fails for some reason.
266 */
267 public function uninstall()
268 {
269 return;
270 }
271 /**
272 * Executed every time the plugin is enabled.
273 *
274 * @return void
275 */
276 public function activate()
277 {
278 return;
279 }
280 /**
281 * Executed every time the plugin is disabled.
282 *
283 * @return void
284 */
285 public function deactivate()
286 {
287 return;
288 }
289 /**
290 * Returns the plugin version number.
291 *
292 * @return string
293 */
294 public final function getVersion()
295 {
296 $info = $this->getInformation();
297 return $info['version'];
298 }
299 /**
300 * Returns `true` if this plugin is a theme, `false` if otherwise.
301 *
302 * @return bool
303 */
304 public function isTheme()
305 {
306 $info = $this->getInformation();
307 return !empty($info['theme']) && (bool) $info['theme'];
308 }
309 /**
310 * Returns the plugin's base class name without the namespace,
311 * e.g., `"UserCountry"` when the plugin class is `"Piwik\Plugins\UserCountry\UserCountry"`.
312 *
313 * @return string
314 */
315 public final function getPluginName()
316 {
317 return $this->pluginName;
318 }
319 /**
320 * Tries to find a component such as a Menu or Tasks within this plugin.
321 *
322 * @param string $componentName The name of the component you want to look for. In case you request a
323 * component named 'Menu' it'll look for a file named 'Menu.php' within the
324 * root of the plugin folder that implements a class named
325 * Piwik\Plugin\$PluginName\Menu . If such a file exists but does not implement
326 * this class it'll silently ignored.
327 * @param string $expectedSubclass If not empty, a check will be performed whether a found file extends the
328 * given subclass. If the requested file exists but does not extend this class
329 * a warning will be shown to advice a developer to extend this certain class.
330 *
331 * @template T of object
332 * @phpstan-param class-string<T>|''|false|null $expectedSubclass
333 *
334 * @return class-string<T>|null Null if the requested component does not exist,
335 * or the class string of the found component.
336 */
337 public function findComponent($componentName, $expectedSubclass)
338 {
339 $this->createCacheIfNeeded();
340 $cacheId = 'Plugin' . $this->pluginName . $componentName . $expectedSubclass;
341 $pluginsDir = Manager::getPluginDirectory($this->pluginName);
342 $componentFile = sprintf('%s/%s.php', $pluginsDir, $componentName);
343 if ($this->cache->contains($cacheId)) {
344 $classname = $this->cache->fetch($cacheId);
345 if (empty($classname)) {
346 return null;
347 // might by "false" in case has no menu, widget, ...
348 }
349 if (file_exists($componentFile)) {
350 include_once $componentFile;
351 }
352 } else {
353 $this->cache->save($cacheId, \false);
354 // prevent from trying to load over and over again for instance if there is no Menu for a plugin
355 if (!file_exists($componentFile)) {
356 return null;
357 }
358 require_once $componentFile;
359 $classname = sprintf('Piwik\\Plugins\\%s\\%s', $this->pluginName, $componentName);
360 if (!class_exists($classname)) {
361 return null;
362 }
363 if (!empty($expectedSubclass) && !is_subclass_of($classname, $expectedSubclass)) {
364 \Piwik\Log::warning(sprintf('Cannot use component %s for plugin %s, class %s does not extend %s', $componentName, $this->pluginName, $classname, $expectedSubclass));
365 return null;
366 }
367 $this->cache->save($cacheId, $classname);
368 }
369 return $classname;
370 }
371 /**
372 * @template T of object
373 * @param class-string<T>|''|false|null $expectedSubclass
374 * @return array<class-string<T>>
375 */
376 public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass)
377 {
378 $this->createCacheIfNeeded();
379 $cacheId = 'Plugin' . $this->pluginName . $directoryWithinPlugin . $expectedSubclass;
380 if ($this->cache->contains($cacheId)) {
381 $components = $this->cache->fetch($cacheId);
382 if ($this->includeComponents($components)) {
383 return $components;
384 } else {
385 // problem including one cached file, refresh cache
386 }
387 }
388 $components = $this->doFindMultipleComponents($directoryWithinPlugin, $expectedSubclass);
389 $this->cache->save($cacheId, $components);
390 return $components;
391 }
392 /**
393 * Detect whether there are any missing dependencies.
394 *
395 * @param null $piwikVersion Defaults to the current Piwik version
396 * @return bool
397 */
398 public function hasMissingDependencies($piwikVersion = null)
399 {
400 $requirements = $this->getMissingDependencies($piwikVersion);
401 return !empty($requirements);
402 }
403 public function getMissingDependencies($piwikVersion = null)
404 {
405 if (empty($this->pluginInformation['require'])) {
406 return array();
407 }
408 $dependency = $this->makeDependency($piwikVersion);
409 return $dependency->getMissingDependencies($this->pluginInformation['require']);
410 }
411 /**
412 * Returns a string (translated) describing the missing requirements for this plugin and the given Piwik version
413 *
414 * @param string $piwikVersion
415 * @return string "AnonymousPiwikUsageMeasurement requires PIWIK >=3.0.0"
416 */
417 public function getMissingDependenciesAsString($piwikVersion = null)
418 {
419 if ($this->requiresInternetConnection() && !\Piwik\SettingsPiwik::isInternetEnabled()) {
420 return \Piwik\Piwik::translate('CorePluginsAdmin_PluginRequiresInternet');
421 }
422 if (empty($this->pluginInformation['require'])) {
423 return '';
424 }
425 $dependency = $this->makeDependency($piwikVersion);
426 $missingDependencies = $dependency->getMissingDependencies($this->pluginInformation['require']);
427 if (empty($missingDependencies)) {
428 return '';
429 }
430 $causedBy = array();
431 foreach ($missingDependencies as $dependency) {
432 $causedBy[] = ucfirst($dependency['requirement']) . ' ' . $dependency['causedBy'];
433 }
434 return \Piwik\Piwik::translate("CorePluginsAdmin_PluginRequirement", array($this->getPluginName(), implode(', ', $causedBy)));
435 }
436 /**
437 * Schedules re-archiving of this plugin's reports from when this plugin was last
438 * deactivated to now. If the last time core:archive was run is earlier than the
439 * plugin's last deactivation time, then we use that time instead.
440 *
441 * Note: this only works for CLI archiving setups.
442 *
443 * Note: the time frame is limited by the `[General] rearchive_reports_in_past_last_n_months`
444 * INI config value.
445 *
446 * @throws \Piwik\Exception\DI\DependencyException
447 * @throws \Piwik\Exception\DI\NotFoundException
448 */
449 public function schedulePluginReArchiving()
450 {
451 $lastDeactivationTime = $this->getPluginLastDeactivationTime();
452 $dateTime = null;
453 $lastCronArchiveTime = (int) \Piwik\Option::get(\Piwik\CronArchive::OPTION_ARCHIVING_FINISHED_TS);
454 if (empty($lastCronArchiveTime)) {
455 $dateTime = $lastDeactivationTime;
456 } elseif (empty($lastDeactivationTime)) {
457 $dateTime = null;
458 // use default earliest time
459 } else {
460 $lastCronArchiveTime = \Piwik\Date::factory($lastCronArchiveTime);
461 $dateTime = $lastDeactivationTime->isEarlier($lastCronArchiveTime) ? $lastDeactivationTime : $lastCronArchiveTime;
462 }
463 if (empty($dateTime)) {
464 // sanity check
465 $dateTime = null;
466 }
467 $archiveInvalidator = StaticContainer::get(ArchiveInvalidator::class);
468 $archiveInvalidator->scheduleReArchiving('all', $this->getPluginName(), $report = null, $dateTime);
469 }
470 /**
471 * Extracts the plugin name from a backtrace array. Returns `false` if we can't find one.
472 *
473 * @param array $backtrace The result of {@link debug_backtrace()} or
474 * [Exception::getTrace()](https://www.php.net/manual/en/exception.gettrace.php).
475 * @return string|false
476 */
477 public static function getPluginNameFromBacktrace($backtrace)
478 {
479 foreach ($backtrace as $tracepoint) {
480 // try and discern the plugin name
481 if (isset($tracepoint['class'])) {
482 $className = self::getPluginNameFromNamespace($tracepoint['class']);
483 if ($className) {
484 return $className;
485 }
486 }
487 }
488 return \false;
489 }
490 /**
491 * Extracts the plugin name from a namespace name or a fully qualified class name. Returns `false`
492 * if we can't find one.
493 *
494 * @param string $namespaceOrClassName The namespace or class string.
495 * @return string|false
496 */
497 public static function getPluginNameFromNamespace($namespaceOrClassName)
498 {
499 if ($namespaceOrClassName && preg_match("/Piwik\\\\Plugins\\\\([a-zA-Z_0-9]+)\\\\/", $namespaceOrClassName, $matches)) {
500 return $matches[1];
501 } else {
502 return \false;
503 }
504 }
505 /**
506 * Override this method in your plugin class if you want your plugin to be loaded during tracking.
507 *
508 * Note: If you define your own dimension or handle a tracker event, your plugin will automatically
509 * be detected as a tracker plugin.
510 *
511 * @return bool
512 * @internal
513 */
514 public function isTrackerPlugin()
515 {
516 return \false;
517 }
518 /**
519 * @return Date|null
520 * @throws \Exception
521 */
522 public function getPluginLastActivationTime()
523 {
524 $optionName = Manager::LAST_PLUGIN_ACTIVATION_TIME_OPTION_PREFIX . $this->pluginName;
525 $time = \Piwik\Option::get($optionName);
526 if (empty($time)) {
527 return null;
528 }
529 return \Piwik\Date::factory((int) $time);
530 }
531 /**
532 * @return Date|null
533 * @throws \Exception
534 */
535 public function getPluginLastDeactivationTime()
536 {
537 $optionName = Manager::LAST_PLUGIN_DEACTIVATION_TIME_OPTION_PREFIX . $this->pluginName;
538 $time = \Piwik\Option::get($optionName);
539 if (empty($time)) {
540 return null;
541 }
542 return \Piwik\Date::factory((int) $time);
543 }
544 /**
545 * @template T of object
546 * @param class-string<T>|''|false|null $expectedSubclass
547 * @return array<class-string<T>>
548 */
549 private function doFindMultipleComponents($directoryWithinPlugin, $expectedSubclass)
550 {
551 $components = array();
552 $pluginsDir = Manager::getPluginDirectory($this->pluginName);
553 $baseDir = $pluginsDir . '/' . $directoryWithinPlugin;
554 $files = \Piwik\Filesystem::globr($baseDir, '*.php');
555 foreach ($files as $file) {
556 require_once $file;
557 $fileName = str_replace(array($baseDir . '/', '.php'), '', $file);
558 $klassName = sprintf('Piwik\\Plugins\\%s\\%s\\%s', $this->pluginName, str_replace('/', '\\', $directoryWithinPlugin), str_replace('/', '\\', $fileName));
559 if (!class_exists($klassName)) {
560 continue;
561 }
562 if (!empty($expectedSubclass) && !is_subclass_of($klassName, $expectedSubclass)) {
563 continue;
564 }
565 $klass = new \ReflectionClass($klassName);
566 if ($klass->isAbstract()) {
567 continue;
568 }
569 $components[$file] = $klassName;
570 }
571 return $components;
572 }
573 /**
574 * @param $components
575 * @return bool true if all files were included, false if any file cannot be read
576 */
577 private function includeComponents($components)
578 {
579 foreach ($components as $file => $klass) {
580 if (!is_readable($file)) {
581 return \false;
582 }
583 }
584 foreach ($components as $file => $klass) {
585 include_once $file;
586 }
587 return \true;
588 }
589 /**
590 * @param $piwikVersion
591 * @return Dependency
592 */
593 private function makeDependency($piwikVersion)
594 {
595 $dependency = new Dependency();
596 if (!is_null($piwikVersion)) {
597 $dependency->setPiwikVersion($piwikVersion);
598 }
599 return $dependency;
600 }
601 /**
602 * Get all changes for this plugin
603 *
604 * @return array Array of changes
605 * [{"title":"abc","description":"xyz","linkName":"def","link":"https://link","version":"1.2.3"}]
606 */
607 public function getChanges()
608 {
609 $file = Manager::getPluginDirectory($this->pluginName) . '/changes.json';
610 if (file_exists($file)) {
611 $json = file_get_contents($file);
612 if ($json) {
613 $changes = json_decode($json, \true);
614 if ($changes && is_array($changes)) {
615 return array_reverse($changes);
616 }
617 }
618 }
619 return [];
620 }
621 }
622 }
623