php-cooper-config

CompiledCache
in package

FinalYes

The compiled cache: a loaded, converted configuration written as a PHP file, so the next request includes it instead of parsing the document.

Why

PHP is shared-nothing: every request starts with an empty process, and Cooper's own cache lives in process memory. Loading eagerly at startup would otherwise mean parsing every document on every request. A PHP file is the one thing PHP keeps between requests for free -- opcache holds its compiled form, and its constant arrays, in shared memory.

When it is used

Only while still valid. A compiled file records what its load depended on (Cooper's LoadTrace):

  • every file the load read, with modification time and size;
  • every filesystem import's expansion -- its root, its pattern, and the files it matched -- expanded again on every check, so a file added where import "parts/*.casc" looks (or removed from there) counts, though the load never read it;
  • every environment variable the document read (values, ${?NAME} guards, import paths, ${COOPER_ENV}), as a salted HMAC of the value, never the value itself.

A load compares all three before using the file; any difference, and the document is loaded afresh and the file rewritten. Environment values are read the way Cooper reads them -- .env files, the real environment, the env option -- so a changed .env counts.

Not compiled at all, and loaded afresh every time, is a load whose result has nothing to fingerprint: one that called a resolvers entry (its answer could change any time), one given importSchemes, or one holding a value PhpExport cannot write. Nor is a load that read a file modified within the last second: a second edit in the same second would keep the modification time the file is fingerprinted by (git's "racy" entries, and the same answer); the next load compiles it.

What is baked in and not fingerprinted: the answers of consumer tags and !module(...). The modules mapping and the tag names are part of the file's name, so a different mapping gets a different file; a changed tag implementation needs cache:clear (part of any deploy).

Security

The file holds the configuration as the application sees it -- revealed secrets included -- and is code PHP will run. So it is written atomically (a temporary file renamed over it, so a request never includes half a file) with mode 0600 into a directory created 0700, and is never included unless it is owned by this process's user and writable by nobody else; one that fails the check is ignored and rewritten. Keep the cache directory out of the web root, out of version control, and out of backups that are less protected than the secrets themselves.

Table of Contents

Constants

FORMAT  : mixed = 2
Bumped whenever the file's layout changes, so an older file is simply invalid.
PREFIX  : mixed = 'cooper-config-'
Every compiled file's name starts with this; `clear()` removes nothing else.

Methods

clear()  : int
Removes every compiled file in `$dir` (and any temporary file an interrupted write left behind), and nothing else.
defaultDirectory()  : string
Where compiled files go by default: `<root>/var/cache/cooper-config` -- `var/` being where Symfony, and PHP projects generally, keep what they generate.
__construct()  : mixed
forget()  : void
Drops opcache's copy of `$file`, which with `opcache.validate_timestamps=0` would otherwise be served forever.
hash()  : string
stamp()  : array{0: int, 1: int}|null
trustworthy()  : bool
Owned by this process's user, writable by nobody else: a file anybody else could have written is code nobody vetted.
version()  : string
writeAtomically()  : bool

Constants

FORMAT

Bumped whenever the file's layout changes, so an older file is simply invalid.

public mixed FORMAT = 2

PREFIX

Every compiled file's name starts with this; `clear()` removes nothing else.

private mixed PREFIX = 'cooper-config-'

Methods

clear()

Removes every compiled file in `$dir` (and any temporary file an interrupted write left behind), and nothing else.

public static clear(string $dir) : int
Parameters
$dir : string

the cache directory

Return values
int —

how many compiled files were removed

defaultDirectory()

Where compiled files go by default: `<root>/var/cache/cooper-config` -- `var/` being where Symfony, and PHP projects generally, keep what they generate.

public static defaultDirectory(string $root) : string
Parameters
$root : string

the project root

Return values
string —

the directory

forget()

Drops opcache's copy of `$file`, which with `opcache.validate_timestamps=0` would otherwise be served forever.

private static forget(string $file) : void
Parameters
$file : string

hash()

private static hash(string $salt, string $name, string $value) : string
Parameters
$salt : string
$name : string
$value : string
Return values
string

stamp()

private static stamp(string $path) : array{0: int, 1: int}|null
Parameters
$path : string
Return values
array{0: int, 1: int}|null —

modification time and size, or null for a missing path

trustworthy()

Owned by this process's user, writable by nobody else: a file anybody else could have written is code nobody vetted.

private static trustworthy(string $file) : bool
Parameters
$file : string
Return values
bool

version()

private static version(string $package) : string
Parameters
$package : string
Return values
string

writeAtomically()

private static writeAtomically(string $file, string $source) : bool
Parameters
$file : string
$source : string
Return values
bool
On this page

Search results