REDROOM
PHP 8.2.31
Path:
Logout
Edit File
Size: 12.55 KB
Close
/home/nshryvcy/radiantskinclinics.org/wp-content/plugins/woocommerce/src/Internal/Api/Main.php
Text
Base64
<?php declare(strict_types=1); namespace Automattic\WooCommerce\Internal\Api; use Automattic\WooCommerce\Utilities\FeaturesUtil; /** * Entry point for the WooCommerce GraphQL API. * * This class is intentionally free of PHP 8.0+ syntax so that it can be * loaded and called on PHP 7.4 without parse errors. The PHP-8.1-only * classes (GraphQLController, QueryCache, etc.) are resolved lazily from * the DI container only after is_enabled() confirms PHP 8.1+ is available. */ class Main { /** * Feature flag slug registered in FeaturesController. */ private const FEATURE_SLUG = 'dual_code_graphql_api'; /** * Option name for the "Enable GET endpoint" setting. * * When disabled, the GraphQL route only accepts POST requests. */ public const OPTION_GET_ENDPOINT_ENABLED = 'woocommerce_graphql_get_endpoint_enabled'; /** * Check whether the Dual Code & GraphQL API feature is active. * * Requires PHP 8.1+ and the dual_code_graphql_api feature flag to be * enabled. * * @return bool */ public static function is_enabled(): bool { return PHP_VERSION_ID >= 80100 && FeaturesUtil::feature_is_enabled( self::FEATURE_SLUG ); } /** * Whether the GraphQL endpoint accepts GET requests. * * Defaults to false. Reads from the option written by the GraphQL * settings section so the REST route registration can decide which * HTTP methods to accept. */ public static function is_get_endpoint_enabled(): bool { return wc_string_to_bool( get_option( self::OPTION_GET_ENDPOINT_ENABLED, 'yes' ) ); } /** * Apply the GraphQL-scoped site settings to a caller-declared list of HTTP * methods. * * Centralises the "what verbs does the admin actually allow on a GraphQL * endpoint" rule so both WooCommerce core's own endpoint and every sibling * plugin endpoint applied through {@see self::register_graphql_endpoint()} * honour the same settings. * * Currently the only rule is: if the GET endpoint has been disabled in the * GraphQL settings section, strip GET from the list. * * @param string[] $methods HTTP methods the caller declared. * @return string[] Possibly narrowed list — may be empty, in which case the * caller should skip the route registration entirely. */ public static function filter_methods_against_settings( array $methods ): array { if ( ! self::is_get_endpoint_enabled() ) { $methods = array_values( array_diff( $methods, array( 'GET' ) ) ); } return $methods; } /** * Register the GraphQL endpoint when the feature is active. * * When the feature is off this is a no-op. Classes in the public * Automattic\WooCommerce\Api\ namespace remain autoloadable — extensions * that want to know whether the feature is active should check * FeaturesUtil::feature_is_enabled( 'dual_code_graphql_api' ) rather * than class_exists() on the Api namespace. * * The feature-enabled check is deferred to the `rest_api_init` callback * to avoid triggering translation loading (via FeaturesController) before * the `init` action has fired, which would cause a * `_load_textdomain_just_in_time` notice on WordPress 6.7+. */ public static function register(): void { add_action( 'rest_api_init', array( self::class, 'handle_rest_api_init_for_core' ) ); $settings = wc_get_container()->get( Settings::class ); $settings->register(); } /** * Hook callback: register WooCommerce core's GraphQL endpoint. * * @internal */ public static function handle_rest_api_init_for_core(): void { if ( ! self::is_enabled() ) { return; } wc_get_container()->get( Autogenerated\GraphQLController::class )->register(); } /** * Instantiate a GraphQL controller subclass and wire up its dependencies. * * Intended for sibling WooCommerce plugins that ship their own * autogenerated GraphQLController subclass (emitted by build-api.php * into their own autogenerated namespace). The returned controller is * ready to have handle_request() attached to a REST route. * * Returns null when the feature flag is off or PHP is < 8.1, so callers * can invoke this unconditionally from inside their own rest_api_init * handler. * * @param string $controller_class_name Fully-qualified name of a subclass of GraphQLController. * * @throws \InvalidArgumentException If $controller_class_name does not extend GraphQLController. */ public static function instantiate_graphql_controller( string $controller_class_name ): ?GraphQLController { if ( ! self::is_enabled() ) { return null; } self::assert_is_controller_subclass( $controller_class_name ); $controller = new $controller_class_name(); $controller->init( wc_get_container()->get( QueryCache::class ) ); return $controller; } /** * Register a GraphQL REST endpoint backed by a plugin-provided controller subclass. * * May be called at any time up to and including the `rest_api_init` hook: * if called earlier, registration is deferred to that hook; if called from * inside another plugin's `rest_api_init` handler, registration happens * immediately. Calls made after `rest_api_init` has already completed * register a REST route that WP_REST_Server won't honour on the current * request — callers should avoid deferring registration past bootstrap. * * When the feature flag is off or PHP is < 8.1 this is a silent no-op. * * The first argument accepts either of two forms: * * - A plugin root directory (recommended): pass `__DIR__` from the plugin's * bootstrap file. The controller class is resolved by convention from * `{dir}/src/Internal/Api/Autogenerated/GraphQLController.php` — ApiBuilder * always emits the generated subclass at that path. * - A controller class FQCN: use this when the plugin keeps its generated * code somewhere other than the conventional location, or registers * multiple endpoints backed by different controller classes. * * Plugins calling this method should guard the call with method_exists(): * * if ( method_exists( \Automattic\WooCommerce\Internal\Api\Main::class, 'register_graphql_endpoint' ) ) { * \Automattic\WooCommerce\Internal\Api\Main::register_graphql_endpoint( * __DIR__, * 'my-plugin', * '/graphql' * ); * } * * @param string $plugin_dir_or_controller_class Plugin root directory, OR a fully-qualified GraphQLController subclass name. See above. * @param string $route_namespace REST route namespace, as passed to register_rest_route(). * @param string $route REST route path, as passed to register_rest_route(). * @param string[] $methods HTTP methods accepted on the endpoint. Defaults to GET + POST. * * @throws \InvalidArgumentException If no controller can be resolved from a directory argument, or if the resolved class does not extend GraphQLController. */ public static function register_graphql_endpoint( string $plugin_dir_or_controller_class, string $route_namespace, string $route, array $methods = array( 'GET', 'POST' ) ): void { if ( ! self::is_enabled() ) { return; } $controller_class_name = self::resolve_controller_class( $plugin_dir_or_controller_class ); // Validate up front so a typo surfaces here at bootstrap rather than // deep inside the deferred rest_api_init callback. self::assert_is_controller_subclass( $controller_class_name ); $registrar = new GraphQLEndpointRegistrar( $controller_class_name, $route_namespace, $route, $methods ); if ( did_action( 'rest_api_init' ) ) { $registrar->handle_rest_api_init(); } else { add_action( 'rest_api_init', array( $registrar, 'handle_rest_api_init' ) ); } } /** * Resolve the first argument of {@see self::register_graphql_endpoint()} to a * controller class name. If the argument is an existing directory, treat it * as the plugin root and read the namespace from the generated controller * file at the conventional path. Otherwise return the argument unchanged so * it's used as a class FQCN. * * @param string $arg Either a plugin root directory or a controller class FQCN. * * @throws \InvalidArgumentException If the argument is a directory but the * generated controller file is missing or * doesn't declare a PHP namespace. */ private static function resolve_controller_class( string $arg ): string { if ( ! is_dir( $arg ) ) { return $arg; } $controller_file = rtrim( $arg, '/\\' ) . '/src/Internal/Api/Autogenerated/GraphQLController.php'; if ( ! is_file( $controller_file ) ) { throw new \InvalidArgumentException( sprintf( 'Expected a generated GraphQL controller at %s, but the file does not exist. Run the plugin\'s API build script first, or pass an explicit controller class name.', esc_html( $controller_file ) ) ); } // The generated controller always declares its namespace near the top // of the file; reading the first few KB is more than enough. Reading a // local file — not a URL — so wp_remote_get() does not apply here. // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $head = file_get_contents( $controller_file, false, null, 0, 4096 ); if ( false === $head ) { throw new \InvalidArgumentException( sprintf( 'Could not read the controller file at %s.', esc_html( $controller_file ) ) ); } $namespace = self::extract_namespace_from_php_source( $head ); if ( null === $namespace ) { throw new \InvalidArgumentException( sprintf( 'Could not determine the PHP namespace of the controller at %s.', esc_html( $controller_file ) ) ); } return $namespace . '\\GraphQLController'; } /** * Extract an unbracketed `namespace …;` declaration from a PHP source fragment. * * Uses PHP's tokenizer rather than a regex so the parse isn't fooled by * declarations inside heredocs, comments, or bracketed-namespace syntax, * and continues to work if the generator's output format changes * (e.g. attributes before the declaration, single-line `<?php namespace`). * * @param string $source PHP source code. * @return ?string The namespace FQN without leading or trailing separator, or null if none found. */ private static function extract_namespace_from_php_source( string $source ): ?string { $tokens = token_get_all( $source ); $count = count( $tokens ); for ( $i = 0; $i < $count; $i++ ) { if ( ! is_array( $tokens[ $i ] ) || T_NAMESPACE !== $tokens[ $i ][0] ) { continue; } $namespace = ''; for ( $j = $i + 1; $j < $count; $j++ ) { $token = $tokens[ $j ]; if ( is_array( $token ) ) { // T_NAME_QUALIFIED and T_NAME_FULLY_QUALIFIED exist on PHP 8+ and already // contain the full namespace path; T_STRING / T_NS_SEPARATOR are the // equivalent pieces on older versions. if ( in_array( $token[0], array( T_STRING, T_NS_SEPARATOR ), true ) || ( defined( 'T_NAME_QUALIFIED' ) && T_NAME_QUALIFIED === $token[0] ) || ( defined( 'T_NAME_FULLY_QUALIFIED' ) && T_NAME_FULLY_QUALIFIED === $token[0] ) ) { $namespace .= $token[1]; continue; } if ( T_WHITESPACE === $token[0] ) { continue; } } // Single-character tokens `;` (unbracketed namespace) and `{` (bracketed) end the declaration. break; } $namespace = trim( $namespace, '\\' ); if ( '' !== $namespace ) { return $namespace; } } return null; } /** * Assert that a class name resolves to a concrete GraphQLController subclass. * * @param string $controller_class_name Fully-qualified name of the class to validate. * * @throws \InvalidArgumentException If the class cannot be autoloaded, or does not extend GraphQLController. */ private static function assert_is_controller_subclass( string $controller_class_name ): void { // Differentiate "class does not exist" (typo / stale autoloader) from // "class exists but is not a subclass" — is_subclass_of() collapses // both into false, which is confusing when debugging a bootstrap typo. if ( ! class_exists( $controller_class_name ) ) { throw new \InvalidArgumentException( sprintf( 'GraphQL controller class "%s" does not exist or is not autoloadable. Check the spelling, or run `composer dump-autoload` if it was added since the last autoloader regeneration.', esc_html( $controller_class_name ) ) ); } if ( ! is_subclass_of( $controller_class_name, GraphQLController::class ) ) { throw new \InvalidArgumentException( sprintf( 'Class "%s" must extend %s.', esc_html( $controller_class_name ), esc_html( GraphQLController::class ) ) ); } } }
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 2 × Files: 7
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
Autogenerated
DIR
-
drwxr-xr-x
2026-05-29 02:43:21
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Schema
DIR
-
drwxr-xr-x
2026-05-29 02:43:21
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
GraphQLController.php
21.50 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
GraphQLEndpointRegistrar.php
2.09 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Main.php
12.55 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
QueryCache.php
4.68 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
QueryInfoExtractor.php
7.14 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Settings.php
2.06 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Utils.php
7.68 KB
lrw-r--r--
2026-05-05 14:26:50
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).