PHP Server SDK
官方 PHP 伺服器端 SDK — 於 Packagist 發佈為 captchala/captchala-php。
SDK 為您處理三項任務:
- 驗證(Validate) — 校驗來自瀏覽器 SDK 的
pt_pass token。 - 簽發(Issue) — 簽發一次性
sct_server token,將即將發起的 challenge 綁定到特定 action / IP / UID。 - 內容審核(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() |