--- title: PHP 服务端 SDK --- # PHP 服务端 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,将本次挑战绑定到具体 action / IP / UID。 3. **审核**(Moderate)—— 多模态(文本 + 图片)内容审核,走 dashboard 同款 OpenAI 兼容管道。 ## 安装 ```bash composer require captchala/captchala-php ``` 依赖:**PHP ≥ 8.0**、`ext-json`。优先用 cURL,缺失则 fallback 到 `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()); // 例: token_expired } // 验证通过, 继续业务流程 // $result->getCaptchaArgs() 含 platform / user_ip / referer / pkg / solved_at / risk_score // 取 CDN / 反代背后的真实终端用户 IP 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` 传给组件。token 单次消费, 绑定 action, 可选绑定 IP / UID。 ```php $issue = $client->issueServerToken( action: 'login', bindingIp: $request->ip(), ttl: 300, // 秒, 默认 300 maxUses: 5, // SDK 重试预算 bindUid: $user->id, // 配合 ValidateResult::getUid() 在校验侧核对 ); if (!$issue->isOk()) { return ['error' => $issue->getError()]; // rate_limit_exceeded / invalid_action ... } return ['server_token' => $issue->getToken()]; // 下发给浏览器 ``` ## 内容审核 多模态 —— 接受文本 + 图片 URL 混合(OpenAI 兼容格式): ```php $result = $client->moderationCheck([ ['type' => 'text', 'text' => $userComment], ['type' => 'image_url', 'image_url' => ['url' => $uploadedImageUrl]], ], userId: $user->id); if (!$result->isOk()) { // 请求出错: invalid_credentials / no_content / 网络失败 ... return ['error' => $result->getError()]; } if ($result->isFlagged()) { // 上游模型判定违规, 看是哪个 category if ($result->hasCategory('violence', 'csam')) { // 硬阻断 } } ``` 纯文本快捷方式: ```php $result = $client->moderationText('user comment here', userId: $user->id); ``` Category 列表由上游模型决定(如 `violence`、`hate`、`sexual`、`self-harm`),用 `getCategories()` 防御性遍历, 不要硬编码固定集合。 ## Result 类 | 类 | 方法 | |---|---| | `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-CN/web-sdk) · [API 参考](/zh-CN/api-reference)