The Game Panel specification

ADR-0021: All file access goes through Flysystem

Context

The engine reads files. Configuration loading is the only thing that touches one today, through PHP’s own functions and a path taken from Paths. More follows as components land: cached content, the engine’s own data, and the compiled output that PHP includes, which it will write as well as read.

A path only means anything where the files are on local disk. Some of what the engine keeps could sit elsewhere: configuration in object storage, or cached content in a database table, where no host path exists. Anything that hands out a path lets its consumers depend on local disk, and nothing but review would notice.

PHP’s include is the exception. An includable, opcached file has to be a real file at a real path.

Decision

Every file the engine reads or writes goes through Flysystem. Filesystems are constructed by whatever composes the engine and handed in fully formed, and nothing exposes a filesystem path at runtime. Consumers are given Flysystem’s own FilesystemOperator, not an engine-owned wrapper around it.

A path is kept only where PHP’s own include is involved: the modules directory, for Composer-based discovery and autoloading; the compiled directory, for compiled templates and module metadata; and the log directory, which is where Monolog writes.

Alternatives

PHP’s file functions with a path, as the engine uses today. A consumer can come to depend on local disk, and a filesystem backed by object storage or a database table has no path to give it, so substitutability would rest on review rather than on the type.

An engine-owned interface wrapping Flysystem. It would cost twenty-one delegating methods, kept in step with a library that already has them, and module authors work with filesystems directly, so the type they already know is the one to expose.

Whether any other library was weighed against Flysystem is not recorded.

Consequences

Easier:

Harder:

Constrained:

Sources