[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/php-ai-client/src/Providers/Http/DTO/ -> Request.php (source)

   1  <?php
   2  
   3  declare (strict_types=1);
   4  namespace WordPress\AiClient\Providers\Http\DTO;
   5  
   6  use JsonException;
   7  use WordPress\AiClientDependencies\Psr\Http\Message\RequestInterface;
   8  use WordPress\AiClient\Common\AbstractDataTransferObject;
   9  use WordPress\AiClient\Common\Exception\InvalidArgumentException;
  10  use WordPress\AiClient\Providers\Http\Collections\HeadersCollection;
  11  use WordPress\AiClient\Providers\Http\Enums\HttpMethodEnum;
  12  /**
  13   * Represents an HTTP request.
  14   *
  15   * This class encapsulates HTTP request data that can be converted
  16   * to PSR-7 requests by the HTTP transporter.
  17   *
  18   * @since 0.1.0
  19   *
  20   * @phpstan-import-type RequestOptionsArrayShape from RequestOptions
  21   * @phpstan-type RequestArrayShape array{
  22   *     method: string,
  23   *     uri: string,
  24   *     headers: array<string, list<string>>,
  25   *     body?: string|null,
  26   *     options?: RequestOptionsArrayShape
  27   * }
  28   *
  29   * @extends AbstractDataTransferObject<RequestArrayShape>
  30   */
  31  class Request extends AbstractDataTransferObject
  32  {
  33      public const KEY_METHOD = 'method';
  34      public const KEY_URI = 'uri';
  35      public const KEY_HEADERS = 'headers';
  36      public const KEY_BODY = 'body';
  37      public const KEY_OPTIONS = 'options';
  38      /**
  39       * @var HttpMethodEnum The HTTP method.
  40       */
  41      protected HttpMethodEnum $method;
  42      /**
  43       * @var string The request URI.
  44       */
  45      protected string $uri;
  46      /**
  47       * @var HeadersCollection The request headers.
  48       */
  49      protected HeadersCollection $headers;
  50      /**
  51       * @var array<string, mixed>|null The request data (for query params or form data).
  52       */
  53      protected ?array $data = null;
  54      /**
  55       * @var string|null The request body (raw string content).
  56       */
  57      protected ?string $body = null;
  58      /**
  59       * @var RequestOptions|null Request transport options.
  60       */
  61      protected ?\WordPress\AiClient\Providers\Http\DTO\RequestOptions $options = null;
  62      /**
  63       * Constructor.
  64       *
  65       * @since 0.1.0
  66       *
  67       * @param HttpMethodEnum $method The HTTP method.
  68       * @param string $uri The request URI.
  69       * @param array<string, string|list<string>> $headers The request headers.
  70       * @param string|array<string, mixed>|null $data The request data.
  71       * @param RequestOptions|null $options The request transport options.
  72       *
  73       * @throws InvalidArgumentException If the URI is empty.
  74       */
  75      public function __construct(HttpMethodEnum $method, string $uri, array $headers = [], $data = null, ?\WordPress\AiClient\Providers\Http\DTO\RequestOptions $options = null)
  76      {
  77          if (empty($uri)) {
  78              throw new InvalidArgumentException('URI cannot be empty.');
  79          }
  80          $this->method = $method;
  81          $this->uri = $uri;
  82          $this->headers = new HeadersCollection($headers);
  83          // Separate data and body based on type
  84          if (is_string($data)) {
  85              $this->body = $data;
  86          } elseif (is_array($data)) {
  87              $this->data = $data;
  88          }
  89          $this->options = $options;
  90      }
  91      /**
  92       * Creates a deep clone of this request.
  93       *
  94       * Clones the headers collection and request options to ensure
  95       * the cloned request is independent of the original.
  96       * The HTTP method enum is immutable and can be safely shared.
  97       *
  98       * @since 0.4.2
  99       */
 100      public function __clone()
 101      {
 102          // Clone headers collection
 103          $this->headers = clone $this->headers;
 104          // Clone request options if present (contains only primitives)
 105          if ($this->options !== null) {
 106              $this->options = clone $this->options;
 107          }
 108          // Note: $method is an immutable enum and can be safely shared
 109      }
 110      /**
 111       * Gets the HTTP method.
 112       *
 113       * @since 0.1.0
 114       *
 115       * @return HttpMethodEnum The HTTP method.
 116       */
 117      public function getMethod(): HttpMethodEnum
 118      {
 119          return $this->method;
 120      }
 121      /**
 122       * Gets the request URI.
 123       *
 124       * For GET requests with array data, appends the data as query parameters.
 125       *
 126       * @since 0.1.0
 127       *
 128       * @return string The URI.
 129       */
 130      public function getUri(): string
 131      {
 132          // If GET request with data, append as query parameters
 133          if ($this->method === HttpMethodEnum::GET() && $this->data !== null && !empty($this->data)) {
 134              $separator = str_contains($this->uri, '?') ? '&' : '?';
 135              return $this->uri . $separator . http_build_query($this->data);
 136          }
 137          return $this->uri;
 138      }
 139      /**
 140       * Gets the request headers.
 141       *
 142       * @since 0.1.0
 143       *
 144       * @return array<string, list<string>> The headers.
 145       */
 146      public function getHeaders(): array
 147      {
 148          return $this->headers->getAll();
 149      }
 150      /**
 151       * Gets a specific header value.
 152       *
 153       * @since 0.1.0
 154       *
 155       * @param string $name The header name (case-insensitive).
 156       * @return list<string>|null The header value(s) or null if not found.
 157       */
 158      public function getHeader(string $name): ?array
 159      {
 160          return $this->headers->get($name);
 161      }
 162      /**
 163       * Gets header values as a comma-separated string.
 164       *
 165       * @since 0.1.0
 166       *
 167       * @param string $name The header name (case-insensitive).
 168       * @return string|null The header values as a comma-separated string, or null if not found.
 169       */
 170      public function getHeaderAsString(string $name): ?string
 171      {
 172          return $this->headers->getAsString($name);
 173      }
 174      /**
 175       * Checks if a header exists.
 176       *
 177       * @since 0.1.0
 178       *
 179       * @param string $name The header name (case-insensitive).
 180       * @return bool True if the header exists, false otherwise.
 181       */
 182      public function hasHeader(string $name): bool
 183      {
 184          return $this->headers->has($name);
 185      }
 186      /**
 187       * Gets the request body.
 188       *
 189       * For GET requests, returns null.
 190       * For POST/PUT/PATCH requests:
 191       * - If body is set, returns it as-is
 192       * - If data is set and Content-Type is JSON, returns JSON-encoded data
 193       * - If data is set and Content-Type is form, returns URL-encoded data
 194       *
 195       * @since 0.1.0
 196       *
 197       * @return string|null The body.
 198       * @throws JsonException If the data cannot be encoded to JSON.
 199       */
 200      public function getBody(): ?string
 201      {
 202          // GET requests don't have a body
 203          if (!$this->method->hasBody()) {
 204              return null;
 205          }
 206          // If body is set, return it as-is
 207          if ($this->body !== null) {
 208              return $this->body;
 209          }
 210          // If data is set, encode based on content type
 211          if ($this->data !== null) {
 212              $contentType = $this->getContentType();
 213              // JSON encoding
 214              if ($contentType !== null && stripos($contentType, 'application/json') !== \false) {
 215                  return json_encode($this->data, \JSON_THROW_ON_ERROR);
 216              }
 217              // Default to URL encoding for forms
 218              return http_build_query($this->data);
 219          }
 220          return null;
 221      }
 222      /**
 223       * Gets the Content-Type header value.
 224       *
 225       * @since 0.1.0
 226       *
 227       * @return string|null The Content-Type header value or null if not set.
 228       */
 229      private function getContentType(): ?string
 230      {
 231          $values = $this->getHeader('Content-Type');
 232          return $values !== null ? $values[0] : null;
 233      }
 234      /**
 235       * Returns a new instance with the specified header.
 236       *
 237       * @since 0.1.0
 238       *
 239       * @param string $name The header name.
 240       * @param string|list<string> $value The header value(s).
 241       * @return self A new instance with the header.
 242       */
 243      public function withHeader(string $name, $value): self
 244      {
 245          $newHeaders = $this->headers->withHeader($name, $value);
 246          $new = clone $this;
 247          $new->headers = $newHeaders;
 248          return $new;
 249      }
 250      /**
 251       * Returns a new instance with the specified data.
 252       *
 253       * @since 0.1.0
 254       *
 255       * @param string|array<string, mixed> $data The request data.
 256       * @return self A new instance with the data.
 257       */
 258      public function withData($data): self
 259      {
 260          $new = clone $this;
 261          if (is_string($data)) {
 262              $new->body = $data;
 263              $new->data = null;
 264          } elseif (is_array($data)) {
 265              $new->data = $data;
 266              $new->body = null;
 267          } else {
 268              $new->data = null;
 269              $new->body = null;
 270          }
 271          return $new;
 272      }
 273      /**
 274       * Gets the request data array.
 275       *
 276       * @since 0.1.0
 277       *
 278       * @return array<string, mixed>|null The request data array.
 279       */
 280      public function getData(): ?array
 281      {
 282          return $this->data;
 283      }
 284      /**
 285       * Gets the request options.
 286       *
 287       * @since 0.2.0
 288       *
 289       * @return RequestOptions|null Request transport options when configured.
 290       */
 291      public function getOptions(): ?\WordPress\AiClient\Providers\Http\DTO\RequestOptions
 292      {
 293          return $this->options;
 294      }
 295      /**
 296       * Returns a new instance with the specified request options.
 297       *
 298       * @since 0.2.0
 299       *
 300       * @param RequestOptions|null $options The request options to apply.
 301       * @return self A new instance with the options.
 302       */
 303      public function withOptions(?\WordPress\AiClient\Providers\Http\DTO\RequestOptions $options): self
 304      {
 305          $new = clone $this;
 306          $new->options = $options;
 307          return $new;
 308      }
 309      /**
 310       * {@inheritDoc}
 311       *
 312       * @since 0.1.0
 313       */
 314      public static function getJsonSchema(): array
 315      {
 316          return ['type' => 'object', 'properties' => [self::KEY_METHOD => ['type' => 'string', 'description' => 'The HTTP method.'], self::KEY_URI => ['type' => 'string', 'description' => 'The request URI.'], self::KEY_HEADERS => ['type' => 'object', 'additionalProperties' => ['type' => 'array', 'items' => ['type' => 'string']], 'description' => 'The request headers.'], self::KEY_BODY => ['type' => ['string'], 'description' => 'The request body.'], self::KEY_OPTIONS => \WordPress\AiClient\Providers\Http\DTO\RequestOptions::getJsonSchema()], 'required' => [self::KEY_METHOD, self::KEY_URI, self::KEY_HEADERS]];
 317      }
 318      /**
 319       * {@inheritDoc}
 320       *
 321       * @since 0.1.0
 322       *
 323       * @return RequestArrayShape
 324       */
 325      public function toArray(): array
 326      {
 327          $array = [
 328              self::KEY_METHOD => $this->method->value,
 329              self::KEY_URI => $this->getUri(),
 330              // Include query params if GET with data
 331              self::KEY_HEADERS => $this->headers->getAll(),
 332          ];
 333          // Include body if present (getBody() handles the conversion)
 334          $body = $this->getBody();
 335          if ($body !== null) {
 336              $array[self::KEY_BODY] = $body;
 337          }
 338          if ($this->options !== null) {
 339              $optionsArray = $this->options->toArray();
 340              if (!empty($optionsArray)) {
 341                  $array[self::KEY_OPTIONS] = $optionsArray;
 342              }
 343          }
 344          return $array;
 345      }
 346      /**
 347       * {@inheritDoc}
 348       *
 349       * @since 0.1.0
 350       */
 351      public static function fromArray(array $array): self
 352      {
 353          static::validateFromArrayData($array, [self::KEY_METHOD, self::KEY_URI, self::KEY_HEADERS]);
 354          return new self(HttpMethodEnum::from($array[self::KEY_METHOD]), $array[self::KEY_URI], $array[self::KEY_HEADERS] ?? [], $array[self::KEY_BODY] ?? null, isset($array[self::KEY_OPTIONS]) ? \WordPress\AiClient\Providers\Http\DTO\RequestOptions::fromArray($array[self::KEY_OPTIONS]) : null);
 355      }
 356      /**
 357       * Creates a Request instance from a PSR-7 RequestInterface.
 358       *
 359       * @since 0.2.0
 360       *
 361       * @param RequestInterface $psrRequest The PSR-7 request to convert.
 362       * @return self A new Request instance.
 363       * @throws InvalidArgumentException If the HTTP method is not supported.
 364       */
 365      public static function fromPsrRequest(RequestInterface $psrRequest): self
 366      {
 367          $method = HttpMethodEnum::from($psrRequest->getMethod());
 368          $uri = (string) $psrRequest->getUri();
 369          // Convert PSR-7 headers to array format expected by our constructor
 370          /** @var array<string, list<string>> $headers */
 371          $headers = $psrRequest->getHeaders();
 372          // Get body content
 373          $body = $psrRequest->getBody()->getContents();
 374          $bodyOrData = !empty($body) ? $body : null;
 375          return new self($method, $uri, $headers, $bodyOrData);
 376      }
 377  }


Generated : Sat Jun 13 09:38:55 2026 Cross-referenced by PHPXref