hmennen90 / php-steamworks
PHP extension providing access to the Valve Steamworks SDK via a flat C API
Package info
github.com/hmennen90/php-steamworks
Language:C
Type:php-ext
Ext name:ext-steamworks
pkg:composer/hmennen90/php-steamworks
Requires
- php: >=8.1
README
Native PHP extension providing access to the Steamworks SDK via a flat C API.
Requirements
- PHP 8.1+
- Steamworks SDK 1.58+ (download from partner.steamgames.com)
- macOS (arm64/x86_64), Linux (x86_64), or Windows (x86_64)
Installation
Via PIE (recommended)
PIE is the official PHP extension installer.
pie install hmennen90/php-steamworks --with-steamworks=/path/to/steamworks-sdk
After installation, copy libsteam_api.dylib (macOS) or libsteam_api.so (Linux) from the SDK's redistributable_bin/ directory next to the built extension:
# macOS cp /path/to/steamworks-sdk/redistributable_bin/osx/libsteam_api.dylib "$(php-config --extension-dir)/" # Linux x86_64 cp /path/to/steamworks-sdk/redistributable_bin/linux64/libsteam_api.so "$(php-config --extension-dir)/"
Note (Laravel Herd / custom PHP builds): If PIE fails to auto-install the
.sodue to a read-only filesystem, the build output still exists at~/.pie/php*/vendor/hmennen90/php-steamworks/modules/steamworks.so. Copy it to your extension directory manually and use an absolute path inphp.ini:extension=/full/path/to/steamworks.so
Manual build
-
Download the Steamworks SDK and extract it.
-
Build the extension:
phpize ./configure --with-steamworks=/path/to/steamworks-sdk make make install
-
Copy the Steam shared library next to the extension (see PIE section above).
-
Enable the extension in
php.ini:
extension=steamworks
See INSTALL.md for detailed platform-specific instructions.
Quick Start
<?php // steam_appid.txt with your App-ID must exist in the working directory if (!steam_init()) { die("Steam not running\n"); } echo "Player: " . steam_friends_get_name() . "\n"; echo "App ID: " . steam_utils_get_app_id() . "\n"; // Game loop while (true) { steam_run_callbacks(); // ... game logic ... usleep(16667); } steam_shutdown();
Available Functions
54 functions across 6 Steamworks interfaces. Full signatures with docblocks live in
stubs/steamworks.php (IDE autocompletion).
Core
steam_init(): boolsteam_shutdown(): voidsteam_run_callbacks(): void— call every frame
User
steam_user_get_steam_id(): int|falsesteam_user_is_logged_on(): boolsteam_user_get_player_steam_level(): int|false
Friends
steam_friends_get_name(): string|falsesteam_friends_set_rich_presence(string $key, ?string $value = null): boolsteam_friends_activate_overlay(string $dialog): voidsteam_friends_activate_overlay_to_web_page(string $url, bool $modal = false): void
Stats & Achievements
steam_stats_set_achievement(string $id): boolsteam_stats_clear_achievement(string $id): boolsteam_stats_store(): boolsteam_stats_get_int(string $name): int|falsesteam_stats_set_int(string $name, int $value): boolsteam_stats_get_float(string $name): float|falsesteam_stats_set_float(string $name, float $value): boolsteam_stats_indicate_achievement_progress(string $id, int $cur, int $max): boolsteam_stats_get_achievement(string $id): ?boolsteam_stats_get_achievement_unlock_time(string $id): ?intsteam_stats_get_num_achievements(): int|falsesteam_stats_get_achievement_name(int $index): string|falsesteam_stats_get_achievement_display_attribute(string $id, string $key): string|falsesteam_stats_reset_all_stats(bool $achievements_too = false): bool
Leaderboards (async — see below)
steam_stats_find_leaderboard(string $name): int|falsesteam_stats_find_or_create_leaderboard(string $name, int $sort, int $display): int|falsesteam_stats_upload_score(int $leaderboard, int $score, int $method = STEAM_LEADERBOARD_UPLOAD_KEEP_BEST, ?array $details = null): int|false—$details= optionalint[]game-specific values stored with the score (maxSTEAM_LEADERBOARD_DETAILS_MAX= 64)steam_stats_download_leaderboard_entries(int $leaderboard, int $request, int $start, int $end): int|falsesteam_stats_get_downloaded_entry(int $entries, int $index): ?array— returns['steam_id' => int, 'global_rank' => int, 'score' => int, 'details' => int[]]steam_stats_get_leaderboard_entry_count(int $leaderboard): int
Async CallResults
steam_get_call_result(int $handle): array|false|null— poll the result of an async call (null= pending,false= failed,array= result with atypefield)
Remote Storage (Cloud Saves)
steam_remote_file_write(string $filename, string $data): boolsteam_remote_file_read(string $filename): string|falsesteam_remote_file_exists(string $filename): boolsteam_remote_file_delete(string $filename): boolsteam_remote_file_list(): array|false
Apps
steam_apps_is_subscribed(): boolsteam_apps_is_dlc_installed(int $dlc_id): boolsteam_apps_get_app_id(): int|falsesteam_apps_get_language(): string|falsesteam_apps_is_subscribed_app(int $app_id): boolsteam_apps_get_current_beta_name(): string|falsesteam_apps_get_earliest_purchase_time(int $app_id): int|falsesteam_apps_get_installed_depots(int $app_id): array|falsesteam_apps_get_dlc_count(): int|falsesteam_apps_get_app_build_id(): int|false
Utils
steam_utils_get_app_id(): int|falsesteam_utils_is_overlay_enabled(): boolsteam_utils_get_country_code(): string|falsesteam_utils_is_steam_deck(): boolsteam_utils_get_steam_ui_language(): string|falsesteam_utils_get_server_real_time(): int|falsesteam_utils_get_current_battery_power(): int|false— 0–100 %, 255 = on AC powersteam_utils_get_seconds_since_app_active(): int|false
Timeline (Game Recording)
Annotate the Steam Game Recording timeline. Most calls are fire-and-forget (bool);
the two does_*_recording_exist calls are async (return a handle — poll steam_get_call_result).
Verified against Steamworks SDK 1.64 (STEAMTIMELINE_INTERFACE_V004).
steam_timeline_set_game_mode(int $mode): bool—STEAM_TIMELINE_GAME_MODE_*steam_timeline_set_tooltip(string $description, float $time_delta = 0.0): boolsteam_timeline_clear_tooltip(float $time_delta = 0.0): boolsteam_timeline_add_instantaneous_event(string $title, string $description, string $icon, int $icon_priority = 0, float $start_offset_seconds = 0.0, int $possible_clip = STEAM_TIMELINE_CLIP_PRIORITY_NONE): intsteam_timeline_add_range_event(string $title, string $description, string $icon, int $icon_priority = 0, float $start_offset_seconds = 0.0, float $duration_seconds = 0.0, int $possible_clip = STEAM_TIMELINE_CLIP_PRIORITY_NONE): intsteam_timeline_start_range_event(...): int/steam_timeline_update_range_event(int $event, ...): bool/steam_timeline_end_range_event(int $event, float $end_offset_seconds = 0.0): boolsteam_timeline_remove_event(int $event): boolsteam_timeline_does_event_recording_exist(int $event): int|false— async (type=timeline_event_recording_exists)steam_timeline_start_game_phase(): bool/steam_timeline_end_game_phase(): boolsteam_timeline_set_game_phase_id(string $phase_id): boolsteam_timeline_does_game_phase_recording_exist(string $phase_id): int|false— async (type=timeline_game_phase_recording_exists)steam_timeline_add_game_phase_tag(string $tag_name, string $tag_icon, string $tag_group, int $priority = 0): boolsteam_timeline_set_game_phase_attribute(string $attribute_group, string $attribute_value, int $priority = 0): boolsteam_timeline_open_overlay_to_game_phase(string $phase_id): bool/steam_timeline_open_overlay_to_event(int $event): bool
Leaderboards & async calls
Leaderboard calls (and other SteamAPICall_t-based APIs) are asynchronous: they
return an integer handle. Poll steam_get_call_result($handle) each frame until it
stops returning null:
$handle = steam_stats_find_leaderboard('HighScores'); do { steam_run_callbacks(); $result = steam_get_call_result($handle); usleep(16667); } while ($result === null); // $result === ['type' => 'leaderboard_found', 'found' => true, 'leaderboard' => 42] $leaderboard = $result['leaderboard']; steam_stats_upload_score($leaderboard, 1500, STEAM_LEADERBOARD_UPLOAD_KEEP_BEST);
See examples/leaderboard.php for a full round-trip.
Constants
- Sort:
STEAM_LEADERBOARD_SORT_ASCENDING,STEAM_LEADERBOARD_SORT_DESCENDING - Display:
STEAM_LEADERBOARD_DISPLAY_NUMERIC,STEAM_LEADERBOARD_DISPLAY_TIME_SECONDS,STEAM_LEADERBOARD_DISPLAY_TIME_MILLISECONDS - Upload:
STEAM_LEADERBOARD_UPLOAD_KEEP_BEST,STEAM_LEADERBOARD_UPLOAD_FORCE_UPDATE - Download:
STEAM_LEADERBOARD_DATA_GLOBAL,STEAM_LEADERBOARD_DATA_GLOBAL_AROUND_USER,STEAM_LEADERBOARD_DATA_FRIENDS
License
MIT