PluginProbe
Metricool – Social media and site statistics / trunk
Metricool – Social media and site statistics vtrunk
2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Managers / FeatureManager.php

FeatureManager.php in Metricool – Social media and site statistics trunk, at app/Managers/FeatureManager.php

164 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Metricool\Managers;
6
7 use Metricool\Bootstrap\App;
8 use Metricool\Interfaces\FeatureInterface;
9 use Metricool\Support\Helpers\Storages\EnvironmentConfig;
10
11 /**
12 * This manager dynamically fetches the features of the plugin. It differs from
13 * other manager classes due to this nature. By preventing any class usage of
14 * features we prevent composer from loading the feature file entirely until
15 * first use. This prevents overhead from loading features that are no longer
16 * needed. We prevent loading feature files by utilizing the
17 * {@see AbstractLoader} class at {@see FeatureManager:92}
18 */
19 final class FeatureManager extends AbstractManager
20 {
21 private const PRO_FEATURE_HANDLE = 'Pro:';
22
23 private EnvironmentConfig $env;
24
25 public function __construct(EnvironmentConfig $env)
26 {
27 $this->env = $env;
28 }
29
30 /**
31 * @inheritDoc
32 */
33 public function isRegistrable(object $class): bool
34 {
35 return $class instanceof FeatureInterface;
36 }
37
38 /**
39 * @inheritDoc
40 */
41 public function registerClass(object $class): void
42 {
43 $class->register();
44 }
45
46 /**
47 * @inheritDoc
48 */
49 public function afterRegister(): void
50 {
51 do_action('metricool_features_loaded');
52 }
53
54 /**
55 * Register and load all features from the src/features directory. This
56 * method automatically loads all classes from the features directory and
57 * injects the dependency classes into the Controller class if they exist.
58 * @uses do_action rss_core_features_loaded
59 */
60 public function registerFeatures(): void
61 {
62 $featureClasses = $this->getFeatureClasses();
63 $this->register($featureClasses);
64 }
65
66 /**
67 * Dynamically build and then return an array of feature classes that are
68 * saved in the features path of the plugin.
69 */
70 public function getFeatureClasses(): array
71 {
72 $features = $this->getFeatures();
73 $featureClasses = [];
74
75 foreach ($features as $featureName) {
76 $needsPro = strpos($featureName, self::PRO_FEATURE_HANDLE) !== false;
77 if ($needsPro && !$this->env->getBoolean('plugin.pro')) {
78 continue; // Pro not installed, don't register pro features
79 }
80
81 if ($needsPro) {
82 $featureName = substr($featureName, strlen(self::PRO_FEATURE_HANDLE));
83 }
84
85 // Check if the feature directory exists
86 $featuresPath = $this->getFeaturePath($featureName, $needsPro);
87 if (!is_dir($featuresPath)) {
88 continue;
89 }
90
91 // Get the feature namespace
92 $prefix = $this->getFeatureNamespace($featureName, $needsPro) . $featureName;
93
94 // Get the {FeatureName}Loader class for the feature
95 if (class_exists($prefix . 'Loader') === false) {
96 continue;
97 }
98
99 $loader = App::getInstance()->make($prefix . 'Loader', false, false);
100 if (!$loader->isEnabled() || !$loader->inScope()) {
101 continue;
102 }
103
104 // The controller is the backbone of a feature
105 $featureClasses[] = $prefix . 'Controller';
106 };
107
108 return $featureClasses;
109 }
110
111 /**
112 * Get all feature directory names. Includes "Pro" features prefixed
113 * with {@see PRO_FEATURE_HANDLE}.
114 */
115 private function getFeatures(): array
116 {
117 $featuresPath = $this->env->getString('plugin.feature_path');
118 $features = [];
119
120 foreach (new \DirectoryIterator($featuresPath) as $fileInfo) {
121 if ($fileInfo->isDot() || !$fileInfo->isDir()) {
122 continue;
123 }
124
125 $proEnabled = $this->env->getBoolean('plugin.pro');
126 $skipPro = ($proEnabled === false && $fileInfo->getFilename() === 'Pro');
127 if ($skipPro) {
128 continue;
129 }
130
131 if ($fileInfo->getFilename() === 'Pro') {
132 foreach (new \DirectoryIterator($fileInfo->getPathname()) as $proInfo) {
133 if ($proInfo->isDot() || !$proInfo->isDir()) {
134 continue;
135 }
136 $features[] = self::PRO_FEATURE_HANDLE . $proInfo->getFilename();
137 }
138 continue;
139 }
140
141 $features[] = $fileInfo->getFilename();
142 }
143
144 return $features;
145 }
146
147 /**
148 * Get the feature path based on the feature name and if it needs the Pro
149 * version.
150 */
151 private function getFeaturePath(string $featureName, bool $needsPro): string
152 {
153 return $this->env->getString('plugin.feature_path') . ($needsPro ? 'Pro/' : '') . $featureName . '/';
154 }
155
156 /**
157 * Get the feature namespace.
158 */
159 private function getFeatureNamespace(string $featureName, bool $needsPro = false): string
160 {
161 return 'Metricool\Features\\' . ($needsPro ? 'Pro\\' : '') . $featureName . '\\';
162 }
163 }
164