Skip to content

omnisense/omniplay API reference

Floor
Family
Version
1.x
Verified for real
Verified in part
Licence
MIT
composer require omnisense/omniplay:1.x-dev

Read from the package's own PHP files at the commit f88e96826904: its namespaces, classes, interfaces and enums, their public constants, properties and methods with their PHPDoc, and the options its factories take. Nothing of it is run.

Omniplay

  • Capabilities class

    What a provider is, said before it is asked anything: what it can read on a store, the identifiers it finds a game by, what a player is to it and what its terms ask of whoever shows its records; whether it starts and stops game servers, of which services, in which regions, and where it runs them; whether it verifies players' proofs from a platform.

    5 methods · 15 properties

  • CatalogueInterface interface

    1 method

  • Config class

    A provider's configuration while its factory builds it: the options given, the factory's defaults under them, and the keys that must be filled.

    7 methods

  • Fleet class

    FleetInterface over a store. A server is healthy while it says so and was heard from within $ttl seconds; one starting is given $startup seconds before its first heartbeat.

    10 methods

  • FleetInterface interface

    The servers the application knows: registered when they start, kept current by their heartbeats, retired when they fall silent.

    9 methods

  • Heartbeats class

    The application's end of a server's heartbeat, framework-free: the application's route hands it the Authorization header and the decoded JSON body, and answers with the result's status and toArray().

    4 methods · 1 constant

  • HostingInterface interface

    Starts and stops game servers: implemented by providers (a Docker Engine, a cloud's API). What a server runs is the provider's configuration (an image, a command), keyed by the service's name; omniplay knows no engine.

    3 methods

  • IdentityInterface interface

    Verifies a player's proof from a platform - a Steam session ticket, a store's signed token - with that platform, on the application's side: implemented by providers (omniplay/steam). The answer is the player the platform vouches for, or a refusal that says why; never a guess.

    2 methods

  • LibraryInterface interface

    1 method

  • OwnershipInterface interface

    1 method

  • ProviderFactory class

    A provider's factory fills a Config - its name ("omniplay.factory_name"), its title ("omniplay.factory_title"), the options it needs ("omniplay.required_options") and the defaults of the others - then builds the provider from it.

    3 methods

  • ProviderFactoryInterface interface

    Builds a provider from its options (a key, a client id, a Docker host, the services it starts...).

    2 methods

  • ProviderInterface interface

    One provider, configured: a store or catalogue of games (Steam, GOG, IGDB...), a way of hosting game servers (a Docker Engine, Edgegap...), a platform that vouches for its players (Steam). Its Capabilities say what it can be asked - search by title (SearchInterface), a game's store record (CatalogueInterface), a player's games (LibraryInterface), whether a player owns a game (OwnershipInterface), start and stop a server of a service (HostingInterface), verify a player's proof (IdentityInterface) - and what its terms ask of whoever shows its records. Nothing on a store is bought, granted or written.

    3 methods

  • Registry class

    The application's providers by name, each built once from its factory and options, whatever contracts each implements:

    15 methods

  • SearchInterface interface

    1 method

  • Seating class

    SeatingInterface over a fleet and tickets. A player goes on the fullest server that still has a seat - a ready one first, then one starting - so that matches fill before new servers are used; the server's tags must hold every tag asked, its region the one asked. When none fits and a hosting provider is given, one is started (its secret read by the service's reference), registered and seated on - when the provider's capabilities say it starts that service in that region. The ticket's room is the server's id, so that a ticket opens that server only; its claims are the request's, the player as "sub".

    2 methods

  • SeatingInterface interface

    Seats a player on a fitting server of a service, with a ticket to it.

    1 method

  • Tickets class

    TicketsInterface over Ticket\TicketSigner, each service's secret read by its reference. The payload is the claims, then "room", then "exp" (now plus the service's ttl).

    3 methods

  • TicketsInterface interface

    Tickets to a service's servers: minted by the application, checked by the server by itself with the secret they share (Ticket\TicketSigner's format). verify() is the application's side of the same check: a server calling the application back with its players' tickets is what tickets avoid, but an application may read one it handed out.

    2 methods

Omniplay\Exception

  • IdentityRefusedException class

    A platform refused a player's proof, or the proof is not one: the message says why, as the platform said it ("Steam refused the ticket: Invalid ticket (error 101)."). Never a network failure, never a provider's error.

    1 method

  • InvalidArgumentException class

    A value nothing can hold: an empty identifier, a search without words, a server of no seat, a payload no ticket can carry.

  • InvalidConfigException class

    A provider asked for with options it cannot work with, or not configured at all; a secret not found.

  • NotSupportedException class

    A provider asked for what it does not do: a library from a catalogue, a game by a store it does not know, a server of a service it does not know, a region it does not run in.

    3 methods

  • OmniplayException interface

    Every exception omniplay throws.

  • ProviderException class

    The provider answered, and not with what was asked: an error status, a body that is not JSON. answered() gives it the HTTP status as its code (410: a server gone already, which a provider may take as stopped).

    1 method

  • UnreachableException class

    The provider could not be reached: a network error, a timeout, a socket that is not there.

Omniplay\Http

  • Data class

    A decoded JSON answer, read by key without trusting its shape: each reader gives its type or null (an empty list), whatever the provider sent.

    15 methods

  • JsonClient class

    A provider's JSON calls over the application's HTTP client: the provider's headers on every request, a 404 read as "not known" (null), any other error status as a ProviderException naming the path only (never the query, where a key may sit), a network failure as an UnreachableException naming the path only too, with the HTTP client's reason without the address it quotes (reason()). Its previous exception is a copy of the client's (Redaction::copy(): same class and code, the key masked in its message), and neither keeps its trace's arguments, where the request's query and headers stood. An empty body (Docker's 204 to a container started, 304 to one already stopped) reads as an empty Data.

    4 methods

  • Redaction class

    What a failed call may carry of its credentials, taken out before the failure leaves the package: an HTTP client's exception quotes the URL called (a key in its query, a password in its user part), and every exception's trace keeps the arguments of the calls under it (the request options, headers and query with the key) unless PHP runs with zend.exception_ignore_args.

    5 methods · 1 constant

Omniplay\Model

  • Game class

    A game as one store or catalogue knows it: its page, texts, dates, studios, platforms, price, pictures, scores and its identifiers elsewhere. What the provider does not give is empty or null, never guessed: a search answer is usually shorter than a record.

    5 methods · 15 properties

  • GameRef class

    A game named by one identifier: GameRef::steam(620), GameRef::itch('leafo/x-moon').

    7 methods · 2 properties

  • Health enum

    What a server says of itself in its heartbeats.

    1 method · 4 cases

  • HeartbeatResult class

    What Heartbeats::receive() made of a server's heartbeat: the HTTP status the application answers with, why, and the server as the fleet now holds it (null when refused).

    3 methods · 3 properties

  • Identifiers class

    The identifiers of one game in the stores and catalogues that know it, keyed by Scheme value: what lets a store page be found from a catalogue's record (IGDB and RAWG know a game's Steam and GOG numbers).

    8 methods

  • Identity class

    A player as a platform verified them: the platform ("steam"), the player's id there (a SteamID), and what the platform said besides (the game's owner when it is lent, a ban). player() - "steam:76561197960287930" - is what a SeatRequest takes as the player's id, and a ticket's "sub".

    4 methods · 3 properties

  • Image class

    A picture of a game, where its store serves it, under the store's terms and the publisher's rights.

    2 methods · 4 properties

  • ImageKind enum

    6 cases

  • OwnedGame class

    A game in a player's library: which, and how long it was played.

    2 methods · 4 properties

  • Platform enum

    The families of platforms a game runs on; a store's finer names are folded into them.

    2 methods · 9 cases

  • Price class

    A price in one country, in minor units: 969 EUR is 9.69 €.

    3 methods · 4 properties

  • Query class

    A search by title: the words, and what narrows them.

    1 method · 4 properties

  • Rating class

    A score given somewhere: Metacritic 95/100, IGDB 91/100 from 1,200 ratings.

    2 methods · 4 properties

  • Scheme enum

    The stores and catalogues a game is known by.

    6 cases

  • Seat class

    A player's seat: the server, the ticket it lets them in with, and whether the server was started for them (then it may still be booting: the client retries the address until the ticket expires).

    2 methods · 4 properties

  • SeatRequest class

    A player asking for a seat: who, where, on what kind of server, and what their ticket says besides. The player is the application's string, or an Identity a platform verified (IdentityInterface): then the player's id is Identity::player() ("steam:76561197960287930") and $identity keeps it.

    1 method · 5 properties

  • Server class

    A game server of a service, as the fleet knows it: where players reach it, how many it seats and holds, what it last said of itself and when.

    8 methods · 12 properties

  • Service class

    A named realtime service the application seats players on: a game's servers, a relay. Its secret is a reference (an environment variable's name, a vault's key), read through a SecretsInterface when a ticket is signed or a server started: the secret itself is never a field of anything omniplay returns.

    3 methods · 6 properties

  • Ticket class

    A signed ticket and what it says: the room it opens, when it expires, the application's own claims. The token is what the player presents; the server reads the rest from it by itself.

    3 methods · 4 properties

Omniplay\Secret

  • EnvironmentSecrets class

    Secrets read from the environment: the reference is the variable's name ($_ENV, $_SERVER, then getenv()).

    1 method

  • FixedSecrets class

    Secrets given by reference: a test's, or an application's that read them itself.

    2 methods

  • SecretsInterface interface

    A secret by its reference: the application's environment, a vault, a fixed list in a test. A Service names its secret by reference so that the secret is never a field of anything stored or returned.

    1 method

Omniplay\Storage

  • InMemoryStore class

    The servers in an array: a test's store, or a single long-running process's. Gone with the process.

    4 methods

  • StoreInterface interface

    Where the fleet keeps its servers: InMemoryStore for tests and a single process, the application's own otherwise (a table, a Redis hash: a Server goes in as toArray() and comes back through Server::fromArray()).

    4 methods

Omniplay\Testing

  • BareTestCase class

    The check every package of the family runs: each of its classes loaded in a PHP process of its own (this one has loaded PHPUnit), with nothing from outside but omnisense/omniplay and what the package requires - no framework, no HTTP client, no application, even in the harness where they are installed beside it. A package names its entry class and the Composer packages it requires; FOREIGN lists what may never load, allowedNamespaces() the exceptions a package's requirements need.

    2 methods

  • FixedHosting class

    A hosting provider that starts nothing: each server it is asked for is given the next port on its host ("127.0.0.1:7001", ":7002"...), Starting, and kept in $started with the secret it was handed; stop() moves it to $stopped. An application's tests, the harness's bare script.

    7 methods · 3 properties

  • FixedHostingFactory class

    FixedHosting's factory: "fixed-hosting", options services, regions, host, port.

    4 options

  • FixedIdentity class

    A platform that vouches for the players it was given: each proof known answers its player's id, any other is refused. Every proof asked is kept in $asked. An application's tests.

    6 methods · 1 property

  • FixedIdentityFactory class

    FixedIdentity's factory: "fixed-identity", options players (the player's id by proof), platform ("test").

    2 options

  • FixedProvider class

    A provider that answers from the games and libraries it was given, with no network: an application's tests, the harness's bare script. It finds a game by any identifier the game carries, searches titles by their words, and owns what a player's library holds.

    8 methods

  • FixedProviderFactory class

    FixedProvider's factory: "fixed", answering from the games and libraries it holds.

    1 method

  • RecordedHttpClient class

    An HTTP client that answers from what it was given - a callable of the method, the URL and the options, returning [status, body, headers] - and keeps every request made ($requests): a provider's calls checked with no network.

    6 methods · 1 property

  • RecordedResponse class

    RecordedHttpClient's answer.

    7 methods

Omniplay\Ticket

  • TicketSigner class

    The tickets' one format: base64url(json payload) "." hex(hmac_sha256(that base64url, secret)). The payload names its room and when it expires (`room`, a string; `exp`, Unix seconds); the rest is the application's.

    3 methods

Omniplay

Capabilities class

final readonly class Capabilities Capabilities.php:15

What a provider is, said before it is asked anything: what it can read on a store, the identifiers it finds a game by, what a player is to it and what its terms ask of whoever shows its records; whether it starts and stops game servers, of which services, in which regions, and where it runs them; whether it verifies players' proofs from a platform.

it searches by title (SearchInterface)

public bool $catalogue

it gives a game's record (CatalogueInterface)

public bool $library

it lists a player's games (LibraryInterface)

public bool $ownership

it says whether a player owns a game (OwnershipInterface)

public array $lookupBy

the identifiers CatalogueInterface finds a game by

public bool $prices

its records carry a price

public ?string $player

what a player is to it ("a 64-bit Steam ID"), for library, ownership and identity

public ?string $attribution

what its terms ask to be shown beside its records

public array $terms

the terms whoever runs it must follow (URLs)

public bool $keyless

its catalogue needs no account nor key

public bool $hosting

it starts and stops game servers (HostingInterface)

public array $services

the services it can start a server of (Service::$name); any, when empty

public array $regions

the regions its servers run in; any asked, when empty

public ?string $where

where it runs them or whom it asks, for a person to read ("Docker Engine at unix:///var/run/docker.sock")

public bool $identity

it verifies players' proofs from a platform (IdentityInterface)

Capabilities::__construct()

public function __construct(public bool $search = false, public bool $catalogue = false, public bool $library = false, public bool $ownership = false, public array $lookupBy = [], public bool $prices = false, public ?string $player = null, public ?string $attribution = null, public array $terms = [], public bool $keyless = false, public bool $hosting = false, public array $services = [], public array $regions = [], public ?string $where = null, public bool $identity = false)
$search

it searches by title (SearchInterface)

$catalogue

it gives a game's record (CatalogueInterface)

$library

it lists a player's games (LibraryInterface)

$ownership

it says whether a player owns a game (OwnershipInterface)

$lookupBy

the identifiers CatalogueInterface finds a game by

$prices

its records carry a price

$player

what a player is to it ("a 64-bit Steam ID"), for library, ownership and identity

$attribution

what its terms ask to be shown beside its records

$terms

the terms whoever runs it must follow (URLs)

$keyless

its catalogue needs no account nor key

$hosting

it starts and stops game servers (HostingInterface)

$services

the services it can start a server of (Service::$name); any, when empty

$regions

the regions its servers run in; any asked, when empty

$where

where it runs them or whom it asks, for a person to read ("Docker Engine at unix:///var/run/docker.sock")

$identity

it verifies players' proofs from a platform (IdentityInterface)

Capabilities::resolves()

public function resolves(GameRef $ref): bool

Whether CatalogueInterface::game() finds this game by this identifier.

Capabilities::starts()

public function starts(string $service, ?string $region = null): bool

Whether it starts a server of this service, in this region (any, when none is asked).

Capabilities::contracts()

public function contracts(): array
Returns

what it can be asked, by contract: search, catalogue, library, ownership, hosting, identity

Capabilities::toArray()

public function toArray(): array

CatalogueInterface interface

interface CatalogueInterface CatalogueInterface.php:9

CatalogueInterface::game()

public function game(GameRef $ref, ?string $language = null, ?string $country = null): ?Game

The game's whole record, null when the provider does not know it.

$language

the language of the texts, where the store translates

$country

the country whose price is answered (ISO 3166-1 alpha-2), where the store has prices

Throws

NotSupportedException when the provider does not find a game by this identifier (Capabilities::resolves())

Config class

final class Config extends ArrayObject Config.php:14

A provider's configuration while its factory builds it: the options given, the factory's defaults under them, and the keys that must be filled.

Config::defaults()

public function defaults(array $defaults): self

Sets each key not set yet.

Config::validateNotEmpty()

public function validateNotEmpty(array $keys): void

Config::get()

public function get(string $key, mixed $default = null): mixed

Config::string()

public function string(string $key): ?string

A string option; null when it is empty.

Config::int()

public function int(string $key, int $default = 0): int

Config::float()

public function float(string $key, float $default = 0.0): float

Config::bool()

public function bool(string $key): bool

A boolean option, as an environment variable gives it too: "1", "true", "yes", "on".

Fleet class

final class Fleet implements FleetInterface Fleet.php:15

FleetInterface over a store. A server is healthy while it says so and was heard from within $ttl seconds; one starting is given $startup seconds before its first heartbeat.

Fleet::__construct()

public function __construct(readonly StoreInterface $store, readonly int $ttl = 30, readonly int $startup = 120, ?\Closure $now = null)
$ttl

seconds without a heartbeat before a server is stale

$startup

seconds a starting server is given

$now

the clock, in Unix seconds (time() when null)

Fleet::register()

public function register(Server $server): Server

Fleet::heartbeat()

public function heartbeat(string $id, int $players, Health $health = Health::Healthy): ?Server

Fleet::get()

public function get(string $id): ?Server

Fleet::servers()

public function servers(string $service, ?string $region = null): array

Fleet::healthy()

public function healthy(string $service, ?string $region = null): array

Fleet::available()

public function available(string $service, ?string $region = null): array

Fleet::occupy()

public function occupy(string $id): ?Server

Fleet::retireStale()

public function retireStale(): array

Fleet::remove()

public function remove(string $id): void

FleetInterface interface

interface FleetInterface FleetInterface.php:12

The servers the application knows: registered when they start, kept current by their heartbeats, retired when they fall silent.

FleetInterface::register()

public function register(Server $server): Server

Keeps the server, its heartbeat stamped now.

FleetInterface::heartbeat()

public function heartbeat(string $id, int $players, Health $health = Health::Healthy): ?Server

What the server says of itself: its players and its health, stamped now. Null when the fleet does not know it (retired, or registered before a store was lost): it registers again.

FleetInterface::get()

public function get(string $id): ?Server

FleetInterface::servers()

public function servers(string $service, ?string $region = null): array
Returns

every server of the service, in the region when one is named, whatever its health

FleetInterface::healthy()

public function healthy(string $service, ?string $region = null): array
Returns

the servers of the service that are healthy and heard from within the fleet's ttl

FleetInterface::available()

public function available(string $service, ?string $region = null): array
Returns

the servers of the service a player may be seated on now: a seat left, and healthy within the ttl or starting within the startup's

FleetInterface::occupy()

public function occupy(string $id): ?Server

Counts one more player on the server, when it has a seat left (the next heartbeat gives the true count).

FleetInterface::retireStale()

public function retireStale(): array

Removes the servers not heard from within the ttl (a longer one while starting) and the ones that said they are down, and returns them: the application stops those a hosting provider started.

FleetInterface::remove()

public function remove(string $id): void

Heartbeats class

final class Heartbeats Heartbeats.php:27

The application's end of a server's heartbeat, framework-free: the application's route hands it the Authorization header and the decoded JSON body, and answers with the result's status and toArray().

A heartbeat is signed as a ticket is, with its service's secret, for the room "heartbeat:<server id>" (room()): a player's ticket, whose room is the server's id, never passes for one. The body names the server, its service, its players and its health; a server the fleet does not know is registered when the body also gives its address (a server started by hand), and is a 404 otherwise. docs/heartbeat.md is the protocol in full; the providers that run in the game server (omniplay/dotnet, /mirror, /netcode, /godot, /node) send it.

const PREFIX = 'heartbeat:'

Heartbeats::__construct()

public function __construct(readonly FleetInterface $fleet, readonly SecretsInterface $secrets, iterable $services, ?\Closure $now = null)
$services

the services whose servers may beat

$now

the clock, in Unix seconds (time() when null)

Heartbeats::room()

public static function room(string $server): string

The room a server's heartbeat token names.

Heartbeats::token()

public static function token(string $server, string $secret, int $ttl = 60, ?int $now = null): string

A heartbeat token for this server: what a server written in PHP sends; the providers in the game server make the same.

Heartbeats::receive()

public function receive(string $authorization, array $body): HeartbeatResult
$authorization

the request's Authorization header ("Bearer <token>"), or the token alone

$body

the request's JSON body, decoded

HostingInterface interface

interface HostingInterface HostingInterface.php:13

Starts and stops game servers: implemented by providers (a Docker Engine, a cloud's API). What a server runs is the provider's configuration (an image, a command), keyed by the service's name; omniplay knows no engine.

HostingInterface::start()

public function start(Service $service, string $secret, ?string $region = null, array $tags = []): Server

Starts a server of the service, its ticket secret handed to it (its environment, for a container), and returns it Starting: not yet registered in any fleet. The secret is in nothing returned.

$tags

what the server will carry

Throws

Omniplay\Exception\NotSupportedException when it starts no server of this service, or not in this region

HostingInterface::stop()

public function stop(Server $server): void

Stops the server it started (nothing when it is gone already).

HostingInterface::running()

public function running(?string $service = null): array
Returns

the servers it runs, of this service only when one is named: what a fleet is rebuilt from

IdentityInterface interface

interface IdentityInterface IdentityInterface.php:16

Verifies a player's proof from a platform - a Steam session ticket, a store's signed token - with that platform, on the application's side: implemented by providers (omniplay/steam). The answer is the player the platform vouches for, or a refusal that says why; never a guess.

Nothing in omniplay requires one: a SeatRequest names its player by any string, or by a verified Identity when the application has one.

IdentityInterface::platform()

public function platform(): string

The platform whose players it verifies, as Identity::$platform names it: "steam".

..

IdentityInterface::verify()

public function verify(string $proof): Identity

The player the proof names, verified by the platform.

Throws

Omniplay\Exception\IdentityRefusedException when the platform refuses the proof, or it is not one (the message says why)

Throws

Omniplay\Exception\ProviderException when the platform answers with an error, or with no verdict

Throws

Omniplay\Exception\UnreachableException when the platform cannot be reached

LibraryInterface interface

interface LibraryInterface LibraryInterface.php:7

LibraryInterface::library()

public function library(string $player): array

The games a player owns, as far as the store lets them be read (a private profile reads as an empty library on some stores).

$player

what Capabilities::$player says a player is

OwnershipInterface interface

interface OwnershipInterface OwnershipInterface.php:7

OwnershipInterface::owns()

public function owns(string $player, GameRef $game): bool

Whether the player owns the game (bought it, was given a key for it), as the store answers it.

$player

what Capabilities::$player says a player is

ProviderFactory class

abstract class ProviderFactory implements ProviderFactoryInterface ProviderFactory.php:15

A provider's factory fills a Config - its name ("omniplay.factory_name"), its title ("omniplay.factory_title"), the options it needs ("omniplay.required_options") and the defaults of the others - then builds the provider from it.

Nothing here calls anything: a factory whose provider is reached over HTTP takes its client in its own constructor - the application's, a MockHttpClient or a RecordedHttpClient in a test.

ProviderFactory::getName()

public function getName(): string

ProviderFactory::create()

public function create(array $options = []): ProviderInterface

ProviderFactory::createConfig()

public function createConfig(array $options = []): Config

ProviderFactoryInterface interface

interface ProviderFactoryInterface ProviderFactoryInterface.php:6

Builds a provider from its options (a key, a client id, a Docker host, the services it starts...).

ProviderFactoryInterface::getName()

public function getName(): string

The name providers are configured with: "steam", "igdb", "docker".

..

ProviderFactoryInterface::create()

public function create(array $options = []): ProviderInterface

ProviderInterface interface

interface ProviderInterface ProviderInterface.php:16

One provider, configured: a store or catalogue of games (Steam, GOG, IGDB...), a way of hosting game servers (a Docker Engine, Edgegap...), a platform that vouches for its players (Steam). Its Capabilities say what it can be asked - search by title (SearchInterface), a game's store record (CatalogueInterface), a player's games (LibraryInterface), whether a player owns a game (OwnershipInterface), start and stop a server of a service (HostingInterface), verify a player's proof (IdentityInterface) - and what its terms ask of whoever shows its records. Nothing on a store is bought, granted or written.

ProviderInterface::getName()

public function getName(): string

The factory's name: "steam", "igdb", "docker".

..

ProviderInterface::getTitle()

public function getTitle(): string

ProviderInterface::capabilities()

public function capabilities(): Capabilities

Registry class

final class Registry Registry.php:25

The application's providers by name, each built once from its factory and options, whatever contracts each implements:

$registry = new Registry([new SteamFactory($http), new IgdbFactory($http), new DockerFactory()], [ 'steam' => ['factory' => 'steam', 'options' => ['key' => '...', 'appid' => 480]], 'igdb' => ['factory' => 'igdb', 'options' => ['client_id' => '...', 'client_secret' => '...']], 'local' => ['factory' => 'docker', 'options' => ['host' => 'unix:///var/run/docker.sock', 'services' => [...]]], ]);

$registry->catalogue('steam')->game(GameRef::steam(620), 'french', 'FR'); $registry->game(GameRef::gog(1207658924)); // the first provider that finds it $registry->hostingFor('game')?->start($service, $secret); $registry->identityFor('steam')?->verify($ticket);

Registry::__construct()

public function __construct(iterable $factories, readonly array $config)

Registry::get()

public function get(string $name): ProviderInterface

Registry::has()

public function has(string $name): bool
public function search(string $name): SearchInterface

Registry::catalogue()

public function catalogue(string $name): CatalogueInterface

Registry::library()

public function library(string $name): LibraryInterface

Registry::ownership()

public function ownership(string $name): OwnershipInterface

Registry::game()

public function game(GameRef $ref, ?string $language = null, ?string $country = null, string ...$names): ?Game

The game from the first provider, in the configuration's order (or the order named), that finds it by this identifier: a Steam app id read from IGDB asked of Steam's store.

Registry::hosting()

public function hosting(string $name): HostingInterface

Registry::hostingFor()

public function hostingFor(string $service, ?string $region = null, string ...$names): ?HostingInterface

The first provider, in the configuration's order (or the order named), that starts a server of this service in this region.

Registry::identity()

public function identity(string $name): IdentityInterface

Registry::identityFor()

public function identityFor(string $platform, string ...$names): ?IdentityInterface

The first provider, in the configuration's order (or the order named), that verifies the players of this platform; null when none does - the application then names its players itself.

Registry::options()

public function options(string $name): array
Returns

the options a provider is configured with

Registry::names()

public function names(): array
Returns

the configured providers' names

Registry::factories()

public function factories(): array
Returns

the factories installed: what factory: may name

SearchInterface interface

interface SearchInterface SearchInterface.php:8

public function search(Query $query): array

Games whose title matches, best first, as short as the provider's search answers them (ask the catalogue for a whole record).

Seating class

final class Seating implements SeatingInterface Seating.php:24

SeatingInterface over a fleet and tickets. A player goes on the fullest server that still has a seat - a ready one first, then one starting - so that matches fill before new servers are used; the server's tags must hold every tag asked, its region the one asked. When none fits and a hosting provider is given, one is started (its secret read by the service's reference), registered and seated on - when the provider's capabilities say it starts that service in that region. The ticket's room is the server's id, so that a ticket opens that server only; its claims are the request's, the player as "sub".

Seating::__construct()

public function __construct(readonly FleetInterface $fleet, readonly TicketsInterface $tickets, readonly ?HostingInterface $hosting = null, readonly ?SecretsInterface $secrets = null)

Seating::seat()

public function seat(Service $service, SeatRequest $request): ?Seat

SeatingInterface interface

interface SeatingInterface SeatingInterface.php:10

Seats a player on a fitting server of a service, with a ticket to it.

SeatingInterface::seat()

public function seat(Service $service, SeatRequest $request): ?Seat

The seat, or null when no server fits and none could be started.

Tickets class

final class Tickets implements TicketsInterface Tickets.php:16

TicketsInterface over Ticket\TicketSigner, each service's secret read by its reference. The payload is the claims, then "room", then "exp" (now plus the service's ttl).

Tickets::__construct()

public function __construct(readonly SecretsInterface $secrets, ?\Closure $now = null)
$now

the clock, in Unix seconds (time() when null)

Tickets::mint()

public function mint(Service $service, string $room, array $claims = []): Ticket

Tickets::verify()

public function verify(Service $service, string $token, string $room): ?Ticket

TicketsInterface interface

interface TicketsInterface TicketsInterface.php:15

Tickets to a service's servers: minted by the application, checked by the server by itself with the secret they share (Ticket\TicketSigner's format). verify() is the application's side of the same check: a server calling the application back with its players' tickets is what tickets avoid, but an application may read one it handed out.

TicketsInterface::mint()

public function mint(Service $service, string $room, array $claims = []): Ticket

A ticket to this room of the service, living the service's ttl.

$claims

the application's own; "room" and "exp" are the ticket's

TicketsInterface::verify()

public function verify(Service $service, string $token, string $room): ?Ticket

The ticket, when the token holds for this room of the service; null otherwise.

Omniplay\Exception

IdentityRefusedException class

final class IdentityRefusedException extends RuntimeException implements OmniplayException IdentityRefusedException.php:10

A platform refused a player's proof, or the proof is not one: the message says why, as the platform said it ("Steam refused the ticket: Invalid ticket (error 101)."). Never a network failure, never a provider's error.

IdentityRefusedException::refused()

public static function refused(string $platform, string $proof, string $reason): self

InvalidArgumentException class

final class InvalidArgumentException extends InvalidArgumentException implements OmniplayException InvalidArgumentException.php:6

A value nothing can hold: an empty identifier, a search without words, a server of no seat, a payload no ticket can carry.

InvalidConfigException class

final class InvalidConfigException extends InvalidArgumentException implements OmniplayException InvalidConfigException.php:6

A provider asked for with options it cannot work with, or not configured at all; a secret not found.

NotSupportedException class

final class NotSupportedException extends LogicException implements OmniplayException NotSupportedException.php:8

A provider asked for what it does not do: a library from a catalogue, a game by a store it does not know, a server of a service it does not know, a region it does not run in.

NotSupportedException::capability()

public static function capability(object $provider, string $name, string $capability): self

NotSupportedException::ref()

public static function ref(string $provider, GameRef $ref): self

NotSupportedException::service()

public static function service(string $provider, string $service, ?string $region = null): self

OmniplayException interface

interface OmniplayException extends Throwable OmniplayException.php:6

Every exception omniplay throws.

ProviderException class

final class ProviderException extends RuntimeException implements OmniplayException ProviderException.php:10

The provider answered, and not with what was asked: an error status, a body that is not JSON. answered() gives it the HTTP status as its code (410: a server gone already, which a provider may take as stopped).

ProviderException::answered()

public static function answered(string $provider, string $what, int|string $status, string $detail = ''): self

UnreachableException class

final class UnreachableException extends RuntimeException implements OmniplayException UnreachableException.php:6

The provider could not be reached: a network error, a timeout, a socket that is not there.

Omniplay\Http

Data class

final readonly class Data Data.php:10

A decoded JSON answer, read by key without trusting its shape: each reader gives its type or null (an empty list), whatever the provider sent.

A key may be a path: "release_date.date", "NetworkSettings.Ports".

Data::__construct()

public function __construct(array $values)

Data::of()

public static function of(mixed $value): self

Data::get()

public function get(string|int $key): mixed

Data::has()

public function has(string|int $key): bool

Data::string()

public function string(string|int $key): ?string

A string, a number as a string; null when empty or absent.

Data::int()

public function int(string|int $key): ?int

Data::float()

public function float(string|int $key): ?float

Data::bool()

public function bool(string|int $key): ?bool

Data::data()

public function data(string|int $key): self

A nested object (an empty one when absent).

Data::list()

public function list(string|int $key): array

The objects of a list (what is not an object is skipped).

Data::items()

public function items(): array

The objects of this list itself (a provider answering a JSON array).

Data::strings()

public function strings(string|int $key, ?string $field = null): array

The strings of a list, or the field named in each of its objects.

Data::keys()

public function keys(): array

Data::toArray()

public function toArray(): array

Data::date()

public function date(string|int $key, ?string $format = null): ?\DateTimeImmutable

A date ("2011-04-17", "2011-04-17T00:00:00Z", or the format given); null when unreadable.

JsonClient class

final class JsonClient JsonClient.php:22

A provider's JSON calls over the application's HTTP client: the provider's headers on every request, a 404 read as "not known" (null), any other error status as a ProviderException naming the path only (never the query, where a key may sit), a network failure as an UnreachableException naming the path only too, with the HTTP client's reason without the address it quotes (reason()). Its previous exception is a copy of the client's (Redaction::copy(): same class and code, the key masked in its message), and neither keeps its trace's arguments, where the request's query and headers stood. An empty body (Docker's 204 to a container started, 304 to one already stopped) reads as an empty Data.

JsonClient::__construct()

public function __construct(readonly HttpClientInterface $http, readonly string $provider, readonly array $headers = [])
$headers

sent with every request

JsonClient::get()

public function get(string $url, array $query = [], array $headers = []): ?Data
Returns

null when the provider answers 404

JsonClient::request()

public function request(string $method, string $url, array $options = [], array $headers = []): ?Data
$options

json, body or query, as Symfony's HttpClient takes them

Returns

null when the provider answers 404

JsonClient::reason()

public static function reason(string $message): string

An HTTP client's message without the addresses it quotes: Symfony's ' for "<url>"' dropped, any other address cut to its scheme, host and path (no credentials, no query, no fragment).

Redaction class

final class Redaction Redaction.php:22

What a failed call may carry of its credentials, taken out before the failure leaves the package: an HTTP client's exception quotes the URL called (a key in its query, a password in its user part), and every exception's trace keeps the arguments of the calls under it (the request options, headers and query with the key) unless PHP runs with zend.exception_ignore_args.

copy() gives the previous exception to wrap instead of the client's own: the same class and code, its message with the key's parameters, the user part and every secret value replaced by ***, its trace without arguments, its own previous copied the same way. Properties of the client's class beyond those of \Exception are not copied (a Symfony HttpException's response, a PSR-18 exception's request: their URL holds the key). withoutArguments() takes the arguments out of the trace of the exception thrown in its place.

const MASK = '***'

Redaction::secrets()

public static function secrets(string $url, array $options = []): array

The secrets a request carries: the values of its query parameters, headers and body fields named as credentials (key, token, secret, password, signature, auth...), a bearer token, a basic password, the URL's user part.

$options

the HTTP client's options

Redaction::sensitive()

public static function sensitive(string $name): bool

Whether a parameter, header or field of this name holds a credential.

Redaction::text()

public static function text(string $text, array $secrets = []): string

A text without credentials: in every address, the user part and the parameters named as credentials masked (api_key=***); then every secret given, raw or URL-encoded, masked wherever it stands.

Redaction::copy()

public static function copy(\Throwable $failure, array $secrets = []): \Throwable

A copy of a failure to wrap in its place: its class, code, file and line, its message through text(), its trace without arguments, its previous copied the same way.

Redaction::withoutArguments()

public static function withoutArguments(\Throwable $failure): \Throwable

The failure given, the arguments out of its trace (and out of its previous ones'), as zend.exception_ignore_args would have left it.

Omniplay\Model

Game class

final readonly class Game Game.php:11

A game as one store or catalogue knows it: its page, texts, dates, studios, platforms, price, pictures, scores and its identifiers elsewhere. What the provider does not give is empty or null, never guessed: a search answer is usually shorter than a record.

public string $title
public Identifiers $identifiers
public string $source

the provider's name ("steam", "igdb"...)

public ?string $url

the game's page at the store or catalogue

public ?string $summary
public ?\DateTimeImmutable $released
public bool $comingSoon

not released yet, as the store says

public array $developers
public array $publishers
public array $genres

the provider's words

public array $tags

the provider's words

public array $platforms
public ?Price $price
public array $images
public array $ratings

Game::__construct()

public function __construct(public string $title, public Identifiers $identifiers, public string $source, public ?string $url = null, public ?string $summary = null, public ?\DateTimeImmutable $released = null, public bool $comingSoon = false, public array $developers = [], public array $publishers = [], public array $genres = [], public array $tags = [], public array $platforms = [], public ?Price $price = null, public array $images = [], public array $ratings = [])
$source

the provider's name ("steam", "igdb"...)

$url

the game's page at the store or catalogue

$comingSoon

not released yet, as the store says

$genres

the provider's words

$tags

the provider's words

Game::ref()

public function ref(Scheme $scheme): ?GameRef

Game::image()

public function image(ImageKind $kind = ImageKind::Cover): ?Image

Game::runsOn()

public function runsOn(Platform $platform): bool

Game::toArray()

public function toArray(): array

GameRef class

final readonly class GameRef implements Stringable GameRef.php:8

A game named by one identifier: GameRef::steam(620), GameRef::itch('leafo/x-moon').

public string $id
public Scheme $scheme

GameRef::__construct()

public function __construct(public Scheme $scheme, string|int $id)

GameRef::steam()

public static function steam(string|int $id): self

GameRef::gog()

public static function gog(string|int $id): self

GameRef::itch()

public static function itch(string $page): self

GameRef::igdb()

public static function igdb(string|int $id): self

GameRef::rawg()

public static function rawg(string|int $id): self

GameRef::__toString()

public function __toString(): string

"steam:620"

Health enum

enum Health Health.php:6

What a server says of itself in its heartbeats.

case Starting = 'starting'

Started, not yet ready: seated only when no ready server has room (it was started for that).

case Healthy = 'healthy'

Ready, taking players.

case Draining = 'draining'

Finishing its matches: seats nobody new.

case Down = 'down'

Stopping or stopped: seats nobody.

Health::seats()

public function seats(): bool

Whether a player may be seated on a server in this state.

HeartbeatResult class

final readonly class HeartbeatResult HeartbeatResult.php:10

What Heartbeats::receive() made of a server's heartbeat: the HTTP status the application answers with, why, and the server as the fleet now holds it (null when refused).

public int $status
public string $reason
public ?Server $server

HeartbeatResult::__construct()

public function __construct(public int $status, public string $reason, public ?Server $server = null)

HeartbeatResult::accepted()

public function accepted(): bool

HeartbeatResult::toArray()

public function toArray(): array
Returns

the body to answer with: nothing secret

Identifiers class

final readonly class Identifiers Identifiers.php:12

The identifiers of one game in the stores and catalogues that know it, keyed by Scheme value: what lets a store page be found from a catalogue's record (IGDB and RAWG know a game's Steam and GOG numbers).

Identifiers::of()

public static function of(array $ids): self

Empty values are dropped; keys are Scheme values, or a register of the application's own (lower case letters, digits, "-" and "_").

Identifiers::none()

public static function none(): self

Identifiers::get()

public function get(Scheme|string $scheme): ?string

Identifiers::has()

public function has(Scheme|string $scheme): bool

Identifiers::with()

public function with(Scheme|string $scheme, string|int|null $id): self

Identifiers::merge()

public function merge(self $other): self

These identifiers, completed by the other's where these have none.

Identifiers::isEmpty()

public function isEmpty(): bool

Identifiers::toArray()

public function toArray(): array

Identity class

final readonly class Identity implements Stringable Identity.php:13

A player as a platform verified them: the platform ("steam"), the player's id there (a SteamID), and what the platform said besides (the game's owner when it is lent, a ban). player() - "steam:76561197960287930" - is what a SeatRequest takes as the player's id, and a ticket's "sub".

public string $platform
public string $id
public array $claims

what the platform said besides the id

Identity::__construct()

public function __construct(string $platform, string $id, public array $claims = [])
$claims

what the platform said besides the id

Identity::player()

public function player(): string

The player's id across platforms: "<platform>:<id>".

Identity::__toString()

public function __toString(): string

Identity::toArray()

public function toArray(): array

Image class

final readonly class Image Image.php:6

A picture of a game, where its store serves it, under the store's terms and the publisher's rights.

public ImageKind $kind
public string $url
public ?int $width
public ?int $height

Image::__construct()

public function __construct(public ImageKind $kind, public string $url, public ?int $width = null, public ?int $height = null)

Image::toArray()

public function toArray(): array

ImageKind enum

enum ImageKind ImageKind.php:5

case Cover = 'cover'

The box art, portrait.

case Header = 'header'

The store's wide banner (Steam's header, a capsule).

case Screenshot = 'screenshot'
case Background = 'background'
case Icon = 'icon'

OwnedGame class

final readonly class OwnedGame OwnedGame.php:6

A game in a player's library: which, and how long it was played.

public Identifiers $identifiers
public string $title
public ?int $playtime

minutes

public ?\DateTimeImmutable $lastPlayed

OwnedGame::__construct()

public function __construct(public Identifiers $identifiers, public string $title, public ?int $playtime = null, public ?\DateTimeImmutable $lastPlayed = null)
$playtime

minutes

OwnedGame::toArray()

public function toArray(): array

Platform enum

enum Platform Platform.php:6

The families of platforms a game runs on; a store's finer names are folded into them.

case Windows = 'windows'
case Mac = 'mac'
case Linux = 'linux'
case Android = 'android'
case Ios = 'ios'
case Web = 'web'
case Switch = 'switch'
case PlayStation = 'playstation'
case Xbox = 'xbox'

Platform::fromLabel()

public static function fromLabel(string $label): ?self

"PC (Microsoft Windows)", "osx", "PlayStation 5", "Nintendo Switch 2", "HTML5"... null when none of these.

Platform::fromLabels()

public static function fromLabels(iterable $labels): array
Returns

each family once, in the order first met

Price class

final readonly class Price Price.php:6

A price in one country, in minor units: 969 EUR is 9.69 €.

public int $amount
public string $currency
public ?int $initial

the price before the discount, when there is one

public int $discount

Price::__construct()

public function __construct(public int $amount, public string $currency, public ?int $initial = null, public int $discount = 0)
$initial

the price before the discount, when there is one

Price::isFree()

public function isFree(): bool

Price::toArray()

public function toArray(): array

Query class

final readonly class Query Query.php:8

A search by title: the words, and what narrows them.

public string $text
public ?string $language

the language of the texts answered, where the store translates ("fr", "french")

public ?string $country

the country whose prices are answered (ISO 3166-1 alpha-2), where the store has them

public int $limit

at most this many games (1 to 50)

Query::__construct()

public function __construct(string $text, public ?string $language = null, public ?string $country = null, public int $limit = 10)
$language

the language of the texts answered, where the store translates ("fr", "french")

$country

the country whose prices are answered (ISO 3166-1 alpha-2), where the store has them

$limit

at most this many games (1 to 50)

Rating class

final readonly class Rating Rating.php:6

A score given somewhere: Metacritic 95/100, IGDB 91/100 from 1,200 ratings.

public string $source
public float $value
public float $scale
public ?int $votes

Rating::__construct()

public function __construct(public string $source, public float $value, public float $scale = 100.0, public ?int $votes = null)

Rating::toArray()

public function toArray(): array

Scheme enum

enum Scheme Scheme.php:6

The stores and catalogues a game is known by.

case Steam = 'steam'

Steam: the app id, "620".

case Gog = 'gog'

GOG: the product id, "1207658924".

case Itch = 'itch'

itch.io: the game's page, "leafo/x-moon" (its number, "3", is what its API answers).

case Epic = 'epic'

Epic Games Store: its product slug or namespace, as catalogues give it.

case Igdb = 'igdb'

IGDB: a number.

case Rawg = 'rawg'

RAWG: a number (its slug is accepted where RAWG takes it).

Seat class

final readonly class Seat Seat.php:10

A player's seat: the server, the ticket it lets them in with, and whether the server was started for them (then it may still be booting: the client retries the address until the ticket expires).

public string $player
public Server $server
public Ticket $ticket
public bool $started

Seat::__construct()

public function __construct(public string $player, public Server $server, public Ticket $ticket, public bool $started = false)

Seat::toArray()

public function toArray(): array

What the application hands the player's client: where to connect, the ticket and its expiry. Nothing secret.

SeatRequest class

final readonly class SeatRequest SeatRequest.php:13

A player asking for a seat: who, where, on what kind of server, and what their ticket says besides. The player is the application's string, or an Identity a platform verified (IdentityInterface): then the player's id is Identity::player() ("steam:76561197960287930") and $identity keeps it.

public string $player
public ?Identity $identity

The platform's verified player, when the request was made with one.

public ?string $region

where the server should run; any, when null

public array $tags

what the server must carry (every one)

public array $claims

what the ticket says besides its room and expiry (the player is "sub")

SeatRequest::__construct()

public function __construct(string|Identity $player, public ?string $region = null, public array $tags = [], public array $claims = [])
$player

the player's id, or the Identity a platform verified

$region

where the server should run; any, when null

$tags

what the server must carry (every one)

$claims

what the ticket says besides its room and expiry (the player is "sub")

Server class

final readonly class Server Server.php:17

A game server of a service, as the fleet knows it: where players reach it, how many it seats and holds, what it last said of itself and when.

Its id is the room its tickets name. A server started by a hosting provider carries that provider's name and its handle there (a container's id), so that it can be stopped.

The engine is declared, as the Service's: stored and returned, never read by omniplay.

public string $id
public string $service
public string $address

where players reach it, as their client takes it ("203.0.113.7:7777", "wss://eu1.example.org/play")

public int $capacity
public int $players
public Health $health
public ?\DateTimeImmutable $heartbeat
public ?string $region
public array $tags

what a seat may ask of it ("ranked", "map:harbour", "v1.4")

public ?string $engine
public ?string $provider

the hosting provider that started it, by its configured name

public ?string $instance

its handle there (a container's id)

Server::__construct()

public function __construct(string $id, public string $service, public string $address, public int $capacity, public int $players = 0, public Health $health = Health::Starting, public ?\DateTimeImmutable $heartbeat = null, public ?string $region = null, public array $tags = [], public ?string $engine = null, public ?string $provider = null, public ?string $instance = null)
$address

where players reach it, as their client takes it ("203.0.113.7:7777", "wss://eu1.example.org/play")

$tags

what a seat may ask of it ("ranked", "map:harbour", "v1.4")

$provider

the hosting provider that started it, by its configured name

$instance

its handle there (a container's id)

Server::free()

public function free(): int

The seats left.

Server::has()

public function has(string ...$tags): bool

Whether it carries every tag asked.

Server::silence()

public function silence(\DateTimeImmutable $now): int

Seconds since its last heartbeat (since ever: PHP_INT_MAX, when it never sent one).

Server::withHeartbeat()

public function withHeartbeat(int $players, Health $health, \DateTimeImmutable $at): self

Server::withPlayers()

public function withPlayers(int $players): self

Server::toArray()

public function toArray(): array

Server::fromArray()

public static function fromArray(array $values): self

A server as toArray() gave it: what an application's store reads back.

Service class

final readonly class Service Service.php:18

A named realtime service the application seats players on: a game's servers, a relay. Its secret is a reference (an environment variable's name, a vault's key), read through a SecretsInterface when a ticket is signed or a server started: the secret itself is never a field of anything omniplay returns.

The engine is declared, never detected: a free string the application sets ("unity-mirror", "unity-netcode", "godot", "node", "custom"...), stored and returned as it is. Nothing in omniplay branches on it.

public string $name
public ?string $url

the public address where its tickets are presented, when the service has one (a relay); a server's own address otherwise

public string $secret

the reference of the secret its servers check tickets with

public int $ttl

a ticket's life, in seconds

public int $capacity

the players a server of it takes, unless the server says otherwise

public ?string $engine

what its servers run, as the application names it ("unity-mirror", "godot"...)

Service::__construct()

public function __construct(string $name, public ?string $url = null, public string $secret = 'OMNIPLAY_TICKET_SECRET', public int $ttl = 60, public int $capacity = 16, public ?string $engine = null)
$url

the public address where its tickets are presented, when the service has one (a relay); a server's own address otherwise

$secret

the reference of the secret its servers check tickets with

$ttl

a ticket's life, in seconds

$capacity

the players a server of it takes, unless the server says otherwise

$engine

what its servers run, as the application names it ("unity-mirror", "godot"...)

Service::fromArray()

public static function fromArray(string $name, array $values): self
$values

as toArray() gives them, or a configuration's

Service::toArray()

public function toArray(): array
Returns

the secret's reference, not the secret

Ticket class

final readonly class Ticket implements Stringable Ticket.php:10

A signed ticket and what it says: the room it opens, when it expires, the application's own claims. The token is what the player presents; the server reads the rest from it by itself.

public string $token
public string $room
public \DateTimeImmutable $expires
public array $claims

the application's own, room and exp left out

Ticket::__construct()

public function __construct(public string $token, public string $room, public \DateTimeImmutable $expires, public array $claims = [])
$claims

the application's own, room and exp left out

Ticket::__toString()

public function __toString(): string

Ticket::toArray()

public function toArray(): array

Omniplay\Secret

EnvironmentSecrets class

final class EnvironmentSecrets implements SecretsInterface EnvironmentSecrets.php:8

Secrets read from the environment: the reference is the variable's name ($_ENV, $_SERVER, then getenv()).

EnvironmentSecrets::secret()

public function secret(string $reference): string

FixedSecrets class

final class FixedSecrets implements SecretsInterface FixedSecrets.php:8

Secrets given by reference: a test's, or an application's that read them itself.

FixedSecrets::__construct()

public function __construct(readonly array $secrets)
$secrets

by reference

FixedSecrets::secret()

public function secret(string $reference): string

SecretsInterface interface

interface SecretsInterface SecretsInterface.php:10

A secret by its reference: the application's environment, a vault, a fixed list in a test. A Service names its secret by reference so that the secret is never a field of anything stored or returned.

SecretsInterface::secret()

public function secret(string $reference): string
Throws

Omniplay\Exception\InvalidConfigException when there is no such secret, or it is empty

Omniplay\Storage

InMemoryStore class

final class InMemoryStore implements StoreInterface InMemoryStore.php:8

The servers in an array: a test's store, or a single long-running process's. Gone with the process.

InMemoryStore::save()

public function save(Server $server): void

InMemoryStore::find()

public function find(string $id): ?Server

InMemoryStore::all()

public function all(?string $service = null): array

InMemoryStore::delete()

public function delete(string $id): void

StoreInterface interface

interface StoreInterface StoreInterface.php:12

Where the fleet keeps its servers: InMemoryStore for tests and a single process, the application's own otherwise (a table, a Redis hash: a Server goes in as toArray() and comes back through Server::fromArray()).

StoreInterface::save()

public function save(Server $server): void

Keeps the server, replacing the one of the same id.

StoreInterface::find()

public function find(string $id): ?Server

StoreInterface::all()

public function all(?string $service = null): array
Returns

every server kept, of this service only when one is named

StoreInterface::delete()

public function delete(string $id): void

Omniplay\Testing

BareTestCase class

abstract class BareTestCase extends TestCase BareTestCase.php:20

The check every package of the family runs: each of its classes loaded in a PHP process of its own (this one has loaded PHPUnit), with nothing from outside but omnisense/omniplay and what the package requires - no framework, no HTTP client, no application, even in the harness where they are installed beside it. A package names its entry class and the Composer packages it requires; FOREIGN lists what may never load, allowedNamespaces() the exceptions a package's requirements need.

BareTestCase::testEveryClassLoadsWithTheRequiredPackagesAlone()

public function testEveryClassLoadsWithTheRequiredPackagesAlone(): void

BareTestCase::testTheReportSeesWhatIsLoaded()

public function testTheReportSeesWhatIsLoaded(): void

The check is not blind: a foreign class, once loaded, is seen.

FixedHosting class

final class FixedHosting implements ProviderInterface, HostingInterface FixedHosting.php:19

A hosting provider that starts nothing: each server it is asked for is given the next port on its host ("127.0.0.1:7001", ":7002"...), Starting, and kept in $started with the secret it was handed; stop() moves it to $stopped. An application's tests, the harness's bare script.

public array $started = []
public array $stopped = []
public array $secrets = []

FixedHosting::__construct()

public function __construct(readonly array $services = [], readonly array $regions = ['local'], readonly string $host = '127.0.0.1', readonly int $port = 7000, readonly string $name = 'fixed-hosting')
$services

the services it starts (every one, when empty)

FixedHosting::getName()

public function getName(): string

FixedHosting::getTitle()

public function getTitle(): string

FixedHosting::capabilities()

public function capabilities(): Capabilities

FixedHosting::start()

public function start(Service $service, string $secret, ?string $region = null, array $tags = []): Server

FixedHosting::stop()

public function stop(Server $server): void

FixedHosting::running()

public function running(?string $service = null): array

FixedHostingFactory class

final class FixedHostingFactory extends ProviderFactory FixedHostingFactory.php:10

FixedHosting's factory: "fixed-hosting", options services, regions, host, port.

Options

OptionDefault
services[]
regions['local']
host'127.0.0.1'
port7000

FixedIdentity class

final class FixedIdentity implements ProviderInterface, IdentityInterface FixedIdentity.php:16

A platform that vouches for the players it was given: each proof known answers its player's id, any other is refused. Every proof asked is kept in $asked. An application's tests.

public array $asked = []

FixedIdentity::__construct()

public function __construct(readonly array $players, readonly string $platform = 'test', readonly string $name = 'fixed-identity')
$players

the player's id by proof

FixedIdentity::getName()

public function getName(): string

FixedIdentity::getTitle()

public function getTitle(): string

FixedIdentity::capabilities()

public function capabilities(): Capabilities

FixedIdentity::platform()

public function platform(): string

FixedIdentity::verify()

public function verify(string $proof): Identity

FixedIdentityFactory class

final class FixedIdentityFactory extends ProviderFactory FixedIdentityFactory.php:10

FixedIdentity's factory: "fixed-identity", options players (the player's id by proof), platform ("test").

Options

OptionDefault
players[]
platform'test'

FixedProvider class

final class FixedProvider implements ProviderInterface, SearchInterface, CatalogueInterface, LibraryInterface, OwnershipInterface FixedProvider.php:24

A provider that answers from the games and libraries it was given, with no network: an application's tests, the harness's bare script. It finds a game by any identifier the game carries, searches titles by their words, and owns what a player's library holds.

FixedProvider::__construct()

public function __construct(readonly array $games = [], readonly array $libraries = [], readonly string $name = 'fixed')
$libraries

by player

FixedProvider::getName()

public function getName(): string

FixedProvider::getTitle()

public function getTitle(): string

FixedProvider::capabilities()

public function capabilities(): Capabilities
public function search(Query $query): array

FixedProvider::game()

public function game(GameRef $ref, ?string $language = null, ?string $country = null): ?Game

FixedProvider::library()

public function library(string $player): array

FixedProvider::owns()

public function owns(string $player, GameRef $game): bool

FixedProviderFactory class

final class FixedProviderFactory extends ProviderFactory FixedProviderFactory.php:12

FixedProvider's factory: "fixed", answering from the games and libraries it holds.

FixedProviderFactory::__construct()

public function __construct(readonly array $games = [], readonly array $libraries = [])

RecordedHttpClient class

final class RecordedHttpClient implements HttpClientInterface RecordedHttpClient.php:15

An HTTP client that answers from what it was given - a callable of the method, the URL and the options, returning [status, body, headers] - and keeps every request made ($requests): a provider's calls checked with no network.

public array $requests = []

RecordedHttpClient::__construct()

public function __construct(readonly \Closure $answer)

RecordedHttpClient::routes()

public static function routes(array $routes): self

Answers by route: the first pattern (a regular expression matched against "METHOD url", the query included) that matches gives the status and the body; a body naming a file ("@/path/to/answer.json") is read from it. Nothing matched: 404.

RecordedHttpClient::request()

public function request(string $method, string $url, array $options = []): ResponseInterface

RecordedHttpClient::stream()

public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterface

RecordedHttpClient::withOptions()

public function withOptions(array $options): static

RecordedHttpClient::header()

public function header(string $name, int $request = -1): ?string

The last request's header, as sent.

RecordedResponse class

final class RecordedResponse implements ResponseInterface RecordedResponse.php:8

RecordedHttpClient's answer.

RecordedResponse::__construct()

public function __construct(readonly int $status, readonly string $body, readonly array $headers, readonly string $url)

RecordedResponse::getStatusCode()

public function getStatusCode(): int

RecordedResponse::getHeaders()

public function getHeaders(bool $throw = true): array

RecordedResponse::getContent()

public function getContent(bool $throw = true): string

RecordedResponse::toArray()

public function toArray(bool $throw = true): array

RecordedResponse::cancel()

public function cancel(): void

RecordedResponse::getInfo()

public function getInfo(?string $type = null): mixed

Omniplay\Ticket

TicketSigner class

final class TicketSigner TicketSigner.php:20

The tickets' one format: base64url(json payload) "." hex(hmac_sha256(that base64url, secret)). The payload names its room and when it expires (room, a string; exp, Unix seconds); the rest is the application's.

A server checks a ticket by itself with the secret it shares with the application, never calling it back. docs/tickets.md is the format in full, Tests/vectors.json the vectors every verifier is checked against.

The JSON is PHP's json_encode() with its default flags (slashes and every character past ASCII escaped): the signature is over the base64url text as it stands, so a verifier never encodes anything again, whatever its language. Pure: no state, no framework.

TicketSigner::sign()

public static function sign(array $payload, string $secret): string

A ticket for this payload. It names its room (a non-empty string) and its expiry (an integer): a ticket without them would never be valid.

TicketSigner::signJson()

public static function signJson(string $json, string $secret): string

A ticket over this JSON text as it is: what a test of another language's verifier, or a payload encoded elsewhere, signs.

TicketSigner::verify()

public static function verify(string $token, string $secret, string $room, ?int $now = null): ?array

A ticket's payload when its signature holds, it is for this room and it has not expired (exp before $now, a Unix time, the clock's when null); null otherwise. An empty secret verifies nothing; room must be a string equal to the one asked; exp a number, its integer part compared (an expiry equal to now still holds).