*/ private const VALID_FEATURES = array( self::FEATURE_SEARCH, self::FEATURE_LOOKUP, self::FEATURE_MULTIPLE_IMAGES, self::FEATURE_PRICE, self::FEATURE_AVAILABILITY, self::FEATURE_RATING, self::FEATURE_REVIEW_COUNT, self::FEATURE_CATEGORIES, self::FEATURE_SORTS, self::FEATURE_AFFILIATE_LINK, self::FEATURE_HEALTH_CHECK, self::FEATURE_RATE_LIMIT_INSPECTION, ); /** * Whether the provider implementation can satisfy keyword * search requests via SearchProviderInterface. */ private bool $supportsSearch; /** * Whether the provider implementation can satisfy single-item * lookup via ItemLookupProviderInterface. */ private bool $supportsLookup; /** * Whether provider responses include a gallery image list in * addition to the primary image. */ private bool $supportsMultipleImages; /** * Whether provider responses include PriceInfo data. */ private bool $supportsPrice; /** * Whether provider responses include a meaningful (non-unknown) * availability signal. */ private bool $supportsAvailability; /** * Whether provider responses include rating / review-count * data. §7.9 collapses both axes under a single capability * flag. */ private bool $supportsRating; /** * Whether provider responses include category-path entries. */ private bool $supportsCategories; /** * Whether the provider's search supports the §7.1 sort * discriminator. */ private bool $supportsSorts; /** * Whether the provider implementation can mint affiliate- * tracked URLs. */ private bool $supportsAffiliateLinkBuild; /** * Whether the provider implementation exposes a * ProviderHealthStatus probe. */ private bool $supportsHealthCheck; /** * Whether the provider implementation exposes a structured * rate-limit signal. */ private bool $supportsRateLimitInspection; /** * Construct a capabilities snapshot with explicit values for * every flag. * * The constructor is exhaustive so a programming error * (forgetting to declare a capability when adding a new * provider implementation) surfaces at the type system * boundary rather than as a runtime "feature silently * disabled" surprise. Named-argument construction is the * ergonomic entry point for most call sites. * * @param bool $supportsSearch Keyword search. * @param bool $supportsLookup Single-item lookup. * @param bool $supportsMultipleImages Gallery image list. * @param bool $supportsPrice PriceInfo data. * @param bool $supportsAvailability Meaningful availability. * @param bool $supportsRating Rating / review count. * @param bool $supportsCategories Category-path entries. * @param bool $supportsSorts Sort discriminator. * @param bool $supportsAffiliateLinkBuild Affiliate URL minting. * @param bool $supportsHealthCheck Health-check probe. * @param bool $supportsRateLimitInspection Rate-limit inspection. */ public function __construct( bool $supportsSearch, bool $supportsLookup, bool $supportsMultipleImages, bool $supportsPrice, bool $supportsAvailability, bool $supportsRating, bool $supportsCategories, bool $supportsSorts, bool $supportsAffiliateLinkBuild, bool $supportsHealthCheck, bool $supportsRateLimitInspection ) { $this->supportsSearch = $supportsSearch; $this->supportsLookup = $supportsLookup; $this->supportsMultipleImages = $supportsMultipleImages; $this->supportsPrice = $supportsPrice; $this->supportsAvailability = $supportsAvailability; $this->supportsRating = $supportsRating; $this->supportsCategories = $supportsCategories; $this->supportsSorts = $supportsSorts; $this->supportsAffiliateLinkBuild = $supportsAffiliateLinkBuild; $this->supportsHealthCheck = $supportsHealthCheck; $this->supportsRateLimitInspection = $supportsRateLimitInspection; } /** * Construct a capabilities snapshot with every flag set to * false. * * Convenience factory for the canonical "minimal provider / * not yet configured" snapshot so test setups and stub * providers do not have to spell out eleven `false` arguments. * Useful as a starting point for a provider that wants to * gradually enable capabilities as its implementation matures. */ public static function none(): self { return new self( false, false, false, false, false, false, false, false, false, false, false ); } /** * Closed set of §8 feature keys, in declaration order. * * Exposed for tests and for diagnostics surfaces that want to * enumerate the valid keys (for example, populating an admin * filter dropdown) without duplicating the list. * * @return list */ public static function validFeatures(): array { return self::VALID_FEATURES; } /** * Whether the provider implementation can satisfy keyword * search. */ public function supportsSearch(): bool { return $this->supportsSearch; } /** * Whether the provider implementation can satisfy single-item * lookup. */ public function supportsLookup(): bool { return $this->supportsLookup; } /** * Whether provider responses include a gallery image list. */ public function supportsMultipleImages(): bool { return $this->supportsMultipleImages; } /** * Whether provider responses include PriceInfo data. */ public function supportsPrice(): bool { return $this->supportsPrice; } /** * Whether provider responses include a meaningful availability * signal. */ public function supportsAvailability(): bool { return $this->supportsAvailability; } /** * Whether provider responses include rating / review-count * data. Per §7.9 the two axes share a single capability flag. */ public function supportsRating(): bool { return $this->supportsRating; } /** * Whether provider responses include category-path entries. */ public function supportsCategories(): bool { return $this->supportsCategories; } /** * Whether the provider's search supports the §7.1 sort * discriminator. */ public function supportsSorts(): bool { return $this->supportsSorts; } /** * Whether the provider implementation can mint affiliate- * tracked URLs. */ public function supportsAffiliateLinkBuild(): bool { return $this->supportsAffiliateLinkBuild; } /** * Whether the provider implementation exposes a * ProviderHealthStatus probe. */ public function supportsHealthCheck(): bool { return $this->supportsHealthCheck; } /** * Whether the provider implementation exposes a structured * rate-limit signal. */ public function supportsRateLimitInspection(): bool { return $this->supportsRateLimitInspection; } /** * Report whether the provider supports the named §8 feature. * * The closed set of accepted feature keys is enumerated by * {@see self::validFeatures()}; an out-of-set token raises * {@see InvalidArgumentException} carrying the offending value * verbatim and a comma-separated list of the valid tokens for * diagnosis. * * Per the §7.9 / §8 contract: * * - `rating` and `review_count` both consult * {@see self::$supportsRating} because §7.9 collapses the * two axes; §8's vocabulary names them separately for * callers that want to render a per-axis label. * * - The remaining ten keys map one-to-one to their named * accessors. * * @param string $feature §8 feature key. * * @throws InvalidArgumentException When `$feature` is not a * member of the §8 closed * set. */ public function supportsFeature( string $feature ): bool { switch ( $feature ) { case self::FEATURE_SEARCH: return $this->supportsSearch; case self::FEATURE_LOOKUP: return $this->supportsLookup; case self::FEATURE_MULTIPLE_IMAGES: return $this->supportsMultipleImages; case self::FEATURE_PRICE: return $this->supportsPrice; case self::FEATURE_AVAILABILITY: return $this->supportsAvailability; case self::FEATURE_RATING: case self::FEATURE_REVIEW_COUNT: return $this->supportsRating; case self::FEATURE_CATEGORIES: return $this->supportsCategories; case self::FEATURE_SORTS: return $this->supportsSorts; case self::FEATURE_AFFILIATE_LINK: return $this->supportsAffiliateLinkBuild; case self::FEATURE_HEALTH_CHECK: return $this->supportsHealthCheck; case self::FEATURE_RATE_LIMIT_INSPECTION: return $this->supportsRateLimitInspection; } $message = sprintf( 'ProviderCapabilities feature must be one of [%s]; got "%s".', implode( ', ', self::VALID_FEATURES ), $feature ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } /** * Enumerate every §8 feature key the provider supports, in * declaration order. * * Useful for admin UIs that want to render a "this provider * supports..." badge list. The list is positionally keyed so * JSON-encoding produces a JSON array, and entries appear in * the same order {@see self::validFeatures()} returns them so * the badge order is stable across providers. * * Note: `rating` and `review_count` both appear when * {@see self::supportsRating()} is true, matching the §8 * vocabulary's per-axis split even though the underlying flag * is shared. * * @return list */ public function supportedFeatures(): array { $supported = array(); foreach ( self::VALID_FEATURES as $feature ) { if ( $this->supportsFeature( $feature ) ) { $supported[] = $feature; } } return $supported; } }