PHPackages                             vaslv/brevity-php-sdk - 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. vaslv/brevity-php-sdk

ActiveLibrary

vaslv/brevity-php-sdk
=====================

PHP SDK for Brevity short links API

1.3.1(yesterday)02↑2900%MITPHP &gt;=7.1

Since Mar 23Compare

[ Source](https://github.com/vaslv/brevity-php-sdk)[ Packagist](https://packagist.org/packages/vaslv/brevity-php-sdk)[ RSS](/packages/vaslv-brevity-php-sdk/feed)WikiDiscussions Synced today

READMEChangelogDependencies (4)Versions (7)Used By (0)

Brevity PHP SDK
===============

[](#brevity-php-sdk)

PHP client for the [Brevity](https://github.com/vaslv/brevity) short-link engine API.

**Русская версия: [README.ru.md](./README.ru.md)**

[![Packagist Version](https://camo.githubusercontent.com/257d01fb037efaea8a9cb31b25bfcab29843595dba3ffffae640092473e2913a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7661736c762f627265766974792d7068702d73646b)](https://packagist.org/packages/vaslv/brevity-php-sdk)[![CI](https://github.com/vaslv/brevity-php-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/vaslv/brevity-php-sdk/actions/workflows/ci.yml)

Features
--------

[](#features)

- Full `/api/v1` surface: create links (`POST /links`), read link state with a click summary (`GET /links/{code}`), partial updates (`PATCH /links/{code}`), domain and domain-group registries.
- Typed request/response DTOs: rules with up to 10 AND-ed conditions, A/B split variants with weights, activity window (`valid_since` / `valid_until`) and click budget (`max_clicks`).
- RFC 7807 error handling: exceptions are dispatched on the stable problem `type` code, never on texts or bare HTTP statuses.
- Contract-recommended transport behavior: retries only for network failures and 5xx, `Accept: application/json` everywhere, obvious mistakes rejected client-side before any HTTP round trip.
- Laravel bridge (5.8+): auto-discovered service provider, facade and publishable config.
- Runs on PHP 7.1+ with Guzzle 6.5 or 7.

Requirements
------------

[](#requirements)

- PHP &gt;= 7.1 with `ext-json`
- `guzzlehttp/guzzle` ^6.5 || ^7.0
- optional: Laravel 5.8+ for the bridge

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

[](#installation)

```
composer require vaslv/brevity-php-sdk
```

Quick start
-----------

[](#quick-start)

```
use Vaslv\Brevity\BrevityClient;

$client = new BrevityClient([
    'base_uri' => 'https://brevity.example.com', // the technical host, not a short-link domain
    'token' => getenv('BREVITY_TOKEN'),
]);

$link = $client->createSimpleLink('https://example.com/landing');
echo $link->getUrl(); // https://short.example.com/AbC12345
```

The API is served **only on the technical host** (the one behind `APP_URL`); short-link domains answer 404 to any `/api/...` request. The token is issued from the admin panel and must carry the `links:create` ability — newly issued tokens also get `links:read` and `links:update`.

Usage
-----

[](#usage)

### Conditions and A/B variants

[](#conditions-and-ab-variants)

```
use Vaslv\Brevity\DTO\CreateLinkCondition;
use Vaslv\Brevity\DTO\CreateLinkRequest;
use Vaslv\Brevity\DTO\CreateLinkRule;
use Vaslv\Brevity\DTO\CreateLinkVariant;

$request = new CreateLinkRequest(
    null,                            // domain (null → the default domain)
    'Spring campaign',               // title
    true,                            // forward_query
    ['click_id' => '{{click.id}}'],  // callback_data template
    [
        // Rules are tried in order; the first whose conditions all match wins.
        new CreateLinkRule('https://example.com/sale', [
            new CreateLinkCondition('time_before', ['before' => '2026-03-05T10:00:00+00:00']),
            new CreateLinkCondition('device', ['device' => 'mobile']),
        ]),
        // A/B split: weights 1:3, sticky per visitor.
        new CreateLinkRule('https://example.com/control', [], null, [
            new CreateLinkVariant('https://example.com/a', 1, 'A'),
            new CreateLinkVariant('https://example.com/b', 3, 'B'),
        ]),
        // Unconditional fallback.
        new CreateLinkRule('https://example.com/home'),
    ]
);

$link = $client->createLink($request);
```

Condition types: `time_before`, `after_date`, `query_param`, `ip_address`, `device`, `language` — see [API.md](./API.md) (§6) for the `data` shape of each. Condition `data` passes through the SDK untouched.

### Activity window and click budget

[](#activity-window-and-click-budget)

```
$request = new CreateLinkRequest(
    null, null, null, null,
    [new CreateLinkRule('https://example.com/landing')],
    null, null,
    new DateTimeImmutable('2026-08-01T00:00:00+00:00'), // valid_since
    new DateTimeImmutable('2026-09-01T00:00:00+00:00'), // valid_until (inclusive)
    100                                                 // max_clicks (bots count too)
);
```

Outside the window, or once the budget is exhausted, the link answers 404.

### Domain selection

[](#domain-selection)

```
// Explicit domain (must exist in the registry):
$client->createSimpleLink('https://example.com/x', 'go.example.com');

// Auto-picked domain: 'random' / 'round_robin' / 'coldest', optionally within a group:
$request = new CreateLinkRequest(
    null, null, null, null,
    [new CreateLinkRule('https://example.com/x')],
    'round_robin',
    'campaigns'
);

// Registries:
$domains = $client->listDomains();     // Domain[]; or listDomains('campaigns')
$groups = $client->listDomainGroups(); // DomainGroup[]
```

### Reading a link

[](#reading-a-link)

```
$link = $client->getLink('AbC12345');  // needs the links:read ability

$link->getClicks()->getTotal();        // all clicks, bots included
$link->getClicks()->getNonBots();      // may lag behind reality by seconds
```

### Updating a link

[](#updating-a-link)

```
use Vaslv\Brevity\DTO\UpdateLinkRequest;

$patch = (new UpdateLinkRequest)
    ->setValidUntil(new DateTimeImmutable('2026-12-31T23:59:59+00:00'))
    ->setMaxClicks(null);              // an explicit null clears the budget

$client->updateLink('AbC12345', $patch); // needs the links:update ability
```

Untouched fields keep their server-side values; `setRules()` replaces the whole rule list. `code` and `domain` cannot be changed.

Error handling
--------------

[](#error-handling)

Every `/api/v1` error is an RFC 7807 problem; the SDK dispatches on the stable `type` code:

`type`HTTPException`unauthenticated`401`AuthenticationException``missing-ability`403`MissingAbilityException``forbidden`403`ForbiddenException``not-found`404`NotFoundException``validation-error`422`ValidationException` (`getErrors()`)`too-many-requests`429`RateLimitException` (`getRetryAfter()`)`http-error`, `server-error`other`ApiException`An unknown `type` (a proxy answering instead of the API, a future contract code) falls back to the HTTP-status mapping; the raw code stays available via `getProblemType()`.

All of the above extend `ApiException` (`getStatusCode()`, `getResponseBody()`, `getProblemType()`); `MissingAbilityException` extends `ForbiddenException`. Network/timeout failures throw `TransportException`; client-side misuse (contradictory options, an empty patch) throws `InvalidRequestException`before any HTTP round trip.

```
use Vaslv\Brevity\Exceptions\ApiException;
use Vaslv\Brevity\Exceptions\RateLimitException;
use Vaslv\Brevity\Exceptions\TransportException;
use Vaslv\Brevity\Exceptions\ValidationException;

try {
    $link = $client->createLink($request);
} catch (ValidationException $e) {
    $errors = $e->getErrors();    // field => messages[]
} catch (RateLimitException $e) {
    $wait = $e->getRetryAfter();  // seconds, or null
} catch (ApiException $e) {
    $type = $e->getProblemType(); // stable machine code, or null
} catch (TransportException $e) {
    // network failure after the configured retries
}
```

Rate limits: two independent budgets — reads and writes — of 120 requests per minute per service.

Laravel
-------

[](#laravel)

The service provider and the `Brevity` facade are auto-discovered on Laravel 5.8+. Configure via `.env`:

```
BREVITY_BASE_URI=https://brevity.example.com
BREVITY_TOKEN=your-token
BREVITY_TIMEOUT=7
BREVITY_CONNECT_TIMEOUT=5
BREVITY_RETRIES=1
```

```
$link = Brevity::createSimpleLink('https://example.com/landing');
```

Publish the config when you need to tweak it: `php artisan vendor:publish --tag=brevity-config`.

Testing
-------

[](#testing)

No local PHP toolchain needed — the suite runs in Docker:

```
docker compose run --rm tests                       # composer install + phpunit
docker compose run --rm tests phpstan analyse       # static analysis (level 8)
docker compose run --rm tests vendor/bin/pint --test # code style
```

Documentation
-------------

[](#documentation)

The full API contract lives in [API.md](./API.md) — a copy of the canonical English `docs/03-api.md` of the [main repository](https://github.com/vaslv/brevity), where a Russian mirror is kept under `docs/ru/`.

License
-------

[](#license)

[MIT](./LICENSE)

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

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 ~23 days

Total

6

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/032ebe8f61d00ae1b569fd7f418e7f09825d093784070f7c2effd1c67d6fc3a0?d=identicon)[vaslv](/maintainers/vaslv)

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/vaslv-brevity-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/vaslv-brevity-php-sdk/health.svg)](https://phpackages.com/packages/vaslv-brevity-php-sdk)
```

###  Alternatives

[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.3k543.5M2.7k](/packages/aws-aws-sdk-php)[neuron-core/neuron-ai

The PHP Agentic Framework.

2.0k656.1k46](/packages/neuron-core-neuron-ai)[tencentcloud/tencentcloud-sdk-php

TencentCloudApi php sdk

3741.3M47](/packages/tencentcloud-tencentcloud-sdk-php)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

293.1k](/packages/eslazarev-wildberries-sdk)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[files.com/files-php-sdk

Files.com PHP SDK

2481.1k](/packages/filescom-files-php-sdk)

PHPackages © 2026

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