steam_array, 'fetched_at' => timestamp] or null
function AdvancedSteamWidget_get_cache($id) {
$val = get_transient(ADVANCED_STEAM_WIDGET_TRANSIENT_PREFIX . $id);
return is_array($val) ? $val : null;
}
//write the cached payload with a fresh 30-day TTL
//$fetched_at can be preserved from an earlier fetch when extending a stale cache
function AdvancedSteamWidget_set_cache($id, $data, $fetched_at = null) {
set_transient(ADVANCED_STEAM_WIDGET_TRANSIENT_PREFIX . $id, array(
'data' => $data,
'fetched_at' => $fetched_at ?: time(),
), ADVANCED_STEAM_WIDGET_CACHE_TTL);
}
//drop the cached payload for a widget instance
function AdvancedSteamWidget_delete_cache($id) {
delete_transient(ADVANCED_STEAM_WIDGET_TRANSIENT_PREFIX . $id);
}
//normalizes user input into a canonical scheme-less steamcommunity.com URL
//returns '' if input can't be parsed into a recognized format
//accepted inputs: profiles/ URL, id/ URL, bare 17-digit id, bare vanity slug, STEAM_0:X:Y
function AdvancedSteamWidget_normalize_profile_input($raw) {
$raw = trim((string)$raw);
if ($raw === '') $raw = 'slserpent';
//STEAM_0:X:Y legacy format - convert to SteamID64 via the standard formula
if (preg_match('/^(?:STEAM_)?\d+:(\d+):(\d+)$/i', $raw, $m)) {
$id64 = ($m[2] * 2) + 0x0110000100000000 + $m[1];
return 'steamcommunity.com/profiles/' . $id64;
}
//full profiles/ URL - extract the 17-digit id (using hash instead of slash as regex delimiters for URL compatibility)
if (preg_match('#steamcommunity\.com/profiles/(\d{17})#i', $raw, $m)) {
return 'steamcommunity.com/profiles/' . $m[1];
}
//full id/ URL - extract the vanity slug. Steam allows 3-32 chars of letters, digits, underscore, hyphen
if (preg_match('#steamcommunity\.com/id/([A-Za-z0-9_-]{2,32})#i', $raw, $m)) {
return 'steamcommunity.com/id/' . $m[1];
}
//bare 17-digit number - treat as SteamID64
if (preg_match('/^\d{17}$/', $raw)) {
return 'steamcommunity.com/profiles/' . $raw;
}
//bare vanity slug final case
if (preg_match('/^[A-Za-z0-9_-]+$/', $raw)) {
return 'steamcommunity.com/id/' . $raw;
}
return '';
}
//fetches steam profile XML and returns parsed steam_array
//throws Exception on any failure (transport, HTTP status, parse, privacy)
//$profile_url is the canonical scheme-less profile URL produced by AdvancedSteamWidget_normalize_profile_input()
function AdvancedSteamWidget_fetch($profile_url) {
//fall back to plugin author's profile if none supplied
if (empty($profile_url)) $profile_url = 'steamcommunity.com/id/slserpent';
//canonical form is scheme-less; add https for the actual request
$profile_url = 'https://' . ltrim($profile_url, '/');
$xml_url = $profile_url . '?xml=1';
//fetch profile XML via WP HTTP API (picks curl or streams transport internally)
$response = wp_remote_get($xml_url, array(
'timeout' => 20,
'redirection' => 2,
'sslverify' => true,
'user-agent' => 'AdvancedSteamWidget/2.0; ' . home_url(),
));
//WP_Error means transport-level failure (DNS, timeout, TLS, etc.)
if (is_wp_error($response)) throw new Exception("Error downloading XML data: " . $response->get_error_message());
//non-2xx counts as failure - profile removed, rate limit, outage, etc.
$code = wp_remote_retrieve_response_code($response);
if ($code < 200 || $code >= 300) throw new Exception("Error downloading XML data. HTTP status: " . $code);
//parse XML body; LIBXML_NONET blocks any external-entity network fetches as defense against future libxml default changes
if (($steam_xml = @simplexml_load_string(wp_remote_retrieve_body($response), null, LIBXML_NONET)) === false) throw new Exception("Error loading XML data. Possibly malformed.");
//check for any profile-level errors in the XML provided by steam
if (isset($steam_xml->error)) throw new Exception("XML Error: " . (string)$steam_xml->error);
//check for profile privacy set to public. only username is available otherwise, which isn't very useful for this plugin
if ((string)$steam_xml->privacyState != "public") throw new Exception("Profile is not public.");
//parse out some values so they're easier to store / use
$steam_array = array();
$steam_array['username'] = (string)$steam_xml->steamID;
$steam_array['ID64'] = (string)$steam_xml->steamID64;
$steam_array['profile_url'] = $profile_url;
$steam_array['avatar']['icon'] = (string)$steam_xml->avatarIcon;
$steam_array['avatar']['medium'] = (string)$steam_xml->avatarMedium;
$steam_array['avatar']['large'] = (string)$steam_xml->avatarFull;
$steam_array['hours_twoweeks'] = (string)$steam_xml->hoursPlayed2Wk;
if ($steam_xml->onlineState == "in-game") {
//game name is now embedded in stateMessage as "In-Game
Game Name" (inGameInfo element was removed)
$steam_array['ingame'] = preg_match('#^In-Game
(.*)$#', (string)$steam_xml->stateMessage, $m) ? trim($m[1]) : true;
} else $steam_array['ingame'] = false;
if ($steam_xml->onlineState == "online") {
$steam_array['online'] = true;
} else $steam_array['online'] = false;
if (isset($steam_xml->mostPlayedGames->mostPlayedGame) && count($steam_xml->mostPlayedGames->mostPlayedGame) > 0) {
//workaround for steam no 2 wks played bug
$cumulative_hours_from_games = false;
if ($steam_array['hours_twoweeks'] == "0.0") {
$steam_array['hours_twoweeks'] = 0;
$cumulative_hours_from_games = true;
}
$k = 0;
foreach ($steam_xml->mostPlayedGames->mostPlayedGame as $game) {
if (strlen($game->gameName) < 1) continue;
$steam_array['games'][$k]['name'] = (string)$game->gameName;
$steam_array['games'][$k]['url'] = (string)$game->gameLink;
$steam_array['games'][$k]['icon'] = (string)$game->gameIcon;
$steam_array['games'][$k]['logo']['small'] = (string)$game->gameLogoSmall;
$steam_array['games'][$k]['logo']['large'] = (string)$game->gameLogo;
$steam_array['games'][$k]['hours_total'] = (string)$game->hoursOnRecord;
$steam_array['games'][$k]['hours_twoweeks'] = (string)round((float)$game->hoursPlayed, 1); //cleans up trailing .0
if ($steam_array['ingame'] && $steam_array['ingame'] == $steam_array['games'][$k]['name']) {
$steam_array['games'][$k]['ingame'] = true;
} else $steam_array['games'][$k]['ingame'] = false;
//see if stats name exists and is valid, i.e. not just the (alphabetical) app id
if (isset($game->statsName) && preg_match('/^\d+$/', $game->statsName)) {
$steam_array['games'][$k]['stats_url'] = $profile_url . "/stats/" . (string)$game->statsName;
} else $steam_array['games'][$k]['stats_url'] = false;
if ($cumulative_hours_from_games === true) {
$steam_array['hours_twoweeks'] += (float)$game->hoursPlayed;
}
$k++;
}
}
return $steam_array;
}