PHPackages                             poli-page/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. [Templating &amp; Views](/categories/templating)
4. /
5. poli-page/sdk

ActiveLibrary[Templating &amp; Views](/categories/templating)

poli-page/sdk
=============

Poli Page SDK for PHP — render PDFs from HTML templates via the Poli Page API

v0.9.0(yesterday)01↑2900%MITPHPPHP ^8.3CI passing

Since Jun 22Pushed todayCompare

[ Source](https://github.com/poli-page/sdk-php)[ Packagist](https://packagist.org/packages/poli-page/sdk)[ Docs](https://github.com/poli-page/sdk-php)[ RSS](/packages/poli-page-sdk/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (13)Versions (3)Used By (0)

Poli Page SDK for PHP
=====================

[](#poli-page-sdk-for-php)

[![Packagist](https://camo.githubusercontent.com/7af7259ea1a1b191e8b1253822814abcc82ff756d8071e5d4eb18cca620b0229/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f706f6c692d706167652f73646b3f7374796c653d666c6174266c6f676f3d706870266c6f676f436f6c6f723d666666666666266c6162656c3d5061636b6167697374)](https://packagist.org/packages/poli-page/sdk)[![Downloads](https://camo.githubusercontent.com/65edf771f3ec7c822b5f9ace2554f41eccf626204b0a95d2d8d01aaae3ba1268/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f706f6c692d706167652f73646b3f7374796c653d666c6174266c6f676f3d706870266c6f676f436f6c6f723d666666666666266c6162656c3d446f776e6c6f616473)](https://packagist.org/packages/poli-page/sdk)[![Ci](https://camo.githubusercontent.com/7bc02af669f94f6c0e1f5506bd27aa302da6c96adfff4508511114dc3638fd4e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f706f6c692d706167652f73646b2d7068702f63692e796d6c3f6272616e63683d6d61696e267374796c653d666c6174266c6f676f3d676974687562616374696f6e73266c6f676f436f6c6f723d666666666666266c6162656c3d4369)](https://github.com/poli-page/sdk-php/actions/workflows/ci.yml)[![Codeql](https://camo.githubusercontent.com/8b72946c8f0cdde2b796ec64249b5e8fe98e938536a18f1bf7c0ad18513e1e51/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f706f6c692d706167652f73646b2d7068702f636f6465716c2e796d6c3f6272616e63683d6d61696e267374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d666666666666266c6162656c3d436f6465716c)](https://github.com/poli-page/sdk-php/actions/workflows/codeql.yml)[![Coverage](https://camo.githubusercontent.com/97b6eb87a11e15768b971141d4aa26ab106ce8d6983cfbcc34a604a367b09ac4/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f706f6c692d706167652f73646b2d7068703f7374796c653d666c6174266c6f676f3d636f6465636f76266c6f676f436f6c6f723d666666666666266c6162656c3d436f766572616765)](https://codecov.io/gh/poli-page/sdk-php)[![Php](https://camo.githubusercontent.com/3c1f7b88a0ebded6559c9f3ef3fad259da22f60d0a90aff76513b9c78fc4d2e8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f706f6c692d706167652f73646b3f7374796c653d666c6174266c6f676f3d706870266c6f676f436f6c6f723d666666666666266c6162656c3d506870)](https://packagist.org/packages/poli-page/sdk)[![Phpstan](https://camo.githubusercontent.com/4c6ecb24ae83f9e86c23d44e781778441ad3c290cca7bebd54ffa5d433a9d0ef/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5068707374616e2d6d61782d626c75653f7374796c653d666c6174266c6f676f3d706870266c6f676f436f6c6f723d666666666666)](phpstan.neon)[![Deps](https://camo.githubusercontent.com/bf1a147998d39a61bbbfd3d7b3a43e47446124352913c90df062902d1ffe1cac/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446570732d7570253230746f253230646174652d627269676874677265656e3f7374796c653d666c6174266c6f676f3d706870266c6f676f436f6c6f723d666666666666)](https://github.com/poli-page/sdk-php/network/dependencies)[![Docs](https://camo.githubusercontent.com/e70b4d17c7566a89dea87848b7c3c57ba28f7e856423d46822143f462d22a049/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f63732d6f6e6c696e652d627269676874677265656e3f7374796c653d666c6174266c6f676f3d72656164746865646f6373266c6f676f436f6c6f723d666666666666)](https://poli-page.github.io/sdk-php/)[![License](https://camo.githubusercontent.com/495ea3bc7b2049bb4c95191b3dfd91c50536993c35ebe3e81e2c68fd06ce67e3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75653f7374796c653d666c6174266c6f676f3d676e75266c6f676f436f6c6f723d666666666666)](LICENSE)

Official PHP SDK for [Poli Page](https://poli.page) — render polished PDFs from HTML templates via the Poli Page API.

→ Docs (auto-generated from source): ****

Install
-------

[](#install)

The SDK declares only [PSR-18](https://www.php-fig.org/psr/psr-18/) / [PSR-17](https://www.php-fig.org/psr/psr-17/) / [PSR-3](https://www.php-fig.org/psr/psr-3/) interfaces plus [`php-http/discovery`](https://github.com/php-http/discovery) as hard dependencies — it deliberately ships **no** concrete HTTP client. You **must** install a PSR-18 client and a PSR-7 / PSR-17 implementation alongside it, or constructing the client will throw (discovery finds nothing to use). Install both in one command:

```
# Guzzle (~80% of PHP apps) — the canonical choice
composer require poli-page/sdk guzzlehttp/guzzle
```

Guzzle bundles its own PSR-7 / PSR-17 implementation, so that single line is everything you need. Prefer something else? Any PSR-18 client + PSR-7 pairing works:

```
# Symfony HTTP Client
composer require poli-page/sdk symfony/http-client nyholm/psr7

# Lightweight, no curl needed
composer require poli-page/sdk php-http/curl-client nyholm/psr7
```

Discovery auto-detects whichever you've installed; you never name it in code (or pass it explicitly via the `httpClient:` constructor argument — see [Configuration](#configuration)).

Requires PHP 8.3 or later.

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

[](#quick-start)

### Project mode — render a published template by slug

[](#project-mode--render-a-published-template-by-slug)

```
use PoliPage\PoliPage;
use PoliPage\ProjectModeInput;

$client = PoliPage::client($_ENV['POLI_PAGE_API_KEY']);

$pdf = $client->render->pdf(new ProjectModeInput(
    project: 'getting-started',
    template: 'welcome',
    version: '1.0.0',
    data: ['name' => 'World'],
));
// $pdf is a string of raw PDF bytes
```

Every Poli Page org comes pre-provisioned with a `getting-started/welcome` template, so the snippet above runs as-is the moment you have an API key — no project setup needed. For your own templates, swap the slugs once you've pushed a version with the `poli` CLI:

```
$pdf = $client->render->pdf(new ProjectModeInput(
    project: 'billing',
    template: 'invoice',
    version: '1.0.0',
    data: ['invoiceNumber' => 'INV-001', 'total' => 1280],
));
```

### Preview inline HTML

[](#preview-inline-html)

`render->preview` accepts raw HTML for live editing and visual inspection without producing a stored document. Use this for editor previews or layout tests.

```
use PoliPage\InlineModeInput;

$result = $client->render->preview(new InlineModeInput(
    template: 'Hello {{ name }}',
    data: ['name' => 'World'],
));
echo "Rendered {$result->totalPages} page(s) in {$result->environment} mode\n";
```

**`render->pdf`, `render->pdfStream`, and `render->document` require project mode** — `project` + `template`, optionally pinned to a specific `version` (omit to render the current draft). Inline HTML is only accepted by `render->preview`. The SDK enforces this via PHP's type system: `ProjectModeInput` and `InlineModeInput` are final readonly classes extending a sealed `RenderInput` base; the three document-producing methods type-hint `ProjectModeInput`, so passing inline mode is a `TypeError`. PHPStan / Psalm also catch the mismatch statically.

### Write a PDF to disk

[](#write-a-pdf-to-disk)

```
use PoliPage\PoliPage;
use PoliPage\ProjectModeInput;

use function PoliPage\renderToFile;

$client = PoliPage::client($_ENV['POLI_PAGE_API_KEY']);
renderToFile(
    $client,
    new ProjectModeInput(
        project: 'getting-started',
        template: 'welcome',
        version: '1.0.0',
        data: ['name' => 'World'],
    ),
    './welcome.pdf',
);
```

`renderToFile` streams response bytes directly to disk in 8 KB chunks (bounded memory regardless of document size).

### Try it locally — runnable demo

[](#try-it-locally--runnable-demo)

The repo ships a single end-to-end demo that exercises every public method against the real API:

```
composer install
php examples/demo.php
```

First run prompts for a `pp_test_*` key and saves it to `.env`. Subsequent runs are silent. See `examples/demo.php` for the full walkthrough.

### Stream — for large PDFs or piping to S3 / HTTP responses

[](#stream--for-large-pdfs-or-piping-to-s3--http-responses)

```
$stream = $client->render->pdfStream(new ProjectModeInput(
    project: 'billing',
    template: 'invoice',
    version: '1.0.0',
    data: ['invoiceNumber' => 'INV-001'],
));
// $stream is a PSR-7 StreamInterface

$file = fopen('invoice.pdf', 'wb');
while (!$stream->eof()) {
    fwrite($file, $stream->read(8192));
}
fclose($file);
$stream->close();
```

Any PSR-7-aware destination works — write to a file, an HTTP response body, or an S3 multipart upload that accepts a stream.

Working with stored documents
-----------------------------

[](#working-with-stored-documents)

Every render produces a stored document, accessible via `documentId` for later download or thumbnails. `render->pdf` and `render->pdfStream` are conveniences that chain a presigned-URL fetch internally to return bytes; `render->document` returns just the descriptor (skip the auto-download when you'll fetch the bytes later).

```
use PoliPage\RenderMetadata;
use PoliPage\ThumbnailOptions;

// 1. Render and store
$doc = $client->render->document(new ProjectModeInput(
    project: 'billing',
    template: 'invoice',
    version: '1.0.0',
    data: ['invoiceNumber' => 'INV-001'],
    metadata: new RenderMetadata(['customerId' => 'cust_123']),  // your own audit data
));
// $doc->documentId, $doc->pageCount, $doc->sizeBytes, $doc->presignedPdfUrl, $doc->metadata, ...

// 2. Save $doc->documentId in your database
$db->invoices->update(['id' => 'INV-001'], ['documentId' => $doc->documentId]);

// 3. Later, fetch a fresh presigned URL + download
$fresh = $client->documents->get($doc->documentId);
$pdf = $fresh->downloadPdf();

// 4. Generate thumbnails (Starter+ tier)
$thumbs = $client->documents->thumbnails(
    $doc->documentId,
    new ThumbnailOptions(width: 320, format: 'png'),
);

// 5. When done, soft-delete
$client->documents->delete($doc->documentId);
```

The presigned URL has a 15-minute TTL. If `downloadPdf()` fails with `errorCode: 'DOWNLOAD_FAILED'` (HTTP 403 from S3), call `documents->get($id)` to refresh and retry.

Authentication &amp; environments
---------------------------------

[](#authentication--environments)

The mode is determined by the API key prefix:

- `pp_test_…` → sandbox mode (not billed, generous rate limits)
- `pp_live_…` → live mode (billed, production rate limits)
- `pp_sa_…` → service-account keys; environment matches the SA's configuration (sandbox or live)

All prefixes hit the same endpoint (`https://api.poli.page`). The SDK passes the key through as a Bearer token and never inspects the prefix — pick whichever fits your deploy model.

Methods
-------

[](#methods)

MethodReturnsDescription`$client->render->pdf($input)``string`Render a PDF, return raw bytes`$client->render->pdfStream($input)``Psr\Http\Message\StreamInterface`Render and stream the response`$client->render->preview($input)``PreviewResult`Paginated HTML preview`$client->render->document($input)``DocumentDescriptor`Render and return descriptor (skip auto-download)`$client->documents->get($id)``DocumentDescriptor`Retrieve a stored document`$client->documents->preview($id)``DocumentPreviewResult`Stored document's paginated HTML`$client->documents->thumbnails($id, $options)``list`Page thumbnails (PNG/JPEG, base64)`$client->documents->delete($id)``void`Soft-delete a stored document`PoliPage\renderToFile($client, $input, $path)``void`Render and stream to diskConfiguration
-------------

[](#configuration)

Construct via the static factory for the common case, or use the named-argument constructor when you want to override anything:

```
// Static factory — uses every default.
$client = PoliPage::client($_ENV['POLI_PAGE_API_KEY']);

// Named-arg constructor — override what you need.
$client = new PoliPage(
    apiKey: $_ENV['POLI_PAGE_API_KEY'],
    baseUrl: 'https://api-develop.poli.page',
    maxRetries: 3,
    timeout: 60.0,
    logger: $monolog,
);
```

OptionTypeDefaultDescription`apiKey``string`(required)`pp_test_*` or `pp_live_*` API key`baseUrl``?string``https://api.poli.page`API base URL`maxRetries``?int``2`Max retry attempts on retryable errors`retryDelay``?float` (seconds)`0.5`Base delay before the first retry`timeout``?float` (seconds)`60.0`Per-request timeout hint forwarded to the PSR-18 client`httpClient``?Psr\Http\Client\ClientInterface`(auto-discovered)Override the discovered PSR-18 client`requestFactory``?Psr\Http\Message\RequestFactoryInterface`(auto-discovered)Override the discovered PSR-17 request factory`streamFactory``?Psr\Http\Message\StreamFactoryInterface`(auto-discovered)Override the discovered PSR-17 stream factory`logger``?Psr\Log\LoggerInterface``NullLogger`PSR-3 logger for SDK debug / retry / error events`onRetry``?\Closure(RetryEvent): void`—Called before each retry sleep`onError``?\Closure(PoliPageException): void`—Called when a call terminates in errorPer-call overrides live on the input object itself: pass `timeout:` and/or `idempotencyKey:` to `ProjectModeInput` / `InlineModeInput` to override the client-level defaults for that one call.

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

[](#error-handling)

The SDK ships a small exception hierarchy under `PoliPage\Exception`, all rooted in `PoliPageException`. Idiomatic PHP usage is `catch` by subclass:

```
use PoliPage\PoliPage;
use PoliPage\PoliPageException;
use PoliPage\Exception\AuthenticationException;
use PoliPage\Exception\BadRequestException;
use PoliPage\Exception\ConnectionException;
use PoliPage\Exception\RateLimitException;

try {
    $pdf = $client->render->pdf(new ProjectModeInput(...));
} catch (AuthenticationException $e) {
    return refreshCredentials();                 // 401 / 403
} catch (RateLimitException $e) {
    return queueForLater();                      // 429 after retries exhausted
} catch (BadRequestException $e) {
    error_log("bad input: {$e->errorCode} {$e->getMessage()}");  // 400
} catch (ConnectionException $e) {
    error_log("network / timeout: {$e->getMessage()}");
} catch (PoliPageException $e) {
    // catch-all for any SDK-raised exception
    error_log("poli error: {$e->errorCode} status={$e->status} requestId={$e->requestId}");
    throw $e;
}
```

The hierarchy:

```
PoliPageException                      (base, extends \RuntimeException)
├── Exception\ConnectionException      (network / DNS / TLS — no $status)
│   └── Exception\TimeoutException     (per-request deadline exceeded)
└── Exception\ApiStatusException       (any non-2xx — carries $status)
    ├── Exception\BadRequestException        (400)
    ├── Exception\AuthenticationException    (401)
    ├── Exception\PermissionDeniedException  (403)
    ├── Exception\NotFoundException          (404)
    ├── Exception\GoneException              (410 — document soft-deleted)
    ├── Exception\RateLimitException         (429)
    └── Exception\InternalServerException    (5xx)

```

Predicate helpers are kept for spec parity across SDK languages:

```
if ($e->isAuthError())       { /* 401 or 403 */ }
if ($e->isRateLimitError())  { /* 429 */ }
if ($e->isValidationError()) { /* 400 */ }
if ($e->isNetworkError())    { /* transport-level */ }
if ($e->isRetryable())       { /* 5xx, 429, network — SDK already retried */ }
```

For lifecycle and billing failures, route the user to actionable messages rather than treating them as opaque errors:

```
try {
    $doc = $client->render->document(new ProjectModeInput(...));
} catch (PoliPageException $e) {
    if ($e->errorCode === PoliPageException::PAYMENT_REQUIRED) {
        return showBanner('Subscription has unpaid invoices.');
    }
    if ($e->errorCode === PoliPageException::ORGANIZATION_CANCELLED) {
        return showBanner('Subscription cancelled — service is read-only.');
    }
    if ($e->errorCode === PoliPageException::DOCUMENT_NOT_FOUND) {
        return show404();
    }
    if ($e->errorCode === PoliPageException::GONE) {
        return show410();    // document was soft-deleted
    }
    throw $e;
}
```

→ Full error reference:

Cancellation
------------

[](#cancellation)

PHP has no first-class cancellation primitive (no `AbortSignal`, no `context.Context`). The SDK exposes timeouts instead:

- `timeout:` on the constructor — applied as the per-request deadline for every call.
- `timeout:` on the input object (`ProjectModeInput` / `InlineModeInput`) — overrides the client default for that one call.

PSR-18 does not standardise per-request timeouts, so the SDK forwards the value to the underlying client where possible (Guzzle, Symfony HTTP Client) and otherwise documents it as a best-effort hint. Configure connect / total timeouts on your injected PSR-18 client for guaranteed enforcement.

Observability
-------------

[](#observability)

### PSR-3 logger (`logger:` constructor argument)

[](#psr-3-logger-logger-constructor-argument)

```
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$logger = new Logger('polipage');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));

$client = new PoliPage(
    apiKey: $_ENV['POLI_PAGE_API_KEY'],
    logger: $logger,    // any PSR-3 LoggerInterface
);
```

Works with Laravel's `Log` facade, Symfony's `LoggerInterface` autowiring, Monolog standalone, or any custom PSR-3 implementation. The SDK emits one `DEBUG` line per HTTP attempt, one `INFO` per success, one `WARN` per retry, and one `ERROR` per terminal failure. The `Authorization` header is never logged.

### SDK-level hooks (`onRetry`, `onError`)

[](#sdk-level-hooks-onretry-onerror)

Hooks fire at well-defined points; they are sync, optional, and never break the request:

```
use PoliPage\Events\RetryEvent;
use PoliPage\PoliPageException;

$client = new PoliPage(
    apiKey: $_ENV['POLI_PAGE_API_KEY'],
    onRetry: fn(RetryEvent $e) => $log->warn(
        "retry {$e->attempt} after {$e->delayMs}ms: {$e->reason->errorCode}",
    ),
    onError: fn(PoliPageException $e) => $sentry->capture($e),
);
```

For per-request / per-response inspection, install middleware on your injected PSR-18 client (Guzzle handler stack, Symfony HTTP Client decoration, etc.) — that's the cross-framework PHP convention and the SDK deliberately doesn't reinvent it.

Retries &amp; idempotency
-------------------------

[](#retries--idempotency)

The SDK retries on **5xx**, **429**, **network errors**, and **timeouts**. Backoff is exponential (`retryDelay × 2^attempt`) with jitter in `[0.5, 1.5]`, capped by `Retry-After` when the server provides it (max 30 s). Every `POST` sends an auto-generated `Idempotency-Key` (UUID v4); pass `idempotencyKey:` in the input to override.

Type system
-----------

[](#type-system)

The SDK is fully type-annotated and tested at **PHPStan level max** with `phpstan-strict-rules`. Every public method has explicit parameter and return types; PHPDoc array shapes are provided wherever native PHP types are insufficient.

`RenderInput` is a sealed-in-package abstract class with exactly two concrete subclasses — `ProjectModeInput` and `InlineModeInput`. The render methods type-hint the specific subclass they accept, so invalid combos (passing inline-mode HTML to `render->pdf`) fail at compile-time (PHPStan) and runtime (PHP `TypeError`).

`final readonly class` is used throughout for input/output DTOs. Mutations require constructing a new instance — pair with PHP 8.4's `clone with` syntax if you need a one-field tweak.

Concurrency &amp; thread-safety
-------------------------------

[](#concurrency--thread-safety)

PHP's per-request execution model means client instances are scoped to a single request — there is no shared mutable state to coordinate across requests. For long-running workers (ReactPHP, Swoole, RoadRunner, FrankenPHP), construct one client per worker rather than sharing across requests, since the underlying PSR-18 client may not be reentrant.

Runtime support
---------------

[](#runtime-support)

RuntimeStatusPHP 8.3SupportedPHP 8.4SupportedPHP 8.5SupportedPHP 8.2 and earlierNot supported (reached EOL Dec 2025)The SDK is sync-only. PHP request lifecycles are typically short-lived; concurrent rendering — if you need it — is handled at the application layer (Symfony Process, ReactPHP / Amp, PHP-FPM workers).

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

[](#requirements)

- PHP 8.3 or later
- A PSR-18 HTTP client + PSR-17 factories (Guzzle, Symfony HTTP Client, etc.)

Documentation &amp; support
---------------------------

[](#documentation--support)

- Platform docs: [docs.poli.page](https://docs.poli.page)
- SDK docs site: [poli-page.github.io/sdk-php](https://poli-page.github.io/sdk-php/)
- Sign up &amp; generate API keys: [app.poli.page](https://app.poli.page)
- Issues: [github.com/poli-page/sdk-php/issues](https://github.com/poli-page/sdk-php/issues)

License
-------

[](#license)

[MIT](LICENSE) © Poli Page

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 85.5% 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

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/278785894?v=4)[Poli Page](/maintainers/poli-page)[@poli-page](https://github.com/poli-page)

---

Top Contributors

[![Mickael45](https://avatars.githubusercontent.com/u/8859666?v=4)](https://github.com/Mickael45 "Mickael45 (59 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (8 commits)")[![xapou](https://avatars.githubusercontent.com/u/2854747?v=4)](https://github.com/xapou "xapou (2 commits)")

---

Tags

pdfhtmltemplateRenderingpoli-page

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/poli-page-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/poli-page-sdk/health.svg)](https://phpackages.com/packages/poli-page-sdk)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k12](/packages/tempest-framework)[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

35729.6k2](/packages/telnyx-telnyx-php)[cakephp/cakephp

The CakePHP framework

8.8k19.1M1.7k](/packages/cakephp-cakephp)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

84735.1k](/packages/flow-php-flow)[gotenberg/gotenberg-php

A PHP client for interacting with Gotenberg, a developer-friendly API for converting numerous document formats into PDF files, and more!

3845.9M26](/packages/gotenberg-gotenberg-php)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)

PHPackages © 2026

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