Skip to content

NextPDF Cloudflare boot and discovery

The bridge includes no service provider, bundle, or auto-discovery hook of its own. It is a set of final classes with explicit constructors. Here, “discovery” means choosing the collaborators to inject and binding them in your framework’s container. This page describes that wiring. It does not invent a registration mechanism that the package does not have.

You construct CloudflareHtmlRenderer; you do not discover it. Its dependencies are PHP Standard Recommendation (PSR) contracts: PSR-18, PSR-17, and PSR-3, plus the package’s own config and contract types. Any framework that can build those collaborators can build the renderer. The package depends only on PSR contracts; the tests/Unit/Architecture/PsrConformanceTest.php test asserts that no concrete client is coupled to the renderer. Your host application can reuse its existing HTTP client binding unchanged. The Laravel, Symfony, and CodeIgniter NextPDF adapters reuse that same PSR-18 binding. This package adds no framework package of its own.

Resolve the collaborators in this order, matching the constructor of CloudflareHtmlRenderer:

  1. Resolve the PSR-18 ClientInterface from the application’s existing HTTP client binding.
  2. Resolve the PSR-17 RequestFactoryInterface and StreamFactoryInterface; also resolve ResponseFactoryInterface when you want the pinned cURL transport.
  3. Build a CloudflareRendererConfig from the resolved configuration (see “Configuration resolution order”).
  4. Optionally resolve a PSR-3 LoggerInterface, a LocalRendererFactoryInterface, and an explicit HtmlSecurityPolicyInterface.
  5. Construct CloudflareHtmlRenderer with those collaborators.

None of these steps contacts the network. The first network interaction happens when you call render() or isAvailable().

A representative binding (framework-agnostic pseudocode):

<?php
declare(strict_types=1);
use NextPDF\Cloudflare\CloudflareHtmlRenderer;
use NextPDF\Cloudflare\CloudflareRendererConfig;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Log\LoggerInterface;
$container->singleton(CloudflareHtmlRenderer::class, function ($c) {
return new CloudflareHtmlRenderer(
config: CloudflareRendererConfig::fromArray($c->get('config')['cloudflare']),
httpClient: $c->get(ClientInterface::class),
requestFactory: $c->get(RequestFactoryInterface::class),
streamFactory: $c->get(StreamFactoryInterface::class),
logger: $c->get(LoggerInterface::class),
responseFactory: $c->get(ResponseFactoryInterface::class),
);
});

The package does not read environment variables itself. It accepts a fully formed CloudflareRendererConfig. In the host application, resolve configuration in this order: environment variables first, then published or framework config, then the package defaults baked into the constructor (documented in /integrations/cloudflare/configuration/). CloudflareRendererConfig::fromArray() applies the constructor defaults for any absent or incorrectly typed key, so a partial config array falls back predictably instead of failing.

CloudflareRendererConfig::isValid() is the boot-time signal. It is a pure check that both workerUrl and apiToken are non-empty and makes no network call, so you can use it as a deploy gate. The runtime signal is CloudflareHtmlRenderer::isAvailable(), an authenticated HTTP HEAD that returns true for a status below 500 and false (never throwing) otherwise, so you can use it as a readiness probe. A passing probe is a hint, not a guarantee: the subsequent POST can still fail.

  • /integrations/cloudflare/integration/ — the complete integration walkthrough.
  • /integrations/cloudflare/overview/ — what the bridge is and which boundary it crosses.
  • /integrations/cloudflare/configuration/ — every field and default.
  • /integrations/cloudflare/security-and-operations/ — when the pinned transport activates.