microscrap/sdl3
Composer 安装命令:
composer require microscrap/sdl3
包简介
LibSDL3 Bindings for The PHP SDL3 Extension
README 文档
README
PHP library that wraps the sdl3 extension with global helpers, enums, and data objects. Every helper delegates to a static wrapper class under Microscrap\Bindings\SDL3.
The package covers the entire extension surface (636+ methods across 22 extension classes): init, error, timers, properties, surfaces, 2D rendering, video/windows, OpenGL, events, keyboard, mouse, display, clipboard, joystick, gamepad, audio, file dialogs, and the full SDL GPU API.
Highlights
- Two calling styles — exact C names (
SDL_CreateWindow(...)) or static wrapper classes (Video::createWindow(...)) - Every opaque SDL handle wrapped in a typed
final readonlydata object (SDLWindow,SDLRenderer,SDLGPUDevice, …) - Full enum layer transcribed from the SDL 3.4.x headers — 67 int/string-backed enums including
Scancode(~290 cases),PixelFormat,EventType, and the GPU format family - C-style error handling: no exceptions in
src/, failed creations returnnull, failed operations returnfalse; details viaSDL_GetError() - Headless-friendly: software renderer, dummy video driver, virtual joysticks, driverless audio streams all work without a display
- Coverage drift guard: a Pest test reflects the extension and fails if any extension method lacks a wrapper method or helper function
Requirements
- PHP 8.3+
- ext-sdl3 ^0.5.0 — install from php-io-extensions/sdl3
Installation
Confirm ext-sdl3 is loaded:
php -m | grep sdl3
composer require microscrap/sdl3
Composer autoloads all helper files in src/Helpers/, registering the global SDL_* functions when the package is installed. Helpers are only defined if the name is not already taken (function_exists guard).
The two calling styles
C-ish — global functions with exact SDL C names:
SDL_Init(InitFlag::SDL_INIT_VIDEO->value); $window = SDL_CreateWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN); SDL_DestroyWindow($window); SDL_Quit();
OO-ish — static wrapper classes, same behavior:
use Microscrap\Bindings\SDL3\Init; use Microscrap\Bindings\SDL3\Video; Init::init(InitFlag::SDL_INIT_VIDEO); $window = Video::createWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN); Video::destroyWindow($window); Init::quit();
Helpers never touch the extension directly; they delegate one-to-one to the wrapper classes, which are the only layer calling Sdl3\SDL\*. Both styles accept and return the same data objects, and every flag/enum parameter takes EnumType|int.
Name transforms
- Wrapper methods drop the
SDLprefix:SDLCreateWindow→Video::createWindow(). - The
GLclass also drops the redundant GL token (SDLGLCreateContext→GL::createContext()); EGL methods keep a lowercaseeglprefix. - The
GPUclass drops the redundant GPU token (SDLCreateGPUTexture→GPU::createTexture()). - Helpers use the exact SDL C name, including the odd ones:
SDL_rand(),SDL_GL_CreateContext(),SDL_GDKSuspendGPU(). The extension-only transfer-buffer conveniences keep their extension names (writeToGPUTransferBuffer(),readFromGPUTransferBuffer()).
Wrapper classes
| Class | Wraps | Methods | Subsystem |
|---|---|---|---|
Init |
Sdl3\SDL\SDL |
22 | init/quit, version, platform, app metadata |
Error |
SDLError |
4 | get/set/clear error |
Timer |
Timer\SDLTimer |
2 | ticks, delay |
Properties |
SDLProperties |
20 | property groups |
Surface |
Surface\SDLSurface |
72 | surfaces, palettes, pixel formats |
Render |
Render\SDLRender |
94 | renderers, textures |
Video |
Video\SDLVideo |
83 | windows, video drivers |
GL |
Video\SDLGL |
20 | OpenGL + EGL |
Events |
Events\SDLEvents + 4 more |
25 | event queue, watches, quit, drops |
Keyboard |
Events\SDLKeyboard |
28 | keyboard state, keycodes |
Mouse |
Events\SDLMouse |
27 | mouse state, cursors |
Display |
Events\SDLDisplayEvents |
13 | displays, modes |
Clipboard |
Events\SDLClipboardEvents |
10 | clipboard text/data |
Joystick |
Input\SDLJoystick |
58 | joysticks incl. virtual |
Gamepad |
Input\SDLGamepad |
72 | gamepads, mappings, sensors |
Audio |
Audio\SDLAudio |
57 | devices, streams, WAV |
Dialog |
Dialog\SDLDialog |
4 | native file dialogs |
GPU |
Gpu\SDLGPU |
105 | full SDL GPU API + render states |
Data objects live under Microscrap\Bindings\SDL3\DataObjects (27 classes; each holds the raw handle as ->ptr, or ->id for instance IDs). Enums live under Microscrap\Bindings\SDL3\Enums (67 enums; case names match the C macros exactly).
Examples
Software rendering (fully headless)
use Microscrap\Bindings\SDL3\Enums\PixelFormat; use Sdl3\SDL\Surface\SDLSurface; SDL_Init(0); $surface = SDLSurface::SDLCreateSurface(64, 64, PixelFormat::SDL_PIXELFORMAT_RGBA8888->value); $renderer = SDL_CreateSoftwareRenderer((int) $surface['ptr']); SDL_SetRenderDrawColor($renderer, 255, 0, 0, 255); SDL_RenderClear($renderer); SDL_RenderFillRect($renderer, ['x' => 8, 'y' => 8, 'w' => 48, 'h' => 48]); SDL_RenderPresent($renderer); $pixels = SDL_RenderReadPixels($renderer); SDL_DestroyRenderer($renderer); SDLSurface::SDLDestroySurface((int) $surface['ptr']); SDL_Quit();
Window + events (dummy driver works headless)
use Microscrap\Bindings\SDL3\Enums\EventType; use Microscrap\Bindings\SDL3\Enums\InitFlag; use Microscrap\Bindings\SDL3\Enums\WindowFlag; putenv('SDL_VIDEODRIVER=dummy'); SDL_Init(InitFlag::SDL_INIT_VIDEO->value); $window = SDL_CreateWindow('demo', 320, 240, WindowFlag::SDL_WINDOW_HIDDEN); while (! is_null($event = SDL_PollEvent())) { // ?SDLEventRef if ($event->eventType === EventType::SDL_EVENT_QUIT->value) { break; } } SDL_DestroyWindow($window); SDL_Quit();
Virtual joystick (no hardware needed)
use Microscrap\Bindings\SDL3\Enums\InitFlag; use Microscrap\Bindings\SDL3\Enums\JoystickType; SDL_Init(InitFlag::SDL_INIT_JOYSTICK->value); $id = SDL_AttachVirtualJoystick([ 'type' => JoystickType::SDL_JOYSTICK_TYPE_GAMEPAD->value, 'naxes' => 2, 'nbuttons' => 4, ]); $joystick = SDL_OpenJoystick($id); SDL_SetJoystickVirtualAxis($joystick, 0, 12345); SDL_UpdateJoysticks(); $value = SDL_GetJoystickAxis($joystick, 0); // 12345 SDL_CloseJoystick($joystick); SDL_DetachVirtualJoystick($id); SDL_Quit();
Audio stream round-trip (driverless)
use Microscrap\Bindings\SDL3\Enums\AudioFormat; SDL_Init(0); $spec = ['format' => AudioFormat::SDL_AUDIO_S16LE->value, 'channels' => 2, 'freq' => 44100]; $stream = SDL_CreateAudioStream($spec, $spec); SDL_PutAudioStreamData($stream, $pcmBytes); SDL_FlushAudioStream($stream); $out = SDL_GetAudioStreamData($stream, strlen($pcmBytes)); SDL_DestroyAudioStream($stream); SDL_Quit();
GPU buffer round-trip (Metal/Vulkan/D3D12)
use Microscrap\Bindings\SDL3\Enums\GPUBufferUsage; use Microscrap\Bindings\SDL3\Enums\GPUShaderFormat; use Microscrap\Bindings\SDL3\Enums\GPUTransferBufferUsage; use Microscrap\Bindings\SDL3\Enums\InitFlag; use Microscrap\Bindings\SDL3\GPU; SDL_Init(InitFlag::SDL_INIT_VIDEO->value); // GPU devices need the video subsystem $device = GPU::createDevice(GPUShaderFormat::SDL_GPU_SHADERFORMAT_MSL->value | GPUShaderFormat::SDL_GPU_SHADERFORMAT_SPIRV->value); $buffer = GPU::createBuffer($device, ['usage' => GPUBufferUsage::SDL_GPU_BUFFERUSAGE_VERTEX->value, 'size' => 256]); $upload = GPU::createTransferBuffer($device, ['usage' => GPUTransferBufferUsage::SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD->value, 'size' => 256]); GPU::writeToTransferBuffer($device, $upload, $payload); $cb = GPU::acquireCommandBuffer($device); $copyPass = GPU::beginCopyPass($cb); GPU::uploadToBuffer($copyPass, ['transfer_buffer' => $upload->ptr, 'offset' => 0], ['buffer' => $buffer->ptr, 'offset' => 0, 'size' => 256]); GPU::endCopyPass($copyPass); $fence = GPU::submitCommandBufferAndAcquireFence($cb); GPU::waitForFences($device, true, [$fence]); GPU::releaseFence($device, $fence); GPU::releaseTransferBuffer($device, $upload); GPU::releaseBuffer($device, $buffer); GPU::destroyDevice($device); SDL_Quit();
Struct parameters (rects, audio specs, GPU create-infos, pass targets) are associative arrays mirroring the C struct field names exactly; each wrapper method documents the expected keys in its docblock.
Error handling
Nothing in src/ throws. The package keeps SDL's C conventions:
- creation functions return
nullon failure (?SDLWindow,?SDLGPUDevice, …) - operations return
false(or-1) on failure - call
SDL_GetError()for the reason
Note: the underlying extension itself throws RuntimeException from a handful of calls (e.g. SDL_CreateGPUDevice with no usable driver, GDK functions off-Xbox). Those propagate as-is.
Testing
./vendor/bin/pest
tests/Unitruns without the extension: coverage drift guard (against a committed 0.5.0 method snapshot), style audit (no class constants, no throws, guarded helpers, backed enums, uppercase cases).tests/Featureis gated onextension_loaded('sdl3')and runs headless: dummy video driver, software renderer pixel round-trips, virtual joysticks, driverless audio streams, and a real GPU buffer round-trip when a device is available (skips gracefully otherwise).
License
MIT. See LICENSE.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-07