Skip to content

Output handling

The SDK gives you four ways to handle a rendered PDF: in-memory bytes, a PSR-7 stream, a write-to-disk helper, or a stored document on the server. Pick the one that matches how your code consumes the output.

use PoliPage\RenderInput;
$pdf = $client->render->pdf(new RenderInput(
project: 'billing',
template: 'invoice',
data: ['invoiceNumber' => 'INV-001'],
));
// $pdf: binary string — write, upload, or return as-is.

Best for documents that fit comfortably in process memory.

For large PDFs or piping straight to an HTTP response or object store, use pdfStream() and let the SDK keep memory bounded. The return value is a PSR-7 StreamInterface.

$stream = $client->render->pdfStream(new RenderInput(
project: 'billing',
template: 'invoice',
data: ['invoiceNumber' => 'INV-001'],
));
// $stream: Psr\Http\Message\StreamInterface — read in chunks, pipe to a response.

The client’s timeout bounds the wait for the first byte and each gap between two chunks, not the whole stream, so a large PDF read at your own pace is not cut off. A stall past it raises DOWNLOAD_FAILED (Failed to download PDF: timeout), from pdfStream() itself or from the read. A body that ends before its announced length raises DOWNLOAD_FAILED (Failed to download PDF: network error).

With Guzzle, this download goes through Guzzle’s stream handler (PHP’s HTTP stream wrapper) rather than cURL: cURL options set on your Guzzle client do not apply to it, and it needs PHP’s allow_url_fopen. render->pdf() and downloadPdf() still use cURL.

Read the PSR-7 stream directly (or use renderToFile()): a PHP stream resource made from it with GuzzleHttp\Psr7\StreamWrapper (guzzlehttp/psr7 3.x) cannot report a failed download, and a copy from it can come out short without an error.

The renderToFile() free function (autoloaded from src/render_to_file.php) streams bytes directly to disk with bounded memory. Parent directories are created automatically.

The bytes go to a temporary file next to the target, renamed into place only once the PDF is complete: if the render, the download or the write fails, the target is left as it was. A file that cannot be written raises io_error with the message Failed to write PDF: <reason> (PHP’s own error text, also chained as getPrevious()); it has no status and is never retried. The rename replaces the target file, so an existing file’s permissions, owner and hard links are not kept, and a symlink at the path is replaced rather than followed. A process killed mid-download can leave a .<name>.<random>.part file next to the target.

use function PoliPage\renderToFile;
renderToFile(
$client,
new RenderInput(project: 'billing', template: 'invoice', data: []),
'./out.pdf',
);

When you want the PDF to live on Poli Page’s servers (for later download, preview, or thumbnails), use render->document(). It returns a DocumentDescriptor with documentId, presignedPdfUrl, and a downloadPdf() method.

$doc = $client->render->document(new RenderInput(
project: 'billing',
template: 'invoice',
data: ['invoiceNumber' => 'INV-001'],
));
$pdf = $doc->downloadPdf();

presignedPdfUrl has a 15-minute TTL. If it expires, call $client->documents->get($documentId) to refresh.

<?php
require __DIR__ . '/vendor/autoload.php';
use PoliPage\PoliPage;
use PoliPage\RenderInput;
use function PoliPage\renderToFile;
$client = PoliPage::client($_ENV['POLI_PAGE_API_KEY']);
renderToFile(
$client,
new RenderInput(
project: 'billing',
template: 'invoice',
data: ['invoiceNumber' => 'INV-001'],
),
'./invoices/INV-001.pdf',
);