Appearance
PHP
PHP applications interact with the local Riot Client session by executing the riotclient CLI as a child process with proc_open() and decoding the JSON output with json_decode(). The whole example lives in examples/php/main.php and passes php -l.
Setup
- Install the CLI globally:
bash
npm install -g @valoranchi/riot-client- PHP 8.1 or later with the built-in
jsonextension.
Read
Run whoami and owned-items with proc_open() and decode the output into associative arrays:
php
/** @return array{0:int,1:string,2:string} exit code, stdout, stderr */
function runCliCommand(array $args): array
{
$descriptors = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$command = array_merge(['riotclient'], $args);
$process = proc_open($command, $descriptors, $pipes);
if (!is_resource($process)) {
throw new RuntimeException('Failed to spawn riotclient');
}
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
return [proc_close($process), (string) $stdout, (string) $stderr];
}
function readExample(): void
{
[$code, $stdout] = runCliCommand(['whoami']);
if ($code === 0) {
$player = json_decode($stdout, true, 512, JSON_THROW_ON_ERROR);
echo "Player: {$player['gameName']}#{$player['tagLine']} ({$player['region']})\n";
}
[$code, $stdout] = runCliCommand(['owned-items', '--language', 'en-US']);
if ($code === 0) {
$collection = json_decode($stdout, true, 512, JSON_THROW_ON_ERROR);
echo "Collection generated at: {$collection['generatedAt']}\n";
foreach ($collection['weapons'] as $weapon) {
if (!empty($weapon['skins'])) {
echo "- {$weapon['name']}: {$weapon['skins'][0]['name']}\n";
}
}
}
}Write with Validation
Every mutation command performs a dry run locally unless --yes is given. If validation fails, riotclient exits with code 6 and writes the details to stderr:
php
function equipCard(string $cardUuid, bool $execute = false): void
{
$args = ['equip', '--card', $cardUuid];
if ($execute) {
$args[] = '--yes';
}
[$exitCode, $stdout, $stderr] = runCliCommand($args);
if ($exitCode === 0) {
$label = $execute ? 'Equipped card:' : 'Dry run passed:';
echo "{$label} " . trim($stdout) . "\n";
} elseif ($exitCode === 6) {
$error = json_decode($stderr, true, 512, JSON_THROW_ON_ERROR)['error'];
$reason = $error['reason'] ?? 'unknown';
fwrite(STDERR, "Validation failed: {$reason} - {$error['message']}\n");
} else {
fwrite(STDERR, "Command failed [code {$exitCode}]: {$stderr}\n");
}
}Events
Stream real-time events line by line with popen():
php
function streamEvents(): void
{
$handle = popen('riotclient watch --only friend:presence,message', 'r');
if ($handle === false) {
throw new RuntimeException('Failed to spawn riotclient watch');
}
echo "Listening for real-time events. Press Ctrl+C to terminate.\n";
while (($line = fgets($handle)) !== false) {
$line = trim($line);
if ($line === '') {
continue;
}
$event = json_decode($line, true, 512, JSON_THROW_ON_ERROR);
$data = $event['data'] ?? [];
if ($event['event'] === 'friend:presence') {
$name = $data['friend']['gameName'] ?? '';
$state = $data['friend']['presence']['state'] ?? '';
echo "Friend {$name} is now {$data['change']} ({$state})\n";
} elseif ($event['event'] === 'message') {
echo "[{$data['from']['gameName']}]: {$data['body']}\n";
}
}
pclose($handle);
}Types
Generate typed PHP classes from the JSON Schemas with quicktype:
bash
npx quicktype schema/OwnedItems.json --src-lang schema -l php -o OwnedItems.phpA hand-written class of the same shape, for a single model:
php
/** Generated with: npx quicktype schema/OwnedItems.json --src-lang schema -l php -o OwnedItems.php */
final class OwnedSkin
{
public function __construct(public readonly string $uuid, public readonly string $name)
{
}
/** @param array<string,mixed> $data */
public static function fromArray(array $data): self
{
return new self((string) $data['uuid'], (string) $data['name']);
}
}