GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( ae7274...bdf558 )
by Leonardo
11:34
created

PluginConfiguration::opposite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQLAPI\GraphQLAPI;
6
7
use PoP\Engine\Component;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, GraphQLAPI\GraphQLAPI\Component. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use PoP\APIEndpoints\EndpointUtils;
9
use GraphQLAPI\GraphQLAPI\Environment;
10
use PoP\AccessControl\Schema\SchemaModes;
11
use PoP\ComponentModel\Misc\GeneralUtils;
12
use PoP\Engine\Environment as EngineEnvironment;
13
use GraphQLAPI\GraphQLAPI\ComponentConfiguration;
14
use PoPSchema\Tags\Environment as TagsEnvironment;
15
use PoPSchema\Pages\Environment as PagesEnvironment;
16
use PoPSchema\Posts\Environment as PostsEnvironment;
17
use PoPSchema\Users\Environment as UsersEnvironment;
18
use GraphQLAPI\GraphQLAPI\Facades\ModuleRegistryFacade;
19
use GraphQLAPI\GraphQLAPI\ConditionalOnEnvironment\Admin\Services\MenuPages\SettingsMenuPage;
20
use GraphQLAPI\GraphQLAPI\Config\PluginConfigurationHelpers;
21
use GraphQLAPI\GraphQLAPI\Facades\UserSettingsManagerFacade;
22
use PoP\CacheControl\Environment as CacheControlEnvironment;
23
use GraphQLByPoP\GraphQLServer\Configuration\MutationSchemes;
24
use PoP\AccessControl\Environment as AccessControlEnvironment;
25
use PoP\ComponentModel\Facades\Instances\InstanceManagerFacade;
26
use PoP\ComponentModel\Environment as ComponentModelEnvironment;
27
use PoPSchema\CustomPosts\Environment as CustomPostsEnvironment;
28
use GraphQLAPI\GraphQLAPI\Facades\CacheConfigurationManagerFacade;
29
use GraphQLAPI\GraphQLAPI\ModuleResolvers\SchemaTypeModuleResolver;
30
use PoP\Engine\ComponentConfiguration as EngineComponentConfiguration;
31
use PoPSchema\Tags\ComponentConfiguration as TagsComponentConfiguration;
32
use PoPSchema\Pages\ComponentConfiguration as PagesComponentConfiguration;
33
use PoPSchema\Posts\ComponentConfiguration as PostsComponentConfiguration;
34
use PoPSchema\Users\ComponentConfiguration as UsersComponentConfiguration;
35
use GraphQLAPI\GraphQLAPI\ModuleResolvers\CacheFunctionalityModuleResolver;
36
use GraphQLAPI\GraphQLAPI\ModuleResolvers\ClientFunctionalityModuleResolver;
37
use PoP\ComponentModel\ComponentConfiguration\ComponentConfigurationHelpers;
38
use GraphQLAPI\GraphQLAPI\ModuleResolvers\EndpointFunctionalityModuleResolver;
39
use PoPSchema\GenericCustomPosts\Environment as GenericCustomPostsEnvironment;
40
use GraphQLAPI\GraphQLAPI\ModuleResolvers\OperationalFunctionalityModuleResolver;
41
use GraphQLAPI\GraphQLAPI\ModuleResolvers\PerformanceFunctionalityModuleResolver;
42
use PoP\CacheControl\ComponentConfiguration as CacheControlComponentConfiguration;
43
use GraphQLAPI\GraphQLAPI\ModuleResolvers\UserInterfaceFunctionalityModuleResolver;
44
use GraphQLByPoP\GraphQLClientsForWP\Environment as GraphQLClientsForWPEnvironment;
45
use PoP\AccessControl\ComponentConfiguration as AccessControlComponentConfiguration;
46
use GraphQLByPoP\GraphQLEndpointForWP\Environment as GraphQLEndpointForWPEnvironment;
47
use GraphQLAPI\GraphQLAPI\ModuleResolvers\PluginManagementFunctionalityModuleResolver;
48
use PoP\ComponentModel\ComponentConfiguration as ComponentModelComponentConfiguration;
49
use PoPSchema\CustomPosts\ComponentConfiguration as CustomPostsComponentConfiguration;
50
use GraphQLAPI\GraphQLAPI\ModuleResolvers\SchemaConfigurationFunctionalityModuleResolver;
51
use PoPSchema\GenericCustomPosts\ComponentConfiguration as GenericCustomPostsComponentConfiguration;
52
use GraphQLByPoP\GraphQLClientsForWP\ComponentConfiguration as GraphQLClientsForWPComponentConfiguration;
53
use GraphQLByPoP\GraphQLEndpointForWP\ComponentConfiguration as GraphQLEndpointForWPComponentConfiguration;
54
use GraphQLByPoP\GraphQLServer\Environment as GraphQLServerEnvironment;
55
use GraphQLByPoP\GraphQLServer\ComponentConfiguration as GraphQLServerComponentConfiguration;
56
use GraphQLByPoP\GraphQLQuery\Environment as GraphQLQueryEnvironment;
57
58
/**
59
 * Sets the configuration in all the PoP components.
60
 *
61
 * To set the value for properties, it uses this order:
62
 *
63
 * 1. Retrieve it as an environment value, if defined
64
 * 2. Retrieve as a constant `GRAPHQL_API_...` from wp-config.php, if defined
65
 * 3. Retrieve it from the user settings, if stored
66
 * 4. Use the default value
67
 *
68
 * If a slug is set or updated in the environment variable or wp-config constant,
69
 * it is necessary to flush the rewrite rules for the change to take effect.
70
 * For that, on the WordPress admin, go to Settings => Permalinks and click on Save changes
71
 */
72
class PluginConfiguration
73
{
74
    /**
75
     * Cache the options after normalizing them
76
     *
77
     * @var array<string, mixed>|null
78
     */
79
    protected static ?array $normalizedOptionValuesCache = null;
80
81
    /**
82
     * Initialize all configuration
83
     */
84
    public static function initialize(): void
85
    {
86
        self::mapEnvVariablesToWPConfigConstants();
87
        self::defineEnvironmentConstantsFromSettings();
88
    }
89
90
    /**
91
     * Get the values from the form submitted to options.php, and normalize them
92
     *
93
     * @return array<string, mixed>
94
     */
95
    protected static function getNormalizedOptionValues(): array
96
    {
97
        if (is_null(self::$normalizedOptionValuesCache)) {
98
            $instanceManager = InstanceManagerFacade::getInstance();
99
            /**
100
             * @var SettingsMenuPage
101
             */
102
            $settingsMenuPage = $instanceManager->getInstance(SettingsMenuPage::class);
103
            // Obtain the values from the POST and normalize them
104
            $value = $_POST[SettingsMenuPage::SETTINGS_FIELD] ?? [];
105
            self::$normalizedOptionValuesCache = $settingsMenuPage->normalizeSettings($value);
106
        }
107
        return self::$normalizedOptionValuesCache;
108
    }
109
110
    /**
111
     * If we are in options.php, already set the new slugs in the hook,
112
     * so that the EndpointHandler's `addRewriteEndpoints` (executed on `init`)
113
     * adds the rewrite with the new slug, which will be persisted on
114
     * flushing the rewrite rules
115
     *
116
     * Hidden input "form-origin" is used to only execute for this plugin,
117
     * since options.php is used everywhere, including WP core and other plugins.
118
     * Otherwise, it may thrown an exception!
119
     *
120
     * @param mixed $value
121
     * @return mixed
122
     */
123
    protected static function maybeOverrideValueFromForm($value, string $module, string $option)
124
    {
125
        global $pagenow;
126
        if (
127
            $pagenow == 'options.php'
128
            && isset($_REQUEST[SettingsMenuPage::FORM_ORIGIN])
129
            && $_REQUEST[SettingsMenuPage::FORM_ORIGIN] == SettingsMenuPage::SETTINGS_FIELD
130
        ) {
131
            $value = self::getNormalizedOptionValues();
132
            // Return the specific value to this module/option
133
            $moduleRegistry = ModuleRegistryFacade::getInstance();
134
            $moduleResolver = $moduleRegistry->getModuleResolver($module);
135
            $optionName = $moduleResolver->getSettingOptionName($module, $option);
136
            return $value[$optionName];
137
        }
138
        return $value;
139
    }
140
141
    /**
142
     * Process the "URL path" option values
143
     *
144
     * @param string $value
145
     * @param string $module
146
     * @param string $option
147
     * @return string
148
     */
149
    protected static function getURLPathSettingValue(
150
        string $value,
151
        string $module,
152
        string $option
153
    ): string {
154
        // If we are on options.php, use the value submitted to the form,
155
        // so it's updated before doing `add_rewrite_endpoint` and `flush_rewrite_rules`
156
        $value = self::maybeOverrideValueFromForm($value, $module, $option);
157
158
        // Make sure the path has a "/" on both ends
159
        return EndpointUtils::slashURI($value);
160
    }
161
162
    /**
163
     * Process the "URL base path" option values
164
     *
165
     * @param string $value
166
     * @param string $module
167
     * @param string $option
168
     * @return string
169
     */
170
    protected static function getCPTPermalinkBasePathSettingValue(
171
        string $value,
172
        string $module,
173
        string $option
174
    ): string {
175
        // If we are on options.php, use the value submitted to the form,
176
        // so it's updated before doing `add_rewrite_endpoint` and `flush_rewrite_rules`
177
        $value = self::maybeOverrideValueFromForm($value, $module, $option);
178
179
        // Make sure the path does not have "/" on either end
180
        return trim($value, '/');
181
    }
182
183
    /**
184
     * Define the values for certain environment constants from the plugin settings
185
     */
186
    protected static function defineEnvironmentConstantsFromSettings(): void
187
    {
188
        $moduleRegistry = ModuleRegistryFacade::getInstance();
189
        // All the environment variables to override
190
        $mappings = [
191
            // Editing Access Scheme
192
            [
193
                'class' => ComponentConfiguration::class,
194
                'envVariable' => Environment::EDITING_ACCESS_SCHEME,
195
                'module' => PluginManagementFunctionalityModuleResolver::SCHEMA_EDITING_ACCESS,
196
                'option' => PluginManagementFunctionalityModuleResolver::OPTION_EDITING_ACCESS_SCHEME,
197
            ],
198
            // GraphQL single endpoint slug
199
            [
200
                'class' => GraphQLEndpointForWPComponentConfiguration::class,
201
                'envVariable' => GraphQLEndpointForWPEnvironment::GRAPHQL_API_ENDPOINT,
202
                'module' => EndpointFunctionalityModuleResolver::SINGLE_ENDPOINT,
203
                'option' => EndpointFunctionalityModuleResolver::OPTION_PATH,
204
                'callback' => fn ($value) => self::getURLPathSettingValue(
205
                    $value,
206
                    EndpointFunctionalityModuleResolver::SINGLE_ENDPOINT,
207
                    EndpointFunctionalityModuleResolver::OPTION_PATH
208
                ),
209
                'condition' => 'any',
210
            ],
211
            // Custom Endpoint path
212
            [
213
                'class' => ComponentConfiguration::class,
214
                'envVariable' => Environment::ENDPOINT_SLUG_BASE,
215
                'module' => EndpointFunctionalityModuleResolver::CUSTOM_ENDPOINTS,
216
                'option' => EndpointFunctionalityModuleResolver::OPTION_PATH,
217
                'callback' => fn ($value) => self::getCPTPermalinkBasePathSettingValue(
218
                    $value,
219
                    EndpointFunctionalityModuleResolver::CUSTOM_ENDPOINTS,
220
                    EndpointFunctionalityModuleResolver::OPTION_PATH
221
                ),
222
                'condition' => 'any',
223
            ],
224
            // Persisted Query path
225
            [
226
                'class' => ComponentConfiguration::class,
227
                'envVariable' => Environment::PERSISTED_QUERY_SLUG_BASE,
228
                'module' => EndpointFunctionalityModuleResolver::PERSISTED_QUERIES,
229
                'option' => EndpointFunctionalityModuleResolver::OPTION_PATH,
230
                'callback' => fn ($value) => self::getCPTPermalinkBasePathSettingValue(
231
                    $value,
232
                    EndpointFunctionalityModuleResolver::PERSISTED_QUERIES,
233
                    EndpointFunctionalityModuleResolver::OPTION_PATH
234
                ),
235
                'condition' => 'any',
236
            ],
237
            // GraphiQL client slug
238
            [
239
                'class' => GraphQLClientsForWPComponentConfiguration::class,
240
                'envVariable' => GraphQLClientsForWPEnvironment::GRAPHIQL_CLIENT_ENDPOINT,
241
                'module' => ClientFunctionalityModuleResolver::GRAPHIQL_FOR_SINGLE_ENDPOINT,
242
                'option' => EndpointFunctionalityModuleResolver::OPTION_PATH,
243
                'callback' => fn ($value) => self::getURLPathSettingValue(
244
                    $value,
245
                    ClientFunctionalityModuleResolver::GRAPHIQL_FOR_SINGLE_ENDPOINT,
246
                    EndpointFunctionalityModuleResolver::OPTION_PATH
247
                ),
248
                'condition' => 'any',
249
            ],
250
            // Voyager client slug
251
            [
252
                'class' => GraphQLClientsForWPComponentConfiguration::class,
253
                'envVariable' => GraphQLClientsForWPEnvironment::VOYAGER_CLIENT_ENDPOINT,
254
                'module' => ClientFunctionalityModuleResolver::INTERACTIVE_SCHEMA_FOR_SINGLE_ENDPOINT,
255
                'option' => EndpointFunctionalityModuleResolver::OPTION_PATH,
256
                'callback' => fn ($value) => self::getURLPathSettingValue(
257
                    $value,
258
                    ClientFunctionalityModuleResolver::INTERACTIVE_SCHEMA_FOR_SINGLE_ENDPOINT,
259
                    EndpointFunctionalityModuleResolver::OPTION_PATH
260
                ),
261
                'condition' => 'any',
262
            ],
263
            // Use private schema mode?
264
            [
265
                'class' => AccessControlComponentConfiguration::class,
266
                'envVariable' => AccessControlEnvironment::USE_PRIVATE_SCHEMA_MODE,
267
                'module' => SchemaConfigurationFunctionalityModuleResolver::PUBLIC_PRIVATE_SCHEMA,
268
                'option' => SchemaConfigurationFunctionalityModuleResolver::OPTION_MODE,
269
                // It is stored as string "private" in DB, and must be passed as bool `true` to component
270
                'callback' => fn ($value) => $value == SchemaModes::PRIVATE_SCHEMA_MODE,
271
            ],
272
            // Enable individual access control for the schema mode?
273
            [
274
                'class' => AccessControlComponentConfiguration::class,
275
                'envVariable' => AccessControlEnvironment::ENABLE_INDIVIDUAL_CONTROL_FOR_PUBLIC_PRIVATE_SCHEMA_MODE,
276
                'module' => SchemaConfigurationFunctionalityModuleResolver::PUBLIC_PRIVATE_SCHEMA,
277
                'option' => SchemaConfigurationFunctionalityModuleResolver::OPTION_ENABLE_GRANULAR,
278
                // Also make sure that the module is enabled.
279
                // Otherwise set the value in `false`, to override a potential `true` in the Settings
280
                'callback' => fn ($value) => $moduleRegistry->isModuleEnabled(SchemaConfigurationFunctionalityModuleResolver::PUBLIC_PRIVATE_SCHEMA) && $value,
281
                'condition' => 'any',
282
            ],
283
            // Use namespacing?
284
            [
285
                'class' => ComponentModelComponentConfiguration::class,
286
                'envVariable' => ComponentModelEnvironment::NAMESPACE_TYPES_AND_INTERFACES,
287
                'module' => SchemaConfigurationFunctionalityModuleResolver::SCHEMA_NAMESPACING,
288
                'option' => SchemaConfigurationFunctionalityModuleResolver::OPTION_USE_NAMESPACING,
289
            ],
290
            // Enable nested mutations?
291
            [
292
                'class' => GraphQLServerComponentConfiguration::class,
293
                'envVariable' => GraphQLServerEnvironment::ENABLE_NESTED_MUTATIONS,
294
                'module' => OperationalFunctionalityModuleResolver::NESTED_MUTATIONS,
295
                'option' => OperationalFunctionalityModuleResolver::OPTION_SCHEME,
296
                'callback' => fn ($value) => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::NESTED_MUTATIONS) && $value != MutationSchemes::STANDARD,
297
            ],
298
            // Disable redundant mutation fields in the root type?
299
            [
300
                'class' => EngineComponentConfiguration::class,
301
                'envVariable' => EngineEnvironment::DISABLE_REDUNDANT_ROOT_TYPE_MUTATION_FIELDS,
302
                'module' => OperationalFunctionalityModuleResolver::NESTED_MUTATIONS,
303
                'option' => OperationalFunctionalityModuleResolver::OPTION_SCHEME,
304
                'callback' => fn ($value) => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::NESTED_MUTATIONS) && $value == MutationSchemes::NESTED_WITHOUT_REDUNDANT_ROOT_FIELDS,
305
            ],
306
            // Cache-Control default max-age
307
            [
308
                'class' => CacheControlComponentConfiguration::class,
309
                'envVariable' => CacheControlEnvironment::DEFAULT_CACHE_CONTROL_MAX_AGE,
310
                'module' => PerformanceFunctionalityModuleResolver::CACHE_CONTROL,
311
                'option' => PerformanceFunctionalityModuleResolver::OPTION_MAX_AGE,
312
            ],
313
            // Custom Post default/max limits, Supported custom post types
314
            [
315
                'class' => GenericCustomPostsComponentConfiguration::class,
316
                'envVariable' => GenericCustomPostsEnvironment::GENERIC_CUSTOMPOST_LIST_DEFAULT_LIMIT,
317
                'module' => SchemaTypeModuleResolver::SCHEMA_GENERIC_CUSTOMPOSTS,
318
                'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
319
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
320
            ],
321
            // [
322
            //     'class' => GenericCustomPostsComponentConfiguration::class,
323
            //     'envVariable' => GenericCustomPostsEnvironment::GENERIC_CUSTOMPOST_LIST_MAX_LIMIT,
324
            //     'module' => SchemaTypeModuleResolver::SCHEMA_GENERIC_CUSTOMPOSTS,
325
            //     'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
326
            //     'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
327
            // ],
328
            [
329
                'class' => GenericCustomPostsComponentConfiguration::class,
330
                'envVariable' => GenericCustomPostsEnvironment::GENERIC_CUSTOMPOST_TYPES,
331
                'module' => SchemaTypeModuleResolver::SCHEMA_GENERIC_CUSTOMPOSTS,
332
                'option' => SchemaTypeModuleResolver::OPTION_CUSTOMPOST_TYPES,
333
            ],
334
            // Post default/max limits, add to CustomPostUnion
335
            [
336
                'class' => PostsComponentConfiguration::class,
337
                'envVariable' => PostsEnvironment::POST_LIST_DEFAULT_LIMIT,
338
                'module' => SchemaTypeModuleResolver::SCHEMA_POSTS,
339
                'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
340
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
341
            ],
342
            [
343
                'class' => PostsComponentConfiguration::class,
344
                'envVariable' => PostsEnvironment::POST_LIST_MAX_LIMIT,
345
                'module' => SchemaTypeModuleResolver::SCHEMA_POSTS,
346
                'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
347
                'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
348
            ],
349
            [
350
                'class' => PostsComponentConfiguration::class,
351
                'envVariable' => PostsEnvironment::ADD_POST_TYPE_TO_CUSTOMPOST_UNION_TYPES,
352
                'module' => SchemaTypeModuleResolver::SCHEMA_POSTS,
353
                'option' => SchemaTypeModuleResolver::OPTION_ADD_TYPE_TO_CUSTOMPOST_UNION_TYPE,
354
            ],
355
            // User default/max limits
356
            [
357
                'class' => UsersComponentConfiguration::class,
358
                'envVariable' => UsersEnvironment::USER_LIST_DEFAULT_LIMIT,
359
                'module' => SchemaTypeModuleResolver::SCHEMA_USERS,
360
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
361
            ],
362
            [
363
                'class' => UsersComponentConfiguration::class,
364
                'envVariable' => UsersEnvironment::USER_LIST_MAX_LIMIT,
365
                'module' => SchemaTypeModuleResolver::SCHEMA_USERS,
366
                'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
367
            ],
368
            // Tag default/max limits
369
            [
370
                'class' => TagsComponentConfiguration::class,
371
                'envVariable' => TagsEnvironment::TAG_LIST_DEFAULT_LIMIT,
372
                'module' => SchemaTypeModuleResolver::SCHEMA_TAGS,
373
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
374
            ],
375
            [
376
                'class' => TagsComponentConfiguration::class,
377
                'envVariable' => TagsEnvironment::TAG_LIST_MAX_LIMIT,
378
                'module' => SchemaTypeModuleResolver::SCHEMA_TAGS,
379
                'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
380
            ],
381
            // Page default/max limits, add to CustomPostUnion
382
            [
383
                'class' => PagesComponentConfiguration::class,
384
                'envVariable' => PagesEnvironment::PAGE_LIST_DEFAULT_LIMIT,
385
                'module' => SchemaTypeModuleResolver::SCHEMA_PAGES,
386
                'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
387
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
388
            ],
389
            [
390
                'class' => PagesComponentConfiguration::class,
391
                'envVariable' => PagesEnvironment::PAGE_LIST_MAX_LIMIT,
392
                'module' => SchemaTypeModuleResolver::SCHEMA_PAGES,
393
                'optionModule' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
394
                'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
395
            ],
396
            [
397
                'class' => PagesComponentConfiguration::class,
398
                'envVariable' => PagesEnvironment::ADD_PAGE_TYPE_TO_CUSTOMPOST_UNION_TYPES,
399
                'module' => SchemaTypeModuleResolver::SCHEMA_PAGES,
400
                'option' => SchemaTypeModuleResolver::OPTION_ADD_TYPE_TO_CUSTOMPOST_UNION_TYPE,
401
            ],
402
            // Custom post default/max limits
403
            [
404
                'class' => CustomPostsComponentConfiguration::class,
405
                'envVariable' => CustomPostsEnvironment::CUSTOMPOST_LIST_DEFAULT_LIMIT,
406
                'module' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
407
                'option' => SchemaTypeModuleResolver::OPTION_LIST_DEFAULT_LIMIT,
408
            ],
409
            [
410
                'class' => CustomPostsComponentConfiguration::class,
411
                'envVariable' => CustomPostsEnvironment::CUSTOMPOST_LIST_MAX_LIMIT,
412
                'module' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
413
                'option' => SchemaTypeModuleResolver::OPTION_LIST_MAX_LIMIT,
414
            ],
415
            // Custom post, if there is only one custom type, use it instead of the Union
416
            [
417
                'class' => CustomPostsComponentConfiguration::class,
418
                'envVariable' => CustomPostsEnvironment::USE_SINGLE_TYPE_INSTEAD_OF_CUSTOMPOST_UNION_TYPE,
419
                'module' => SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS,
420
                'option' => SchemaTypeModuleResolver::OPTION_USE_SINGLE_TYPE_INSTEAD_OF_UNION_TYPE,
421
            ],
422
        ];
423
        // For each environment variable, see if its value has been saved in the settings
424
        $userSettingsManager = UserSettingsManagerFacade::getInstance();
425
        foreach ($mappings as $mapping) {
426
            $module = $mapping['module'];
427
            $condition = $mapping['condition'] ?? true;
428
            // Check if the hook must be executed always (condition => 'any') or with
429
            // stated enabled (true) or disabled (false). By default, it's enabled
430
            if ($condition !== 'any' && $condition !== $moduleRegistry->isModuleEnabled($module)) {
431
                continue;
432
            }
433
            // If the environment value has been defined, or the constant in wp-config.php,
434
            // then do nothing, since they have priority
435
            $envVariable = $mapping['envVariable'];
436
            if (getenv($envVariable) !== false || PluginConfigurationHelpers::isWPConfigConstantDefined($envVariable)) {
437
                continue;
438
            }
439
            $hookName = ComponentConfigurationHelpers::getHookName(
440
                $mapping['class'],
441
                $envVariable
442
            );
443
            $option = $mapping['option'];
444
            $optionModule = $mapping['optionModule'] ?? $module;
445
            // Make explicit it can be null so that PHPStan level 3 doesn't fail
446
            $callback = $mapping['callback'] ?? null;
447
            \add_filter(
448
                $hookName,
449
                function () use ($userSettingsManager, $optionModule, $option, $callback) {
450
                    $value = $userSettingsManager->getSetting($optionModule, $option);
451
                    if (!is_null($callback)) {
452
                        return $callback($value);
453
                    }
454
                    return $value;
455
                }
456
            );
457
        }
458
    }
459
460
    /**
461
     * Map the environment variables from the components, to WordPress wp-config.php constants
462
     */
463
    protected static function mapEnvVariablesToWPConfigConstants(): void
464
    {
465
        // All the environment variables to override
466
        $mappings = [
467
            [
468
                'class' => ComponentConfiguration::class,
469
                'envVariable' => Environment::ADD_EXCERPT_AS_DESCRIPTION,
470
            ],
471
            [
472
                'class' => GraphQLEndpointForWPComponentConfiguration::class,
473
                'envVariable' => GraphQLEndpointForWPEnvironment::GRAPHQL_API_ENDPOINT,
474
            ],
475
            [
476
                'class' => GraphQLClientsForWPComponentConfiguration::class,
477
                'envVariable' => GraphQLClientsForWPEnvironment::GRAPHIQL_CLIENT_ENDPOINT,
478
            ],
479
            [
480
                'class' => GraphQLClientsForWPComponentConfiguration::class,
481
                'envVariable' => GraphQLClientsForWPEnvironment::VOYAGER_CLIENT_ENDPOINT,
482
            ],
483
            [
484
                'class' => AccessControlComponentConfiguration::class,
485
                'envVariable' => AccessControlEnvironment::USE_PRIVATE_SCHEMA_MODE,
486
            ],
487
            [
488
                'class' => AccessControlComponentConfiguration::class,
489
                'envVariable' => AccessControlEnvironment::ENABLE_INDIVIDUAL_CONTROL_FOR_PUBLIC_PRIVATE_SCHEMA_MODE,
490
            ],
491
            [
492
                'class' => ComponentModelComponentConfiguration::class,
493
                'envVariable' => ComponentModelEnvironment::NAMESPACE_TYPES_AND_INTERFACES,
494
            ],
495
            [
496
                'class' => CacheControlComponentConfiguration::class,
497
                'envVariable' => CacheControlEnvironment::DEFAULT_CACHE_CONTROL_MAX_AGE,
498
            ],
499
        ];
500
        // For each environment variable, see if it has been defined as a wp-config.php constant
501
        foreach ($mappings as $mapping) {
502
            $class = $mapping['class'];
503
            $envVariable = $mapping['envVariable'];
504
505
            // If the environment value has been defined, then do nothing, since it has priority
506
            if (getenv($envVariable) !== false) {
507
                continue;
508
            }
509
            $hookName = ComponentConfigurationHelpers::getHookName(
510
                $class,
511
                $envVariable
512
            );
513
514
            \add_filter(
515
                $hookName,
516
                /**
517
                 * Override the value of an environment variable if it has been definedas a constant
518
                 * in wp-config.php, with the environment name prepended with "GRAPHQL_API_"
519
                 */
520
                function ($value) use ($envVariable) {
521
                    if (PluginConfigurationHelpers::isWPConfigConstantDefined($envVariable)) {
522
                        return PluginConfigurationHelpers::getWPConfigConstantValue($envVariable);
523
                    }
524
                    return $value;
525
                }
526
            );
527
        }
528
    }
529
530
    /**
531
     * Provide the configuration to cache the container
532
     *
533
     * @return array<mixed> Array with args to pass to `AppLoader::initializeContainers` - [0]: cache container? (bool), [1]: container namespace (string|null)
534
     */
535
    public static function getContainerCacheConfiguration(): array
536
    {
537
        $moduleRegistry = ModuleRegistryFacade::getInstance();
538
        $containerConfigurationCacheNamespace = null;
539
        if ($cacheContainerConfiguration = $moduleRegistry->isModuleEnabled(CacheFunctionalityModuleResolver::CONFIGURATION_CACHE)) {
540
            $cacheConfigurationManager = CacheConfigurationManagerFacade::getInstance();
541
            $containerConfigurationCacheNamespace = $cacheConfigurationManager->getNamespace();
542
        }
543
        return [
544
            $cacheContainerConfiguration,
545
            $containerConfigurationCacheNamespace
546
        ];
547
    }
548
549
    /**
550
     * Provide the configuration for all components required in the plugin
551
     *
552
     * @return array<string, array> [key]: Component class, [value]: Configuration
553
     */
554
    public static function getComponentClassConfiguration(): array
555
    {
556
        $componentClassConfiguration = [];
557
        self::addPredefinedComponentClassConfiguration($componentClassConfiguration);
558
        self::addBasedOnModuleEnabledStateComponentClassConfiguration($componentClassConfiguration);
559
        return $componentClassConfiguration;
560
    }
561
562
    /**
563
     * Add the fixed configuration for all components required in the plugin
564
     *
565
     * @param array<string, array> $componentClassConfiguration [key]: Component class, [value]: Configuration
566
     */
567
    protected static function addPredefinedComponentClassConfiguration(array &$componentClassConfiguration): void
568
    {
569
        $moduleRegistry = ModuleRegistryFacade::getInstance();
570
        $isDev = PluginEnvironment::isPluginEnvironmentDev();
571
572
        $componentClassConfiguration[Component::class] = [
573
            \PoP\Engine\Environment::ADD_MANDATORY_CACHE_CONTROL_DIRECTIVE => false,
574
        ];
575
        $componentClassConfiguration[\GraphQLByPoP\GraphQLClientsForWP\Component::class] = [
576
            \GraphQLByPoP\GraphQLClientsForWP\Environment::GRAPHQL_CLIENTS_COMPONENT_URL => \GRAPHQL_API_URL . 'vendor/graphql-by-pop/graphql-clients-for-wp',
577
        ];
578
        $componentClassConfiguration[\PoP\APIEndpointsForWP\Component::class] = [
579
            // Disable the Native endpoint
580
            \PoP\APIEndpointsForWP\Environment::DISABLE_NATIVE_API_ENDPOINT => true,
581
        ];
582
        $componentClassConfiguration[\GraphQLByPoP\GraphQLRequest\Component::class] = [
583
            // Disable processing ?query=...
584
            \GraphQLByPoP\GraphQLRequest\Environment::DISABLE_GRAPHQL_API_FOR_POP => true,
585
            // Enable Multiple Query Execution?
586
            \GraphQLByPoP\GraphQLRequest\Environment::ENABLE_MULTIPLE_QUERY_EXECUTION => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::MULTIPLE_QUERY_EXECUTION),
587
        ];
588
        // Cache the container
589
        if ($moduleRegistry->isModuleEnabled(CacheFunctionalityModuleResolver::CONFIGURATION_CACHE)) {
590
            $componentClassConfiguration[\PoP\Root\Component::class] = [
591
                \PoP\Root\Environment::THROW_EXCEPTION_IF_CACHE_SETUP_ERROR => $isDev,
592
            ];
593
        }
594
        $componentClassConfiguration[\GraphQLByPoP\GraphQLServer\Component::class] = [
595
            // Expose the "self" field when doing Low Level Query Editing
596
            GraphQLServerEnvironment::ADD_SELF_FIELD_FOR_ROOT_TYPE_TO_SCHEMA => $moduleRegistry->isModuleEnabled(UserInterfaceFunctionalityModuleResolver::LOW_LEVEL_PERSISTED_QUERY_EDITING),
597
            // Enable @removeIfNull?
598
            GraphQLServerEnvironment::ENABLE_REMOVE_IF_NULL_DIRECTIVE => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::REMOVE_IF_NULL_DIRECTIVE),
599
            // Enable Proactive Feedback?
600
            GraphQLServerEnvironment::ENABLE_PROACTIVE_FEEDBACK => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::PROACTIVE_FEEDBACK),
601
        ];
602
        $componentClassConfiguration[\PoP\API\Component::class] = [
603
            // Enable Embeddable Fields?
604
            \PoP\API\Environment::ENABLE_EMBEDDABLE_FIELDS => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::EMBEDDABLE_FIELDS),
605
            // Enable Mutations?
606
            \PoP\API\Environment::ENABLE_MUTATIONS => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::MUTATIONS),
607
608
        ];
609
        $componentClassConfiguration[\GraphQLByPoP\GraphQLQuery\Component::class] = [
610
            // Temporarily disabled
611
            // // Enable Composable Directives?
612
            // GraphQLQueryEnvironment::ENABLE_COMPOSABLE_DIRECTIVES => $moduleRegistry->isModuleEnabled(OperationalFunctionalityModuleResolver::COMPOSABLE_DIRECTIVES),
613
        ];
614
    }
615
616
    /**
617
     * Return the opposite value
618
     *
619
     * @param boolean $value
620
     * @return boolean
621
     */
622
    protected static function opposite(bool $value): bool
623
    {
624
        return !$value;
625
    }
626
627
    /**
628
     * Add configuration values if modules are enabled or disabled
629
     *
630
     * @param array<string, array> $componentClassConfiguration [key]: Component class, [value]: Configuration
631
     */
632
    protected static function addBasedOnModuleEnabledStateComponentClassConfiguration(array &$componentClassConfiguration): void
633
    {
634
        $moduleRegistry = ModuleRegistryFacade::getInstance();
635
        $moduleToComponentClassConfigurationMappings = [
636
            [
637
                'module' => EndpointFunctionalityModuleResolver::SINGLE_ENDPOINT,
638
                'class' => \GraphQLByPoP\GraphQLEndpointForWP\Component::class,
639
                'envVariable' => \GraphQLByPoP\GraphQLEndpointForWP\Environment::DISABLE_GRAPHQL_API_ENDPOINT,
640
                'callback' => [self::class, 'opposite'],
641
            ],
642
            [
643
                'module' => ClientFunctionalityModuleResolver::GRAPHIQL_FOR_SINGLE_ENDPOINT,
644
                'class' => \GraphQLByPoP\GraphQLClientsForWP\Component::class,
645
                'envVariable' => \GraphQLByPoP\GraphQLClientsForWP\Environment::DISABLE_GRAPHIQL_CLIENT_ENDPOINT,
646
                'callback' => [self::class, 'opposite'],
647
            ],
648
            [
649
                'module' => ClientFunctionalityModuleResolver::INTERACTIVE_SCHEMA_FOR_SINGLE_ENDPOINT,
650
                'class' => \GraphQLByPoP\GraphQLClientsForWP\Component::class,
651
                'envVariable' => \GraphQLByPoP\GraphQLClientsForWP\Environment::DISABLE_VOYAGER_CLIENT_ENDPOINT,
652
                'callback' => [self::class, 'opposite'],
653
            ],
654
            [
655
                'module' => ClientFunctionalityModuleResolver::GRAPHIQL_EXPLORER,
656
                'class' => \GraphQLByPoP\GraphQLClientsForWP\Component::class,
657
                'envVariable' => \GraphQLByPoP\GraphQLClientsForWP\Environment::USE_GRAPHIQL_EXPLORER,
658
            ],
659
            // Cache the component model configuration
660
            [
661
                'module' => CacheFunctionalityModuleResolver::CONFIGURATION_CACHE,
662
                'class' => \PoP\ComponentModel\Component::class,
663
                'envVariable' => \PoP\ComponentModel\Environment::USE_COMPONENT_MODEL_CACHE,
664
            ],
665
            // Cache the schema
666
            [
667
                'module' => CacheFunctionalityModuleResolver::SCHEMA_CACHE,
668
                'class' => \PoP\API\Component::class,
669
                'envVariable' => \PoP\API\Environment::USE_SCHEMA_DEFINITION_CACHE,
670
            ],
671
        ];
672
        foreach ($moduleToComponentClassConfigurationMappings as $mapping) {
673
            // Copy the state (enabled/disabled) to the component
674
            $value = $moduleRegistry->isModuleEnabled($mapping['module']);
675
            // Make explicit it can be null so that PHPStan level 3 doesn't fail
676
            $callback = $mapping['callback'] ?? null;
677
            if (!is_null($callback)) {
678
                $value = $callback($value);
679
            }
680
            $componentClassConfiguration[$mapping['class']][$mapping['envVariable']] = $value;
681
        }
682
    }
683
684
    /**
685
     * Provide the classes of the components whose
686
     * schema initialization must be skipped
687
     *
688
     * @return string[]
689
     */
690
    public static function getSkippingSchemaComponentClasses(): array
691
    {
692
        $moduleRegistry = ModuleRegistryFacade::getInstance();
693
694
        // Component classes enabled/disabled by module
695
        $maybeSkipSchemaModuleComponentClasses = [
696
            SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTS => [
697
                \PoPSchema\CustomPostMedia\Component::class,
698
            ],
699
            SchemaTypeModuleResolver::SCHEMA_GENERIC_CUSTOMPOSTS => [
700
                \PoPSchema\GenericCustomPosts\Component::class,
701
            ],
702
            SchemaTypeModuleResolver::SCHEMA_POSTS => [
703
                \PoPSchema\Posts\Component::class,
704
            ],
705
            SchemaTypeModuleResolver::SCHEMA_COMMENTS => [
706
                \PoPSchema\Comments\Component::class,
707
            ],
708
            SchemaTypeModuleResolver::SCHEMA_USERS => [
709
                \PoPSchema\Users\Component::class,
710
                \PoPSchema\UserState\Component::class,
711
            ],
712
            SchemaTypeModuleResolver::SCHEMA_USER_ROLES => [
713
                \PoPSchema\UserRoles\Component::class,
714
            ],
715
            SchemaTypeModuleResolver::SCHEMA_PAGES => [
716
                \PoPSchema\Pages\Component::class,
717
            ],
718
            SchemaTypeModuleResolver::SCHEMA_MEDIA => [
719
                \PoPSchema\CustomPostMedia\Component::class,
720
                \PoPSchema\Media\Component::class,
721
            ],
722
            SchemaTypeModuleResolver::SCHEMA_TAGS => [
723
                \PoPSchema\Tags\Component::class,
724
            ],
725
            SchemaTypeModuleResolver::SCHEMA_POST_TAGS => [
726
                \PoPSchema\PostTags\Component::class,
727
            ],
728
            SchemaTypeModuleResolver::SCHEMA_USER_STATE_MUTATIONS => [
729
                \PoPSchema\UserStateMutations\Component::class,
730
            ],
731
            SchemaTypeModuleResolver::SCHEMA_POST_MUTATIONS => [
732
                \PoPSchema\PostMutations\Component::class,
733
            ],
734
            SchemaTypeModuleResolver::SCHEMA_CUSTOMPOSTMEDIA_MUTATIONS => [
735
                \PoPSchema\CustomPostMediaMutations\Component::class,
736
            ],
737
            SchemaTypeModuleResolver::SCHEMA_COMMENT_MUTATIONS => [
738
                \PoPSchema\CommentMutations\Component::class,
739
            ],
740
        ];
741
        $skipSchemaModuleComponentClasses = array_filter(
742
            $maybeSkipSchemaModuleComponentClasses,
743
            fn ($module) => !$moduleRegistry->isModuleEnabled($module),
744
            ARRAY_FILTER_USE_KEY
745
        );
746
        return GeneralUtils::arrayFlatten(
747
            array_values(
748
                $skipSchemaModuleComponentClasses
749
            )
750
        );
751
    }
752
}
753