detain / phlix-shared
Shared interfaces, DTOs, event names, and protocol types used by both phlix-server and phlix-hub. Composer-installable, PHP 8.3+, zero I/O by charter — the only bundled network code is the blocking CurlArrTransport (CLI/test only); event-loop consumers MUST inject an async ArrTransportInterface.
Requires
- php: ^8.3
- psr/clock: ^1.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/log: ^3.0
Requires (Dev)
- ext-curl: *
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.0
- squizlabs/php_codesniffer: ^3.10
- vimeo/psalm: ^5.0
Suggests
- ext-curl: Required only for the bundled blocking Arr\Transport\CurlArrTransport (CLI/test use). Event-loop (Workerman/Webman) consumers should inject an async ArrTransportInterface instead and do not need ext-curl.
- workerman/http-client: Event-loop (Workerman/Webman) consumers should inject a workerman/http-client-backed Arr\Transport\ArrTransportInterface so *arr API calls do not block the worker; the bundled CurlArrTransport is blocking and for CLI/test only.
This package is auto-updated.
Last update: 2026-08-12 19:25:11 UTC
README
Shared interfaces, DTOs, event names, and protocol types used by both
detain/phlix-server (the media server)
and detain/phlix-hub (the multi-server hub).
Composer-installable, PHP 8.3+, zero I/O — pure interfaces and value objects only.
Status
v0.48.0 — completes the restart-flag audit of schemas/server-settings.schema.json: all 72 keys are traced to their consumer (49 restart: true, 23 restart: false), with no key added or removed. Cumulative surface:
Phlix\Shared\Plugin\{LifecycleInterface, Manifest, ManifestType, ManifestValidationError, EventNameMap}Phlix\Shared\Events\{AbstractEvent, Playback\*, Library\*, Auth\*}— 12 event DTOs.Phlix\Shared\Auth\{JwtClaims, ProviderInterface, AuthResult, UserInfo}Phlix\Shared\Metadata\MetadataSourceInterface— the typed contract a metadata-source plugin implements (sourceName(),supportedMediaTypes(),search(),getDetails(),getImages()) so the server'sSourceRegistrycan register/deregister it on plugin enable/disable without the oldmethod_exists/FQCN-sniffing convention (0.15.0+).Phlix\Shared\Subtitle\{SubtitleSourceInterface, SubtitleCandidate, SubtitleFile, Exception\QuotaExceeded}— the subtitle analogue ofMetadataSourceInterface:getName(),getPriority(), thesearchByPath()/searchByHash()/searchByImdbId()fan-out returningSubtitleCandidatevalues, and the quota-consumingdownload()that may throwQuotaExceeded(0.42.0+).Phlix\Shared\Hub\{ClaimRequest, ClaimResponse, ServerInfoDto, HeartbeatDto}Phlix\Shared\Relay\{RelayFrameType, RelayWireCodecInterface, RelayFrame}— channel-mux protocol (0.5+); plus{RelayHttpRequest, RelayHttpResponseHead, RelayHttpResponseChunk, RelayHttpResponseCodec}— HTTP-over-relay request/response envelopes +HEAD→BODY*→ENDchunk framing (0.10.0+).Phlix\Shared\Arr\{BazarrClient, ProwlarrClient, RadarrClient, SonarrClient}— *arr HTTP clients.Phlix\Shared\Schema\SchemaPaths— pure path resolver for the bundledschemas/files (0.7.0+).
Bundled schemas
The package ships the canonical JSON files used by both phlix-server and the
admin SPA under schemas/ (resolve their absolute paths via
Phlix\Shared\Schema\SchemaPaths):
-
schemas/manifest.schema.json— JSON Schema (draft 2020-12) for plugin manifests, loaded at runtime byphlix-server'sPhlix\Plugins\Manifest\ManifestSchemavalidator (0.6.0+). Per-settinglabelanddescriptionare permitted, andinteger/booleanare accepted as aliases ofint/boolin the settingtypeenum (0.9.1+). An optional per-settingtier(standard|advanced) is accepted on plugin settings fields so aplugin.jsondeclaring it passes install-time manifest validation (0.43.0+). -
schemas/hub-settings.schema.json— JSON Schema (draft 2020-12) for the editable hub settings (/api/v1/me/hub-settings), resolved viaSchemaPaths::hubSettings()(0.22.0+). Its property keys must stay in lockstep withphlix-hub'sHubSettingsRepository::ALLOWED_KEYS— that constant is what the hub settings controller enumerates, and the schema supplies only the render metadata for those keys (0.24.0+). -
schemas/server-settings.schema.json— JSON Schema (draft 2020-12) for the editable server settings (/api/v1/admin/settings); phlix-server derives its writable allow-list from this schema and the admin SPA renders the settings form from it (0.7.0+). Extended with optional UI-metadata keywords (label,helpText,helpLinks,tier,enumLabels,optionHelp,secret,restart) so the admin settings UI can render per-option help text and split Standard vs Advanced options (0.22.0+).Key-naming contract (0.25.0+). Every property key must be a dotted path that
Phlix\Admin\SettingsRepository::getDefault()can resolve: a LEADING RUN of segments names a config file under the consuming repo'sconfig/, and the remaining segments index into that file's returned array. The file part may span subdirectories — the longest matching file path wins, soscrobblers.trakt.client_idresolvesconfig/scrobblers/trakt.phpin preference to a same-named flat file. Every file segment is jailed to/^[A-Za-z0-9_-]+$/, so no key can escapeconfig/. A key that does not resolve renders an empty control and can never take effect, so no such key may be declared. Thetrakt.*keys deliberately use a FLAT prefix backed byphlix-server'sconfig/trakt.phpre-export shim rather than the nestedscrobblers.trakt.*form:trakt.*overrides are already persisted in liveserver_settingstables andTraktOAuthController::SETTING_KEY_MAPreads those exact keys, so renaming them would orphan real rows (0.25.0+). Addsmatching.noise_suffixes(array of strings; the admin-extensible match-title noise list — 0.13.0+) andmetadata.provider_priority(object: media type → ordered array of source names; defaultsmovie/series=["tmdb","imdb"],anime=["anidb","myanimelist","tvdb","fanart","local"])metadata.genres_mode(enumfirst|union, defaultfirst) for the per-media-type metadata source-priority editor (0.14.0+). Addsmetadata.overwrite_existing(boolean,group: metadata,tier: standard, 0.41.0+),subtitles.provider_priority(a single flat ordered array of subtitle-source names, default["opensubtitles"], read bySubtitleFetchService/SubtitleSourceRegistry::byPriority()— 0.44.0+), anddlna.allowed_cidrs(array, default[]) +dlna.restrict_to_lan(boolean, defaulttrue), bothtier: advanced/group: subsystem, read live byDlnaAllowlistMiddlewareto gate the unauthenticated DLNA browse/stream endpoints (0.45.0+).
restartflags (0.46.0–0.48.0). Every one of the 72 keys has been traced to its consumer: 49 carryrestart: trueand 23 carryrestart: false.restart: truemeans the admin Restart server control (a graceful SIGUSR2 reload that cycles workers, re-runsonWorkerStart, and rebuilds DI containers) — required for any value captured at worker start, container build, or route build. The 23restart: falsekeys resolve throughSettingsRepository::getEffective()at use time and are genuinely live. No key was added or removed by this audit. -
schemas/webhook-events.json— data catalog of the supported webhook event types for the admin SPA webhook picker. Distinct from the plugin PSR-14 events inEventNameMap(0.7.0+). -
schemas/library-query.schema.json— JSON Schema (draft 2020-12) for the query parameters of the movie-list browse API (GET /api/v1/media); drivesItemRepository::query()and the admin SPA browse page (0.8.0+). Adds thelibraryId/parentId/topLevelscoping parameters for series→season→episode navigation (0.9.0+). -
schemas/media-item.schema.json— JSON Schema (draft 2020-12) for a single media item returned by the browse API; flattensmetadata_jsoninto stable top-level fields and always includesposter_url(0.8.0+). Thetypeenum gainsseasonalongsideparent_id,season_number,episode_number, andepisode_titlefor the series hierarchy (0.9.0+).
The PSR-14 dispatcher wiring (Tukio) and the schema validators stay in
phlix-server and consume this package via Composer.
Requirements
- PHP
^8.3 - Composer 2.x
psr/container ^2.0psr/event-dispatcher ^1.0
The package has zero framework dependencies — no Workerman, no Monolog, no Smarty. It is intended to be safely required by any PHP 8.3+ codebase.
Installation
Until detain/phlix-shared is published to Packagist (planned post-v1.0),
consumers require it via a Composer VCS repository entry. Use the HTTPS URL
so CI runners without SSH keys can resolve it:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/detain/phlix-shared.git"
}
],
"require": {
"detain/phlix-shared": "^0.6"
}
}
Then:
composer update detain/phlix-shared
Related repositories
detain/phlix-server— the Phlix media server (consumes this package).detain/phlix-hub— the multi-server hub + reverse-tunnel relay.
Development
composer install ./vendor/bin/phpunit ./vendor/bin/phpstan analyze --no-progress ./vendor/bin/phpcs --standard=PSR12 src/ ./vendor/bin/psalm --no-progress composer validate --strict php scripts/security-audit-check.php
⚠ The last step replaces composer audit --no-dev. Never audit with a
development-dependency exclusion: --no-dev drops every require-dev package
from the audited set, and it hid CVE-2026-67434 (HIGH, OS command injection) in
squizlabs/php_codesniffer — a require-dev package — so CI reported SUCCESS
against a vulnerable lock. scripts/security-audit-check.php audits the whole
lock, labels each finding [require] / [require-dev], and prints the number of
packages it examined so a truncated audit cannot pass as a clean one. See
AGENTS.md for the full policy.
The phpunit job in .github/workflows/ci.yml uploads
./coverage.xml to both Codecov and Codacy.
License
MIT — see LICENSE.