• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql / 18790791685

24 Oct 2025 08:03PM UTC coverage: 83.207% (-1.4%) from 84.575%
18790791685

push

github

actions-user
release: merge develop into master for v2.5.0

2 of 4 new or added lines in 2 files covered. (50.0%)

189 existing lines in 10 files now uncovered.

16143 of 19401 relevant lines covered (83.21%)

257.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.98
/src/WPGraphQL.php
1
<?php
2
/**
3
 * The global WPGraphQL class.
4
 *
5
 * @package WPGraphQL
6
 */
7

8
use WPGraphQL\Admin\Admin;
9
use WPGraphQL\AppContext;
10
use WPGraphQL\Registry\SchemaRegistry;
11
use WPGraphQL\Registry\TypeRegistry;
12
use WPGraphQL\Router;
13
use WPGraphQL\Utils\InstrumentSchema;
14
use WPGraphQL\Utils\Preview;
15

16
/**
17
 * Class WPGraphQL
18
 *
19
 * This is the one true WPGraphQL class
20
 */
21
final class WPGraphQL {
22

23
        /**
24
         * Stores the instance of the WPGraphQL class
25
         *
26
         * @var \WPGraphQL The one true WPGraphQL
27
         * @since  0.0.1
28
         */
29
        private static self $instance;
30

31
        /**
32
         * Holds the Schema def
33
         *
34
         * @var ?\WPGraphQL\WPSchema $schema The Schema used for the GraphQL API
35
         */
36
        protected static $schema;
37

38
        /**
39
         * Holds the TypeRegistry instance
40
         *
41
         * @var ?\WPGraphQL\Registry\TypeRegistry $type_registry The registry that holds all GraphQL Types
42
         */
43
        protected static $type_registry;
44

45
        /**
46
         * Stores an array of allowed post types
47
         *
48
         * @var ?\WP_Post_Type[] allowed_post_types
49
         * @since  0.0.5
50
         */
51
        protected static ?array $allowed_post_types;
52

53
        /**
54
         * Stores an array of allowed taxonomies
55
         *
56
         * @var ?\WP_Taxonomy[] allowed_taxonomies
57
         * @since  0.0.5
58
         */
59
        protected static ?array $allowed_taxonomies;
60

61
        /**
62
         * Whether a GraphQL request is currently being processed.
63
         */
64
        protected static bool $is_graphql_request = false;
65

66
        /**
67
         * Whether an Introspection query is currently being processed.
68
         */
69
        protected static bool $is_introspection_query = false;
70

71
        /**
72
         * The instance of the WPGraphQL object
73
         *
74
         * @return \WPGraphQL - The one true WPGraphQL
75
         * @since  0.0.1
76
         */
77
        public static function instance(): self {
3✔
78
                if ( ! isset( self::$instance ) ) {
3✔
79
                        self::$instance = new self();
×
80
                        self::$instance->setup_constants();
×
81
                        self::$instance->includes();
×
82
                        self::$instance->actions();
×
83
                        self::$instance->filters();
×
84
                        self::$instance->upgrade();
×
85
                        self::$instance->deprecated();
×
86
                }
87

88
                /**
89
                 * Return the WPGraphQL Instance
90
                 */
91
                return self::$instance;
3✔
92
        }
93

94
        /**
95
         * Throw error on object clone.
96
         * The whole idea of the singleton design pattern is that there is a single object
97
         * therefore, we don't want the object to be cloned.
98
         *
99
         * @return void
100
         * @since  0.0.1
101
         */
102
        public function __clone() {
×
103
                // Cloning instances of the class is forbidden.
104
                _doing_it_wrong( __FUNCTION__, esc_html__( 'The WPGraphQL class should not be cloned.', 'wp-graphql' ), '0.0.1' );
×
105
        }
106

107
        /**
108
         * Disable unserializing of the class.
109
         *
110
         * @return void
111
         * @since  0.0.1
112
         */
113
        public function __wakeup() {
×
114
                // De-serializing instances of the class is forbidden.
115
                _doing_it_wrong( __FUNCTION__, esc_html__( 'De-serializing instances of the WPGraphQL class is not allowed', 'wp-graphql' ), '0.0.1' );
×
116
        }
117

118
        /**
119
         * Setup plugin constants.
120
         *
121
         * @since  0.0.1
122
         */
123
        private function setup_constants(): void {
×
124
                graphql_setup_constants();
×
125
        }
126

127
        /**
128
         * Setup Experiments.
129
         */
UNCOV
130
        public function setup_experiments(): void {
×
UNCOV
131
                $experimental = new \WPGraphQL\Experimental\Experimental();
×
UNCOV
132
                $experimental->init();
×
133
        }
134

135
        /**
136
         * Include required files.
137
         * Uses composer's autoload
138
         *
139
         * @since  0.0.1
140
         */
UNCOV
141
        private function includes(): void {
×
UNCOV
142
        }
×
143

144
        /**
145
         * Set whether the request is a GraphQL request or not
146
         *
147
         * @param bool $is_graphql_request
148
         */
149
        public static function set_is_graphql_request( $is_graphql_request = false ): void {
784✔
150
                self::$is_graphql_request = $is_graphql_request;
784✔
151
        }
152

153
        /**
154
         * Whether the request is a graphql request or not
155
         */
156
        public static function is_graphql_request(): bool {
768✔
157
                return self::$is_graphql_request;
768✔
158
        }
159

160
        /**
161
         * Set whether the request is an introspection query or not
162
         *
163
         * @param bool $is_introspection_query
164
         *
165
         * @since 1.28.0
166
         */
167
        public static function set_is_introspection_query( bool $is_introspection_query = false ): void {
772✔
168
                self::$is_introspection_query = $is_introspection_query;
772✔
169
        }
170

171
        /**
172
         * Whether the request is an introspection query or not (query for __type or __schema)
173
         *
174
         * @since 1.28.0
175
         */
176
        public static function is_introspection_query(): bool {
628✔
177
                return (bool) self::$is_introspection_query;
628✔
178
        }
179

180
        /**
181
         * Sets up actions to run at certain spots throughout WordPress and the WPGraphQL execution
182
         * cycle
183
         */
184
        private function actions(): void {
780✔
185
                /**
186
                 * Init WPGraphQL after themes have been set up,
187
                 * allowing for both plugins and themes to register
188
                 * things before graphql_init
189
                 */
UNCOV
190
                add_action(
×
UNCOV
191
                        'after_setup_theme',
×
UNCOV
192
                        static function () {
×
UNCOV
193
                                new \WPGraphQL\Data\Config();
×
UNCOV
194
                                $router = new Router();
×
195
                                $router->init();
×
196
                                $instance = self::instance();
×
197

198
                                /**
199
                                 * Fire off init action
200
                                 *
201
                                 * @param \WPGraphQL $instance The instance of the WPGraphQL class
202
                                 */
UNCOV
203
                                do_action( 'graphql_init', $instance );
×
204
                        }
×
UNCOV
205
                );
×
206

207
                // Initialize the plugin url constant
208
                // see: https://developer.wordpress.org/reference/functions/plugins_url/#more-information
209
                add_action( 'init', [ $this, 'setup_plugin_url' ] );
×
210

211
                // Prevent WPGraphQL Insights from running
UNCOV
212
                remove_action( 'init', '\WPGraphQL\Extensions\graphql_insights_init' );
×
213

214
                /**
215
                 * Flush permalinks if the registered GraphQL endpoint has not yet been registered.
216
                 */
217
                add_action( 'wp_loaded', [ $this, 'maybe_flush_permalinks' ] );
×
218

219
                /**
220
                 * Hook in before fields resolve to check field permissions
221
                 */
222
                add_action(
×
UNCOV
223
                        'graphql_before_resolve_field',
×
UNCOV
224
                        [
×
225
                                '\WPGraphQL\Utils\InstrumentSchema',
×
UNCOV
226
                                'check_field_permissions',
×
UNCOV
227
                        ],
×
228
                        10,
×
229
                        8
×
UNCOV
230
                );
×
231

232
                // Determine what to show in graphql
UNCOV
233
                add_action( 'init_graphql_request', 'register_initial_settings', 10 );
×
234

235
                // Throw an exception
UNCOV
236
                add_action( 'do_graphql_request', [ $this, 'min_php_version_check' ] );
×
UNCOV
237
                add_action( 'do_graphql_request', [ $this, 'introspection_check' ], 10, 4 );
×
238

239
                // Initialize Admin functionality
UNCOV
240
                add_action( 'after_setup_theme', [ $this, 'init_admin' ] );
×
UNCOV
241
                add_action( 'after_setup_theme', [ $this, 'setup_experiments' ] );
×
242

243
                add_action(
780✔
244
                        'init_graphql_request',
780✔
245
                        static function () {
780✔
246
                                $tracing = new \WPGraphQL\Utils\Tracing();
780✔
247
                                $tracing->init();
780✔
248

249
                                $query_log = new \WPGraphQL\Utils\QueryLog();
780✔
250
                                $query_log->init();
780✔
251
                        }
780✔
252
                );
780✔
253

254
                // Initialize Update functionality.
UNCOV
255
                ( new \WPGraphQL\Admin\Updates\Updates() )->init();
×
256
        }
257

258
        /**
259
         * @param ?string                         $query     The GraphQL query
260
         * @param ?string                         $operation The name of the operation
261
         * @param ?array<mixed>                   $variables Variables to be passed to your GraphQL
262
         *                                                   request
263
         * @param \GraphQL\Server\OperationParams $params    The Operation Params. This includes any
264
         *                                                   extra params,
265
         *
266
         * @throws \GraphQL\Error\SyntaxError
267
         * @throws \Exception
268
         */
269
        public function introspection_check( ?string $query, ?string $operation, ?array $variables, \GraphQL\Server\OperationParams $params ): void {
774✔
270

271
                if ( empty( $query ) ) {
774✔
272
                        return;
1✔
273
                }
274

275
                $ast              = \GraphQL\Language\Parser::parse( $query );
773✔
276
                $is_introspection = false;
772✔
277

278
                \GraphQL\Language\Visitor::visit(
772✔
279
                        $ast,
772✔
280
                        [
772✔
281
                                'Field' => static function ( \GraphQL\Language\AST\Node $node ) use ( &$is_introspection ) {
772✔
282
                                        if ( $node instanceof \GraphQL\Language\AST\FieldNode && ( '__schema' === $node->name->value || '__type' === $node->name->value ) ) {
772✔
283
                                                $is_introspection = true;
73✔
284
                                                return \GraphQL\Language\Visitor::stop();
73✔
285
                                        }
286
                                },
772✔
287
                        ]
772✔
288
                );
772✔
289

290
                self::set_is_introspection_query( $is_introspection );
772✔
291
        }
292

293
        /**
294
         * Check if the minimum PHP version requirement is met before execution begins.
295
         *
296
         * If the server is running a lower version than required, throw an exception and prevent
297
         * further execution.
298
         *
299
         * @throws \Exception
300
         */
301
        public function min_php_version_check(): void {
774✔
302
                if ( defined( 'GRAPHQL_MIN_PHP_VERSION' ) && version_compare( PHP_VERSION, GRAPHQL_MIN_PHP_VERSION, '<' ) ) {
774✔
303
                        throw new \Exception(
×
UNCOV
304
                                esc_html(
×
UNCOV
305
                                        sprintf(
×
306
                                        // translators: %1$s is the current PHP version, %2$s is the minimum required PHP version.
UNCOV
307
                                                __( 'The server\'s current PHP version %1$s is lower than the WPGraphQL minimum required version: %2$s', 'wp-graphql' ),
×
UNCOV
308
                                                PHP_VERSION,
×
UNCOV
309
                                                GRAPHQL_MIN_PHP_VERSION
×
310
                                        )
×
UNCOV
311
                                )
×
312
                        );
×
313
                }
314
        }
315

316
        /**
317
         * Sets up the plugin url
318
         */
UNCOV
319
        public function setup_plugin_url(): void {
×
320
                // Plugin Folder URL.
UNCOV
321
                if ( ! defined( 'WPGRAPHQL_PLUGIN_URL' ) ) {
×
UNCOV
322
                        define( 'WPGRAPHQL_PLUGIN_URL', plugin_dir_url( dirname( __DIR__ ) . '/wp-graphql.php' ) );
×
323
                }
324
        }
325

326
        /**
327
         * Determine the post_types and taxonomies, etc that should show in GraphQL.
328
         */
UNCOV
329
        public function setup_types(): void {
×
330
                /**
331
                 * Set up the settings, post_types and taxonomies to show_in_graphql
332
                 */
333
                self::show_in_graphql();
×
334
        }
335

336
        /**
337
         * Flush permalinks if the GraphQL Endpoint route isn't yet registered.
338
         */
UNCOV
339
        public function maybe_flush_permalinks(): void {
×
340
                $rules = get_option( 'rewrite_rules' );
×
UNCOV
341
                if ( ! isset( $rules[ graphql_get_endpoint() . '/?$' ] ) ) {
×
342
                        flush_rewrite_rules(); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules
×
343
                }
344
        }
345

346
        /**
347
         * Setup filters
348
         */
349
        private function filters(): void {
×
350
                // Filter the post_types and taxonomies to show in the GraphQL Schema
351
                $this->setup_types();
×
352

353
                /**
354
                 * Instrument the Schema to provide Resolve Hooks and sanitize Schema output
355
                 */
UNCOV
356
                add_filter(
×
UNCOV
357
                        'graphql_get_type',
×
358
                        [
×
359
                                InstrumentSchema::class,
×
360
                                'instrument_resolvers',
×
361
                        ],
×
362
                        10,
×
363
                        2
×
364
                );
×
365

366
                // Filter how metadata is retrieved during GraphQL requests
UNCOV
367
                add_filter(
×
UNCOV
368
                        'get_post_metadata',
×
UNCOV
369
                        [
×
UNCOV
370
                                Preview::class,
×
UNCOV
371
                                'filter_post_meta_for_previews',
×
UNCOV
372
                        ],
×
UNCOV
373
                        10,
×
374
                        4
×
375
                );
×
376

377
                /**
378
                 * Prevent WPML from redirecting within WPGraphQL requests
379
                 *
380
                 * @see https://github.com/wp-graphql/wp-graphql/issues/1626#issue-769089073
381
                 * @since 1.27.0
382
                 */
383
                add_filter(
×
384
                        'wpml_is_redirected',
×
UNCOV
385
                        static function ( bool $is_redirect ) {
×
UNCOV
386
                                if ( is_graphql_request() ) {
×
UNCOV
387
                                        return false;
×
388
                                }
UNCOV
389
                                return $is_redirect;
×
390
                        },
×
391
                        10,
×
392
                        1
×
UNCOV
393
                );
×
394
        }
395

396
        /**
397
         * Private function to register deprecated functionality.
398
         */
399
        private function deprecated(): void {
×
UNCOV
400
                $deprecated = new WPGraphQL\Deprecated();
×
UNCOV
401
                $deprecated->register();
×
402
        }
403

404
        /**
405
         * Upgrade routine.
406
         */
UNCOV
407
        public function upgrade(): void {
×
UNCOV
408
                $version = get_option( 'wp_graphql_version', null );
×
409

410
                // If the version is not set, this is a fresh install, not an update.
411
                // set the version and return.
UNCOV
412
                if ( ! $version ) {
×
UNCOV
413
                        update_option( 'wp_graphql_version', WPGRAPHQL_VERSION );
×
UNCOV
414
                        return;
×
415
                }
416

417
                // If the version is less than the current version, run the update routine
UNCOV
418
                if ( version_compare( $version, WPGRAPHQL_VERSION, '<' ) ) {
×
UNCOV
419
                        $this->run_update_routines( $version );
×
UNCOV
420
                        update_option( 'wp_graphql_version', WPGRAPHQL_VERSION );
×
421
                }
422
        }
423

424
        /**
425
         * Executes update routines based on the previously stored version.
426
         *
427
         * This triggers an action that passes the previous version and new version and allows for specific actions or
428
         * modifications needed to bring installations up-to-date with the current plugin version.
429
         *
430
         * Each update routine (callback that hooks into "graphql_do_update_routine") should handle backward compatibility as gracefully as possible.
431
         *
432
         * @since 1.2.3
433
         * @param string|null $stored_version The version number currently stored in the database.
434
         *                                    Null if no version has been previously stored.
435
         */
UNCOV
436
        public function run_update_routines( ?string $stored_version = null ): void {
×
437

438
                // bail if the stored version is empty, or the WPGRAPHQL_VERSION constant is not set
UNCOV
439
                if ( ! defined( 'WPGRAPHQL_VERSION' ) || ! $stored_version ) {
×
UNCOV
440
                        return;
×
441
                }
442

443
                // If the stored version is less than the current version, run the upgrade routine
UNCOV
444
                if ( version_compare( $stored_version, WPGRAPHQL_VERSION, '<' ) ) {
×
445

446
                        // Clear the extensions cache
UNCOV
447
                        $this->clear_extensions_cache();
×
448

449
                        /**
450
                         * Fires the update routine.
451
                         *
452
                         * @param string $stored_version The version number currently stored in the database.
453
                         * @param string $new_version    The version number of the current plugin.
454
                         */
UNCOV
455
                        do_action( 'graphql_do_update_routine', $stored_version, WPGRAPHQL_VERSION );
×
456
                }
457
        }
458

459
        /**
460
         * Clear all caches in the "wpgraphql_extensions" cache group.
461
         */
UNCOV
462
        public function clear_extensions_cache(): void {
×
UNCOV
463
                global $wp_object_cache;
×
464

UNCOV
465
                if ( isset( $wp_object_cache->cache['wpgraphql_extensions'] ) ) {
×
466
                        foreach ( $wp_object_cache->cache['wpgraphql_extensions'] as $key => $value ) {
×
467
                                wp_cache_delete( $key, 'wpgraphql_extensions' );
×
468
                        }
469
                }
470
        }
471

472
        /**
473
         * Initialize admin functionality.
474
         */
UNCOV
475
        public function init_admin(): void {
×
UNCOV
476
                $admin = new Admin();
×
UNCOV
477
                $admin->init();
×
478
        }
479

480
        /**
481
         * This sets up built-in post_types and taxonomies to show in the GraphQL Schema.
482
         *
483
         * @since  0.0.2
484
         */
485
        public static function show_in_graphql(): void {
14✔
486
                add_filter( 'register_post_type_args', [ self::class, 'setup_default_post_types' ], 10, 2 );
14✔
487
                add_filter( 'register_taxonomy_args', [ self::class, 'setup_default_taxonomies' ], 10, 2 );
14✔
488

489
                // Run late so the user can filter the args themselves.
490
                add_filter( 'register_post_type_args', [ self::class, 'register_graphql_post_type_args' ], 99, 2 );
14✔
491
                add_filter( 'register_taxonomy_args', [ self::class, 'register_graphql_taxonomy_args' ], 99, 2 );
14✔
492
        }
493

494
        /**
495
         * Sets up the default post types to show_in_graphql.
496
         *
497
         * @param array<string,mixed> $args      Array of arguments for registering a post type.
498
         * @param string              $post_type Post type key.
499
         *
500
         * @return array<string,mixed>
501
         */
502
        public static function setup_default_post_types( $args, $post_type ) {
142✔
503
                // Adds GraphQL support for attachments.
504
                if ( 'attachment' === $post_type ) {
142✔
UNCOV
505
                        $args['show_in_graphql']     = true;
×
506
                        $args['graphql_single_name'] = 'mediaItem';
×
507
                        $args['graphql_plural_name'] = 'mediaItems';
×
508
                        $args['graphql_description'] = __( 'Represents uploaded media, including images, videos, documents, and audio files.', 'wp-graphql' );
×
509
                } elseif ( 'page' === $post_type ) { // Adds GraphQL support for pages.
142✔
UNCOV
510
                        $args['show_in_graphql']     = true;
×
UNCOV
511
                        $args['graphql_single_name'] = 'page';
×
UNCOV
512
                        $args['graphql_plural_name'] = 'pages';
×
UNCOV
513
                        $args['graphql_description'] = __( 'A standalone content entry generally used for static, non-chronological content such as "About Us" or "Contact" pages.', 'wp-graphql' );
×
514
                } elseif ( 'post' === $post_type ) { // Adds GraphQL support for posts.
142✔
UNCOV
515
                        $args['show_in_graphql']     = true;
×
UNCOV
516
                        $args['graphql_single_name'] = 'post';
×
UNCOV
517
                        $args['graphql_plural_name'] = 'posts';
×
UNCOV
518
                        $args['graphql_description'] = __( 'A chronological content entry typically used for blog posts, news articles, or similar date-based content.', 'wp-graphql' );
×
519
                }
520

521
                return $args;
142✔
522
        }
523

524
        /**
525
         * Sets up the default taxonomies to show_in_graphql.
526
         *
527
         * @param array<string,mixed> $args     Array of arguments for registering a taxonomy.
528
         * @param string              $taxonomy Taxonomy key.
529
         *
530
         * @return array<string,mixed>
531
         * @since 1.12.0
532
         */
533
        public static function setup_default_taxonomies( $args, $taxonomy ) {
155✔
534
                // Adds GraphQL support for categories.
535
                if ( 'category' === $taxonomy ) {
155✔
536
                        $args['show_in_graphql']     = true;
68✔
537
                        $args['graphql_single_name'] = 'category';
68✔
538
                        $args['graphql_plural_name'] = 'categories';
68✔
539
                        $args['graphql_description'] = __( 'A taxonomy term that classifies content. Categories support hierarchy and can be used to create a nested structure.', 'wp-graphql' );
68✔
540
                } elseif ( 'post_tag' === $taxonomy ) { // Adds GraphQL support for tags.
155✔
541
                        $args['show_in_graphql']     = true;
68✔
542
                        $args['graphql_single_name'] = 'tag';
68✔
543
                        $args['graphql_plural_name'] = 'tags';
68✔
544
                        $args['graphql_description'] = __( 'A taxonomy term used to organize and classify content. Tags do not have a hierarchy and are generally used for more specific classifications.', 'wp-graphql' );
68✔
545
                } elseif ( 'post_format' === $taxonomy ) { // Adds GraphQL support for post formats.
155✔
546
                        $args['show_in_graphql']     = true;
68✔
547
                        $args['graphql_single_name'] = 'postFormat';
68✔
548
                        $args['graphql_plural_name'] = 'postFormats';
68✔
549
                        $args['graphql_description'] = __( 'A standardized classification system for content presentation styles. These formats can be used to display content differently based on type, such as "standard", "gallery", "video", etc.', 'wp-graphql' );
68✔
550
                }
551

552
                return $args;
155✔
553
        }
554

555
        /**
556
         * Set the GraphQL Post Type Args and pass them through a filter.
557
         *
558
         * @param array<string,mixed> $args           The graphql specific args for the post type
559
         * @param string              $post_type_name The name of the post type being registered
560
         *
561
         * @return array<string,mixed>
562
         * @throws \Exception
563
         * @since 1.12.0
564
         */
565
        public static function register_graphql_post_type_args( array $args, string $post_type_name ): array {
142✔
566
                // Bail early if the post type is hidden from the WPGraphQL schema.
567
                if ( empty( $args['show_in_graphql'] ) ) {
142✔
568
                        return $args;
1✔
569
                }
570

571
                $graphql_args = self::get_default_graphql_type_args();
142✔
572

573
                /**
574
                 * Filters the graphql args set on a post type
575
                 *
576
                 * @param array<string,mixed> $args           The graphql specific args for the post type
577
                 * @param string              $post_type_name The name of the post type being registered
578
                 */
579
                $graphql_args = apply_filters( 'register_graphql_post_type_args', $graphql_args, $post_type_name );
142✔
580

581
                return wp_parse_args( $args, $graphql_args );
142✔
582
        }
583

584
        /**
585
         * Set the GraphQL Taxonomy Args and pass them through a filter.
586
         *
587
         * @param array<string,mixed> $args          The graphql specific args for the taxonomy
588
         * @param string              $taxonomy_name The name of the taxonomy being registered
589
         *
590
         * @return array<string,mixed>
591
         * @throws \Exception
592
         * @since 1.12.0
593
         */
594
        public static function register_graphql_taxonomy_args( array $args, string $taxonomy_name ): array {
155✔
595
                // Bail early if the taxonomy  is hidden from the WPGraphQL schema.
596
                if ( empty( $args['show_in_graphql'] ) ) {
155✔
597
                        return $args;
68✔
598
                }
599

600
                $graphql_args = self::get_default_graphql_type_args();
155✔
601

602
                /**
603
                 * Filters the graphql args set on a taxonomy
604
                 *
605
                 * @param array<string,mixed> $args          The graphql specific args for the taxonomy
606
                 * @param string              $taxonomy_name The name of the taxonomy being registered
607
                 */
608
                $graphql_args = apply_filters( 'register_graphql_taxonomy_args', $graphql_args, $taxonomy_name );
155✔
609

610
                return wp_parse_args( $args, $graphql_args );
155✔
611
        }
612

613
        /**
614
         * This sets the post type /taxonomy GraphQL properties.
615
         *
616
         * @since 1.12.0
617
         *
618
         * @return array{
619
         *   graphql_kind: 'interface'|'object'|'union',
620
         *   graphql_resolve_type: ?callable,
621
         *   graphql_interfaces: string[],
622
         *   graphql_connections: string[],
623
         *   graphql_union_types: string[],
624
         *   graphql_register_root_field: bool,
625
         *   graphql_register_root_connection: bool,
626
         * }
627
         */
628
        public static function get_default_graphql_type_args(): array {
177✔
629
                return [
177✔
630
                        // The "kind" of GraphQL type to register. Can be `interface`, `object`, or `union`.
631
                        'graphql_kind'                     => 'object',
177✔
632
                        // The callback used to resolve the type. Only used if `graphql_kind` is an `interface` or `union`.
633
                        'graphql_resolve_type'             => null,
177✔
634
                        // An array of custom interfaces the type should implement.
635
                        'graphql_interfaces'               => [],
177✔
636
                        // An array of default interfaces the type should exclude.
637
                        'graphql_exclude_interfaces'       => [],
177✔
638
                        // An array of custom connections the type should implement.
639
                        'graphql_connections'              => [],
177✔
640
                        // An array of default connection field names the type should exclude.
641
                        'graphql_exclude_connections'      => [],
177✔
642
                        // An array of possible type the union can resolve to. Only used if `graphql_kind` is a `union`.
643
                        'graphql_union_types'              => [],
177✔
644
                        // Whether to register default connections to the schema.
645
                        'graphql_register_root_field'      => true,
177✔
646
                        'graphql_register_root_connection' => true,
177✔
647
                ];
177✔
648
        }
649

650
        /**
651
         * Get the post types that are allowed to be used in GraphQL.
652
         *
653
         * This gets all post_types that are set to show_in_graphql, but allows for external code
654
         * (plugins/theme) to filter the list of allowed_post_types to add/remove additional post_types
655
         *
656
         * @param 'names'|'objects'   $output Optional. The type of output to return. Accepts post type 'names' or 'objects'. Default 'names'.
657
         * @param array<string,mixed> $args   Optional. Arguments to filter allowed post types
658
         *
659
         * @return string[]|\WP_Post_Type[]
660
         * @phpstan-return ( $output is 'objects' ? \WP_Post_Type[] : string[] )
661
         *
662
         * @since  0.0.4
663
         * @since  1.8.1 adds $output as first param, and stores post type objects in class property.
664
         */
665
        public static function get_allowed_post_types( $output = 'names', $args = [] ): array {
697✔
666
                // Support deprecated param order.
667
                if ( is_array( $output ) ) {
697✔
UNCOV
668
                        _deprecated_argument( __METHOD__, '1.8.1', 'Passing `$args` to the first parameter will no longer be supported in the next major version of WPGraphQL.' );
×
UNCOV
669
                        $args   = $output;
×
UNCOV
670
                        $output = 'names';
×
671
                }
672

673
                // Initialize array of allowed post type objects.
674
                if ( empty( self::$allowed_post_types ) ) {
697✔
675
                        /**
676
                         * Get all post types objects.
677
                         *
678
                         * @var \WP_Post_Type[] $post_type_objects
679
                         */
680
                        $post_type_objects = get_post_types(
613✔
681
                                [ 'show_in_graphql' => true ],
613✔
682
                                'objects'
613✔
683
                        );
613✔
684

685
                        $post_type_names = wp_list_pluck( $post_type_objects, 'name' );
613✔
686

687
                        /**
688
                         * Pass through a filter to allow the post_types to be modified.
689
                         * For example if a certain post_type should not be exposed to the GraphQL API.
690
                         *
691
                         * @param string[]        $post_type_names   Array of post type names.
692
                         * @param \WP_Post_Type[] $post_type_objects Array of post type objects.
693
                         *
694
                         * @since 1.8.1 add $post_type_objects parameter.
695
                         * @since 0.0.2
696
                         */
697
                        $allowed_post_type_names = apply_filters( 'graphql_post_entities_allowed_post_types', $post_type_names, $post_type_objects );
613✔
698

699
                        // Filter the post type objects if the list of allowed types have changed.
700
                        $post_type_objects = array_filter(
613✔
701
                                $post_type_objects,
613✔
702
                                static function ( $obj ) use ( $allowed_post_type_names ) {
613✔
703
                                        if ( empty( $obj->graphql_plural_name ) && ! empty( $obj->graphql_single_name ) ) {
613✔
704
                                                $obj->graphql_plural_name = $obj->graphql_single_name;
2✔
705
                                        }
706

707
                                        /**
708
                                         * Validate that the post_types have a graphql_single_name and graphql_plural_name
709
                                         */
710
                                        if ( empty( $obj->graphql_single_name ) || empty( $obj->graphql_plural_name ) ) {
613✔
711
                                                graphql_debug(
1✔
712
                                                        sprintf(
1✔
713
                                                        /* translators: %s will replaced with the registered type */
714
                                                                __( 'The "%s" post_type isn\'t configured properly to show in GraphQL. It needs a "graphql_single_name" and a "graphql_plural_name"', 'wp-graphql' ),
1✔
715
                                                                $obj->name
1✔
716
                                                        ),
1✔
717
                                                        [
1✔
718
                                                                'invalid_post_type' => $obj,
1✔
719
                                                        ]
1✔
720
                                                );
1✔
721
                                                return false;
1✔
722
                                        }
723

724
                                        return in_array( $obj->name, $allowed_post_type_names, true );
613✔
725
                                }
613✔
726
                        );
613✔
727

728
                        self::$allowed_post_types = $post_type_objects;
613✔
729
                }
730

731
                /**
732
                 * Filter the list of allowed post types either by the provided args or to only return an array of names.
733
                 */
734
                if ( ! empty( $args ) || 'names' === $output ) {
697✔
735
                        $field = 'names' === $output ? 'name' : false;
695✔
736

737
                        return wp_filter_object_list( self::$allowed_post_types, $args, 'and', $field );
695✔
738
                }
739

740
                return self::$allowed_post_types;
615✔
741
        }
742

743
        /**
744
         * Get the taxonomies that are allowed to be used in GraphQL.
745
         * This gets all taxonomies that are set to "show_in_graphql" but allows for external code
746
         * (plugins/themes) to filter the list of allowed_taxonomies to add/remove additional
747
         * taxonomies
748
         *
749
         * @param 'names'|'objects'   $output Optional. The type of output to return. Accepts taxonomy 'names' or 'objects'. Default 'names'.
750
         * @param array<string,mixed> $args   Optional. Arguments to filter allowed taxonomies.
751
         *
752
         * @return string[]|\WP_Taxonomy[]
753
         * @phpstan-return ( $output is 'objects' ? \WP_Taxonomy[] : string[] )
754
         * @since  0.0.4
755
         */
756
        public static function get_allowed_taxonomies( $output = 'names', $args = [] ): array {
628✔
757

758
                // Initialize array of allowed post type objects.
759
                if ( empty( self::$allowed_taxonomies ) ) {
628✔
760
                        /**
761
                         * Get all post types objects.
762
                         *
763
                         * @var \WP_Taxonomy[] $tax_objects
764
                         */
765
                        $tax_objects = get_taxonomies(
613✔
766
                                [ 'show_in_graphql' => true ],
613✔
767
                                'objects'
613✔
768
                        );
613✔
769

770
                        $tax_names = wp_list_pluck( $tax_objects, 'name' );
613✔
771

772
                        /**
773
                         * Pass through a filter to allow the taxonomies to be modified.
774
                         * For example if a certain taxonomy should not be exposed to the GraphQL API.
775
                         *
776
                         * @param string[]       $tax_names   Array of taxonomy names
777
                         * @param \WP_Taxonomy[] $tax_objects Array of taxonomy objects.
778
                         *
779
                         * @since 1.8.1 add $tax_names and $tax_objects parameters.
780
                         * @since 0.0.2
781
                         */
782
                        $allowed_tax_names = apply_filters( 'graphql_term_entities_allowed_taxonomies', $tax_names, $tax_objects );
613✔
783

784
                        $tax_objects = array_filter(
613✔
785
                                $tax_objects,
613✔
786
                                static function ( $obj ) use ( $allowed_tax_names ) {
613✔
787
                                        if ( empty( $obj->graphql_plural_name ) && ! empty( $obj->graphql_single_name ) ) {
613✔
788
                                                $obj->graphql_plural_name = $obj->graphql_single_name;
1✔
789
                                        }
790

791
                                        /**
792
                                         * Validate that the post_types have a graphql_single_name and graphql_plural_name
793
                                         */
794
                                        if ( empty( $obj->graphql_single_name ) || empty( $obj->graphql_plural_name ) ) {
613✔
795
                                                graphql_debug(
1✔
796
                                                        sprintf(
1✔
797
                                                        /* translators: %s will replaced with the registered taxonomy */
798
                                                                __( 'The "%s" taxonomy isn\'t configured properly to show in GraphQL. It needs a "graphql_single_name" and a "graphql_plural_name"', 'wp-graphql' ),
1✔
799
                                                                $obj->name
1✔
800
                                                        ),
1✔
801
                                                        [
1✔
802
                                                                'invalid_taxonomy' => $obj,
1✔
803
                                                        ]
1✔
804
                                                );
1✔
805
                                                return false;
1✔
806
                                        }
807

808
                                        return in_array( $obj->name, $allowed_tax_names, true );
613✔
809
                                }
613✔
810
                        );
613✔
811

812
                        self::$allowed_taxonomies = $tax_objects;
613✔
813
                }
814

815
                $taxonomies = self::$allowed_taxonomies;
628✔
816
                /**
817
                 * Filter the list of allowed taxonomies either by the provided args or to only return an array of names.
818
                 */
819
                if ( ! empty( $args ) || 'names' === $output ) {
628✔
820
                        $field = 'names' === $output ? 'name' : false;
624✔
821

822
                        $taxonomies = wp_filter_object_list( $taxonomies, $args, 'and', $field );
624✔
823
                }
824

825
                return $taxonomies;
628✔
826
        }
827

828
        /**
829
         * Allow Schema to be cleared.
830
         */
831
        public static function clear_schema(): void {
637✔
832
                self::$type_registry      = null;
637✔
833
                self::$schema             = null;
637✔
834
                self::$allowed_post_types = null;
637✔
835
                self::$allowed_taxonomies = null;
637✔
836
        }
837

838
        /**
839
         * Returns the Schema as defined by static registrations throughout
840
         * the WP Load.
841
         *
842
         * @return \WPGraphQL\WPSchema
843
         *
844
         * @throws \Exception
845
         */
846
        public static function get_schema() {
790✔
847
                if ( ! isset( self::$schema ) ) {
790✔
848
                        $schema_registry = new SchemaRegistry();
613✔
849
                        $schema          = $schema_registry->get_schema();
613✔
850

851
                        /**
852
                         * Generate & Filter the schema.
853
                         *
854
                         * @param \WPGraphQL\WPSchema $schema The executable Schema that GraphQL executes against
855
                         * @param \WPGraphQL\AppContext $app_context Object The AppContext object containing all of the
856
                         * information about the context we know at this point
857
                         *
858
                         * @since 0.0.5
859
                         */
860
                        self::$schema = apply_filters( 'graphql_schema', $schema, self::get_app_context() );
613✔
861
                }
862

863
                /**
864
                 * Fire an action when the Schema is returned
865
                 */
866
                do_action( 'graphql_get_schema', self::$schema );
790✔
867

868
                /**
869
                 * Return the Schema after applying filters
870
                 */
871
                return self::$schema;
790✔
872
        }
873

874
        /**
875
         * Whether WPGraphQL is operating in Debug mode
876
         */
877
        public static function debug(): bool {
789✔
878
                if ( defined( 'GRAPHQL_DEBUG' ) ) {
789✔
879
                        $enabled = (bool) GRAPHQL_DEBUG;
789✔
880
                } else {
UNCOV
881
                        $enabled = get_graphql_setting( 'debug_mode_enabled', 'off' );
×
UNCOV
882
                        $enabled = 'on' === $enabled;
×
883
                }
884

885
                /**
886
                 * @param bool $enabled Whether GraphQL Debug is enabled or not
887
                 */
888
                return (bool) apply_filters( 'graphql_debug_enabled', $enabled );
789✔
889
        }
890

891
        /**
892
         * Returns the type registry, instantiating it if it doesn't exist.
893
         *
894
         * @return \WPGraphQL\Registry\TypeRegistry
895
         *
896
         * @throws \Exception
897
         */
898
        public static function get_type_registry() {
792✔
899
                if ( ! isset( self::$type_registry ) ) {
792✔
900
                        $type_registry = new TypeRegistry();
615✔
901

902
                        /**
903
                         * Generate & Filter the schema.
904
                         *
905
                         * @param \WPGraphQL\Registry\TypeRegistry $type_registry The TypeRegistry for the API
906
                         * @param \WPGraphQL\AppContext $app_context Object The AppContext object containing all of the
907
                         * information about the context we know at this point
908
                         *
909
                         * @since 0.0.5
910
                         */
911
                        self::$type_registry = apply_filters( 'graphql_type_registry', $type_registry, self::get_app_context() );
615✔
912
                }
913

914
                /**
915
                 * Fire an action when the Type Registry is returned
916
                 */
917
                do_action( 'graphql_get_type_registry', self::$type_registry );
792✔
918

919
                /**
920
                 * Return the Schema after applying filters
921
                 */
922
                return self::$type_registry;
792✔
923
        }
924

925
        /**
926
         * Return the static schema if there is one.
927
         */
928
        public static function get_static_schema(): ?string {
1✔
929
                $schema_file = WPGRAPHQL_PLUGIN_DIR . 'schema.graphql';
1✔
930

931
                if ( ! file_exists( $schema_file ) ) {
1✔
UNCOV
932
                        return null;
×
933
                }
934

935
                $schema = file_get_contents( WPGRAPHQL_PLUGIN_DIR . 'schema.graphql' );
1✔
936

937
                return ! empty( $schema ) ? $schema : null;
1✔
938
        }
939

940
        /**
941
         * Get the AppContext for use in passing down the Resolve Tree
942
         */
943
        public static function get_app_context(): AppContext {
792✔
944
                /**
945
                 * Configure the app_context which gets passed down to all the resolvers.
946
                 *
947
                 * @since 0.0.4
948
                 */
949
                $app_context           = new AppContext();
792✔
950
                $app_context->viewer   = wp_get_current_user();
792✔
951
                $app_context->root_url = get_bloginfo( 'url' );
792✔
952
                $app_context->request  = ! empty( $_REQUEST ) ? $_REQUEST : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
792✔
953

954
                return $app_context;
792✔
955
        }
956
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc