WooCommerce Code Reference

ResolverHelpers
in package

Shared utilities for the auto-generated GraphQL resolvers.

The public surface uses only {@see} (a stable subclass of the engine's Error) on throws/returns so generated code never imports an engine-specific symbol — a future engine switch can rewrite the bodies here without invalidating already-committed plugin trees.

Table of Contents

authorize_command()  : bool
Invoke a command's authorize() method, translating any thrown exceptions into spec-compliant GraphQL errors.
build_authorization_error()  : Error
Build the GraphQL error to throw when an authorization check fails.
build_field_authorization_error()  : Error
Like {@see self::build_authorization_error()} but carries a structured `subject` payload identifying *what* was denied — the enclosing type, the field (when applicable), and the attribute class name driving the decision. Clients can branch on `extensions.subject.field` to tell a field-level deny apart from an operation-level one.
complexity_from_pagination()  : int
Compute the complexity cost of a paginated connection field.
compute_preauthorized()  : bool
Compute the value `_preauthorized` would carry for the given command and principal (the AND of the autodiscovered authorization attributes' authorize() outcomes).
create_input()  : mixed
Invoke a factory callable, catching InvalidArgumentException and converting it to a client-visible GraphQL error.
create_pagination_params()  : PaginationParams
Build a PaginationParams instance from the standard GraphQL pagination arguments (first, last, after, before).
execute_command()  : mixed
Execute a command's execute() method, translating any thrown exceptions into spec-compliant GraphQL errors.
translate_exceptions()  : mixed
Invoke a callable, translating any thrown exception into a spec-compliant GraphQL error with a machine-readable code.
wp_filesystem()  : WP_Filesystem_Base|null
Lazy-initialize and return the WP_Filesystem global, or null when the direct method isn't available (e.g. credentials prompt would be needed).
authorize_method_shape_is_valid()  : bool
Whether a method's shape matches the authorization-attribute contract: public, non-static, returns bool, and parameters drawn from the accepted set — at most one principal (any non-`_`-prefixed name, non-nullable typed) plus any subset of `$_metadata` (array), `$_args` (array), and `$_parent` (any type).
build_authorize_call_args()  : array<int|string, mixed>
Build the positional/named argument list for an attribute's `authorize()` method based on which opt-in slots its signature declares.
collect_authorization_instances()  : array<int, object>
Collect attribute instances declared on $source whose class declares an authorization-shaped `authorize()` method.
harvest_class_metadata()  : array<string, bool|int|float|string|null>
Mirror of `ApiBuilder::harvest_metadata()` for the runtime path. Walks {@see \Automattic\WooCommerce\Api\Attributes\Metadata}-subclass attributes on a class reflector and returns `name => value`. Duplicate names are resolved last-wins — the build-time validator already errors on duplicates, so this is only relevant for in-process classes that never went through a build.

Methods

authorize_command()

Invoke a command's authorize() method, translating any thrown exceptions into spec-compliant GraphQL errors.

public static authorize_command(object $command, array<string|int, mixed> $authorize_args) : bool

Mirror of execute_command() for the authorize step. Needed because an authorize() call can throw an ApiException (e.g. UnauthorizedException when a target record does not exist); without this wrapper the exception would propagate up to the engine and lose its error code and user-visible message on its way through the generic error formatter.

Parameters
$command : object

The command instance (must have an authorize() method).

$authorize_args : array<string|int, mixed>

Named arguments to pass to authorize().

Tags
throws
Error

On any exception from the authorize method.

Return values
boolThe return value of authorize().

build_authorization_error()

Build the GraphQL error to throw when an authorization check fails.

public static build_authorization_error(object $principal) : Error

Distinguishes the two HTTP-correct shapes:

  • UNAUTHORIZED (401) when the principal is anonymous — the caller could plausibly fix it by authenticating, so the response invites re-auth.
  • FORBIDDEN (403) otherwise — the principal is recognised but isn't allowed; re-authenticating wouldn't help.

The "anonymous" check is opt-in by convention: the principal's is_authenticated(): bool method, when present, decides. Principals that don't define it fall through to FORBIDDEN — generated resolvers still emit a coded error, just without the 401/403 distinction.

Used for class-level denials (operation-level "you cannot call this query/mutation"). For field-level denials that should carry a structured subject payload (type / field / attribute), see {@see}.

Parameters
$principal : object

The resolved request principal.

Return values
Error

build_field_authorization_error()

Like {@see self::build_authorization_error()} but carries a structured `subject` payload identifying *what* was denied — the enclosing type, the field (when applicable), and the attribute class name driving the decision. Clients can branch on `extensions.subject.field` to tell a field-level deny apart from an operation-level one.

public static build_field_authorization_error(object $principal, string $type, string|null $field, string $attribute_short) : Error

The error code (UNAUTHORIZED / FORBIDDEN) is preserved verbatim so existing client handlers continue to work; the subject payload is additive.

Parameters
$principal : object

The resolved request principal.

$type : string

GraphQL type name carrying the gate.

$field : string|null

Field name when the deny is field-level; null for type/operation-level denies.

$attribute_short : string

Short class name of the deciding authorization attribute (no namespace).

Return values
Error

complexity_from_pagination()

Compute the complexity cost of a paginated connection field.

public static complexity_from_pagination(int $child_complexity, array<string|int, mixed> $args) : int

Used as the complexity callable on every generated resolver field that returns a Connection. Runs during query validation (before resolver execution, so before PaginationParams::validate_args() has a chance to reject bad input) — so out-of-range / wrong-type values are clamped to MAX_PAGE_SIZE here. Using MAX_PAGE_SIZE as the fallback means a malicious attempt to shrink cost via e.g. a negative first value only inflates the computed complexity, closing the cost-bypass angle.

Parameters
$child_complexity : int

The complexity of a single child node.

$args : array<string|int, mixed>

The field arguments (expects first / last).

Return values
intThe total complexity for this connection field.

compute_preauthorized()

Compute the value `_preauthorized` would carry for the given command and principal (the AND of the autodiscovered authorization attributes' authorize() outcomes).

public static compute_preauthorized(string $command_fqcn, object $principal) : bool

Lets code-API callers (and tests) ask "would this command's attribute-based authorization grant access to this principal?" without going through the GraphQL pipeline.

Note that it returns true when the command has no authorization attributes (in that case the command's own authorize() method, if any, is the sole guard; and consulting it requires running the command, which this helper deliberately doesn't do).

Note: this provides the attribute-level authorization only. A command with both attributes and an authorize() method composes the two via the _preauthorized infrastructure parameter; this helper returns the value that _preauthorized would carry, not the final authorize() outcome.

Scope is class-level (queries / mutations). Field-level authorization lives on output-type / input-type properties and is enforced inside the generated resolvers. To inspect a field's declared authorization from code, walk {@see} and read the authorization slice on each row.

Parameters
$command_fqcn : string

Fully-qualified command class name.

$principal : object

The resolved principal. Anonymous requests are represented by a sentinel principal (e.g. {@see} whose underlying WP_User has ID=0), not by null.

Tags
throws
InvalidArgumentException

When $command_fqcn does not name an existing class.

Return values
bool

create_input()

Invoke a factory callable, catching InvalidArgumentException and converting it to a client-visible GraphQL error.

public static create_input(callable $factory) : mixed

Used to wrap construction of unrolled input types (PaginationParams, ProductFilterInput, etc.) whose constructors may validate their arguments and throw.

Parameters
$factory : callable

A callable that returns the constructed object.

Tags
throws
Error

When the factory throws InvalidArgumentException.

Return values
mixedThe return value of the factory.

create_pagination_params()

Build a PaginationParams instance from the standard GraphQL pagination arguments (first, last, after, before).

public static create_pagination_params(array<string|int, mixed> $args) : PaginationParams
Parameters
$args : array<string|int, mixed>

The GraphQL field arguments.

Tags
throws
Error

When a pagination value is out of range.

Return values
PaginationParams

execute_command()

Execute a command's execute() method, translating any thrown exceptions into spec-compliant GraphQL errors.

public static execute_command(object $command, array<string|int, mixed> $execute_args) : mixed
Parameters
$command : object

The command instance (must have an execute() method).

$execute_args : array<string|int, mixed>

Named arguments to pass to execute().

Tags
throws
Error

On any exception from the command.

Return values
mixedThe return value of execute().

translate_exceptions()

Invoke a callable, translating any thrown exception into a spec-compliant GraphQL error with a machine-readable code.

public static translate_exceptions(callable $operation) : mixed
  • ApiException → its own code + extensions, with the original message.
  • InvalidArgumentException → INVALID_ARGUMENT, with the original message.
  • Any other Throwable → INTERNAL_ERROR, with a generic message; the original throwable is attached as previous for debug-mode surfacing.

Public so that generated resolvers can wrap Code-API calls that happen outside the execute()/authorize() pair (e.g. the Connection::slice() call emitted for nested paginated connection fields, which can throw InvalidArgumentException when pagination bounds are exceeded).

Parameters
$operation : callable

Callable to invoke.

Tags
throws
Error

On any exception from the callable.

Return values
mixedThe return value of the callable.

wp_filesystem()

Lazy-initialize and return the WP_Filesystem global, or null when the direct method isn't available (e.g. credentials prompt would be needed).

public static wp_filesystem() : WP_Filesystem_Base|null
Return values
WP_Filesystem_Base|null

authorize_method_shape_is_valid()

Whether a method's shape matches the authorization-attribute contract: public, non-static, returns bool, and parameters drawn from the accepted set — at most one principal (any non-`_`-prefixed name, non-nullable typed) plus any subset of `$_metadata` (array), `$_args` (array), and `$_parent` (any type).

private static authorize_method_shape_is_valid(ReflectionMethod $method) : bool

Mirrors the build-time ApiBuilder::validate_attribute_authorize_shape() check so the runtime helper recognises the same set of attributes ApiBuilder would have emitted into a resolver.

Parameters
$method : ReflectionMethod

The method to inspect.

Return values
bool

build_authorize_call_args()

Build the positional/named argument list for an attribute's `authorize()` method based on which opt-in slots its signature declares.

private static build_authorize_call_args(ReflectionMethod $method, object $principal, array<string|int, mixed> $metadata, array<string|int, mixed> $args, mixed $parent) : array<int|string, mixed>

The principal is always passed first (positionally) when the method declares a non-_-prefixed parameter; infrastructure parameters ($_metadata, $_args, $_parent) are passed as named arguments so the attribute can omit any subset without affecting the call shape.

Parameters
$method : ReflectionMethod

The attribute's authorize() method.

$principal : object

The resolved principal to pass when the method takes one.

$metadata : array<string|int, mixed>

Value for $_metadata (passed if the method declares it).

$args : array<string|int, mixed>

Value for $_args (passed if the method declares it).

$parent : mixed

Value for $_parent (passed if the method declares it).

Return values
array<int|string, mixed>Positional principal first (if any), then named infra slots. Use with `...` spread.

collect_authorization_instances()

Collect attribute instances declared on $source whose class declares an authorization-shaped `authorize()` method.

private static collect_authorization_instances(ReflectionClass $source) : array<int, object>

Mirrors {@see} for the runtime path: same direct-then-inherited precedence, same "any class with a bool-returning authorize() method qualifies" rule.

Parameters
$source : ReflectionClass

Class/trait/interface to read attributes from.

Return values
array<int, object>

harvest_class_metadata()

Mirror of `ApiBuilder::harvest_metadata()` for the runtime path. Walks {@see \Automattic\WooCommerce\Api\Attributes\Metadata}-subclass attributes on a class reflector and returns `name => value`. Duplicate names are resolved last-wins — the build-time validator already errors on duplicates, so this is only relevant for in-process classes that never went through a build.

private static harvest_class_metadata(ReflectionClass $ref) : array<string, bool|int|float|string|null>

The per-target _apiMetadata opt-out (shows_in_metadata_query()) is not applied here: the $_metadata slot threaded into a class- level attribute's authorize() is for policy input, not discovery, so attribute authors see every entry regardless of how it surfaces through _apiMetadata.

Parameters
$ref : ReflectionClass

The class to read metadata from.

Return values
array<string, bool|int|float|string|null>