php-cooper-config

Writer
in package

FinalYes

Writes a PHP value tree as a CASC document (`casc/CASC.md` in `joetjen/cooper`) that loads back as the same values -- the shared writer behind every generated `.casc` file (a framework's existing configuration converted, a scaffold).

echo (new Writer())->write([
    'database' => [
        'host' => new Commented(new Raw('${DB_HOST:"127.0.0.1"}'), 'was: env("DB_HOST")'),
        'port' => 5432,
    ],
]);

What becomes what

  • The tree is the document: each entry a key = value line, in the order given. A nested associative array is a block (key { ... }), a list (array_is_list()) a list literal -- inline when it fits on one line, else one element per line.
  • A map inside a list, at any depth, is a block element (CASC.md §6.10): [{ host = "a" }, { host = "b" }], each one inline when it fits and holds no block or comment, else over several lines.
  • An empty array is []: a PHP array cannot say whether it is an empty list or an empty map, and both load back as []. MapValue says it is a map -- } when empty (CASC.md §5.4), which written over a map already there leaves it as it is, where [] replaces it.
  • int, float (shortest digits that read back the same, always with a fraction or exponent, so it stays a float; inf/-inf), bool, null (nil).
  • A string is double-quoted, escaped, unless it holds a reference opener (${, @{, %{, !{, !Name(): then it is single-quoted, which CASC never interpolates. One holding an opener and a ' or a control character fits neither; it is double-quoted with each opener's first character interpolated from a private variable (@*casc_writer_dollar = '$'), since escapes are processed before interpolation and no escape keeps ${ literal.
  • A key matching CASC.md §4.1 and not a reserved word is written bare; any other is quoted (§4.2), by the same rule as a string.
  • Raw is written as given; Commented puts # lines above its key.
  • Secret marks its key secret (CASC.md §4.3): *key = value, or *key { ... } for a block, every value in which loads as a secret. It nests with Commented in either order. A list element has no key to mark, so a Secret there is refused, as a Commented is -- but an entry of a map that is a list element has one, and is written [{ *key = value }].

Anything else -- an object, a resource, NAN, text that is not UTF-8 -- is an \InvalidArgumentException naming where in the tree it is.

Table of Contents

Constants

HELPERS  : mixed = ['$' => 'casc_writer_dollar', '@' => 'casc_writ...
The private variable that stands in for each opener's first character.
IDENTIFIER  : mixed = '/\A[a-zA-Z][a-zA-Z0-9_+\-]*[?!]?\z/'
CASC.md §4.1.
OPENER  : mixed = '/[@$%!]\{|![A-Za-z][A-Za-z0-9_+\-]*[?!]?\(/'
Where interpolation starts in a double-quoted string (CASC.md §7).
RESERVED_KEYS  : mixed = ['nil', 'true', 'false', 'inf', 'import', 'for']
Words a bare key must not be: §6.4's reserved values, which the grammar reads as values in key position, and the two statement keywords, quoted so a key never reads as the start of a statement.
WIDTH  : mixed = 80
A list is written inline while its line stays this short.

Properties

$helpers  : array<string, true>
$indent  : string
$version  : string

Methods

__construct()  : mixed
write()  : string
The CASC document for `$tree`.
comment()  : string
`# ` lines -- the space is what keeps `#key` from reading as a disabled statement (CASC.md §3.2).
entries()  : string
A map's entries at `$level`, a blank line around every block and every commented entry so each reads as its own paragraph.
escape()  : string
Double-quoted string escapes (CASC.md §6.5); a control character without a letter escape is a `\uXXXX`.
float()  : string
The shortest digits that read back as `$value` -- PHP's own round-trip formatting (`serialize_precision = -1`), set for the call rather than trusted to an ini file that may have changed it.
isBlock()  : bool
Whether `$value` is written as a block: a `MapValue`, or a non-empty array that is not a list. A list holding maps is a list now, its maps block elements (CASC.md §6.10); it was once a block keyed `"0"`, `"1"`, ..., when CASC could not write a map inside a list, and a map in a list in a list could not be written at all.
key()  : string
list()  : string
mapElement()  : string
A map as a list element (CASC.md §6.10): `{}` when empty, inline (`{ host = "a", port = 1 }`) when it fits on its line and holds no block or comment -- an inline block has nowhere to put either -- else one entry per line, as a block anywhere else is written.
string()  : string
A string literal that reads back as exactly `$text`.
unwrap()  : array{0: mixed, 1: list, 2: bool}
A map entry's value without its `Commented` and `Secret` wrappers, which nest in any order: the comment lines (outer first -- a comment on a comment writes both) and whether any layer marked the key secret.
value()  : string
One value in value position: anything `isBlock()` does not take -- or, in a list, anything at all, a map as a block element.

Constants

HELPERS

The private variable that stands in for each opener's first character.

private mixed HELPERS = ['$' => 'casc_writer_dollar', '@' => 'casc_writer_at', '%' => 'casc_writer_percent', '!' => 'casc_writer_bang']

IDENTIFIER

CASC.md §4.1.

private mixed IDENTIFIER = '/\A[a-zA-Z][a-zA-Z0-9_+\-]*[?!]?\z/'

OPENER

Where interpolation starts in a double-quoted string (CASC.md §7).

private mixed OPENER = '/[@$%!]\{|![A-Za-z][A-Za-z0-9_+\-]*[?!]?\(/'

RESERVED_KEYS

Words a bare key must not be: §6.4's reserved values, which the grammar reads as values in key position, and the two statement keywords, quoted so a key never reads as the start of a statement.

private mixed RESERVED_KEYS = ['nil', 'true', 'false', 'inf', 'import', 'for']

WIDTH

A list is written inline while its line stays this short.

private mixed WIDTH = 80

Properties

$helpers

private array<string, true> $helpers = []

the helper variables this document uses, by opener character

$indent read-only

private string $indent = ' '

$version read-only

private string $version = '1.0'

Methods

__construct()

public __construct([string $version = '1.0' ][, string $indent = ' ' ]) : mixed
Parameters
$version : string = '1.0'

the version header's value

$indent : string = ' '

one level of block and list indentation

write()

The CASC document for `$tree`.

public write(array<string|int, mixed> $tree[, string $comment = '' ]) : string
Parameters
$tree : array<string|int, mixed>

the document's top-level entries (a list's indices become keys)

$comment : string = ''

lines written as # comments under the header

Tags
throws
InvalidArgumentException

when a value has no CASC spelling

Return values
string —

the document, ending in a newline

comment()

`# ` lines -- the space is what keeps `#key` from reading as a disabled statement (CASC.md §3.2).

private static comment(array<int, string> $lines, string $pad) : string
Parameters
$lines : array<int, string>
$pad : string
Return values
string

entries()

A map's entries at `$level`, a blank line around every block and every commented entry so each reads as its own paragraph.

private entries(array<string|int, mixed> $map, int $level, string $path) : string

Impure: a string on the way may need a helper variable, which it records in $helpers for write() to declare.

Parameters
$map : array<string|int, mixed>
$level : int
$path : string
Tags
phpstan-impure
Return values
string

escape()

Double-quoted string escapes (CASC.md §6.5); a control character without a letter escape is a `\uXXXX`.

private static escape(string $text) : string
Parameters
$text : string
Return values
string

float()

The shortest digits that read back as `$value` -- PHP's own round-trip formatting (`serialize_precision = -1`), set for the call rather than trusted to an ini file that may have changed it.

private static float(float $value, string $where) : string
Parameters
$value : float
$where : string
Return values
string

isBlock()

Whether `$value` is written as a block: a `MapValue`, or a non-empty array that is not a list. A list holding maps is a list now, its maps block elements (CASC.md §6.10); it was once a block keyed `"0"`, `"1"`, ..., when CASC could not write a map inside a list, and a map in a list in a list could not be written at all.

private static isBlock(mixed $value) : bool
Parameters
$value : mixed
Return values
bool

key()

private key(string $key, string $where) : string
Parameters
$key : string
$where : string
Return values
string

list()

private list(array<string|int, mixed> $list, int $level, string $where) : string
Parameters
$list : array<string|int, mixed>
$level : int
$where : string
Return values
string

mapElement()

A map as a list element (CASC.md §6.10): `{}` when empty, inline (`{ host = "a", port = 1 }`) when it fits on its line and holds no block or comment -- an inline block has nowhere to put either -- else one entry per line, as a block anywhere else is written.

private mapElement(array<string|int, mixed> $map, int $level, string $where) : string

Impure, as entries() is.

Parameters
$map : array<string|int, mixed>
$level : int
$where : string
Tags
phpstan-impure
Return values
string

string()

A string literal that reads back as exactly `$text`.

private string(string $text, string $where) : string
Parameters
$text : string
$where : string
Return values
string

unwrap()

A map entry's value without its `Commented` and `Secret` wrappers, which nest in any order: the comment lines (outer first -- a comment on a comment writes both) and whether any layer marked the key secret.

private static unwrap(mixed $value) : array{0: mixed, 1: list, 2: bool}
Parameters
$value : mixed
Return values
array{0: mixed, 1: list, 2: bool}

value()

One value in value position: anything `isBlock()` does not take -- or, in a list, anything at all, a map as a block element.

private value(mixed $value, int $level, string $where) : string
Parameters
$value : mixed
$level : int
$where : string
Return values
string
On this page

Search results