The Game Panel specification

RFC-0005: Paths value object

Abstract

A single Paths value object holds every filesystem location the engine needs: configuration, data, modules, cache and logs. Bootstrap constructs it once, from the arguments the panel is booted with, and it replaces the configuration component’s ConfigPaths.

Motivation

The configuration loader receives its locations through ConfigPaths, which holds configuration locations and nothing else. More components need locations of their own: modules, the cache and logs. A single object holding every location, constructed once during early bootstrap, is cleaner than a paths object for each component.

Proposal

Concepts

Term Meaning
Root An absolute directory under which the engine keeps one type of file.
Relative path A path beneath a root, joined onto it by Paths.

Components

Component Responsibility
Paths Final readonly value object holding every root, and joining relative paths onto them.

Roots

Property Holds
config The configuration directory, holding config.toml, config.d/ and modules-enabled/.
data The application data directory.
modules The Composer project that external modules are installed into, per ADR-0012.
cache The cache directory.
logs The log directory.

Each root is an absolute path. Paths holds the roots and does nothing else with them: it does not work out where they are, check that they exist, or create them.

$paths = new Paths(
    config: '/etc/tgp',
    data: '/var/lib/tgp',
    modules: '/var/lib/tgp/modules',
    cache: '/var/lib/tgp/cache',
    logs: '/var/log/tgp',
);

Joining paths

Each root has a method of the same name that joins a relative path onto it:

Method Returns
config(string $path): string The path beneath the configuration directory.
data(string $path): string The path beneath the data directory.
modules(string $path): string The path beneath the modules directory.
cache(string $path): string The path beneath the cache directory.
logs(string $path): string The path beneath the log directory.

The root’s trailing separator and the path’s leading separator are removed, and the two are joined with a single separator. A path passed to a method is always treated as relative to the root, so $paths->config('config.d') and $paths->config('/config.d') both return /etc/tgp/config.d.

Construction

Bootstrap constructs Paths once, during early bootstrap, before configuration is loaded or modules are discovered. Its roots come from arguments given when the panel is booted, which are passed in during bootstrapping.

Configuration loading

TomlLoader::load() takes Paths in place of ConfigPaths, and reads from the configuration directory: config.toml, config.d/ and modules-enabled/ beneath $paths->config. ConfigPaths is removed.

$tree = new TomlLoader()->load($paths);

Out of scope

Alternatives considered

A paths object for each component, of which ConfigPaths is the existing one. Once modules, the cache and logs need locations as well as configuration, a single object holding every root, constructed once during early bootstrap, is cleaner.

No other alternatives were weighed.

Backwards compatibility

Open questions

Changelog

Sources