1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace GraphQLAPI\GraphQLAPI\Registries; |
||
6 | |||
7 | use InvalidArgumentException; |
||
8 | use GraphQLAPI\GraphQLAPI\Facades\UserSettingsManagerFacade; |
||
9 | use GraphQLAPI\GraphQLAPI\ModuleResolvers\ModuleResolverInterface; |
||
10 | |||
11 | class ModuleRegistry implements ModuleRegistryInterface |
||
12 | { |
||
13 | /** |
||
14 | * @var array<string, ModuleResolverInterface> |
||
15 | */ |
||
16 | protected array $moduleResolvers = []; |
||
17 | |||
18 | public function addModuleResolver(ModuleResolverInterface $moduleResolver): void |
||
19 | { |
||
20 | foreach ($moduleResolver::getModulesToResolve() as $module) { |
||
21 | $this->moduleResolvers[$module] = $moduleResolver; |
||
22 | } |
||
23 | } |
||
24 | /** |
||
25 | * @return string[] |
||
26 | */ |
||
27 | public function getAllModules( |
||
28 | bool $onlyEnabled = false, |
||
29 | bool $onlyHasSettings = false, |
||
30 | bool $onlyVisible = true |
||
31 | ): array { |
||
32 | $modules = array_keys($this->moduleResolvers); |
||
33 | if ($onlyEnabled) { |
||
34 | $modules = array_filter( |
||
35 | $modules, |
||
36 | fn ($module) => $this->isModuleEnabled($module) |
||
37 | ); |
||
38 | } |
||
39 | if ($onlyHasSettings) { |
||
40 | $modules = array_filter( |
||
41 | $modules, |
||
42 | fn ($module) => $this->getModuleResolver($module)->hasSettings($module) |
||
43 | ); |
||
44 | } |
||
45 | if ($onlyVisible) { |
||
46 | $modules = array_filter( |
||
47 | $modules, |
||
48 | fn ($module) => !$this->getModuleResolver($module)->isHidden($module) |
||
49 | ); |
||
50 | } |
||
51 | return $modules; |
||
52 | } |
||
53 | /** |
||
54 | * @throws InvalidArgumentException If module does not exist |
||
55 | */ |
||
56 | public function getModuleResolver(string $module): ModuleResolverInterface |
||
57 | { |
||
58 | if (!isset($this->moduleResolvers[$module])) { |
||
59 | throw new InvalidArgumentException(sprintf( |
||
60 | \__('Module \'%s\' does not exist', 'graphql-api'), |
||
61 | $module |
||
62 | )); |
||
63 | } |
||
64 | return $this->moduleResolvers[$module]; |
||
65 | } |
||
66 | public function isModuleEnabled(string $module): bool |
||
67 | { |
||
68 | $moduleResolver = $this->getModuleResolver($module); |
||
69 | // Check that all requirements are satisfied |
||
70 | if (!$moduleResolver->areRequirementsSatisfied($module)) { |
||
71 | return false; |
||
72 | } |
||
73 | // Check that all depended-upon modules are enabled |
||
74 | if (!$this->areDependedModulesEnabled($module)) { |
||
75 | return false; |
||
76 | } |
||
77 | // If the user can't disable it, then it must be enabled |
||
78 | if (!$moduleResolver->canBeDisabled($module)) { |
||
79 | return true; |
||
80 | } |
||
81 | $moduleID = $moduleResolver->getID($module); |
||
82 | // Check if the value has been saved on the DB |
||
83 | $userSettingsManager = UserSettingsManagerFacade::getInstance(); |
||
84 | if ($userSettingsManager->hasSetModuleEnabled($moduleID)) { |
||
85 | return $userSettingsManager->isModuleEnabled($moduleID); |
||
86 | } |
||
87 | // Get the default value from the resolver |
||
88 | return $moduleResolver->isEnabledByDefault($module); |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Indicate if a module's depended-upon modules are all enabled |
||
93 | * |
||
94 | * @param string $module |
||
95 | * @return boolean |
||
96 | */ |
||
97 | protected function areDependedModulesEnabled(string $module): bool |
||
98 | { |
||
99 | $moduleResolver = $this->getModuleResolver($module); |
||
100 | // Check that all depended-upon modules are enabled |
||
101 | $dependedModuleLists = $moduleResolver->getDependedModuleLists($module); |
||
102 | /** |
||
103 | * This is a list of lists of modules, as to model both OR and AND conditions |
||
104 | * The innermost list is an OR: if any module is enabled, then the condition succeeds |
||
105 | * The outermost list is an AND: all list must succeed for this module to be enabled |
||
106 | * Eg: the Schema Configuration is enabled if either the Custom Endpoints or |
||
107 | * the Persisted Query are enabled: |
||
108 | * [ |
||
109 | * [self::PERSISTED_QUERIES, self::CUSTOM_ENDPOINTS], |
||
110 | * ] |
||
111 | */ |
||
112 | foreach ($dependedModuleLists as $dependedModuleList) { |
||
113 | if (!$dependedModuleList) { |
||
0 ignored issues
–
show
|
|||
114 | continue; |
||
115 | } |
||
116 | $dependedModuleListEnabled = array_map( |
||
117 | function ($dependedModule) { |
||
118 | // Check if it has the "inverse" token at the beginning, |
||
119 | // then it depends on the module being disabled, not enabled |
||
120 | if (substr($dependedModule, 0, strlen(ModuleRegistryTokens::INVERSE_DEPENDENCY)) == ModuleRegistryTokens::INVERSE_DEPENDENCY) { |
||
121 | // The module is everything after the token |
||
122 | $dependedModule = substr($dependedModule, strlen(ModuleRegistryTokens::INVERSE_DEPENDENCY)); |
||
123 | return !$this->isModuleEnabled($dependedModule); |
||
124 | } |
||
125 | return $this->isModuleEnabled($dependedModule); |
||
126 | }, |
||
127 | $dependedModuleList |
||
128 | ); |
||
129 | if (!in_array(true, $dependedModuleListEnabled)) { |
||
130 | return false; |
||
131 | } |
||
132 | } |
||
133 | return true; |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * If a module was disabled by the user, then the user can enable it. |
||
138 | * If it is disabled because its requirements are not satisfied, |
||
139 | * or its dependencies themselves disabled, then it cannot be enabled by the user. |
||
140 | * |
||
141 | * @param string $module |
||
142 | * @return boolean |
||
143 | */ |
||
144 | public function canModuleBeEnabled(string $module): bool |
||
145 | { |
||
146 | $moduleResolver = $this->getModuleResolver($module); |
||
147 | // Check that all requirements are satisfied |
||
148 | if (!$moduleResolver->areRequirementsSatisfied($module)) { |
||
149 | return false; |
||
150 | } |
||
151 | // Check that all depended-upon modules are enabled |
||
152 | if (!$this->areDependedModulesEnabled($module)) { |
||
153 | return false; |
||
154 | } |
||
155 | return true; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Used to indicate that the dependency on the module is on its being disabled, not enabled |
||
160 | * |
||
161 | * @param string $dependedModule |
||
162 | * @return string |
||
163 | */ |
||
164 | public function getInverseDependency(string $dependedModule): string |
||
165 | { |
||
166 | // Check if it already has the "inverse" token at the beginning, |
||
167 | // then take it back to normal |
||
168 | if ($this->isInverseDependency($dependedModule)) { |
||
169 | // The module is everything after the token "!" |
||
170 | return substr($dependedModule, strlen(ModuleRegistryTokens::INVERSE_DEPENDENCY)); |
||
171 | } |
||
172 | // Add "!" before the module |
||
173 | return ModuleRegistryTokens::INVERSE_DEPENDENCY . $dependedModule; |
||
174 | } |
||
175 | /** |
||
176 | * Indicate if the dependency is on its being disabled, not enabled |
||
177 | */ |
||
178 | public function isInverseDependency(string $dependedModule): bool |
||
179 | { |
||
180 | return substr($dependedModule, 0, strlen(ModuleRegistryTokens::INVERSE_DEPENDENCY)) == ModuleRegistryTokens::INVERSE_DEPENDENCY; |
||
181 | } |
||
182 | } |
||
183 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.