Cloudflare API 参考
这个 NextPDF\Cloudflare 包是一层边缘渲染桥接:你的 PHP 程序持有 HTML,Cloudflare Worker 持有无头浏览器。这个包提供以 Worker 为后端的 HTML renderer(CloudflareHtmlRenderer)及其返回的值对象、用于保护渲染 endpoint 的请求防护层(ApiProtection)、用于存储已渲染 PDF 的 R2 归档服务(R2ArchiveManager),以及用于 TLS/DNS 加固的固定传输辅助工具。配置存放在三个不可变对象中(CloudflareRendererConfig、ApiProtectionConfig、R2ArchiveConfig)。
从这里开始:如果你刚接触这个包,先创建一个 CloudflareRendererConfig,把它接入 CloudflareHtmlRenderer,再调用 render()。这次调用会把你的 HTML 发送到 Worker,并返回一个带有 PDF 字节的 CloudflareRenderResult。其余所有功能(防护、归档、固定)都围绕这次调用叠加。
常见任务
标题为“常见任务”的章节以下代码片段展示了这个包最常见的实际工作流程。每一段都可以独立运行,已对照 src/Cloudflare/ 源代码验证,并从环境变量读取密钥。
在边缘把一段 HTML 字符串渲染成 PDF,这是唯一的标准渲染调用:
<?php
declare(strict_types=1);
use GuzzleHttp\Client;use GuzzleHttp\Psr7\HttpFactory;use NextPDF\Cloudflare\CloudflareHtmlRenderer;use NextPDF\Cloudflare\CloudflareRendererConfig;
$httpFactory = new HttpFactory();
$renderer = new CloudflareHtmlRenderer( config: new CloudflareRendererConfig( workerUrl: 'https://pdf-renderer.example.workers.dev/render', apiToken: getenv('CF_PDF_TOKEN') ?: throw new RuntimeException('CF_PDF_TOKEN not set'), ), httpClient: new Client(), requestFactory: $httpFactory, streamFactory: $httpFactory, responseFactory: $httpFactory,);
$result = $renderer->render('<h1>Hello from the edge</h1>', widthPt: 595.28);
if ($result->isValid()) { file_put_contents('output.pdf', $result->pdfData);}作用:通过 HTTPS 把 HTML 发送到 Worker,并在 isValid() 确认为真正的 PDF 后,把返回的 A4 PDF 字节写入磁盘。
把已渲染的 PDF 归档到 R2,并返回一个短期链接:
<?php
declare(strict_types=1);
use NextPDF\Cloudflare\R2ArchiveConfig;use NextPDF\Cloudflare\R2ArchiveManager;
$archive = new R2ArchiveManager( config: R2ArchiveConfig::fromArray([ 'bucket_name' => 'pdf-archive', 'account_id' => getenv('CF_ACCOUNT_ID') ?: '', 'access_key_id' => getenv('R2_ACCESS_KEY_ID') ?: '', 'secret_access_key' => getenv('R2_SECRET_ACCESS_KEY') ?: '', ]), httpClient: $httpClient, // PSR-18 ClientInterface requestFactory: $requestFactory, // PSR-17 RequestFactoryInterface streamFactory: $streamFactory, // PSR-17 StreamFactoryInterface);
$upload = $archive->upload($result->pdfData, 'invoice-1234.pdf');
$signedUrl = $upload->isValid() ? $archive->generateSignedUrl($upload->key, expiresInSeconds: 600) : null;作用:把 PDF 字节上传到按日期分区的 R2 键,成功后生成一个 10 分钟有效的预签名 URL,供临时下载。
在执行昂贵的 Worker 任务之前,先保护渲染 endpoint:
<?php
declare(strict_types=1);
use NextPDF\Cloudflare\ApiKeyValidator;use NextPDF\Cloudflare\ApiProtection;use NextPDF\Cloudflare\ApiProtectionConfig;
$protection = new ApiProtection( config: new ApiProtectionConfig(maxRequestsPerMinute: 30), keyValidator: new ApiKeyValidator([getenv('RENDER_API_KEY') ?: '']),);
$decision = $protection->checkRequest( clientId: $clientIp, payloadSize: strlen($html), apiKey: $presentedApiKey,);
if (!$decision->allowed) { // Reject with 429 and rate-limit headers before any render call. return [429, $decision->toHeaders(), $decision->denialReason];}作用:依次验证 API 密钥、载荷大小,再验证每个客户端的速率限制,返回单一决策以及请求被拒时要附带的响应头。
renderer(渲染器)
标题为“renderer(渲染器)”的章节renderer 表格是核心接口。当你要构建配置、创建 renderer,或发起渲染与可用性调用时,就使用这张表。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败条件 | 备注 |
|---|---|---|---|---|---|
new CloudflareRendererConfig(string $workerUrl, string $apiToken, int $renderTimeout = 30, string $defaultCss = '', int $maxHtmlSize = 5000000, ?string $r2FontBucket = null, bool $fallbackToLocal = true, array $pinnedPublicKeys = [], array $backupPublicKeys = []) | Worker URL、bearer token、超时、CSS、大小上限、可选的 R2 字体 bucket、fallback 标志、pin 集合。 | 默认启用本地 fallback;当 pin 数组为空时停用固定。 | CloudflareRendererConfig | 预期无。 | 妥善保管 API token;优先使用 HTTPS 的 worker URL。 |
CloudflareRendererConfig::fromArray(array $config) | worker_url、api_token、render_timeout、default_css、max_html_size、r2_font_bucket、fallback_to_local、pin 数组。 | 未提供的可选键会采用构造函数默认值。 | CloudflareRendererConfig | 预期无。 | 对应 Framework(框架)风格的配置数组。 |
CloudflareRendererConfig::isValid() | 无。 | 需要非空的 worker URL 与 API token。 | bool | 预期无。 | 无效配置会让 renderer 触发 fallback 或失败。 |
CloudflareRendererConfig::allPublicKeyPins() | 无。 | 合并主公钥 pin 与备用公钥 pin。 | list<string> | 预期无。 | 空列表会停用固定。 |
new CloudflareHtmlRenderer(CloudflareRendererConfig $config, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, ?LoggerInterface $logger = null, ?LocalRendererFactoryInterface $localRendererFactory = null, ?HtmlSecurityPolicyInterface $htmlSecurityPolicy = null, ?ResponseFactoryInterface $responseFactory = null) | 配置、PSR HTTP 依赖项、可选的 logger、可选的本地 fallback 工厂、可选的 HTML 策略、可选的 response 工厂。 | 未提供 HTML 策略时,使用 DefaultHtmlSecurityPolicy。 | CloudflareHtmlRenderer | container(容器)装配错误。 | 需要时,response 工厂会启用固定 cURL 传输。 |
CloudflareHtmlRenderer::render(string $html, float $widthPt = 595.28, float $heightPt = 0, array $fontFiles = []) | HTML、页面宽度、页面高度、R2 中的字体文件。 | A4 宽度;自动高度;无字体文件。 | CloudflareRenderResult | CloudflareNotAvailableException、CloudflareRenderException、验证失败。 | 在网络 I/O 之前先验证 HTML 大小与 worker URL。 |
CloudflareHtmlRenderer::getHtmlSecurityPolicy() | 无。 | 返回已配置的解析层策略。 | HtmlSecurityPolicyInterface | 预期无。 | 与 endpoint 防护及 Worker URL 验证搭配使用。 |
CloudflareHtmlRenderer::isAvailable() | 无。 | 当配置有效时,向 worker 发出 HEAD 请求。 | bool | 发生错误时返回 false。 | 用于就绪检查,不要当作唯一的运行时防线。 |
renderer 值对象与安全性
标题为“renderer 值对象与安全性”的章节当你需要 request/result 值对象(CloudflareRenderResult、CloudflareRenderPayload),或需要在任何网络 I/O 之前验证 HTML、Worker URL 与 DNS pin 的静态传输层检查时,就使用这张表。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败条件 | 备注 |
|---|---|---|---|---|---|
new CloudflareRenderResult(string $pdfData, float $widthPt, float $heightPt, float $contentHeightPx = 0.0, string $renderLocation = '', float $renderTimeMs = 0.0) | PDF 字节、宽度、高度、测得的内容高度、边缘位置、渲染时间。 | 当 Worker 未返回时,元数据为空。 | CloudflareRenderResult | 预期无。 | 通常由 CloudflareResponseParser::parse() 返回。 |
CloudflareRenderResult::isValid() | 无。 | 检查 PDF 字节非空且以 PDF 头开头。 | bool | 预期无。 | 在归档或把字节传给其他层之前使用。 |
CloudflareRenderResult::size() | 无。 | 计算已渲染 PDF 的字节数。 | int | 预期无。 | 把这个值送入配额与审计逻辑。 |
new CloudflareRenderPayload(string $html, float $widthPt, float $heightPt = 0, string $defaultCss = '', ?string $r2FontBucket = null, array $fontFiles = []) | HTML、尺寸、CSS、可选的 R2 字体 bucket、字体文件列表。 | 自动高度、无默认 CSS、无 R2 字体 bucket、无字体文件。 | CloudflareRenderPayload | 预期无。 | 请求载荷值对象。 |
CloudflareRenderPayload::toJson() | 无。 | 把 HTML、尺寸、CSS 与字体引用序列化给 Worker。 | string | JSON 编码错误。 | 底层的请求载荷 API。 |
CloudflareResponseParser::parse(ResponseInterface $response, float $requestedWidthPt) | Worker 响应与请求宽度。 | 接受二进制 PDF 响应与结构化 JSON 响应。 | CloudflareRenderResult | CloudflareRenderException:当 Worker 输出失败或无效时抛出。 | renderer 使用的中央 parser(解析器)。 |
CloudflareSecurityPolicy::validate(string $html, int $maxSize) | HTML 输入与大小上限。 | 应用这个包的 HTML 输入策略。 | void | 验证异常。 | 在 Worker 边界外检查不受信任的输入。 |
CloudflareSecurityPolicy::validateWorkerUrl(string $url) | Worker URL(网址)。 | 解析并验证目标地址。 | array | 验证异常。 | 在网络 I/O 之前阻止不安全的 endpoint 形式。 |
CloudflareSecurityPolicy::assertPinsStillValid(string $host, array $pinnedIps) | 主机与固定的 IP 列表。 | 验证 DNS pin 的预期值。 | void | 当 pin 过期或无效时抛出验证异常。 | 在启用固定的部署运维检查期间使用。 |
API 防护
标题为“API 防护”的章节当你要保护渲染 endpoint 时使用这张表:API 密钥验证、载荷大小与速率限制检查,以及它们产生的 result/header 对象。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败条件 | 备注 |
|---|---|---|---|---|---|
new ApiProtection(ApiProtectionConfig $config, ?ApiKeyValidator $keyValidator = null, ?Closure $clock = null) | 防护配置、可选的密钥验证器、可选的时钟。 | 未提供时钟时使用系统时间。 | ApiProtection | 预期无。 | 在测试中注入确定性时钟。 |
ApiProtection::checkRequest(string $clientId, int $payloadSize, string $apiKey = '') | 客户端标识符、载荷大小、可选的 API 密钥。 | 只有当配置不要求密钥时,才允许空的 API 密钥。 | ApiProtectionResult | 预期无。 | 依次检查 API 密钥、大小,再检查速率限制。 |
ApiProtection::getRateLimit(string $clientId) | 客户端标识符。 | 不会记录一次请求。 | RateLimitResult | 预期无。 | 用来添加速率限制响应头。 |
new ApiKeyValidator(array $validKeys = []) | 明文有效密钥列表。 | 空列表会拒绝所有密钥。 | ApiKeyValidator | 预期无。 | 把密钥存放在代码之外,通过配置加载。 |
ApiKeyValidator::validate(string $key) | 原始密钥。 | 与配置好的明文密钥做时序安全比较。 | bool | 预期无。 | 敏感参数;不要记录原始密钥。 |
ApiKeyValidator::addKey(string $key) | 原始密钥。 | 把哈希后的密钥加入一个新的验证器实例。 | self | 预期无。 | 把返回的实例当作更新后的验证器。 |
ApiKeyValidator::revokeKey(string $key) | 原始密钥。 | 从一个新的验证器实例移除匹配的哈希。 | self | 预期无。 | 把返回的实例当作更新后的验证器。 |
ApiKeyValidator::hashKey(string $key) | 原始密钥。 | 生成用于存储的哈希表示。 | string | 预期无。 | 不要在日志或客户端响应中暴露哈希。 |
ApiKeyValidator::validateHashed(string $key, array $hashedKeys) | 原始密钥与候选哈希。 | 与提供的哈希做常量时间比较。 | bool | 预期无。 | 供自定义密钥存储使用的底层辅助方法。 |
new ApiProtectionConfig(int $maxRequestsPerMinute = 60, int $maxRequestsPerHour = 1000, int $maxPayloadSizeBytes = 10485760, array $allowedOrigins = [], bool $requireApiKey = true, string $apiKeyHeader = 'X-Api-Key', int $rateLimitWindowSeconds = 60) | 请求上限、载荷上限、允许的来源、API 密钥要求、请求头名称、窗口长度。 | 60/minute、1000/hour、10 MiB 载荷、需要 API 密钥。 | ApiProtectionConfig | 预期无。 | 在测试中直接构建,或以 fromArray() 加载。 |
ApiProtectionConfig::fromArray(array $data) | max_requests_per_minute、max_requests_per_hour、max_payload_size_bytes、allowed_origins、require_api_key、api_key_header、rate_limit_window_seconds。 | 未提供的键会采用构造函数默认值。 | ApiProtectionConfig | 预期无。 | 用于框架配置加载。 |
ApiProtectionConfig::isValid() | 无。 | 需要正值上限与一致的 size/window 值。 | bool | 预期无。 | 在对外开放 endpoint 之前先验证。 |
new ApiProtectionResult(bool $allowed, string $denialReason = '', ?RateLimitResult $rateLimit = null) | 决策、拒绝原因、可选的速率限制结果。 | 空的拒绝原因,且无速率限制结果。 | ApiProtectionResult | 预期无。 | 由 ApiProtection::checkRequest() 返回。 |
ApiProtectionResult::toHeaders() | 无。 | 当有速率数据时,输出速率限制响应头。 | array<string, string> | 预期无。 | 附加到 worker 或框架响应上。 |
new RateLimitResult(bool $allowed, int $remainingRequests, int $retryAfterSeconds, string $clientId) | 决策、剩余次数、重试延迟、客户端 ID。 | 无默认值。 | RateLimitResult | 预期无。 | 单次检查的不可变结果。 |
RateLimitResult::toHeaders() | 无。 | 输出剩余限制与重置响应头。 | array<string, string> | 预期无。 | 用于可观测性与客户端退避。 |
new RateLimitEntry(string $clientId, int $requestCount = 0, int $windowStart = 0, int $hourlyCount = 0, int $hourlyWindowStart = 0) | 客户端 ID 与可变计数器。 | 计数器从零开始。 | RateLimitEntry | 预期无。 | 内存中的跟踪对象。 |
RateLimitEntry::increment() | 无。 | 为单个 client/window 递增内存计数器。 | void | 预期无。 | 由 ApiProtection 使用的底层辅助方法。 |
RateLimitEntry::isExpired(int $windowSeconds) | 以秒为单位的窗口长度。 | 与当前时间比较。 | bool | 预期无。 | 运行时的过期辅助方法。 |
RateLimitEntry::isExpiredAt(int $now, int $windowSeconds) | 时钟值与窗口长度。 | 与提供的时钟值比较。 | bool | 预期无。 | 确定性测试辅助方法。 |
RateLimitEntry::reset() | 无。 | 重置计数与窗口起始时间。 | void | 预期无。 | 在新窗口开始时使用。 |
R2 归档
标题为“R2 归档”的章节当你要把已渲染的 PDF 存储到 Cloudflare R2 时使用这张表:归档服务、它的配置与对象键类型,以及你在对外开放 URL 之前要检查的上传结果。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败条件 | 备注 |
|---|---|---|---|---|---|
new R2ArchiveManager(R2ArchiveConfig $config, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory) | R2 配置与 PSR HTTP factories/client. | 构建期间不会发出网络调用。 | R2ArchiveManager | 容器装配错误。 | 主要的归档服务。 |
R2ArchiveManager::upload(string $pdfData, string $filename, array $metadata = []) | 原始 PDF 字节、原始文件名、字符串元数据。 | 空的元数据;按日期分区的键。 | R2UploadResult | 当配置、大小、HTTP 或传输失败时,返回不成功的结果。 | 一般上传失败时不会抛出异常。 |
R2ArchiveManager::generateSignedUrl(string $key, int $expiresInSeconds = 3600) | 对象键与 URL TTL。 | 一小时有效的签名 URL。 | string | 无效配置造成的签名错误。 | 对敏感 PDF 保持较短的 TTL。 |
R2ArchiveManager::buildObjectKey(string $filename) | 原始文件名。 | 使用配置好的路径前缀与当前日期。 | R2ObjectKey | 预期无。 | 用于可预测的归档分区。 |
R2ArchiveManager::createPutRequest(R2ObjectKey $key, string $data, array $metadata = []) | 对象键、原始字节、元数据。 | 签名一个 PUT 请求。 | RequestInterface | 请求构建错误。 | 供自定义传输使用的底层 API。 |
new R2ArchiveConfig(string $bucketName, string $accountId, string $accessKeyId, string $secretAccessKey, string $endpoint = '', string $pathPrefix = 'pdfs/', int $maxFileSizeBytes = 104857600) | bucket、账户 ID、凭证、endpoint 覆盖值、键前缀、对象大小上限。 | 推导出的 endpoint、pdfs/ 前缀、100 MiB 对象大小上限。 | R2ArchiveConfig | InvalidArgumentException:当 bucket 名称无效时抛出。 | 把凭证视为带有密钥的配置。 |
R2ArchiveConfig::fromArray(array $data) | 账户 ID、bucket、凭证、路径前缀、endpoint 覆盖值、大小上限。 | 未提供的值会采用构造函数默认值。 | R2ArchiveConfig | 提供时,bucket 名称无效。 | 用于应用程序配置加载。 |
R2ArchiveConfig::isValid() | 无。 | 需要非空的账户、bucket、访问密钥与密钥。 | bool | 预期无。 | 无效配置会让上传以结构化结果失败。 |
R2ArchiveConfig::getEndpoint() | 无。 | 使用明确的 endpoint,或从账户 ID 推导出 Cloudflare R2 endpoint。 | string | 预期无。 | 用于构建签名请求。 |
new R2ObjectKey(string $key, string $bucket) | 完整的对象键与 bucket。 | 不做规范化。 | R2ObjectKey | 预期无。 | 通常由 R2ObjectKey::generate() 创建。 |
R2ObjectKey::generate(string $prefix, string $filename, ?DateTimeInterface $date = null) | 前缀、原始文件名、可选的日期。 | 按日期分区并清理过的对象键。 | R2ObjectKey | 预期无。 | 在测试中注入日期,以取得确定性的键。 |
R2ObjectKey::fullPath() | 无。 | 拼接分区路径与对象文件名。 | string | 预期无。 | 把这个值存储为对象键。 |
new R2UploadResult(bool $success, string $key, string $etag = '', int $size = 0, string $error = '') | 成功标志、对象键、ETag、字节大小、错误消息。 | 空的 ETag、零大小、空的错误。 | R2UploadResult | 预期无。 | 由 R2ArchiveManager::upload() 返回。 |
R2UploadResult::isValid() | 无。 | 当上传成功且键与 ETag 都存在时,视为有效。 | bool | 预期无。 | 在对外开放 URL 之前先检查。 |
R2UploadResult::publicUrl(string $customDomain = '') | 可选的自定义公开域名。 | 未提供自定义域名时,返回裸对象键。 | string | 预期无。 | 除非策略允许,否则避免对敏感文件使用公开 URL。 |
传输辅助工具
标题为“传输辅助工具”的章节这张表只用于底层装配:cURL 层面的 IP/SPKI 固定,以及当 Worker 无法连接时作为 fallback 路径的本地 renderer 契约。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败条件 | 备注 |
|---|---|---|---|---|---|
new PinnedCurlTransport(ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory, array $pinnedIps = [], array $pinnedPublicKeys = [], int $timeoutSeconds = 30) | PSR-17 工厂、固定的 IP、固定的公钥、超时。 | 无 pin,且超时 30 秒。 | PinnedCurlTransport | 预期无。 | 只在需要 cURL 层面固定时使用。 |
PinnedCurlTransport::sendRequest(RequestInterface $request) | PSR-7 请求。 | 以配置好的超时与固定控制通过 cURL 发送。 | ResponseInterface | PSR-18 传输异常。 | 只在框架的 HTTP 客户端无法强制执行相同固定策略时使用。 |
PinnedCurlTransport::buildCurlOptions(RequestInterface $request, string $host, int $port) | 请求、目标主机、目标端口。 | 构建 sendRequest() 所用的 cURL 选项数组。 | array | 无效的请求或 pin 配置错误。 | 底层的测试与诊断钩子。 |
LocalRendererInterface::render(string $html, array $options = []) | HTML 与 renderer 选项。 | 仅为契约;由实现决定默认值。 | string | 实现各自定义的渲染错误。 | 当 Worker 渲染无法使用时,作为本地 fallback 使用。 |
LocalRendererFactoryInterface::create() | 无。 | 创建一个本地 renderer 实现。 | LocalRendererInterface | 工厂或依赖项错误。 | 把 fallback renderer 的构建工作移出 CloudflareHtmlRenderer。 |
开发备注
标题为“开发备注”的章节- 把 Worker URL 视为一个网络边界。在渲染之前,先验证目标地址、大小与身份验证。
- 把 API 防护结果当作策略输出,而不是异常控制流。
- R2 上传会返回结构化的成功或错误结果;两条路径都要处理。