Merges one configuration object across a base, a target and a branch.
This works because configuration is captured WHOLE. Every save records the object's complete
raw data, so its value at the merge base, at the target tip and at the branch tip are three
complete documents and comparing them is an ordinary operation. Content entities and table rows are
captured as field deltas against a parent, so the same operation over them would mean replaying two
divergent delta chains and inventing a value wherever they disagree. Nothing here is given a
non-configuration realm, and Merger refuses one by name.
The comparison runs key by key and recurses into nested maps, so two people editing different
settings of the same object both get their edit. It stops recursing at a list. A sequential array
carries meaning in its order and not in its indices, so merging two lists position by position
would splice the third item of one into the third slot of the other; a list both sides changed is
one value in disagreement, which is a conflict.
A conflict is never resolved on its own. Two values written for the same key is a disagreement
between two people, and the only safe automatic answer is to stop and show both. A strategy the
caller names can decide them, and it decides only the keys that actually collided.
How deep the key-by-key comparison recurses before treating a value as one lump.
Configuration nests a handful of levels at most; anything past this is a structure that will be
compared whole, which is conservative rather than wrong.
The entry for an object that does not exist on all three sides.
Creation and removal are decided before any key is looked at, because a key-by-key comparison
against a document that is not there would read every key as an addition.
Parameters
string
$name
The configuration object name.
array|null
$base
Its data at the merge base.
array|null
$ours
Its data at the target tip.
array|null
$theirs
Its data at the branch tip.
\Drupal\strata\Branch\MergeStrategy
$strategy
What to do with a disagreement.
Returns
\Drupal\strata\Branch\MergeEntry|null
—
The entry, or NULL when all three sides have the object and the keys decide it.
Whether a value is a map this merges into rather than compares whole.
Parameters
mixed
$value
The value.
Returns
bool
—
TRUE for a non-empty associative array.
same()
same(mixed $left, mixed $right) : bool
Whether two values are the same for merge purposes.
Compared by identity rather than equality, so 0, '', false and null stay four different
settings. Arrays are compared after sorting by key at every level, because two saves that wrote
the same settings in a different order wrote the same configuration.
Parameters
mixed
$left
One value.
mixed
$right
The other.
Returns
bool
—
TRUE when they are the same.
normalize()
normalize(array$value, int $depth = 0) : array<array-key,mixed>
An array with every map level sorted by key.
Parameters
array
$value
The array.
int
$depth
Recursion guard.
Returns
array
—
The array, key-sorted at every level. A list keeps its order, which is its meaning.
paths()
paths(array$value, string $prefix, int $depth) : list<string>
Every dotted path a map holds, stopping at a leaf.