PHP调用OpenAI API最简路径是直接用curl发POST请求,需严格设置JSON body、Bearer认证头和Content-Type头;国产模型需适配鉴权、参数名及响应结构差异。
PHP 调用 OpenAI API 最简可行路径
直接用 curl 发 POST 请求是最稳的方式,避开 SDK 版本兼容问题和自动重试等“聪明”逻辑带来的调试黑盒。OpenAI 的 /v1/chat/completions 接口要求 JSON body、Authorization: Bearer sk-xxx 头、Content-Type: application/json,缺一不可。
常见错误现象:cURL error 60: SSL certificate problem(本地开发没关 SSL 验证)、401 Unauthorized(key 拼错或没复制全)、400 Bad Request(messages 格式不对,比如少了 role 或 content)。
实操建议:
Content at Scale
SEO长内容自动化创作平台
下载
- 先用
curl -X POST https://api.openai.com/v1/chat/completions -H "Authorization: Bearer sk-xxx" -H "Content-Type: application/json" -d '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"hi"}]}'在命令行验证 key 和网络是否通 - PHP 中用
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false)仅限本地调试,上线必须删掉 -
messages数组必须是索引数组,不能是关联键名(如['msg' => [...]]会 400) - 响应体要
json_decode($response, true),检查['error']['message']字段再取['choices'][0]['message']['content']
调用国产模型(如通义千问、讯飞星火、智谱 GLM)的关键差异
国产模型接口结构看似类似 OpenAI,但参数名、鉴权方式、字段嵌套层级几乎全都不一样,不能简单替换 URL 和 key 就跑通。
立即学习“PHP免费学习笔记(深入)”;
使用场景:国内服务更倾向用 access_token(需先调 /auth/token 换取,有时效)、app_id + api_key + timestamp + sign 组合签名、或固定 Authorization: Bearer xxx 但要求额外传 Authorization: Bearer xxx 外加 X-DashScope-Signature 等头。
实操建议:
- 通义千问(DashScope):必须计算
X-DashScope-Signature(HMAC-SHA256),且body中的input是{ "messages": [...] },不是平级字段 - 讯飞星火:需先调
https://spark-api.xf-yun.com/v1/session获取session_id,再把该 ID 放在后续请求 URL path 里(如/v1/chat/xxx-session-id) - 智谱 GLM:用
Authorization: Bearer {api_key},但model参数值必须是glm-4这类固定字符串,传gpt-3.5-turbo会 404 - 所有国产模型响应中,文本内容通常在
['output']['text']、['result']['text']或['data']['choices'][0]['delta']['content'],不能默认套 OpenAI 路径
封装一个可切换模型的基础请求函数
硬编码多个 if-else 切换模型太脆弱,推荐按「提供者」抽象一层,每个提供者有自己的 buildRequest() 和 parseResponse()。
性能影响:不要在每次请求时都重新初始化 cURL handler;复用 curl_init() 实例 + curl_setopt_array() 更轻量。
示例片段(不依赖任何 SDK):
function callAiApi(string $provider, array $params): array
{
$config = [
'openai' => [
'url' => 'https://api.openai.com/v1/chat/completions',
'headers' => ['Authorization: Bearer ' . $_ENV['OPENAI_KEY']],
],
'dashscope' => [
'url' => 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation',
'headers' => [
'Authorization: Bearer ' . $_ENV['DASHSCOPE_KEY'],
'X-DashScope-Signature: ' . generateDashScopeSignature(...),
],
],
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $config[$provider]['url'],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_HTTPHEADER => $config[$provider]['headers'] + ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$raw = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'status' => $httpCode === 200,
'body' => $httpCode === 200 ? json_decode($raw, true) : null,
'error' => $httpCode !== 200 ? $raw : null,
];
}
绕不开的坑:流式响应(stream=true)在 PHP 中怎么接
OpenAI 和部分国产模型支持 stream=true 返回 SSE(server-sent events),但 PHP 默认 curl_exec() 是等整个响应结束才返回,拿不到中间 chunk。
容易踩的坑:用 ob_flush() + flush() 想边收边吐?没用——cURL 不触发输出缓冲,而且 Web 服务器(Nginx/Apache)通常会缓存响应体。
实操建议:
- 流式响应只适合 CLI 场景或长连接 Websocket 后端,别在普通 PHP-FPM 请求里硬搞
- 真要流式,改用
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback),自己拼接data: {...}行并解析 JSON - 注意:SSE 响应末尾有空行分隔,
data:后可能带空格,json_decode(trim(str_replace('data: ', '', $line)))才安全 - 超时设置必须放宽(
CURLOPT_TIMEOUT至少 120),否则第一个 chunk 没来得及发就断连
实际写的时候,最耗时间的往往不是调通第一个请求,而是反复比对文档里那个“示例响应”和你收到的 raw body 差在哪一层 key。国产模型文档常把 input 和 output 当作概念讲,但字段名可能叫 prompt 或 query,多打两次 var_dump(array_keys($res)) 比读三遍文档管用。