DEV Community

Cover image for How to Build an AI Chatbot with PHP (Step-by-Step, 2026)
Serhii Kalyna
Serhii Kalyna

Posted on • Originally published at kalyna.pro

How to Build an AI Chatbot with PHP (Step-by-Step, 2026)

How to Build an AI Chatbot with PHP (Step-by-Step, 2026)

This guide builds a working web AI chatbot with PHP: a backend that calls OpenAI, session-based conversation history, streaming SSE, and a clean HTML/JS frontend.

Requirements

  • PHP 8.1+, Composer, OpenAI API key

Install

composer require openai-php/client
Enter fullscreen mode Exit fullscreen mode

chat.php — Non-Streaming Backend

<?php
require 'vendor/autoload.php';
session_start();
header('Content-Type: application/json');

$input = json_decode(file_get_contents('php://input'), true)['message'] ?? '';

if (!isset($_SESSION['history'])) {
    $_SESSION['history'] = [['role' => 'system', 'content' => 'You are a helpful assistant.']];
}

$_SESSION['history'][] = ['role' => 'user', 'content' => $input];

$client = OpenAI::client(getenv('OPENAI_API_KEY'));
$response = $client->chat()->create([
    'model' => 'gpt-4o-mini',
    'messages' => array_slice($_SESSION['history'], -20),
]);

$reply = $response->choices[0]->message->content;
$_SESSION['history'][] = ['role' => 'assistant', 'content' => $reply];

echo json_encode(['reply' => $reply]);
Enter fullscreen mode Exit fullscreen mode

stream.php — Streaming SSE Backend

<?php
require 'vendor/autoload.php';
session_start();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$input = json_decode(file_get_contents('php://input'), true)['message'] ?? '';
$_SESSION['history'][] = ['role' => 'user', 'content' => $input];

$stream = OpenAI::client(getenv('OPENAI_API_KEY'))->chat()->createStreamed([
    'model' => 'gpt-4o-mini',
    'messages' => $_SESSION['history'],
]);

$full = '';
foreach ($stream as $response) {
    $delta = $response->choices[0]->delta->content;
    if ($delta !== null) {
        $full .= $delta;
        echo "data: " . json_encode(['chunk' => $delta]) . "\n\n";
        ob_flush(); flush();
    }
}
$_SESSION['history'][] = ['role' => 'assistant', 'content' => $full];
echo "data: [DONE]\n\n";
Enter fullscreen mode Exit fullscreen mode

Frontend (EventSource / fetch streaming)

const res = await fetch('stream.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: text }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    for (const line of buffer.split('\n')) {
        if (!line.startsWith('data: ')) continue;
        const data = line.slice(6);
        if (data === '[DONE]') return;
        const { chunk } = JSON.parse(data);
        outputEl.textContent += chunk;
    }
}
Enter fullscreen mode Exit fullscreen mode

Production Checklist

  • Set OPENAI_API_KEY as server env var (never hardcode)
  • Use HTTPS for SSE + sessions
  • Trim $_SESSION['history'] to avoid token overflow
  • Add per-user rate limiting to control costs
  • Catch ErrorException + TransporterException

Originally published at kalyna.pro

Top comments (0)