It's a php alternative to Tracery by GalaxyKate, from which it was heavily inspired. It is used to generate procedural texts based on a grammar.
Grab the phar at https://github.com/OwlyCode/interlacing/releases or install it as a dependency of your
project with composer: composer req owlycode/interlacing
.
Firt declare a grammar:
# grammar.yaml
content:
root: "the {{ adjective }} {{ animal }}"
animal: ["warthog", "hedgehog", "ocelot"]
adjective: ["warty", "hoary", "oneiric"]
And then use it by running interlacing.phar grammar.yaml
or inside your own code:
use OwlyCode\Interlacing\Interlacing;
// index.php
$i = Interlacing::fromFile(__DIR__.'/grammar.yaml');
echo $i->resolve('root'); // Will output things like "the hoary ocelot".
The grammar file is composed of a list of placeholders and their possible associated values. You
can put placeholders inside content using the {{ }}
delimiters. Each time interlacing encounters
a placeholder, it lookups for possible values from the grammar and picks one randomly.
Usually the root
placeholder is used as an entrypoint.
You can apply alterations to your placeholders by appending them with a leading |
. Alterations can be chained.
Resolvers are not manually applied, they just affect the way a placeholder is replaced by a value. Everytime a placeholder is used, Interlacing runs every known resolvers and returns the value of the first one that gives a non-null result. Otherwise, it lookups into the grammar for the possible values and picks one.
Pluralize a word. You can change the locale inside the grammar (english by default):
content:
root: "{{ animal|s }}"
name: ["cat", "dog", "mouse"] # cats, dogs, mice
locale: fr
content:
root: "{{ animal|s }}"
name: ["chat", "chien", "cheval"] # chats, chiens, chevaux
Capitalizes the first letter.
content:
proverb: "{{ animal|capitalize }} can eat {{ animal }}, as they say."
name: ["the lion", "the mouse", "the cat", "the cow"]
Could produce: The mouse can eat the cow, as they say.
Allows to dynamically build placeholders during the execution.
- store: stores the displayed placeholder value.
- storeAll: stores all possibles values of the placeholder.
- storeOthers: stores all possibles values of the placeholder except the one displayed.
- push: adds the displayed placeholder value to the already stored values.
- pop: removes the displayed memorized value from the memory.
Examples:
content:
root: ["We pick {{ value|store(selected_value) }} and we can show it later: {{ selected_value }}"]
value: ['A', 'B', 'C']
content:
root: ["To make a cake you need {{ ingredient|storeOthers(i) }}, {{ i|pop }} and {{ i }}"]
ingredient: ['eggs', 'flour', 'water']
content:
root: ["{{ duo }} went on an adventure. {{ adventurer|pop }} survived, but not {{ adventurer }}."]
duo: ['{{ name|storeOthers(c)|push(adventurer) }} and {{ c|push(adventurer) }}']
name: ['Alice', 'Peter', 'John', 'Frantz', 'Albert', 'Mark']
Beware: If you override a grammar based placeholder by a memory value, it will replace it entirely.
Prevents a placeholder from rendering, often used with memory alterations: it allows to pair some results.
content:
root: ["Fishes are {{ level }} at {{ skill }}."]
skill: [ "swimming{{ good|store(level)|silence }}", "flying{{ bad|store(level)|silence }}"]
good: ["good", "skilled"]
bad: ["bad", "unskilled"]
Will produce Fishes are good at swimming.
or Fishes are bad at flying.
but never Fishes are good at flying.
.
You can create your own alterations by implementing the OwlyCode\Interlacing\Plugin\AlterationInterface
interface:
namespace Custom\Plugin;
class Prepend implements AlterationInterface
{
// An alteration always receive the placeholder name, the actual resolved value and the args provided to the alteration
public function prepend(string $placeholder, string $input, array $args): string
{
return $args[0] . $input;
}
// Returns the list of alterations by name.
public function getAlterations(): array
{
return [
'prepend' => [$this, 'prepend'], // Usage: {{ placeholder|prepend("prefix") }}
];
}
}
And you can create a resolver by implementing the OwlyCode\Interlacing\Plugin\ResolverInterface
interface:
namespace Custom\Plugin;
class Now implements ResolverInterface
{
public function resolve($name): ?string
{
if ($name === 'now') {
return (new \Datetime())->format('Y-m-d H:i:s');
}
return null;
}
}
Now let's use our previously declared alteration and resolver:
plugins:
- Custom\Plugin\Now
- Custom\Plugin\Prepend
content:
root: "{{ now|prepend("time: ") }}"
This would display: time: 2019-11-08 22:44:00
, where the date and time are set to now.