Model or dataset
orhanerday/open-ai avatar
orhanerday/open-ai

orhanerday/open-ai PHP SDK for REST and Streaming Calls

A community-driven PHP SDK for OpenAI.

2,361 stars293 forksPHPMIT

At a glance

What is it?
orhanerday/open-ai is a community-driven PHP client for the OpenAI REST API. It provides dedicated methods for common APIs, a generic request path for uncovered endpoints, streaming callbacks, multipart uploads, and configuration for compatible providers.
Who is it for?
Adopt this SDK when PHP is already part of your application and you need OpenAI REST access without another runtime dependency. Do not adopt it for WebSocket workloads or token exchange, which the README says require a separate client.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • orhanerday/open-ai2,361 ★ · -2
Star history
Repository2026-10-162026-10-23
orhanerday/open-ai2.4k2.4k

-2 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What the PHP SDK Covers

orhanerday/open-ai targets developers who want to call the OpenAI REST API from PHP 7.4 or later. The repository describes it as a community-maintained client with streaming support, and it requires PHP's native cURL and JSON extensions. It has no third-party runtime dependencies, which makes it a direct option for applications that do not want to add another HTTP or JSON layer.

The documented dedicated coverage includes chat completions, stored completions, model listing and retrieval, image generation and editing, embeddings, text-to-speech, and transcription. The README also directs users to API_COVERAGE.md for the endpoint inventory. That file is important because an SDK can expose a convenient method for common resources while still leaving less common APIs to the generic request method.

The library is for server-side PHP integrations such as applications built with Laravel, Symfony, CakePHP, or Yii, although the README does not require any one framework. Its primary interface is the OpenAi class. The class can be constructed with an API key and then used for dedicated methods or for arbitrary REST paths under the configured OpenAI prefix. This makes it useful for prototypes as well as applications that need a small PHP-specific integration layer.

Installation and the First Chat Request

The README gives one Composer installation command. Composer places the package in the application's dependency tree and loads it through the generated `vendor/autoload.php` file.

bash
composer require orhanerday/open-ai

Set the API key in the environment. The README gives separate commands for Unix-like shells and Windows environments. Choose the command that matches the shell used to start PHP.

bash
export OPENAI_API_KEY='your-api-key'
powershell
$Env:OPENAI_API_KEY = "your-api-key"
cmd
set "OPENAI_API_KEY=your-api-key"

Save the quick-start program as `example.php`. The constructor receives the environment value, while `chat()` sends a model and one user message. The response is a raw JSON string, so the example uses `json_decode()` before printing the first choice.

php
<?php

require __DIR__ . '/vendor/autoload.php';

use Orhanerday\OpenAi\OpenAi;

$open_ai = new OpenAi(getenv('OPENAI_API_KEY'));
$result = $open_ai->chat([
    'model' => 'gpt-4o-mini',
    'messages' => [['role' => 'user', 'content' => 'Hello']],
]);

$response = json_decode($result, true, 512, JSON_THROW_ON_ERROR);
echo $response['choices'][0]['message']['content'];

Run it with:

bash
php example.php

The expected application behavior is a print of the assistant response. The README does not define a special output format or a separate client setup beyond this constructor and Composer autoloading.

The Generic Request Path

The SDK's `request()` method handles REST endpoints without a dedicated method, including Containers, Evals, organization administration, and Vaults. Its documented signature is:

php
public function request(
    string $method,
    string $path,
    ?array $opts = null,
    ?callable $stream = null,
    bool $multipart = false
);

The method combines the configured base URL, `/v1`, and the supplied path. For example, `request('GET', '/models')` calls `https://api.openai.com/v1/models`. API keys and client settings such as `setORG()`, `setHeader()`, `setProxy()`, `setTimeout()`, `setCURLOptions()`, `setBaseURL()`, and `setApiVersion()` apply to these calls. This is the central architectural point: generic requests run through the same code path as built-in methods instead of requiring a separate transport.

For `GET`, `$opts` becomes query parameters. Nested arrays are encoded with bracket syntax, lists use `include[]`, and booleans become `true` or `false`. For `POST`, `PUT`, and `PATCH`, the options become a JSON body. An empty array sends `{}`. `DELETE` normally has no body unless options are supplied. A `file` or `image` key, or the explicit `$multipart` argument, changes non-`GET` requests to `multipart/form-data`.

One example creates a container and then lists containers with a limit and ordering:

php
$created = json_decode($open_ai->request('POST', '/containers', ['name' => 'my-container']), true);

$list = json_decode($open_ai->request('GET', '/containers', ['limit' => 10, 'order' => 'desc']), true);

$open_ai->request('DELETE', '/containers/' . $created['id']);

The generic method is useful for new endpoints, but its flexibility also shifts responsibility to the caller for path correctness, required headers, and interpretation of the response.

Streaming, Uploads, and Response Handling

Streaming requires a callback as the fourth argument to `request()`. The callback receives the cURL handle and a raw server-sent event chunk. It must return the number of bytes it handled, while the application decides whether to print, persist, or otherwise process each chunk. The documented example sets `stream` to `true` and passes an input prompt.

php
$open_ai->request('POST', '/responses', [
    'model' => 'gpt-4o-mini',
    'input' => 'Write a haiku about PHP.',
    'stream' => true,
], function ($curl, $chunk) {
    echo $chunk;

    return strlen($chunk);
});

For file uploads, the presence of a `file` key automatically selects multipart encoding. `CURLFile` carries the local path into the request. The fifth argument is available when a body must be multipart without a `file` or `image` key.

php
$open_ai->request('POST', '/files', [
    'purpose' => 'user_data',
    'file' => new CURLFile('/path/to/data.jsonl'),
]);

Ordinary calls return the raw response body as a string. HTTP errors from the API, including 4xx and 5xx responses, return their response bodies by default rather than always becoming exceptions. The README's error example reads `getCURLInfo()['http_code']` and checks the `error` field. `setThrowOnError(true)` changes that default. Transport-level problems such as missing credentials, unsupported methods, invalid paths, timeouts, and cURL failures throw an `Exception`. This split between HTTP error bodies and thrown transport errors requires explicit handling in production code.

Provider Configuration and Security Boundaries

The SDK can target an OpenAI-compatible provider with `setBaseURL()` and `setApiVersion()`. The README does not claim compatibility with every provider, so changing the base URL should be treated as a configuration boundary rather than proof that all endpoint semantics match.

Path validation is built into `request()`. Paths must begin with a single `/`. Absolute URLs, paths beginning with `//host/`, and paths containing `://` are rejected. To target another host, the documented approach is to call `setBaseURL()` once. The README also warns against passing untrusted user input directly as `$path`. IDs should be validated or encoded with `rawurlencode()` before they are concatenated into a path.

This design narrows a common URL-injection surface, but it does not remove application-level responsibility. A caller can still send a syntactically valid path with the wrong HTTP method, omit a required beta header, provide an insufficient permission key, or choose an endpoint that is not supported by the selected provider. `API_COVERAGE.md` and the linked API reference are the places to check those details. The repository also includes `MIGRATING.md` for breaking changes, which matters when moving between major SDK versions.

WebSocket connections and workload identity token exchange are explicit transport gaps. The README says they need a separate client. For those workloads, this PHP package is not a complete solution even though its REST interface is broad.

Maintenance, License, and Upgrade Cost

The repository is not archived. Its recorded last push was on 2026-10-12, and the release list includes versions 6.1.0, 6.2.0, and 6.3.0 dated 2026-10-11. Those dates indicate recent activity around the documented release period, but the release numbers alone do not establish the stability of every endpoint or the compatibility of every provider.

The project uses the MIT license. The repository includes `LICENSE.md` and `legal.md`. The MIT label indicates a permissive license, but applications still need to review the license files and their own distribution obligations. This article does not provide legal advice.

Upgrade planning has a concrete component because the repository contains `MIGRATING.md`, `CHANGELOG.md`, and `API_COVERAGE.md`. A team moving from an earlier major version should read the migration guide before replacing its installed package, then compare its methods and request paths with the current coverage file. The lack of third-party runtime dependencies reduces package installation work, but PHP 7.4+, cURL, and JSON extensions remain hard requirements.

Editorial conclusion

Adopt this SDK when PHP is already part of your application and you need OpenAI REST access without another runtime dependency. Do not adopt it for WebSocket workloads or token exchange, which the README says require a separate client. Verify endpoint coverage in API_COVERAGE.md, error handling in your own integration, and the exact provider behavior before relying on setBaseURL() or setApiVersion().

Frequently asked questions

How do I install orhanerday/open-ai in PHP?

Run `composer require orhanerday/open-ai`, then require the generated `vendor/autoload.php` file. The README requires PHP 7.4 or later and the native cURL and JSON extensions.

How does orhanerday/open-ai handle streaming responses?

Pass a callback as the fourth argument to `request()`. The callback receives raw server-sent event chunks and must return the number of bytes it handled, such as `strlen($chunk)`.

Can orhanerday/open-ai call endpoints without a dedicated method?

Yes. `request()` supports REST endpoints without dedicated methods, including JSON and multipart requests under the configured API prefix. WebSocket connections and workload identity token exchange require a separate client.

Official sources

  1. Issues
  2. License: MIT
  3. orhanerday/open-ai on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/orhanerday-open-ai.svg)](https://hysenlabs.com/projects/orhanerday-open-ai)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/orhanerday-open-ai.svg)](https://hysenlabs.com/projects/orhanerday-open-ai)

Was this useful?