Lue dans les fichiers PHP du paquet au commit f88e96826904 : ses espaces de noms, classes, interfaces et énumérations, leurs constantes, propriétés et méthodes publiques avec leur PHPDoc, et les options que prennent ses fabriques. Rien n’en est exécuté.
CapabilitiesclassWhat 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
CatalogueInterfaceinterface1 method
ConfigclassA 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
FleetclassFleetInterface 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
FleetInterfaceinterfaceThe servers the application knows: registered when they start, kept current by their heartbeats, retired when they fall silent.
9 methods
HeartbeatsclassThe 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
HostingInterfaceinterfaceStarts 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
IdentityInterfaceinterfaceVerifies 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
LibraryInterfaceinterface1 method
OwnershipInterfaceinterface1 method
ProviderFactoryclassA 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
ProviderFactoryInterfaceinterfaceBuilds a provider from its options (a key, a client id, a Docker host, the services it starts...).
2 methods
ProviderInterfaceinterfaceOne 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
RegistryclassThe application's providers by name, each built once from its factory and options, whatever contracts each implements:
15 methods
SearchInterfaceinterface1 method
SeatingclassSeatingInterface 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
SeatingInterfaceinterfaceSeats a player on a fitting server of a service, with a ticket to it.
1 method
TicketsclassTicketsInterface 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
TicketsInterfaceinterfaceTickets 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
IdentityRefusedExceptionclassA 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
InvalidArgumentExceptionclassA value nothing can hold: an empty identifier, a search without words, a server of no seat, a payload no ticket can carry.
InvalidConfigExceptionclassA provider asked for with options it cannot work with, or not configured at all; a secret not found.
NotSupportedExceptionclassA 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
OmniplayExceptioninterfaceEvery exception omniplay throws.
ProviderExceptionclassThe 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
UnreachableExceptionclassThe provider could not be reached: a network error, a timeout, a socket that is not there.
DataclassA 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
JsonClientclassA 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
RedactionclassWhat 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
GameclassA 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
GameRefclassA game named by one identifier: GameRef::steam(620), GameRef::itch('leafo/x-moon').
7 methods · 2 properties
HealthenumWhat a server says of itself in its heartbeats.
1 method · 4 cases
HeartbeatResultclassWhat 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
IdentifiersclassThe 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
IdentityclassA 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
ImageclassA picture of a game, where its store serves it, under the store's terms and the publisher's rights.
2 methods · 4 properties
ImageKindenum6 cases
OwnedGameclassA game in a player's library: which, and how long it was played.
2 methods · 4 properties
PlatformenumThe families of platforms a game runs on; a store's finer names are folded into them.
2 methods · 9 cases
PriceclassA price in one country, in minor units: 969 EUR is 9.69 €.
3 methods · 4 properties
QueryclassA search by title: the words, and what narrows them.
1 method · 4 properties
RatingclassA score given somewhere: Metacritic 95/100, IGDB 91/100 from 1,200 ratings.
2 methods · 4 properties
SchemeenumThe stores and catalogues a game is known by.
6 cases
SeatclassA 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
SeatRequestclassA 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
ServerclassA 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
ServiceclassA 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
TicketclassA 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
EnvironmentSecretsclassSecrets read from the environment: the reference is the variable's name ($_ENV, $_SERVER, then getenv()).
1 method
FixedSecretsclassSecrets given by reference: a test's, or an application's that read them itself.
2 methods
SecretsInterfaceinterfaceA 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
InMemoryStoreclassThe servers in an array: a test's store, or a single long-running process's. Gone with the process.
4 methods
StoreInterfaceinterfaceWhere 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
BareTestCaseclassThe 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
FixedHostingclassA 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
FixedHostingFactoryclassFixedHosting's factory: "fixed-hosting", options services, regions, host, port.
4 options
FixedIdentityclassA 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
FixedIdentityFactoryclassFixedIdentity's factory: "fixed-identity", options players (the player's id by proof), platform ("test").
2 options
FixedProviderclassA 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
FixedProviderFactoryclassFixedProvider's factory: "fixed", answering from the games and libraries it holds.
1 method
RecordedHttpClientclassAn 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
RecordedResponseclassRecordedHttpClient's answer.
7 methods
TicketSignerclassThe 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.
public bool $searchit searches by title (SearchInterface)
public bool $catalogueit gives a game's record (CatalogueInterface)
public bool $libraryit lists a player's games (LibraryInterface)
public bool $ownershipit says whether a player owns a game (OwnershipInterface)
public array $lookupBythe identifiers CatalogueInterface finds a game by
public bool $pricesits records carry a price
public ?string $playerwhat a player is to it ("a 64-bit Steam ID"), for library, ownership and identity
public ?string $attributionwhat its terms ask to be shown beside its records
public array $termsthe terms whoever runs it must follow (URLs)
public bool $keylessits catalogue needs no account nor key
public bool $hostingit starts and stops game servers (HostingInterface)
public array $servicesthe services it can start a server of (Service::$name); any, when empty
public array $regionsthe regions its servers run in; any asked, when empty
public ?string $wherewhere it runs them or whom it asks, for a person to read ("Docker Engine at unix:///var/run/docker.sock")
public bool $identityit 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)$searchit searches by title (SearchInterface)
$catalogueit gives a game's record (CatalogueInterface)
$libraryit lists a player's games (LibraryInterface)
$ownershipit says whether a player owns a game (OwnershipInterface)
$lookupBythe identifiers CatalogueInterface finds a game by
$pricesits records carry a price
$playerwhat a player is to it ("a 64-bit Steam ID"), for library, ownership and identity
$attributionwhat its terms ask to be shown beside its records
$termsthe terms whoever runs it must follow (URLs)
$keylessits catalogue needs no account nor key
$hostingit starts and stops game servers (HostingInterface)
$servicesthe services it can start a server of (Service::$name); any, when empty
$regionsthe regions its servers run in; any asked, when empty
$wherewhere it runs them or whom it asks, for a person to read ("Docker Engine at unix:///var/run/docker.sock")
$identityit verifies players' proofs from a platform (IdentityInterface)
Capabilities::resolves()
public function resolves(GameRef $ref): boolWhether CatalogueInterface::game() finds this game by this identifier.
Capabilities::starts()
public function starts(string $service, ?string $region = null): boolWhether 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(): arrayCatalogueInterface interface
interface CatalogueInterface CatalogueInterface.php:9
CatalogueInterface::game()
public function game(GameRef $ref, ?string $language = null, ?string $country = null): ?GameThe game's whole record, null when the provider does not know it.
$languagethe language of the texts, where the store translates
$countrythe 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): selfSets each key not set yet.
Config::validateNotEmpty()
public function validateNotEmpty(array $keys): voidConfig::get()
public function get(string $key, mixed $default = null): mixedConfig::string()
public function string(string $key): ?stringA string option; null when it is empty.
Config::int()
public function int(string $key, int $default = 0): intConfig::float()
public function float(string $key, float $default = 0.0): floatConfig::bool()
public function bool(string $key): boolA 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)$ttlseconds without a heartbeat before a server is stale
$startupseconds a starting server is given
$nowthe clock, in Unix seconds (time() when null)
Fleet::register()
public function register(Server $server): ServerFleet::heartbeat()
public function heartbeat(string $id, int $players, Health $health = Health::Healthy): ?ServerFleet::get()
public function get(string $id): ?ServerFleet::servers()
public function servers(string $service, ?string $region = null): arrayFleet::healthy()
public function healthy(string $service, ?string $region = null): arrayFleet::available()
public function available(string $service, ?string $region = null): arrayFleet::occupy()
public function occupy(string $id): ?ServerFleet::retireStale()
public function retireStale(): arrayFleet::remove()
public function remove(string $id): voidFleetInterface 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): ServerKeeps the server, its heartbeat stamped now.
FleetInterface::heartbeat()
public function heartbeat(string $id, int $players, Health $health = Health::Healthy): ?ServerWhat 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): ?ServerFleetInterface::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): ?ServerCounts one more player on the server, when it has a seat left (the next heartbeat gives the true count).
FleetInterface::retireStale()
public function retireStale(): arrayRemoves 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): voidHeartbeats 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)$servicesthe services whose servers may beat
$nowthe clock, in Unix seconds (time() when null)
Heartbeats::room()
public static function room(string $server): stringThe room a server's heartbeat token names.
Heartbeats::token()
public static function token(string $server, string $secret, int $ttl = 60, ?int $now = null): stringA 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$authorizationthe request's Authorization header ("Bearer <token>"), or the token alone
$bodythe 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 = []): ServerStarts 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.
$tagswhat 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): voidStops 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(): stringThe platform whose players it verifies, as Identity::$platform names it: "steam".
..
IdentityInterface::verify()
public function verify(string $proof): IdentityThe 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): arrayThe games a player owns, as far as the store lets them be read (a private profile reads as an empty library on some stores).
$playerwhat Capabilities::$player says a player is
OwnershipInterface interface
interface OwnershipInterface OwnershipInterface.php:7
OwnershipInterface::owns()
public function owns(string $player, GameRef $game): boolWhether the player owns the game (bought it, was given a key for it), as the store answers it.
$playerwhat 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(): stringProviderFactory::create()
public function create(array $options = []): ProviderInterfaceProviderFactory::createConfig()
public function createConfig(array $options = []): ConfigProviderFactoryInterface 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(): stringThe name providers are configured with: "steam", "igdb", "docker".
..
ProviderFactoryInterface::create()
public function create(array $options = []): ProviderInterfaceProviderInterface 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(): stringThe factory's name: "steam", "igdb", "docker".
..
ProviderInterface::getTitle()
public function getTitle(): stringProviderInterface::capabilities()
public function capabilities(): CapabilitiesRegistry 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): ProviderInterfaceRegistry::has()
public function has(string $name): boolRegistry::search()
public function search(string $name): SearchInterfaceRegistry::catalogue()
public function catalogue(string $name): CatalogueInterfaceRegistry::library()
public function library(string $name): LibraryInterfaceRegistry::ownership()
public function ownership(string $name): OwnershipInterfaceRegistry::game()
public function game(GameRef $ref, ?string $language = null, ?string $country = null, string ...$names): ?GameThe 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): HostingInterfaceRegistry::hostingFor()
public function hostingFor(string $service, ?string $region = null, string ...$names): ?HostingInterfaceThe 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): IdentityInterfaceRegistry::identityFor()
public function identityFor(string $platform, string ...$names): ?IdentityInterfaceThe 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
SearchInterface::search()
public function search(Query $query): arrayGames 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): ?SeatSeatingInterface 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): ?SeatThe 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)$nowthe clock, in Unix seconds (time() when null)
Tickets::mint()
public function mint(Service $service, string $room, array $claims = []): TicketTickets::verify()
public function verify(Service $service, string $token, string $room): ?TicketTicketsInterface 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 = []): TicketA ticket to this room of the service, living the service's ttl.
$claimsthe application's own; "room" and "exp" are the ticket's
TicketsInterface::verify()
public function verify(Service $service, string $token, string $room): ?TicketThe 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): selfInvalidArgumentException 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): selfNotSupportedException::ref()
public static function ref(string $provider, GameRef $ref): selfNotSupportedException::service()
public static function service(string $provider, string $service, ?string $region = null): selfOmniplayException 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 = ''): selfUnreachableException 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): selfData::get()
public function get(string|int $key): mixedData::has()
public function has(string|int $key): boolData::string()
public function string(string|int $key): ?stringA string, a number as a string; null when empty or absent.
Data::int()
public function int(string|int $key): ?intData::float()
public function float(string|int $key): ?floatData::bool()
public function bool(string|int $key): ?boolData::data()
public function data(string|int $key): selfA nested object (an empty one when absent).
Data::list()
public function list(string|int $key): arrayThe objects of a list (what is not an object is skipped).
Data::items()
public function items(): arrayThe objects of this list itself (a provider answering a JSON array).
Data::strings()
public function strings(string|int $key, ?string $field = null): arrayThe strings of a list, or the field named in each of its objects.
Data::keys()
public function keys(): arrayData::toArray()
public function toArray(): arrayData::date()
public function date(string|int $key, ?string $format = null): ?\DateTimeImmutableA 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 = [])$headerssent 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$optionsjson, body or query, as Symfony's HttpClient takes them
- Returns
null when the provider answers 404
JsonClient::reason()
public static function reason(string $message): stringAn 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 = []): arrayThe 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.
$optionsthe HTTP client's options
Redaction::sensitive()
public static function sensitive(string $name): boolWhether a parameter, header or field of this name holds a credential.
Redaction::text()
public static function text(string $text, array $secrets = []): stringA 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 = []): \ThrowableA 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): \ThrowableThe 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 $titlepublic Identifiers $identifierspublic string $sourcethe provider's name ("steam", "igdb"...)
public ?string $urlthe game's page at the store or catalogue
public ?string $summarypublic ?\DateTimeImmutable $releasedpublic bool $comingSoonnot released yet, as the store says
public array $developerspublic array $publisherspublic array $genresthe provider's words
the provider's words
public array $platformspublic ?Price $pricepublic array $imagespublic 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 = [])$sourcethe provider's name ("steam", "igdb"...)
$urlthe game's page at the store or catalogue
$comingSoonnot released yet, as the store says
$genresthe provider's words
$tagsthe provider's words
Game::ref()
public function ref(Scheme $scheme): ?GameRefGame::image()
public function image(ImageKind $kind = ImageKind::Cover): ?ImageGame::runsOn()
public function runsOn(Platform $platform): boolGame::toArray()
public function toArray(): arrayGameRef 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 $idpublic Scheme $scheme
GameRef::__construct()
public function __construct(public Scheme $scheme, string|int $id)GameRef::steam()
public static function steam(string|int $id): selfGameRef::gog()
public static function gog(string|int $id): selfGameRef::itch()
public static function itch(string $page): selfGameRef::igdb()
public static function igdb(string|int $id): selfGameRef::rawg()
public static function rawg(string|int $id): selfGameRef::__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(): boolWhether 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 $statuspublic string $reasonpublic ?Server $server
HeartbeatResult::__construct()
public function __construct(public int $status, public string $reason, public ?Server $server = null)HeartbeatResult::accepted()
public function accepted(): boolHeartbeatResult::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): selfEmpty 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(): selfIdentifiers::get()
public function get(Scheme|string $scheme): ?stringIdentifiers::has()
public function has(Scheme|string $scheme): boolIdentifiers::with()
public function with(Scheme|string $scheme, string|int|null $id): selfIdentifiers::merge()
public function merge(self $other): selfThese identifiers, completed by the other's where these have none.
Identifiers::isEmpty()
public function isEmpty(): boolIdentifiers::toArray()
public function toArray(): arrayIdentity 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 $platformpublic string $idpublic array $claimswhat the platform said besides the id
Identity::__construct()
public function __construct(string $platform, string $id, public array $claims = [])$claimswhat the platform said besides the id
Identity::player()
public function player(): stringThe player's id across platforms: "<platform>:<id>".
Identity::__toString()
public function __toString(): stringIdentity::toArray()
public function toArray(): arrayImage 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 $kindpublic string $urlpublic ?int $widthpublic ?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(): arrayImageKind 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 Logo = 'logo'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 $identifierspublic string $titlepublic ?int $playtimeminutes
public ?\DateTimeImmutable $lastPlayed
OwnedGame::__construct()
public function __construct(public Identifiers $identifiers, public string $title, public ?int $playtime = null, public ?\DateTimeImmutable $lastPlayed = null)$playtimeminutes
OwnedGame::toArray()
public function toArray(): arrayPlatform 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 $amountpublic string $currencypublic ?int $initialthe 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)$initialthe price before the discount, when there is one
Price::isFree()
public function isFree(): boolPrice::toArray()
public function toArray(): arrayQuery class
final readonly class Query Query.php:8
A search by title: the words, and what narrows them.
public string $textpublic ?string $languagethe language of the texts answered, where the store translates ("fr", "french")
public ?string $countrythe country whose prices are answered (ISO 3166-1 alpha-2), where the store has them
public int $limitat 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)$languagethe language of the texts answered, where the store translates ("fr", "french")
$countrythe country whose prices are answered (ISO 3166-1 alpha-2), where the store has them
$limitat 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 $sourcepublic float $valuepublic float $scalepublic ?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(): arrayScheme 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 $playerpublic Server $serverpublic Ticket $ticketpublic 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(): arrayWhat 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 $playerpublic ?Identity $identityThe platform's verified player, when the request was made with one.
public ?string $regionwhere the server should run; any, when null
what the server must carry (every one)
public array $claimswhat 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 = [])$playerthe player's id, or the Identity a platform verified
$regionwhere the server should run; any, when null
$tagswhat the server must carry (every one)
$claimswhat 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 $idpublic string $servicepublic string $addresswhere players reach it, as their client takes it ("203.0.113.7:7777", "wss://eu1.example.org/play")
public int $capacitypublic int $playerspublic Health $healthpublic ?\DateTimeImmutable $heartbeatpublic ?string $regionwhat a seat may ask of it ("ranked", "map:harbour", "v1.4")
public ?string $enginepublic ?string $providerthe hosting provider that started it, by its configured name
public ?string $instanceits 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)$addresswhere players reach it, as their client takes it ("203.0.113.7:7777", "wss://eu1.example.org/play")
$tagswhat a seat may ask of it ("ranked", "map:harbour", "v1.4")
$providerthe hosting provider that started it, by its configured name
$instanceits handle there (a container's id)
Server::free()
public function free(): intThe seats left.
Server::has()
public function has(string ...$tags): boolWhether it carries every tag asked.
Server::silence()
public function silence(\DateTimeImmutable $now): intSeconds 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): selfServer::withPlayers()
public function withPlayers(int $players): selfServer::toArray()
public function toArray(): arrayServer::fromArray()
public static function fromArray(array $values): selfA 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 $namepublic ?string $urlthe public address where its tickets are presented, when the service has one (a relay); a server's own address otherwise
public string $secretthe reference of the secret its servers check tickets with
public int $ttla ticket's life, in seconds
public int $capacitythe players a server of it takes, unless the server says otherwise
public ?string $enginewhat 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)$urlthe public address where its tickets are presented, when the service has one (a relay); a server's own address otherwise
$secretthe reference of the secret its servers check tickets with
$ttla ticket's life, in seconds
$capacitythe players a server of it takes, unless the server says otherwise
$enginewhat its servers run, as the application names it ("unity-mirror", "godot"...)
Service::fromArray()
public static function fromArray(string $name, array $values): self$valuesas 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 $tokenpublic string $roompublic \DateTimeImmutable $expirespublic array $claimsthe 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 = [])$claimsthe application's own, room and exp left out
Ticket::__toString()
public function __toString(): stringTicket::toArray()
public function toArray(): arrayOmniplay\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): stringFixedSecrets 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)$secretsby reference
FixedSecrets::secret()
public function secret(string $reference): stringSecretsInterface 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): voidInMemoryStore::find()
public function find(string $id): ?ServerInMemoryStore::all()
public function all(?string $service = null): arrayInMemoryStore::delete()
public function delete(string $id): voidStoreInterface 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): voidKeeps the server, replacing the one of the same id.
StoreInterface::find()
public function find(string $id): ?ServerStoreInterface::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): voidOmniplay\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(): voidBareTestCase::testTheReportSeesWhatIsLoaded()
public function testTheReportSeesWhatIsLoaded(): voidThe 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')$servicesthe services it starts (every one, when empty)
FixedHosting::getName()
public function getName(): stringFixedHosting::getTitle()
public function getTitle(): stringFixedHosting::capabilities()
public function capabilities(): CapabilitiesFixedHosting::start()
public function start(Service $service, string $secret, ?string $region = null, array $tags = []): ServerFixedHosting::stop()
public function stop(Server $server): voidFixedHosting::running()
public function running(?string $service = null): arrayFixedHostingFactory class
final class FixedHostingFactory extends ProviderFactory FixedHostingFactory.php:10
FixedHosting's factory: "fixed-hosting", options services, regions, host, port.
Options
| Option | Default | |
|---|---|---|
services | [] | |
regions | ['local'] | |
host | '127.0.0.1' | |
port | 7000 |
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')$playersthe player's id by proof
FixedIdentity::getName()
public function getName(): stringFixedIdentity::getTitle()
public function getTitle(): stringFixedIdentity::capabilities()
public function capabilities(): CapabilitiesFixedIdentity::platform()
public function platform(): stringFixedIdentity::verify()
public function verify(string $proof): IdentityFixedIdentityFactory 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
| Option | Default | |
|---|---|---|
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')$librariesby player
FixedProvider::getName()
public function getName(): stringFixedProvider::getTitle()
public function getTitle(): stringFixedProvider::capabilities()
public function capabilities(): CapabilitiesFixedProvider::search()
public function search(Query $query): arrayFixedProvider::game()
public function game(GameRef $ref, ?string $language = null, ?string $country = null): ?GameFixedProvider::library()
public function library(string $player): arrayFixedProvider::owns()
public function owns(string $player, GameRef $game): boolFixedProviderFactory 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): selfAnswers 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 = []): ResponseInterfaceRecordedHttpClient::stream()
public function stream(ResponseInterface|iterable $responses, ?float $timeout = null): ResponseStreamInterfaceRecordedHttpClient::withOptions()
public function withOptions(array $options): staticRecordedHttpClient::header()
public function header(string $name, int $request = -1): ?stringThe 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(): intRecordedResponse::getHeaders()
public function getHeaders(bool $throw = true): arrayRecordedResponse::getContent()
public function getContent(bool $throw = true): stringRecordedResponse::toArray()
public function toArray(bool $throw = true): arrayRecordedResponse::cancel()
public function cancel(): voidRecordedResponse::getInfo()
public function getInfo(?string $type = null): mixedOmniplay\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): stringA 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): stringA 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): ?arrayA 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).