Skip to content

Releases: fkrzski/php-steam-api-sdk

0.9.0

Choose a tag to compare

@fkrzski fkrzski released this 09 Oct 20:50
Immutable release. Only release title and notes can be modified.
d4d5238

Adds ISteamWebAPIUtil (GetServerInfo, GetSupportedAPIList), any Saloon sender in place of Guzzle, and configurable rate limits and 429 cool-down, and resets the daily budget at midnight UTC. Contains a breaking change.

Added

  • GetServerInfoRequest (ISteamWebAPIUtil), returning Steam's clock as a DateTimeImmutable in UTC, reached anonymously through the new WebApiResource as $connector->webApi()->serverTime() (#98).
  • GetSupportedApiListRequest (ISteamWebAPIUtil) with the WebApiInterface, WebApiMethod and WebApiParameter DTOs, listing every Web API method Steam lets the caller see as $connector->webApi()->supportedApis(). It implements the new SendsOptionalApiKey contract, so the configured key goes along and adds the methods it can call, a connector without one gets the anonymous list, and a rejected key raises InvalidApiKeyException (#99).
  • rateLimits on SteamConfig, a list of the rate limit plugin's Limits that replaces the daily budget of 100 000, with a key or without; [] meters nothing locally, and a 429 from Steam still raises SteamRateLimitException. Two limits sharing a name throw the new InvalidRateLimitException as the config is built (#101).
  • sender on SteamConfig, any Saloon Sender in place of the default GuzzleSender. Whatever is injected, a request Steam never answered, a PSR-18 network exception included, raises SteamConnectionException and is retried (#102).
  • tooManyRequestsCooldown on SteamConfig, the seconds a 429 without Retry-After refuses requests locally, 60 by default, with a key or without and whatever rateLimits is. A Retry-After from Steam still wins, and a value under 1 throws InvalidRateLimitException as the config is built (#117).

Changed

  • BC break. SteamConnector::sender() returns SteamSender, which wraps the sender in use, instead of a GuzzleSender, so addMiddleware(), getHandlerStack() and getGuzzleClient() are gone from it. Callers that need Guzzle pass a GuzzleSender of their own as sender (#102).
  • GitHub Actions are pinned to full commit SHAs with the version in a trailing comment, and the docs workflow runs @fkrzski/docs-schema 0.1.0 instead of the latest release (#103).

Fixed

  • The daily budget resets at 00:00 UTC instead of 24 hours after the first request of its window. The limit is now named 100000_every_utc_midnight, so counters already held in a persistent store restart from zero once (#100).
  • PlayerSummary maps the communityvisibilitystate 2 that Steam returns for friends-only profiles to the new CommunityVisibility::FriendsOnly, where it threw UnexpectedValueException and failed the whole GetPlayerSummariesRequest batch (#106).

Details

0.8.0

Choose a tag to compare

@fkrzski fkrzski released this 07 Oct 05:20
Immutable release. Only release title and notes can be modified.
cb04d9f

Adds GetNewsForApp (ISteamNews) and request, response and failure hooks on the connector, and keeps the API key out of debug output and exceptions. Contains breaking changes.

Added

  • GetNewsForAppRequest (ISteamNews) with the AppNews and NewsItem DTOs, returning a page of a game's news with Steam's total for the filter, reached anonymously through the new NewsResource as $connector->news()->appNews(). Steam answers an app ID it does not know exactly like some apps that exist, so both raise the new AppNewsUnavailableException (#77).
  • SteamConnector::onRequest() and onResponse(), fired on every attempt including under sendAsync() and pool(), with the key-free RequestSending and ResponseReceived payloads: the Steam method, the query, the attempt number and, on the response, the status and the duration in seconds. A 429 reaches onResponse before it becomes SteamRateLimitException, and an exception thrown in a hook reaches the caller (#79).
  • SteamConnector::onFailure(), fired once per call under send(), sendAsync() and pool() with the exception the caller gets: after the retries, as the SDK exception, and also for a request refused locally for the quota or a missing key. An exception thrown in the hook is dropped, so the caller still gets the original (#80).
  • symfony/var-dumper in suggest, because the default dumpers behind debug() need it and Composer does not surface Saloon's own suggestion (#78).

Changed

  • BC break. getPrevious() is null on SteamConnectionException and on a SteamApiException raised from a 4xx or 5xx, where 0.7.0 chained Saloon's FatalRequestException or RequestException, so callers read the status from getCode() and the payload from response. Guzzle 7 quotes the request URI, API key included, in the exception beneath, and error trackers store every exception on the chain (#86).
  • BC break. ProfileNotPublicException::forSteamId(), ProfileNotPublicException::forPrivateOrMissing() and SteamUserNotFoundException::forVanity() require the Saloon Response, so callers building them by hand have to pass one. GetOwnedGamesRequest and ResolveVanityUrlRequest now do, which gives their failures a response, code 200 rather than 0 and the Steam method in the message (#90).
  • BC break. send(), sendAsync() and pool() raise the failure Steam reports in a 200 payload for GetOwnedGamesRequest, GetSteamLevelRequest, GetBadgesRequest, GetRecentlyPlayedGamesRequest, GetCommunityBadgeProgressRequest, ResolveVanityUrlRequest, UpToDateCheckRequest and GetServersAtAddressRequest, where they used to return the Response and leave the exception to dto(), so callers catch it around the call itself. The resource methods throw what they did before (#94).
  • Every exception built from a Steam response, and SteamConnectionException, opens its message with the Steam method called, as in GetFriendList: Steam API request failed with HTTP 500. Classes and codes are unchanged, so only code matching on the message text is affected (#81).

Fixed

  • SteamConnector::debugRequest(), and debug() through it, pass the callable a copy of the PSR request with the API key masked as key=***, while the request sent to Steam keeps it (#78).
  • SteamConnectionException masks the API key as key=*** in the Guzzle message it quotes (#86).
  • SteamConfig marks apiKey as #[\SensitiveParameter], so an invalid timeout or retry setting no longer puts the key among the stack-trace arguments when zend.exception_ignore_args is off (#86).
  • sendAsync() and pool() count a 4xx or 5xx against the daily budget and turn a 429 into SteamRateLimitException, refusing the requests after it locally, where they used to count only a 2xx and raise SteamApiException with code 429 (#85).
  • sendAsync() and pool() return the promise their response pipeline settles, so a middleware throwing on a 2xx rejects the call and a Response a middleware swaps in reaches the caller (#79).

Details

  • GetNewsForApp (ISteamNews) by @fkrzski in #83
  • debug() masks the API key by @fkrzski in #87
  • Failure exceptions no longer carry the API key by @fkrzski in #88
  • Meter failed responses in sendAsync() and pool() by @fkrzski in #89
  • Steam method in exception messages by @fkrzski in #91
  • Response missing on GetOwnedGames and ResolveVanityURL failures by @fkrzski in #92
  • Request and response hooks by @fkrzski in #93
  • Failure hook by @fkrzski in #95
  • Raise 200 failures in the response pipeline by @fkrzski in #96
  • Docs pass and release prep for 0.8.0 by @fkrzski in #97

0.7.0

Choose a tag to compare

@fkrzski fkrzski released this 01 Oct 07:01
Immutable release. Only release title and notes can be modified.
bcfad79

Closes out ISteamApps with UpToDateCheck, GetServersAtAddress and GetSDRConfig, and adds timeouts, retries and an optional API key to SteamConfig. Contains breaking changes.

Added

  • UpToDateCheckRequest (ISteamApps) with the AppVersionCheck DTO, telling whether a game server's version is current and which one Steam requires, reached anonymously through the new AppsResource as $connector->apps()->upToDateCheck(). Steam answers an app ID it does not know exactly like a game running no versioned servers, so both raise the new AppVersionUnavailableException (#64).
  • GetServersAtAddressRequest (ISteamApps) with the GameServer DTO and the ServerRegion enum, listing the game servers Steam knows at an IPv4 address, reached anonymously as $connector->apps()->serversAtAddress(). An address Steam rejects raises the new InvalidServerAddressException (#65).
  • GetSdrConfigRequest (ISteamApps) with the SdrConfig, SdrPointOfPresence and SdrRelay DTOs, mapping the Steam Datagram Relay network a game connects through, reached anonymously as $connector->apps()->sdrConfig(). An app ID Steam does not know raises AppNotFoundException at once rather than being retried (#66).
  • connectTimeout and requestTimeout on SteamConfig, in seconds, falling back to Saloon's 10 and 30 when left null. 0 disables the limit and a negative value throws InvalidTimeoutException (#61).
  • tries, retryInterval (in milliseconds) and exponentialBackoff on SteamConfig, defaulting to a single attempt. Only a request Steam never answered or a 5xx is retried, never a 4xx or SteamRateLimitException, because every attempt spends a slot of the daily budget (#62).
  • SteamConnectionException for a request that never reached Steam — a timeout, a DNS failure, a TLS error, a refused connection. It carries no response and a 0 code, with Saloon's FatalRequestException on getPrevious() (#60).
  • ApiKeyNotConfiguredException for a request that needs an API key sent from a config carrying none, and for a blank key handed to SteamConfig. Both are raised locally, so neither costs a round trip or a slot of the daily budget (#59).

Changed

  • BC break. SteamConfig::$apiKey is a ?string defaulting to null, which makes GetNumberOfCurrentPlayersRequest and GetGlobalAchievementPercentagesForAppRequest reachable with no key configured. Callers reading the property as a string have to handle null (#59).
  • BC break. Transport failures out of send(), sendAsync() and pool() surface as SteamConnectionException rather than Saloon's FatalRequestException. Callers catching the Saloon exception have to catch the SDK one instead (#60).
  • A connector without an API key meters no daily budget, because Steam bills the 100 000 requests to the key; a 429 from Steam still raises SteamRateLimitException (#59).
  • InvalidApiKeyException::missing() reads "Steam received no API key" rather than "Steam API key is missing" (#59).
  • A status the SDK gives no meaning of its own raises the root SteamApiException with Saloon's RequestException on getPrevious(), which is what lets the retry loop see it (#62).

Details

0.6.0

Choose a tag to compare

@fkrzski fkrzski released this 01 Sep 20:33
Immutable release. Only release title and notes can be modified.
698e203

Closes out ISteamUserStats with GetSchemaForGame, GetGlobalAchievementPercentagesForApp and GetNumberOfCurrentPlayers, and adds a connector-wide default language. Contains a breaking change.

Added

  • GetSchemaForGameRequest (ISteamUserStats) with the GameSchema, SchemaAchievement and SchemaStat DTOs, listing every stat and achievement a game publishes, localised through the Language enum. An app publishing no schema comes back as an empty GameSchema rather than a failure (#48).
  • GetGlobalAchievementPercentagesForAppRequest (ISteamUserStats) with the GlobalAchievement DTO, listing how much of the playerbase has unlocked each achievement in a game. Steam spells the parameter gameid rather than appid and the SDK follows, so this one method takes $gameId (#50).
  • GetNumberOfCurrentPlayersRequest (ISteamUserStats) returning a game's concurrent player count as a plain int. Steam serves the endpoint anonymously, so requests implementing the new SendsNoApiKey contract go out with the configured key stripped from the query (#49).
  • Language enum backing Steam's l query parameter, and a language on SteamConfig that applies it to every request whose endpoint localises its payload. Steam's codes are Valve's own rather than ISO, so the enum transcribes their table instead of mapping to one (#46).
  • AppNotFoundException for an app ID Steam does not know, thrown only where the response separates it from an app that merely exposes no stats (#47).

Changed

  • BC break. $language is a ?Language instead of a ?string on GetPlayerAchievementsRequest, GetUserStatsForGameRequest and both StatsResource methods. Callers passing a raw code swap 'english' for Language::English (#46).

Details

Full Changelog: 0.5.0...0.6.0

0.5.0

Choose a tag to compare

@fkrzski fkrzski released this 26 Aug 04:49
Immutable release. Only release title and notes can be modified.
47534a5

Closes out IPlayerService with GetRecentlyPlayedGames, GetSteamLevel, GetBadges and GetCommunityBadgeProgress, and adds fluent resources on the connector. Contains a breaking change.

Added

  • Fluent resources on the connector — $connector->players(), ->users() and ->stats(), each method sending its request and returning the DTO. The request classes are unchanged and stay the way to reach the Response itself or Saloon's pool() (#40).
  • GetBadgesRequest (IPlayerService) with the PlayerBadges and Badge DTOs, covering every earned badge alongside the experience behind the community level (#38).
  • GetCommunityBadgeProgressRequest (IPlayerService) with the CommunityBadgeQuest DTO, listing the quests behind the Steam community badge and whether each one is done (#39).
  • GetRecentlyPlayedGamesRequest (IPlayerService) with the RecentlyPlayedGames and RecentlyPlayedGame DTOs and an optional count limit — RecentlyPlayedGames::$totalCount carries Steam's unlimited total (#36).
  • GetSteamLevelRequest (IPlayerService) returning the player's Steam community level as a plain int (#37).
  • Testing guidance in the docs: faking the connector with Saloon's MockClient, and clearing the MemoryStore daily counter between tests.

Changed

  • Documentation pages are .mdx and use Starlight components — tabs, asides, steps, badges for what each request throws, and filename bars on code blocks.

Fixed

  • BC break. PlayerSummary::$timeCreated is nullable, because Steam omits timecreated for a hidden profile and reading it unguarded fataled the whole GetPlayerSummariesRequest batch. Callers reading the date need a null check (#33).
  • InvalidApiKeyException also covers a 401 whose HTML names the key parameter, not just a 403 — GetCommunityBadgeProgressRequest answers 401 for a broken or absent key, which used to surface as ProfileNotPublicException.

Details

  • docs: rebuild pages as MDX with Starlight by @fkrzski in #34
  • fix(dto): make PlayerSummary::timeCreated nullable by @fkrzski in #35
  • feat: add GetRecentlyPlayedGames endpoint by @fkrzski in #41
  • feat: add GetSteamLevel endpoint by @fkrzski in #42
  • feat: add GetBadges endpoint by @fkrzski in #43
  • feat: add GetCommunityBadgeProgress endpoint by @fkrzski in #44
  • Fluent resources on the connector by @fkrzski in #45

Full Changelog: 0.4.0...0.5.0

0.4.0

Choose a tag to compare

@fkrzski fkrzski released this 16 Aug 12:28
Immutable release. Only release title and notes can be modified.
a59e8e3

Closes out ISteamUser with GetPlayerBans and GetUserGroupList, and makes SteamApiException cover every HTTP failure. Contains breaking changes.

Added

  • GetPlayerBansRequest (ISteamUser) with the PlayerBan DTO and EconomyBan enum — unknown ban values fall back to Unknown instead of throwing (#18).
  • GetUserGroupListRequest (ISteamUser) with the UserGroup DTO (#19).
  • InvalidApiKeyException for a missing (400) or rejected (403) API key.
  • StatsUnavailableException, thrown by GetPlayerAchievementsRequest when Steam withholds achievements.
  • SteamApiException::$response exposing the response behind an HTTP failure.
  • JsonSerializable on SteamId, which now encodes to a bare string instead of an object (#25).

Changed

  • BC break. SteamApiException declares its own constructor, and getCode() is now the HTTP status — or 0 when the failure was raised before a request went out.
  • BC break. HTTP failures raise SDK exceptions instead of Saloon's RequestException subclasses, so catching SteamApiException covers every failure (#20).
  • BC break. TooManySteamIdsException::forCount() takes a required string $endpoint.
  • BC break. CommentPermission and CommunityVisibility are backed enums, by string and int respectively. Case names and fromApiValue() are unchanged; only UnitEnum type hints and instanceof checks need updating.
  • GetPlayerAchievementsRequest, GetUserStatsForGameRequest and GetUserGroupListRequest no longer detect a private profile from the response body — Steam answers with an error status instead.

Fixed

  • PlayerSummary can be passed to json_encode(), which previously returned false (#25).
  • SteamId::tryFromInput() and SteamId::extractVanityName() parse profile and vanity URLs carrying a sub-path, query string or fragment.
  • Rate limit counters are scoped to the API key instead of shared by every connector in the process (#30).

Details

  • chore(deps-dev): update phpstan/phpstan requirement from >=2.2.0 <2.2.6 to ^2.2.6 by @dependabot[bot] in #22
  • fix: parse Steam URLs with a trailing path, query or fragment by @fkrzski in #24
  • fix: back CommentPermission and CommunityVisibility enums by @fkrzski in #26
  • feat: implement JsonSerializable on SteamId by @fkrzski in #27
  • feat: add GetUserGroupList endpoint by @fkrzski in #28
  • feat: add GetPlayerBans endpoint by @fkrzski in #29
  • fix: map HTTP failures to the SDK exception hierarchy by @fkrzski in #31
  • fix: scope rate limit counter to the API key by @fkrzski in #32

Full Changelog: 0.3.0...0.4.0

0.3.0

Choose a tag to compare

@fkrzski fkrzski released this 30 Jul 05:19
Immutable release. Only release title and notes can be modified.
4518c97

Adds the GetFriendList endpoint and launches the documentation site.

Added

  • GetFriendList endpoint (ISteamUser) with the Friend DTO and FriendRelationship enum.
  • Documentation site under docs/, published at docs.fkrzski.dev/php-steam-api-sdk, with a CI job validating the docs frontmatter.

Changed

  • Reorganize request classes into per-interface subnamespaces (Http\Requests\ISteamUser, Http\Requests\ISteamUserStats, Http\Requests\IPlayerService) — update your imports when upgrading.
  • Reorganize test fixtures into per-interface, per-request folders with shortened variant filenames (default, empty, private, …).
  • Pin GitHub Actions to version tags instead of commit hashes.
  • Store the PHPUnit cache under .cache/phpunit so all tools share the .cache directory.

Removed

  • Drop dead .gitignore entries for unused tooling.

Details

  • chore(deps): bump actions/checkout from 6.0.2 to 6.0.3 by @dependabot[bot] in #10
  • feat: add GetFriendList endpoint by @fkrzski in #8
  • refactor: group request classes by steam interface by @fkrzski in #11
  • refactor: group test fixtures by steam interface and request by @fkrzski in #12
  • chore(deps): bump actions/cache from 5.0.5 to 6.0.0 by @dependabot[bot] in #14
  • chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 by @dependabot[bot] in #13
  • chore(deps): bump actions/cache from 6.0.0 to 6.1.0 by @dependabot[bot] in #15
  • chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 by @dependabot[bot] in #16
  • docs: add documentation site by @fkrzski in #17

Full Changelog: 0.2.0...0.3.0

0.2.0

Choose a tag to compare

@fkrzski fkrzski released this 08 Jun 06:00
Immutable release. Only release title and notes can be modified.
a8a3e59

What's Changed

  • chore(deps): bump shivammathur/setup-php from 2.37.0 to 2.37.1 by @dependabot[bot] in #1
  • chore(deps): bump actions/checkout from 6.0.2 to 6.0.3 by @dependabot[bot] in #2
  • test: add mutation testing with 100% msi threshold by @fkrzski in #3
  • chore: add laravel/pao dev dependency by @fkrzski in #4
  • ci: prevent duplicate workflow runs on pull requests by @fkrzski in #5
  • chore: improve gitattributes for dist archives and line endings by @fkrzski in #6
  • ci: cache phpstan and rector tools in formats workflow by @fkrzski in #7

Full Changelog: 0.1.0...0.2.0

0.1.0

Choose a tag to compare

@fkrzski fkrzski released this 03 Jun 06:03
Immutable release. Only release title and notes can be modified.
eb7286c

First public release of php-steam-api-sdk — a framework-agnostic PHP SDK for the Steam Web API, built on Saloon v4.

Highlights

  • Strongly typed — PHP 8.5, PHPStan max, 100% type coverage, readonly DTOs with DateTimeImmutable.
  • Zero framework coupling — a Laravel bridge ships separately.
  • Daily 100 000-request rate limit baked in via saloonphp/rate-limit-plugin; swap in any RateLimitStore (Predis, PSR-16, custom).

What's included

  • SteamConnector + SteamConfig (API key, optional rate-limit store).

  • SteamId value object — strict fromSteamId64(), lenient tryFromInput(), extractVanityName().

  • Requests returning readonly DTOs:

    • ResolveVanityUrlRequest
    • GetPlayerSummariesRequest (batch, ≤100 IDs)
    • GetOwnedGamesRequest
    • GetUserStatsForGameRequest
    • GetPlayerAchievementsRequest
  • Domain exception hierarchy rooted at SteamApiException.

  • Strongly typed — PHP 8.5, PHPStan max, 100% type coverage, readonly DTOs with DateTimeImmutable.

  • Zero framework coupling — a Laravel bridge ships separately.

  • Daily 100 000-request rate limit baked in via saloonphp/rate-limit-plugin; swap in any RateLimitStore (Predis, PSR-16, custom).

Requirements

  • PHP 8.5+

Install

composer require fkrzski/php-steam-api-sdk

Full changelog: see Changelog.