Writer
in package
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 = valueline, 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[].MapValuesays 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.
Rawis written as given;Commentedputs#lines above its key.Secretmarks its key secret (CASC.md §4.3):*key = value, or*key { ... }for a block, every value in which loads as a secret. It nests withCommentedin either order. A list element has no key to mark, so aSecretthere is refused, as aCommentedis -- 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
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
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
stringentries()
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
Return values
stringescape()
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
stringfloat()
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
stringisBlock()
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
boolkey()
private
key(string $key, string $where) : string
Parameters
- $key : string
- $where : string
Return values
stringlist()
private
list(array<string|int, mixed> $list, int $level, string $where) : string
Parameters
- $list : array<string|int, mixed>
- $level : int
- $where : string
Return values
stringmapElement()
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
Return values
stringstring()
A string literal that reads back as exactly `$text`.
private
string(string $text, string $where) : string
Parameters
- $text : string
- $where : string
Return values
stringunwrap()
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: listvalue()
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