jovian / ogx
OpenGL, EGL and CGL bindings for The PHP OpenGL Extension
Requires
- php: ^8.4|^8.5|^8.6
- ext-opengl: ^0.8.0
Requires (Dev)
- pestphp/pest: ^4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-17 17:34:52 UTC
README
OpenGL, typed in PHP. ext-opengl 0.8.0 projected 1:1 into typed
classes, plus the enums the extension deliberately withholds.
use Jovian\Bindings\OpenGL\Enums\GL\PrimitiveType; use Jovian\Bindings\OpenGL\Enums\GL\StringName; use Jovian\Bindings\OpenGL\GL\GL10; use Jovian\Bindings\OpenGL\GL\GL11; echo GL10::glGetString(StringName::VERSION); // "4.1 Metal - 89.4" GL11::glDrawArrays(PrimitiveType::TRIANGLES, 0, 3);
Before this package existed you wrote that as
const GL_VERSION = 0x1F02; // glcorearb.h:232 const GL_TRIANGLES = 0x0004; // glcorearb.h:81 echo OpenGL\GL\GL10\GL10::glGetString(GL_VERSION); OpenGL\GL\GL11\GL11::glDrawArrays(GL_TRIANGLES, 0, 3);
— which is exactly what ext-opengl's own proof still does, with a
header:line citation on all 41 of its literals.
What it is
| Surface | 581 methods — 573 generated + 8 hand-written Bridge calls, the extension's own count |
| Classes | GL10 .. GL41, EGL, CGL (16), namespace root Jovian\Bindings\OpenGL\ |
| Enums | 98, int-backed, 1474 cases, 15 documented aliases |
| Typed | 310 enum parameters, 39 enum returns; bitfields stay int |
| Platforms | macOS and Linux, same package, same enums |
| Requires | PHP ^8.4, ext-opengl ^0.8.0 |
What it is not
It adds types and enums, and nothing else. One PHP method is exactly one extension call, with the same arguments in the same order:
public static function glDrawArrays(PrimitiveType|int $mode, int $first, int $count): void { ExtGL11::glDrawArrays($mode instanceof \BackedEnum ? $mode->value : $mode, $first, $count); }
There is no convenience layer here. No createShader($type, $source) that
compiles and links; no glGetIntegerv that allocates a buffer, reads it and
returns an array; no object that owns a pointer's lifetime. All of that is
venusian-ogx, one layer up.
Install
composer require jovian/ogx
You need ext-opengl built and loaded. On a Mac with Herd, non-interactive
shells need the scan dir:
export HERD_PHP_84_INI_SCAN_DIR=$(zsh -ic 'echo $HERD_PHP_84_INI_SCAN_DIR')
Three things to know before you write any
1. An enum parameter accepts the enum or an int. Every enum-typed slot
is SomeEnum|int, so a raw constant still works and a migration can be
gradual.
2. A bitfield stays int. PHP cannot OR two enum cases, so a mask is
built from ->value:
GL10::glClear(ClearBufferMask::COLOR_BUFFER_BIT->value | ClearBufferMask::DEPTH_BUFFER_BIT->value);
3. An enum-typed return is documentation and hands back an int.
Converting would be behaviour, and this layer adds none:
$error = GL10::glGetError(); // int, declared ErrorCode|int ErrorCode::tryFrom($error); // ← the idiom $error === ErrorCode::NO_ERROR; // ← always false
Pointers are still pointers
ext-opengl crosses every non-string pointer as raw pointer bits in an
int, and this package does not change that. Runtime\Bridge gives you the
memory; pack() and unpack() give you the typing:
use Jovian\Bindings\OpenGL\Runtime\Bridge; use Jovian\Bindings\OpenGL\Enums\GL\BufferTargetARB; use Jovian\Bindings\OpenGL\GL\GL15; $out = Bridge::alloc(4); GL15::glGenBuffers(1, $out); $vbo = unpack('l', (string) Bridge::read($out, 0, 4))[1]; GL15::glBindBuffer(BufferTargetARB::ARRAY_BUFFER, $vbo); Bridge::free($out);
No count argument is ever invented and none is ever dropped: the PHP
signature is the C prototype.
Where the enums come from
glcorearb.h is a flat list of #define GL_* with no type information, so
nothing in it can say that GL_TRIANGLES is a primitive mode. Khronos's
gl.xml registry — the file the header is generated from — can, and it is
vendored at scripts/khronos/gl.xml.
- Grouping comes from
gl.xml(<enums group>,<param group>,<proto group>) for GL, fromegl.h's own version sections for EGL, and from the SDK's realtypedef enums for CGL. - Values always come from the headers
ext-openglitself vendors, so the two packages cannot drift. - Nothing was grouped by hand. The complete rule set is
.okf/enum-mapping.md.
Both context APIs, on both boxes
EGL and CGL are declared everywhere, because every entry point is
resolved at runtime. A call the current context cannot make raises an
E_WARNING and returns 0/void — no exception, no error side channel. So
CGL::CGLCreateContext() on Linux warns and returns 0, and
EGL::eglGetError() on a Mac does the same.
Choosing is the caller's job. This package has no PHP_OS_FAMILY anywhere
in src/.
Proof
php examples/proof_headless_typed.php # PROOF_HEADLESS_TYPED_OK
Creates a headless core-profile context (CGL on macOS, EGL surfaceless on
Linux), renders a triangle into a 64×64 RGBA8 texture through an FBO with a
GLSL shader pair, reads the pixels back and byte-checks them. It is
ext-opengl's own proof_headless.php with the literals replaced by enums
and nothing else changed — and scripts/gates/verify-smoke.mjs runs both
proofs on the same box and fails on a single differing pixel.
| centre | corner | |
|---|---|---|
| Mac, CGL, GL 4.1, Apple M1 Pro | 255,128,64,255 |
0,0,0,255 |
| Pi 5, EGL surfaceless, Mesa GL 3.1, V3D 7.1.7.0 | 255,128,64,255 |
0,0,0,255 |
Layering
ext-opengl (1:1 binding + unavoidable glue) → jovian/ogx (enums +
typed projection) → venusian-ogx (composition) → Surface (cross-platform
abstraction).
Licence
MIT. scripts/khronos/gl.xml is © 2013-2026 The Khronos Group Inc.,
Apache-2.0.