Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions v3.0/packages/console/built-in-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Contract:

- the command requires `.env.example` in the project base directory
- if `.env` already exists, the command asks before overwriting unless `--yes` is passed
- both files are looked up in the project base directory, not in the folder you run the command from, so the question appears wherever you start it

### `core:key`

Expand Down
4 changes: 2 additions & 2 deletions v3.0/packages/httpclient/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ For a normal multi request, `start()` calls the native `MultiCurlAdapter`. Each

### Async multi request path

For async multi requests, Quantum installs the same completion collector used by normal multi requests and also wires your success and error callbacks. Your callbacks are useful for per-request side effects, while `getResponse()` and `getErrors()` remain available after `start()` finishes.
For async multi requests, Quantum installs the same completion collector used by normal multi requests and also wires your success and error callbacks. For each finished request the collector runs first, so inside your callback `getResponse()` and `getErrors()` already hold that request. Your callbacks are useful for per-request side effects, while `getResponse()` and `getErrors()` remain available after `start()` finishes.

## Response storage model

Expand Down Expand Up @@ -78,7 +78,7 @@ The package keeps a separate `$requestHeaders` array for headers you set through
- `setHeader($name, $value)`
- `setHeaders([...])`

Those keys are lowercased before storage.
Those keys are lowercased before storage, so read them back in lowercase too: after `setHeader('X-Token', 'abc')`, `getRequestHeaders('x-token')` returns `abc` and `getRequestHeaders('X-Token')` returns `null`. `setHeader()` adds one entry, while `setHeaders()` replaces the whole tracked set.

This is only a bookkeeping layer for later reads through `getRequestHeaders()`. It does not inspect headers set through unrelated low-level cURL options.

Expand Down
13 changes: 11 additions & 2 deletions v3.0/packages/httpclient/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This package is intentionally small. It does not add retries, middleware, authen

## Package shape

The package is built from three pieces:
The package is built from these pieces:

- `Quantum\HttpClient\HttpClient` is the runtime wrapper
- `Quantum\HttpClient\Factories\HttpClientFactory` creates fresh request facades
Expand Down Expand Up @@ -61,7 +61,9 @@ Use this when your application wants callback handling while still allowing late
- `PATCH`
- `DELETE`

Any other method triggers `HttpClientException::requestMethodNotAvailable(...)`.
Any other method triggers `HttpClientException::requestMethodNotAvailable(...)`. The list is also available as `HttpClient::METHODS`.

You can read back what you set with `getMethod()` and `getData()`, get the underlying adapter with `getAdapter()`, and ask `isMultiRequest()` which kind of request the client holds.

## Response model

Expand All @@ -73,6 +75,13 @@ Responses are stored in three sections:

Headers are normalized to lowercase before storage, so `Content-Type` becomes `content-type` when you read it back.

The `body` is decoded for you:

- when the `Content-Type` contains `json` and the body is valid JSON, it is the result of `json_decode()`, so an object (`stdClass`), not an array
- when the `Content-Type` contains `xml` and the body is valid XML, it is a `SimpleXMLElement`
- a body sent with `Content-Encoding: gzip` is decompressed first
- in every other case, including invalid JSON or XML, it is the raw string

For a single request, `getResponse()` returns one response array.

For multi requests, `getResponse()` returns a map keyed by curl request ID.
Expand Down
35 changes: 29 additions & 6 deletions v3.0/packages/httpclient/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Because response headers are normalized to lowercase, read them like this:
$contentType = $client->getResponseHeaders('content-type');
```

A JSON or XML response body arrives already decoded; see the [response model](overview.md#response-model).

## Send JSON or form data in a single request

```php
Expand All @@ -31,9 +33,21 @@ $client = httpRequest('https://api.example.com/users')
->start();
```

`setData()` is passed through the native adapter's post-data builder before the request is executed.
How `setData()` is sent depends on the `Content-Type` header you set before `start()`:

- an array (or a `JsonSerializable`) with an `application/json` or `application/...+json` header is sent as a JSON body
- an array without a JSON header is form-encoded, like `name=Arman&role=admin`
- an array with a `multipart/form-data` header, or one that holds a `CURLFile`, is sent as multipart, which is how you upload a file
- a string is sent as it is

```php
$client = httpRequest('https://api.example.com/avatar')
->setMethod('POST')
->setData(['user' => 'arman', 'avatar' => new CURLFile('/path/to/avatar.png')])
->start();
```

## Check transport errors
## Check errors

```php
$errors = $client->getErrors();
Expand All @@ -44,22 +58,31 @@ if (!empty($errors)) {
}
```

The package records transport-layer curl errors. It does not treat HTTP 4xx or 5xx responses as package exceptions by itself.
`getErrors()` is empty when the request worked. It holds an entry when cURL failed to complete the request, and also when the server answered with an HTTP 4xx or 5xx status. A request to a missing page gives:

```php
['code' => 404, 'message' => 'HTTP/1.1 404 Not Found']
```

For a cURL failure, `code` is the cURL error number and `message` is its description. Neither case throws an exception. The response body of a 4xx or 5xx answer is still available through `getResponseBody()`.

## Queue multiple requests

```php
$client = httpMultiRequest();

$client->addGet('https://api.example.com/users');
$client->addGet('https://api.example.com/users', ['page' => 2]);
$client->addGet('https://api.example.com/teams');
$client->addPost('https://api.example.com/teams', ['name' => 'Core']);
$client->start();

$responses = $client->getResponse();
$errors = $client->getErrors();
```

In multi mode, `$responses` is keyed by curl request ID. Inspect each entry's `headers`, `cookies`, and `body` sections.
`addGet()` appends its data array to the URL as a query string, with `&` when the URL already has one, so the first request above asks for `/users?page=2`. `addPost()` builds its body the same way as `setData()`.

In multi mode, `$responses` is keyed by curl request ID. Inspect each entry's `headers`, `cookies`, and `body` sections. `$errors` is keyed the same way and holds only the requests that failed.

## Use async callbacks for multi requests

Expand All @@ -79,7 +102,7 @@ $client->addGet('https://api.example.com/users');
$client->start();
```

In this mode, handle each result inside your callbacks when you need per-request side effects. `getResponse()` and `getErrors()` are also populated after `start()` finishes.
In this mode, handle each result inside your callbacks when you need per-request side effects. A request that failed, including one that got an HTTP 4xx or 5xx answer, goes to the error callback; every other request goes to the success callback. `getResponse()` and `getErrors()` are also populated after `start()` finishes.

## Recommended usage patterns

Expand Down
2 changes: 2 additions & 0 deletions v3.0/packages/logger/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ The Logger package is small, but a few runtime contracts matter when you build o
- `debug()`
- `log()`

Each method takes the message as `string|Stringable $message` and an optional `array $context`; `log()` takes the level first. A class that extends `Logger` and overrides these methods must declare the same parameter type.

But the package does not validate arbitrary custom level strings in `log()`.

Instead:
Expand Down
2 changes: 1 addition & 1 deletion v3.0/packages/ratelimit/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Use it when the app runs on one machine or when each node can keep its own indep

### What it guarantees

- The storage directory is created if it does not exist.
- The storage directory is created if it does not exist, together with its missing parent folders (for example `cache/data` when `cache/` is missing).
- Each hit stores `count` and `reset_at`.
- When the window expires, the next hit starts a fresh counter.
- `retryAfter()` returns `0` when the state file is missing or unreadable.
Expand Down