PHPackages                             flytachi/winter-cast - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. flytachi/winter-cast

ActiveLibrary[Framework](/categories/framework)

flytachi/winter-cast
====================

Winter framework cast component

v1.1.1(3mo ago)025MITPHPPHP &gt;=8.3

Since Jan 9Pushed 3mo agoCompare

[ Source](https://github.com/Flytachi/winter-cast)[ Packagist](https://packagist.org/packages/flytachi/winter-cast)[ RSS](/packages/flytachi-winter-cast/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (3)Versions (6)Used By (0)

Winter Cast Component
=====================

[](#winter-cast-component)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a2bfd8760ecfad7820115715567836686b758366442bfa7b5090ae944115c549/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f666c7974616368692f77696e7465722d636173742e737667)](https://packagist.org/packages/flytachi/winter-cast)[![Software License](https://camo.githubusercontent.com/074b89bca64d3edc93a1db6c7e3b1636b874540ba91d66367c0e5e354c56d0ea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e737667)](LICENSE)

A modern, fluent HTTP client for PHP 8.3+ with a focus on developer experience, type safety, and ease of use.

Features
--------

[](#features)

✨ **Fluent API** - Chainable, immutable request builder
🎯 **Type-Safe** - Full PHP 8.3+ type declarations
🚀 **Zero Config** - Works out of the box with sensible defaults
🔄 **Auto-Retry** - Exponential backoff with jitter
⚡ **Fast** - Powered by cURL for maximum performance
🛡️ **Secure** - URL validation, response size limits, timeout controls
🔌 **Middleware** - Intercept requests/responses (logging, auth, retry)
🏗️ **ApiService** - Abstract base class for building API clients
📝 **PSR-3 Logging** - Built-in LoggingMiddleware
💥 **Smart Exceptions** - Specific exception types for different errors

---

Installation
------------

[](#installation)

```
composer require flytachi/winter-cast
```

**Requirements:**

- PHP &gt;= 8.3
- ext-curl

---

Quick Start
-----------

[](#quick-start)

### Simple GET Request

[](#simple-get-request)

```
use Flytachi\Winter\Cast\Cast;

// One-liner
$response = Cast::sendGet('https://api.example.com/users');
$users = $response->json();

// Or with fluent API
$response = Cast::get('https://api.example.com/users')
    ->timeout(5)
    ->send();
```

### POST with JSON

[](#post-with-json)

```
use Flytachi\Winter\Cast\Cast;
use Flytachi\Winter\Cast\Common\CastHeader;

$response = Cast::post('https://api.example.com/users')
    ->withHeaders(
        CastHeader::instance()
            ->json()
            ->authBearer($token)
    )
    ->withJsonBody([
        'name' => 'John Doe',
        'email' => 'john@example.com'
    ])
    ->throwOnError()
    ->send();

$user = $response->json();
```

---

Core Concepts
-------------

[](#core-concepts)

### 1. Cast Facade (Simple Usage)

[](#1-cast-facade-simple-usage)

The `Cast` facade provides a zero-configuration entry point:

```
// Build request (returns CastRequest)
$request = Cast::get('https://api.com/data');

// Send immediately (returns CastResponse)
$response = Cast::sendGet('https://api.com/data');
```

**Available methods:**

- `get()`, `post()`, `put()`, `patch()`, `delete()`, `head()`
- `sendGet()`, `sendPost()`, `sendPut()`, `sendPatch()`, `sendDelete()`, `sendHead()`

### 2. CastRequest (Request Builder)

[](#2-castrequest-request-builder)

Build requests with a fluent interface:

```
use Flytachi\Winter\Cast\Common\CastRequest;

$request = CastRequest::get('https://api.com/users')
    ->withQueryParam('page', 1)
    ->withQueryParam('limit', 10)
    ->timeout(30)
    ->connectTimeout(5)
    ->retry(3, 1000)  // 3 retries, 1s delay
    ->maxResponseSize(50 * 1024 * 1024)  // 50MB limit
    ->throwOnError()
    ->send();
```

### 3. CastHeader (Header Builder)

[](#3-castheader-header-builder)

Fluent interface for headers:

```
use Flytachi\Winter\Cast\Common\CastHeader;

$headers = CastHeader::instance()
    ->json()                      // Accept + Content-Type
    ->authBearer($token)          // Authorization: Bearer
    ->acceptLanguage('ru')        // Accept-Language
    ->userAgent('MyApp/1.0')      // User-Agent
    ->set('X-Custom', 'value');   // Custom header
```

**Built-in helpers:**

- `json()` - Set Accept and Content-Type for JSON
- `authBearer($token)` - Bearer authentication
- `authBasic($user, $pass)` - Basic authentication
- `acceptLanguage($lang)` - Accept-Language header
- `userAgent($agent)` - User-Agent header
- `referer($url)` - Referer header
- `contentType($type)` - Content-Type header

### 4. CastResponse (Response Object)

[](#4-castresponse-response-object)

Immutable response with helper methods:

```
$response->statusCode;        // int: HTTP status code
$response->body();            // ?string: Raw body
$response->json();            // ?array: Parsed JSON
$response->headers();         // array: All headers
$response->header('X-Rate-Limit');  // ?string: Specific header
$response->info();            // array: cURL info

// Status checks
$response->isSuccess();       // 2xx
$response->isClientError();   // 4xx
$response->isServerError();   // 5xx
$response->isRedirection();   // 3xx
$response->isConnectionError(); // Connection failed
```

---

Advanced Usage
--------------

[](#advanced-usage)

### Query Parameters

[](#query-parameters)

```
// Static params
Cast::get('https://api.com/search', ['q' => 'php', 'type' => 'repos']);

// Dynamic params
Cast::get('https://api.com/search')
    ->withQueryParam('q', 'php')
    ->withQueryParam('type', 'repos')
    ->withQueryParams(['sort' => 'stars', 'order' => 'desc'])
    ->send();
```

### Request Body

[](#request-body)

```
// JSON body
Cast::post($url)
    ->withJsonBody(['key' => 'value'])
    ->send();

// Form data
Cast::post($url)
    ->withFormParams(['key' => 'value'])
    ->send();

// Multipart (file upload)
Cast::post($url)
    ->withMultipartBody([
        'file' => new CURLFile('/path/to/file.jpg'),
        'title' => 'My Image'
    ])
    ->send();

// Raw body
Cast::post($url)
    ->withBody('data')
    ->send();
```

### Timeouts &amp; Retries

[](#timeouts--retries)

```
Cast::get($url)
    ->timeout(30)           // Total request timeout (seconds)
    ->connectTimeout(10)    // Connection timeout (seconds)
    ->retry(3, 1000)        // 3 retries, 1s base delay, exponential backoff
    ->send();

// Disable exponential backoff (fixed delay)
Cast::get($url)
    ->retry(3, 1000, false) // 3 retries, fixed 1s delay
    ->send();
```

**Exponential Backoff with Jitter:**

- Retry 1: ~1000ms (1s × 2⁰ ± 30% jitter)
- Retry 2: ~2000ms (1s × 2¹ ± 30% jitter)
- Retry 3: ~4000ms (1s × 2² ± 30% jitter)

Jitter prevents thundering herd problem when multiple clients retry simultaneously.

### Error Handling

[](#error-handling)

```
use Flytachi\Winter\Cast\Exception\TimeoutException;
use Flytachi\Winter\Cast\Exception\ConnectionException;
use Flytachi\Winter\Cast\Exception\RequestException;

try {
    $response = Cast::get($url)
        ->throwOnError()  // Enable exceptions for HTTP errors
        ->send();

} catch (TimeoutException $e) {
    // Request timed out - retry with longer timeout
    echo "Timeout: {$e->getMessage()}";

} catch (ConnectionException $e) {
    // Connection failed - try backup server
    echo "Connection failed: {$e->getMessage()}";

} catch (RequestException $e) {
    // HTTP error (4xx/5xx) - access response object
    echo "HTTP {$e->response->statusCode}: {$e->response->body()}";
}
```

**Exception hierarchy:**

- `CastException` - Base exception
    - `TimeoutException` - Request/connection timeout
    - `ConnectionException` - DNS, connection refused, etc.
    - `RequestException` - HTTP errors (4xx/5xx)

### Dependency Injection

[](#dependency-injection)

```
use Flytachi\Winter\Cast\Common\CastClient;
use Flytachi\Winter\Cast\Common\CastRequest;

class ApiService
{
    public function __construct(
        private readonly CastClient $httpClient
    ) {}

    public function fetchUsers(): array
    {
        $request = CastRequest::get('https://api.com/users')
            ->timeout(5);

        $response = $this->httpClient->send($request);
        return $response->json();
    }
}

// DI Container configuration
$client = new CastClient(
    defaultTimeout: 30,
    defaultConnectTimeout: 10
);
$service = new ApiService($client);
```

### Custom Client Configuration

[](#custom-client-configuration)

```
$client = new CastClient(
    defaultTimeout: 60,
    defaultConnectTimeout: 15,
    defaultOptions: [
        CURLOPT_SSL_VERIFYPEER => false,  // Disable SSL verification (dev only!)
        CURLOPT_FOLLOWLOCATION => true,
    ]
);

// Use custom client
Cast::setGlobalClient($client);
```

---

Middleware
----------

[](#middleware)

Middleware allows you to intercept and modify requests/responses. Use cases include logging, authentication, caching, and retry logic.

### Creating Custom Middleware

[](#creating-custom-middleware)

```
use Flytachi\Winter\Cast\Common\CastMiddleware;
use Flytachi\Winter\Cast\Common\CastRequest;
use Flytachi\Winter\Cast\Common\CastResponse;

class TimingMiddleware implements CastMiddleware
{
    public function handle(CastRequest $request, callable $next): CastResponse
    {
        $start = microtime(true);

        $response = $next($request);  // Pass to next middleware/client

        $duration = microtime(true) - $start;
        echo "Request took {$duration}s\n";

        return $response;
    }
}
```

### Adding Middleware to Client

[](#adding-middleware-to-client)

```
use Flytachi\Winter\Cast\Common\CastClient;

$client = new CastClient();
$client
    ->addMiddleware(new LoggingMiddleware($logger))
    ->addMiddleware(new BearerAuthMiddleware($token))
    ->addMiddleware(new TimingMiddleware());

// Middleware executes in order: Logging → Auth → Timing → Request
```

### Using with Facade

[](#using-with-facade)

```
$client = new CastClient();
$client->addMiddleware(new BearerAuthMiddleware($token));

Cast::setGlobalClient($client);

// All requests now use middleware
Cast::sendGet('https://api.com/data');
```

---

Built-in Middleware
-------------------

[](#built-in-middleware)

Ready-to-use middleware classes in `Flytachi\Winter\Cast\Stereotype`:

### LoggingMiddleware

[](#loggingmiddleware)

Logs all HTTP requests and responses with PSR-3 logger:

```
use Flytachi\Winter\Cast\Stereotype\LoggingMiddleware;

$client->addMiddleware(new LoggingMiddleware(
    logger: $psrLogger,
    requestLevel: LogLevel::INFO,     // Log level for requests
    responseLevel: LogLevel::INFO,    // Log level for 2xx/3xx
    errorLevel: LogLevel::WARNING,    // Log level for 4xx/5xx
    logBody: false,                   // Log request/response body
    bodyMaxLength: 500                // Truncate body at N chars
));

// Output:
// [INFO] HTTP Request: GET https://api.com/users
// [INFO] HTTP Response: 200 (125.4ms)
```

### BearerAuthMiddleware

[](#bearerauthmiddleware)

Automatically adds Bearer token to all requests:

```
use Flytachi\Winter\Cast\Stereotype\BearerAuthMiddleware;

// Static token
$client->addMiddleware(new BearerAuthMiddleware('your-api-token'));

// Dynamic token (fetched for each request)
$client->addMiddleware(new BearerAuthMiddleware(
    fn() => $tokenService->getAccessToken()
));
```

### BasicAuthMiddleware

[](#basicauthmiddleware)

Adds HTTP Basic authentication:

```
use Flytachi\Winter\Cast\Stereotype\BasicAuthMiddleware;

$client->addMiddleware(new BasicAuthMiddleware('username', 'password'));
```

### HeadersMiddleware

[](#headersmiddleware)

Adds custom headers to all requests:

```
use Flytachi\Winter\Cast\Stereotype\HeadersMiddleware;

$client->addMiddleware(new HeadersMiddleware(
    CastHeader::instance()
        ->userAgent('MyApp/1.0')
        ->acceptLanguage('ru')
        ->set('X-API-Version', '2')
));
```

### RetryOnUnauthorizedMiddleware

[](#retryonunauthorizedmiddleware)

Automatically refreshes token on 401 and retries:

```
use Flytachi\Winter\Cast\Stereotype\RetryOnUnauthorizedMiddleware;

$client->addMiddleware(new RetryOnUnauthorizedMiddleware(
    tokenRefresher: function (): string {
        // Refresh your token
        $response = Cast::sendPost('https://auth.api.com/refresh', [
            'refresh_token' => $storedRefreshToken
        ]);
        return $response->json()['access_token'];
    },
    maxRetries: 1  // Retry once after refresh
));
```

---

ApiService (Abstract Base Class)
--------------------------------

[](#apiservice-abstract-base-class)

Build clean API clients by extending `ApiService`. Each service has its own isolated `CastClient`.

### Basic Usage

[](#basic-usage)

```
use Flytachi\Winter\Cast\Stereotype\ApiService;
use Flytachi\Winter\Cast\Common\CastHeader;

class PaymentApi extends ApiService
{
    protected static function baseUrl(): string
    {
        return 'https://api.payment.com/v1';
    }

    protected static function headers(): CastHeader
    {
        return CastHeader::instance()
            ->authBearer(env('PAYMENT_TOKEN'))
            ->json();
    }

    public static function getBalance(): array
    {
        $response = self::get('balance')->send(self::client());
        return self::tryResult($response);
    }

    public static function createPayment(array $data): array
    {
        $response = self::post('payments')
            ->withJsonBody($data)
            ->send(self::client());
        return self::tryResult($response);
    }
}

// Usage
$balance = PaymentApi::getBalance();
$payment = PaymentApi::createPayment(['amount' => 100]);
```

### With Custom Client &amp; Middleware

[](#with-custom-client--middleware)

```
class PaymentApi extends ApiService
{
    protected static function baseUrl(): string
    {
        return env('PAYMENT_API_URL');
    }

    protected static function headers(): CastHeader
    {
        return CastHeader::instance()->json();
    }

    protected static function createClient(): CastClient
    {
        return (new CastClient(defaultTimeout: 30))
            ->addMiddleware(new LoggingMiddleware($logger))
            ->addMiddleware(new BearerAuthMiddleware(
                fn() => TokenService::getToken()
            ))
            ->addMiddleware(new RetryOnUnauthorizedMiddleware(
                fn() => TokenService::refresh()
            ));
    }
}
```

### Available Methods

[](#available-methods)

MethodDescription`get(string $path)`Create GET request`post(string $path)`Create POST request`put(string $path)`Create PUT request`patch(string $path)`Create PATCH request`delete(string $path)`Create DELETE request`head(string $path)`Create HEAD request`client()`Get service's CastClient`setClient(CastClient)`Replace service's client`tryResult(CastResponse)`Extract data or throw exception---

Security Features
-----------------

[](#security-features)

### URL Validation

[](#url-validation)

Automatically validates URLs and rejects dangerous protocols:

```
// ✅ Allowed
Cast::get('https://api.com/data')->send();
Cast::get('http://localhost/api')->send();

// ❌ Rejected (throws CastException)
Cast::get('file:///etc/passwd')->send();
Cast::get('ftp://example.com')->send();
```

### Response Size Limit

[](#response-size-limit)

Prevent memory exhaustion with automatic size limits:

```
// Default: 10MB limit
Cast::get($url)->send();

// Custom limit
Cast::get($url)
    ->maxResponseSize(50 * 1024 * 1024)  // 50MB
    ->send();

// Disable limit (use with caution!)
Cast::get($url)
    ->maxResponseSize(null)
    ->send();
```

---

Testing
-------

[](#testing)

```
use Flytachi\Winter\Cast\Common\CastClient;
use Flytachi\Winter\Cast\Common\CastResponse;

// Mock client for testing
class MockClient extends CastClient
{
    public function send(CastRequest $request): CastResponse
    {
        return new CastResponse(
            statusCode: 200,
            body: '{"id":1,"name":"Test User"}',
            headers: ['Content-Type' => 'application/json'],
            info: []
        );
    }
}

$service = new ApiService(new MockClient());
```

---

Examples
--------

[](#examples)

### GitHub API

[](#github-api)

```
$repos = Cast::get('https://api.github.com/users/flytachi/repos')
    ->withHeaders(
        CastHeader::instance()
            ->acceptLanguage('en')
            ->userAgent('Winter-Cast/1.0')
    )
    ->send()
    ->json();
```

### Authenticated API Request

[](#authenticated-api-request)

```
$response = Cast::post('https://api.example.com/data')
    ->withHeaders(
        CastHeader::instance()
            ->json()
            ->authBearer($accessToken)
    )
    ->withJsonBody(['key' => 'value'])
    ->throwOnError()
    ->send();
```

### File Download

[](#file-download)

```
$response = Cast::get('https://example.com/file.pdf')
    ->maxResponseSize(100 * 1024 * 1024)  // 100MB
    ->timeout(120)
    ->send();

file_put_contents('/tmp/file.pdf', $response->body());
```

### Pagination

[](#pagination)

```
$page = 1;
$allUsers = [];

do {
    $response = Cast::get('https://api.com/users')
        ->withQueryParam('page', $page)
        ->withQueryParam('limit', 100)
        ->send();

    $data = $response->json();
    $allUsers = array_merge($allUsers, $data['users']);
    $page++;

} while (!empty($data['users']));
```

---

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

---

Credits
-------

[](#credits)

- **Author:** Flytachi
- **Framework:** Winter Framework
- **Built with:** PHP 8.3+, cURL

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance80

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~6 days

Total

5

Last Release

106d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/861b81dd97c8ddfa919522d2a4e17626120bd3e3d7464857cc03784676bc74a8?d=identicon)[Flytachi](/maintainers/Flytachi)

---

Top Contributors

[![Flytachi](https://avatars.githubusercontent.com/u/68924300?v=4)](https://github.com/Flytachi "Flytachi (12 commits)")

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/flytachi-winter-cast/health.svg)

```
[![Health](https://phpackages.com/badges/flytachi-winter-cast/health.svg)](https://phpackages.com/packages/flytachi-winter-cast)
```

###  Alternatives

[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

712181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)[laravel/pail

Easily delve into your Laravel application's log files directly from the command line.

91545.3M590](/packages/laravel-pail)[nette/bootstrap

🅱 Nette Bootstrap: the simple way to configure and bootstrap your Nette application.

68535.8M592](/packages/nette-bootstrap)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
