--- title: PHP Server SDK --- # PHP Server SDK 官方 PHP 伺服器端 SDK — 於 Packagist 發佈為 [`captchala/captchala-php`](https://packagist.org/packages/captchala/captchala-php)。 SDK 為您處理三項任務: 1. **驗證(Validate)** — 校驗來自瀏覽器 SDK 的 `pt_` pass token。 2. **簽發(Issue)** — 簽發一次性 `sct_` server token,將即將發起的 challenge 綁定到特定 action / IP / UID。 3. **內容審核(Moderate)** — 多模態(文字 + 圖片)內容審核,與控制台共用同一條 OpenAI 相容管線。 ## 安裝 ```bash composer require captchala/captchala-php ``` 需要 **PHP ≥ 8.0** 並啟用 `ext-json`。可用時使用 cURL,否則回退至 `file_get_contents`。 ## 驗證(`pt_` token) 請將終端使用者的 IP 作為第三個參數傳入(取自 `CF-Connecting-IP` / `X-Forwarded-For`,最終回退至 `REMOTE_ADDR`)。此參數為選填,但 **建議帶上** — 它會用於額外的風險檢查。 ```php use Captchala\Client; $client = new Client('your_app_key', 'your_app_secret'); $result = $client->validate($_POST['captcha_token'], false, captchala_client_ip()); if (!$result->isValid()) { http_response_code(400); exit($result->getError()); // e.g. "token_expired" } // Verification passed; proceed with the request. // $result->getCaptchaArgs() has platform / user_ip / referer / pkg / solved_at / risk_score // Real end-user IP behind a CDN / proxy function captchala_client_ip(): string { foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'] as $h) { if (!empty($_SERVER[$h])) { $ip = trim(explode(',', $_SERVER[$h])[0]); if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip; } } return ''; } ``` 沒有現成的 IP?改用 `$client->validate($_POST['captcha_token'])` — 仍可完成 驗證,只是少了基於 IP 的額外風險訊號。 若您簽發原始 `server_token` 時帶有 `bind_uid`,請將回傳的 `uid` 與實際預期的使用者進行比對: ```php if ($result->getUid() !== $expectedUserId) { http_response_code(400); exit('user mismatch'); } ``` ## 簽發 server token 高價值流程(登入、註冊、支付)建議採用以下模式: 後端簽發一次性 `sct_` token,交給瀏覽器,瀏覽器作為 `serverToken` prop 傳遞。 每個 token 為單次使用、限定 action,亦可於簽發時綁定 IP / UID。 ```php $issue = $client->issueServerToken( action: 'login', bindingIp: $request->ip(), ttl: 300, // seconds; default 300 maxUses: 5, // SDK retry budget bindUid: $user->id, // pair with ValidateResult::getUid() on verify ); if (!$issue->isOk()) { return ['error' => $issue->getError()]; // rate_limit_exceeded, invalid_action, ... } return ['server_token' => $issue->getToken()]; // hand to browser ``` ## 內容審核 多模態 — 接受 OpenAI 相容格式下混合的文字與圖片 URL: ```php $result = $client->moderationCheck([ ['type' => 'text', 'text' => $userComment], ['type' => 'image_url', 'image_url' => ['url' => $uploadedImageUrl]], ], userId: $user->id); if (!$result->isOk()) { // request error: invalid_credentials, no_content, transport failure, ... return ['error' => $result->getError()]; } if ($result->isFlagged()) { // upstream model flagged; inspect categories for the why if ($result->hasCategory('violence', 'csam')) { // hard block } } ``` 純文字快捷寫法: ```php $result = $client->moderationText('user comment here', userId: $user->id); ``` 分類由模型動態定義(例如 `violence`、`hate`、`sexual`、`self-harm`);請以遍歷 `getCategories()` 的方式防禦性處理,而非寫死固定集合。 ## 結果類別 | 類別 | 方法 | |---|---| | `ValidateResult` | `isValid()`、`getError()`、`getUid()`、`getChallengeId()`、`getAction()`、`isOffline()`、`isClientOnly()`、`getWarning()`、`toArray()` | | `IssueResult` | `isOk()`、`getToken()`、`getExpiresIn()`、`getIssuedAt()`、`getError()`、`getMessage()`、`toArray()` | | `ModerationResult` | `isOk()`、`isFlagged()`、`hasCategory(...$names)`、`getCategories()`、`getContentType()`、`getRaw()`、`getError()`、`getMessage()`、`toArray()` | ## 相關連結 - [Packagist](https://packagist.org/packages/captchala/captchala-php) · [GitHub](https://github.com/Captcha-La/captchala-php) - [Web SDK 概覽](/zh-TW/web-sdk) · [API 參考](/zh-TW/api-reference)