clientId = self::normalizeRequired( 'clientId', $clientId ); $this->affiliateSid = self::normalizeOptional( 'affiliateSid', $affiliateSid ); $this->affiliatePid = self::normalizeOptional( 'affiliatePid', $affiliatePid ); } /** * Yahoo! Developer Network Client ID per §9.3 "必須". * * Returned verbatim — the value is the canonical form supplied * to the constructor, with surrounding whitespace already * trimmed off. Provider implementations pass this directly to * the `appid` query parameter on every商品検索 v3 request. */ public function clientId(): string { return $this->clientId; } /** * Value Commerce affiliate `sid` per §9.3, or null when not * configured. * * Provider implementations forward this only when non-null. */ public function affiliateSid(): ?string { return $this->affiliateSid; } /** * Value Commerce affiliate `pid` per §9.3, or null when not * configured. * * Provider implementations forward this only when non-null. */ public function affiliatePid(): ?string { return $this->affiliatePid; } /** * Report whether an affiliate `sid` is configured. * * Convenience guard so call sites can branch on * "is the sid set?" without manually comparing * {@see self::affiliateSid()} against null. */ public function hasAffiliateSid(): bool { return $this->affiliateSid !== null; } /** * Report whether an affiliate `pid` is configured. * * Convenience guard so call sites can branch on * "is the pid set?" without manually comparing * {@see self::affiliatePid()} against null. */ public function hasAffiliatePid(): bool { return $this->affiliatePid !== null; } /** * Report whether both affiliate fields are configured. * * Convenience for {@see YahooShoppingAffiliateLinkBuilder}, * which requires both `sid` and `pid` to mint a Value * Commerce redirect URL. Either field missing forces the * passthrough route. */ public function hasAffiliateCredentials(): bool { return $this->affiliateSid !== null && $this->affiliatePid !== null; } /** * Validate and normalize a required credential field at * construction. * * Trims surrounding whitespace and rejects the result when it * is the empty string. The error message names the field * (`clientId`) so a misconfiguration surfaces with enough * context to fix without dumping the raw value — per the * §10.1 secret-material rule, the value itself MUST NOT * appear in the message. * * @param string $field Field name for the error message. * @param string $value Raw value as supplied by the caller. * * @throws InvalidArgumentException When `$value` is empty * after trimming. */ private static function normalizeRequired( string $field, string $value ): string { $trimmed = trim( $value ); if ( $trimmed === '' ) { $message = sprintf( 'YahooShoppingCredentials %s must be a non-empty string.', $field ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } return $trimmed; } /** * Validate and normalize an optional credential field at * construction. * * Null passes through unchanged. A non-null value is trimmed * and rejected when the trim leaves the empty string. As with * {@see self::normalizeRequired()}, the error message names * the field but MUST NOT include the raw value. * * @param string $field Field name for the error message. * @param string|null $value Raw value as supplied by the caller. * * @throws InvalidArgumentException When `$value` is the empty * string after trimming. */ private static function normalizeOptional( string $field, ?string $value ): ?string { if ( $value === null ) { return null; } $trimmed = trim( $value ); if ( $trimmed === '' ) { $message = sprintf( 'YahooShoppingCredentials %s must be null or a non-empty string.', $field ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } return $trimmed; } }