xpaw/steamid
Lightweight PHP library for converting Steam IDs between SteamID64, SteamID32, and legacy SteamID formats, with support for individual, group, and lobby accounts. Simple, dependency-free API for parsing and formatting Steam identifiers in Laravel or any PHP project.
Install with Composer — the package is xpaw/steamid (the repo filename is SteamID.php, but the class lives in xPaw\SteamID):
composer require xpaw/steamid
There is no service provider, config file, or facade. This is a framework-agnostic, dependency-free value object. Composer autoloading is all the wiring you get — you call new SteamID(...) directly, or wrap it yourself.
Confirm it works in Tinker:
use xPaw\SteamID;
$id = new SteamID('76561197960287930');
$id->RenderSteam64(); // "76561197960287930"
$id->RenderSteam2(); // "STEAM_0:0:22202"
$id->RenderSteam3(); // "[U:1:44404]"
$id->RenderLink(); // "https://steamcommunity.com/profiles/76561197960287930"
$id->IsValid(); // true
Where to look first: the README shows the accepted input formats (SteamID64, STEAM_X:Y:Z, [U:1:Z]). Then skim the class for the account-type/universe/instance constants and the IsValid* / Get* methods — those are what you'll branch on.
First use case: you accept a Steam ID from a user in some form (a profile URL, a legacy STEAM_0, or a 64-bit number), normalize it once at the boundary, and store the canonical SteamID64 string in your database. Every other format is a rendering concern.
1. Normalize on input, store SteamID64. Use a FormRequest rule so controllers never see malformed IDs:
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use xPaw\SteamID;
class SteamIdRule implements ValidationRule
{
public function __construct(private bool $requireIndividual = true) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$id = new SteamID(is_string($value) ? trim($value) : '');
if (!$id->IsValid()) {
$fail("The {$attribute} field is not a valid Steam ID.");
return;
}
if ($this->requireIndividual && !$id->IsValidIndividual()) {
$fail("The {$attribute} must belong to an individual Steam account.");
}
}
}
2. Eloquent cast returning the object. Keeps the model pleasant to work with and guarantees anything written back is a valid SteamID64 string:
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use xPaw\SteamID;
class SteamIdCast implements CastsAttributes
{
public function get(Model $model, string $key, mixed $value, array $attributes): ?SteamID
{
return $value === null ? null : new SteamID((string) $value);
}
public function set(Model $model, string $key, mixed $value, array $attributes): ?string
{
if ($value === null) {
return null;
}
$id = $value instanceof SteamID ? $value : new SteamID(trim((string) $value));
if (!$id->IsValid()) {
throw new \InvalidArgumentException("Invalid Steam ID for [{$key}].");
}
return $id->RenderSteam64();
}
}
// app/Models/User.php
protected function casts(): array
{
return [
'steam_id' => \App\Casts\SteamIdCast::class,
];
}
3. Wrap “resolution” (vanity URLs, full profile URLs) in one service. This is where Steam Web API rate limits and URL parsing belong:
namespace App\Support;
use xPaw\SteamID;
class SteamIdResolver
{
public function parse(string $input): ?SteamID
{
$input = trim($input);
if (preg_match('~steamcommunity\.com/profiles/(\d+)~i', $input, $m)) {
$input = $m[1];
}
$id = new SteamID($input);
return $id->IsValid() ? $id : null;
}
public function fromVanity(string $vanity): ?SteamID
{
$vanity = strtolower(trim($vanity, "/ \t\n\r\0\x0B"));
$steam64 = cache()->remember(
"steam:vanity:{$vanity}",
now()->addDay(),
fn () => SteamID::ResolveVanityURL($vanity, config('services.steam.key'))
);
if (!$steam64) {
return null;
}
$id = new SteamID($steam64);
return $id->IsValid() ? $id : null;
}
}
Bind it as a singleton if you want app(SteamIdResolver::class) access, but the class itself is stateless.
4. Serialization belongs in API Resources, and must emit strings. Never rely on the model's raw attribute:
public function toArray($request): array
{
return [
'steam_id' => $this->steam_id?->RenderSteam64(), // string
'steam_id_2' => $this->steam_id?->RenderSteam2(),
'steam_id_3' => $this->steam_id?->RenderSteam3(),
'profile_url' => $this->steam_id?->RenderLink(),
];
}
5. Migration shape. SteamID64 fits in UNSIGNED BIGINT, but char(17) is usually safer to reason about (no numeric casts anywhere, no 32-bit surprises, straightforward unique index):
$table->char('steam_id', 17)->nullable()->unique();
6. Queue jobs and DTOs. Pass the SteamID64 string across job boundaries and rehydrate:
SyncSteamProfile::dispatch($user->steam_id->RenderSteam64());
// inside the job
$id = new SteamID($this->steamId64);
7. Steam login handoff. If you use Socialite with a Steam/OpenID driver, the callback gives you a SteamID64. Push it through SteamIdResolver::parse() before persisting so STEAM_0/[U:1:] variants can't sneak into the column.
8. Display helpers on the model. Add accessors for the legacy formats once, so Blade doesn't call the class inline:
protected function steam2(): Attribute
{
return Attribute::get(fn () => $this->steam_id?->RenderSteam2());
}
Number.MAX_SAFE_INTEGER. Always serialize as a string; never let Eloquent return it as an integer in JSON.new SteamID($anything) does not throw on garbage. Invalid input produces an invalid object, so always check IsValid() before trusting it. Wrap parsing in your own method that returns null or throws a domain exception.IsValid() ≠ “is a user.” A clan ID or game-server ID is structurally valid. Use IsValidIndividual() whenever you expect a human account, especially for auth and profile linking.ResolveVanityURL helper (present in recent versions — verify against your installed tag) hits the network. Cache results; a day is reasonable. Handle null/failure without breaking the request./profiles/…, and route /id/… through vanity resolution yourself.RenderSteam2() is lossy by design. It encodes universe/instance, and for non-individual accounts (clans, game servers, anonymous servers) the legacy form is not meaningful. SteamID64 is your canonical identifier; everything else is display.GetAccountID() is great for compact storage/comparisons, but it drops universe and instance. Don't make it your primary key unless you deliberately only ever store public individual accounts.char(17) column, (string) casts, string in queues).Steam::parse(...), write the facade/wrapper yourself — don't expect one.76561197960287930) and assert every rendered format. Add a test for '', 'not-an-id', a clan ID, and a numeric-string ID to catch the “valid but wrong type” case.RenderSteam64() returns a string in current versions — lean on that and skip (string) noise in your API layer, but don't assume GetAccountID() is also a string.config/services.php under steam.key, sourced from .env; the library will happily use null and fail silently-ish if you forget.composer require xpaw/steamid (lowercase), while the GitHub repo and class file are SteamID.php. Autoloading resolves it to xPaw\SteamID, so keep that use statement exact.How can I help you explore Laravel packages today?