Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cooklang PHP

A Cooklang recipe parser for PHP.

Installation

composer require devapeu/cooklang

Requires PHP 8.0 or higher.

Usage

use Devapeu\Cooklang\CooklangParser;

$recipe = CooklangParser::parse(<<<COOKLANG
    >> servings: 4

    Crack the @eggs{3}(any type) into a blender, then add the @flour{125%g},
    @milk{250%ml} and @sea salt{1%pinch}, and blitz until smooth.

    Pour into a #bowl and leave to stand for ~{15%minutes}.
    COOKLANG);

$recipe->meta;         // ['servings' => '4']
$recipe->ingredients;  // Ingredient[]
$recipe->cookware;     // ['bowl']
$recipe->instructions; // Instruction[], ingredients wrapped in <ingredient> tags by default
$recipe->sections;     // Section[]

CooklangParser::parse() returns a Devapeu\Cooklang\Models\Recipe:

Property Type Description
meta array Key/value metadata pairs (>> key: value)
ingredients Ingredient[] Top-level ingredients (@ingredient)
cookware string[] Top-level cookware (#cookware)
instructions Instruction[] Top-level instruction sentences
sections Section[] Named sections (= Section Name), each with its own ingredients, cookware and instructions

Each Ingredient has name, quantity, measure, note, and optional.

Each Instruction implements Stringable (so it works directly in string interpolation, concatenation, echo, etc. with no cast needed), and also exposes text() for the raw tagged string explicitly and plain() to strip ingredient tags.

Formatting ingredients, cookware, and timers

By default, @ingredient, #cookware, and ~{timer} references inside instructions are each wrapped in a matching tag — <ingredient>, <cookware>, and <timer> respectively (e.g. <ingredient>2 cups of flour</ingredient>) — so you can style or target them from HTML/CSS/JS.

If you don't want any markup, call plainInstructions() on the Recipe (or Section) to get all instructions stripped at once, or plain() on an individual Instruction:

echo $recipe->instructions[0];        // "Add <ingredient>2 cups of flour</ingredient> to the mix." (implicit __toString)
$recipe->instructions[0]->text();     // "Add <ingredient>2 cups of flour</ingredient> to the mix." (explicit)
$recipe->instructions[0]->plain();    // "Add 2 cups of flour to the mix."
$recipe->plainInstructions()[0];      // "Add 2 cups of flour to the mix."

Serializing

Recipe, Section, Ingredient, and Instruction all expose toArray(), and implement JsonSerializable, so you can convert a parsed recipe (recursively, including its sections) to a plain array or JSON:

$recipe->toArray(); // ['meta' => [...], 'ingredients' => [...], ..., 'sections' => [...]]
json_encode($recipe);

Or customize/disable each tag independently at parse time via ParserOptions:

use Devapeu\Cooklang\ParserOptions;

CooklangParser::parse($source, new ParserOptions(
    ingredientTag: 'span',
    cookwareTag: 'span',
    timerTag: null, // disables timer tagging entirely
));

Supported syntax

  • Ingredients: @name, @name{quantity%measure}, @*optional ingredient{}
  • Cookware: #cookware
  • Timers: ~{quantity%measure}
  • Notes: @ingredient{}(note)
  • Sections: = Section Name or == Section Name ==
  • Comments: -- line comment and inline -- trailing comment
  • Metadata: >> key: value, or a YAML frontmatter block delimited by --- lines (parsed with symfony/yaml, so nested maps and lists are supported)

Testing

composer test

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages