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 上傳會回傳結構化的成功或錯誤結果;兩條路徑都必須處理。