Integrating ChatGPT into a PHP application means calling the OpenAI Chat Completions API from your backend. This guide covers every approach: the official PHP SDK, raw cURL, streaming responses, conversation history, and a working web chat form. All examples use gpt-4o-mini — fast, capable, and cost-effective.
For the full PHP SDK reference, see OpenAI PHP SDK Guide. For a Laravel-specific integration, see Laravel OpenAI Integration.
Option A: Official PHP SDK (Recommended)
Install via Composer:
composer require openai-php/clientBasic chat completion:
<?php
require 'vendor/autoload.php';
$client = OpenAI::client(getenv('OPENAI_API_KEY'));
$response = $client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
['role' => 'system', 'content' => 'You are a helpful assistant.'],
['role' => 'user', 'content' => 'Explain closures in PHP in one sentence.'],
],
'max_tokens' => 200,
]);
echo $response->choices[0]->message->content;Option B: Raw cURL (No Dependencies)
If you can’t install Composer, use cURL directly:
<?php
function chatgpt(string $message, string $apiKey): string
{
$payload = json_encode([
'model' => 'gpt-4o-mini',
'messages' => [
['role' => 'system', 'content' => 'You are a helpful assistant.'],
['role' => 'user', 'content' => $message],
],
'max_tokens' => 512,
]);
$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey,
],
CURLOPT_TIMEOUT => 30,
]);
$result = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
if ($errno) {
throw new RuntimeException('cURL error: ' . curl_strerror($errno));
}
$data = json_decode($result, true);
if (isset($data['error'])) {
throw new RuntimeException('OpenAI error: ' . $data['error']['message']);
}
return $data['choices'][0]['message']['content'];
}
echo chatgpt('What is the difference between == and === in PHP?', getenv('OPENAI_API_KEY'));Conversation History
Maintain context by keeping the full message history in a session:
<?php
require 'vendor/autoload.php';
session_start();
if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = [
['role' => 'system', 'content' => 'You are a helpful PHP developer assistant.'],
];
}
function chat(string $input): string
{
$client = OpenAI::client(getenv('OPENAI_API_KEY'));
$_SESSION['messages'][] = ['role' => 'user', 'content' => $input];
// Keep last 20 exchanges to avoid token overflow
$messages = array_merge(
[array_shift($_SESSION['messages'])], // always keep system prompt
array_slice($_SESSION['messages'], -20)
);
$response = $client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => $messages,
]);
$reply = $response->choices[0]->message->content;
$_SESSION['messages'][] = ['role' => 'assistant', 'content' => $reply];
return $reply;
}
// Usage
echo chat("My name is Alex."); // Nice to meet you, Alex!
echo "\n";
echo chat("What's my name?"); // Your name is Alex.Streaming Responses
Stream tokens as they are generated for a faster-feeling UI:
<?php
require 'vendor/autoload.php';
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$input = json_decode(file_get_contents('php://input'), true)['message'] ?? '';
$client = OpenAI::client(getenv('OPENAI_API_KEY'));
$stream = $client->chat()->createStreamed([
'model' => 'gpt-4o-mini',
'messages' => [
['role' => 'user', 'content' => $input],
],
]);
foreach ($stream as $response) {
$delta = $response->choices[0]->delta->content;
if ($delta !== null) {
echo "data: " . json_encode(['chunk' => $delta]) . "\n\n";
ob_flush();
flush();
}
}
echo "data: [DONE]\n\n";Web Chat Form
A minimal HTML form that sends messages and displays replies:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ChatGPT PHP</title>
<style>
body { font-family: sans-serif; max-width: 600px; margin: 40px auto; padding: 0 16px; }
#chat { border: 1px solid #ddd; height: 360px; overflow-y: auto;
padding: 12px; border-radius: 8px; margin-bottom: 12px; }
.user { color: #1a56db; margin-bottom: 8px; }
.bot { color: #111; margin-bottom: 8px; }
#form { display: flex; gap: 8px; }
input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; }
button { padding: 8px 16px; background: #1a56db; color: #fff; border: none;
border-radius: 6px; cursor: pointer; }
</style>
</head>
<body>
<div id="chat"></div>
<form id="form">
<input id="msg" placeholder="Ask ChatGPT...">
<button>Send</button>
</form>
<script>
const chat = document.getElementById('chat');
const msg = document.getElementById('msg');
function append(cls, text) {
const d = document.createElement('div');
d.className = cls;
d.textContent = text;
chat.appendChild(d);
chat.scrollTop = chat.scrollHeight;
return d;
}
document.getElementById('form').addEventListener('submit', async (e) => {
e.preventDefault();
const text = msg.value.trim();
if (!text) return;
msg.value = '';
append('user', 'You: ' + text);
const bot = append('bot', 'AI: ');
const res = await fetch('stream.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: text }),
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
for (const line of buf.split('\n')) {
if (!line.startsWith('data: ')) continue;
const d = line.slice(6);
if (d === '[DONE]') break;
try { bot.textContent += JSON.parse(d).chunk; } catch {}
}
buf = buf.split('\n').pop();
}
});
</script>
</body>
</html>Using a Custom System Prompt
Control ChatGPT’s persona by changing the system message:
$messages = [
[
'role' => 'system',
'content' => 'You are an expert PHP developer. '
. 'Give short, precise answers with code examples. '
. 'Always follow PSR-12 coding standards.',
],
['role' => 'user', 'content' => $userInput],
];Error Handling
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;
try {
$response = $client->chat()->create([...]);
$reply = $response->choices[0]->message->content;
} catch (ErrorException $e) {
$code = $e->getCode();
$reply = match (true) {
$code === 401 => 'Invalid API key.',
$code === 429 => 'Rate limit reached. Please wait a moment.',
$code >= 500 => 'OpenAI is temporarily unavailable.',
default => 'API error: ' . $e->getMessage(),
};
} catch (TransporterException $e) {
$reply = 'Network error. Please check your connection.';
}Summary
- SDK:
composer require openai-php/clientthenOpenAI::client($key)->chat()->create([...]) - Raw cURL: works without Composer — POST JSON to
https://api.openai.com/v1/chat/completions - History: store
$_SESSION['messages'], trim to last 20 to avoid token overflow - Streaming: use
createStreamed()with SSE headers for real-time output - Catch
ErrorException(API errors) andTransporterException(network errors)
Subscribe to my newsletter — practical guides on Claude API, AI agents, RAG, and automation.