Parser Service
Introduction
Winter uses several standards for processing markup, templates and configuration. Each has been carefully selected to serve their role in making your development process and learning curve as simple as possible. As an example, the objects found in a theme use the Twig and INI format in their template structure. Each parser is described in more detail below.
Markdown parser
Markdown allows you to write easy-to-read and easy-to-write plain text format, which then converts to HTML. The Markdown
facade is used for parsing Markdown syntax and is based on GitHub flavored markdown. Some quick examples of markdown:
This text is **bold**, this text is *italic*, this text is ~~crossed out~~.
# The largest heading (an <h1> tag)
## The second largest heading (an <h2> tag)
...
###### The 6th largest heading (an <h6> tag)
Use the Markdown::parse
method to render Markdown to HTML:
$html = Markdown::parse($markdown);
You may also use the |md
filter for parsing Markdown in your frontend markup.
{{ '**Text** is bold.' | md }}
Note: Since Winter CMS v1.2.2, the Markdown parser is using the CommonMark library to parse content, which follows the CommonMark spec. If you have Markdown content that is no longer parsing correctly, you may install the Parsedown plugin to restore the Parsedown library that was used prior to Winter CMS v1.2.2.
Twig template parser
Twig is a simple but powerful template engine that parses HTML templates in to optimized PHP code, it the driving force behind the frontend markup, view content and mail message content.
The Twig
facade is used for parsing Twig syntax, you may use the Twig::parse
method to render Twig to HTML.
$html = Twig::parse($twig);
The second argument can be used for passing variables to the Twig markup.
$html = Twig::parse($twig, ['foo' => 'bar']);
The Twig parser can be extended to register custom features via the plugin registration file.
Bracket parser
Winter also ships with a simple bracket template parser as an alternative to the Twig parser, currently used for passing variables to theme content blocks. This engine is faster to render HTML and is designed to be more suitable for non-technical users. There is no facade for this parser so the fully qualified Winter\Storm\Parse\Bracket
class should be used with the parse
method.
use Winter\Storm\Parse\Bracket;
$html = Bracket::parse($content, ['foo' => 'bar']);
The syntax uses singular curly brackets for rendering variables:
<p>Hello there, {foo}</p>
You may also pass an array of objects to parse as a variable.
$html = Template::parse($content, ['likes' => [
['name' => 'Dogs'],
['name' => 'Fishing'],
['name' => 'Golf']
]]);
The array can be iterated using the following syntax:
<ul>
{likes}
<li>{name}</li>
{/likes}
</ul>
YAML configuration parser
YAML ("YAML Ain't Markup Language") is a configuration format, similar to Markdown it was designed to be an easy-to-read and easy-to-write format that converts to a PHP array. It is used practically everywhere for the backend development of Winter, such as form field and list column definitions. An example of some YAML:
receipt: Acme Purchase Invoice
date: 2015-10-02
user:
name: Joe
surname: Blogs
The Yaml
facade is used for parsing YAML and you use the Yaml::parse
method to render YAML to a PHP array:
$array = Yaml::parse($yamlString);
Use the parseFile
method to parse the contents of a file:
$array = Yaml::parseFile($filePath);
The parser also supports operation in reverse, outputting YAML format from a PHP array. You may use the render
method for this:
$yamlString = Yaml::render($array);
Initialization (INI) configuration parser
The INI file format is a standard for defining simple configuration files, commonly used by components inside theme templates. It could be considered a cousin of the YAML format, although unlike YAML, it is incredibly simple, less sensitive to typos and does not rely on indentation. It supports basic key-value pairs with sections, for example:
receipt = "Acme Purchase Invoice"
date = "2015-10-02"
[user]
name = "Joe"
surname = "Blogs"
The Ini
facade is used for parsing INI and you use the Ini::parse
method to render INI to a PHP array:
$array = Ini::parse($iniString);
Use the parseFile
method to parse the contents of a file:
$array = Ini::parseFile($filePath);
The parser also supports operation in reverse, outputting INI format from a PHP array. You may use the render
method for this:
$iniString = Ini::render($array);
Winter flavored INI
Traditionally, the INI parser used by the PHP function parse_ini_string
is restricted to arrays that are 3 levels deep. For example:
level1Value = "foo"
level1Array[] = "bar"
[level1Object]
level2Value = "hello"
level2Array[] = "world"
level2Object[level3Value] = "stop here"
Winter has extended this functionality with Winter flavored INI to allow arrays of infinite depth, inspired by the syntax of HTML forms. Following on from the above example, the following syntax is supported:
[level1Object]
level2Object[level3Array][] = "Yay!"
level2Object[level3Object][level4Value] = "Yay!"
level2Object[level3Object][level4Array][] = "Yay!"
level2Object[level3Object][level4Object][level5Value] = "Yay!"
; ... to infinity and beyond!
Dynamic Syntax parser
Dynamic Syntax is a templating engine unique to Winter that fundamentally supports two modes of rendering. Parsing a template will produce two results, either a view or editor mode. Take this template text as an example, the inner part of the {text}...{/text}
tags represents the default text for the view mode, while the inner attributes, name
and label
, are used as properties for the editor mode.
<h1>{text name="websiteName" label="Website Name"}Our wonderful website{/text}</h1>
There is no facade for this parser so the fully qualified Winter\Storm\Parse\Syntax\Parser
class should be used with the parse
method. The first argument of the parse
method takes the template content as a string and returns a Parser
object.
use Winter\Storm\Parse\Syntax\Parser as SyntaxParser;
$syntax = SyntaxParser::parse($content);
View mode
Let's say we used the first example above as the template content, calling the render
method by itself will render the template with the default text:
echo $syntax->render();
// <h1>Our wonderful website</h1>
Just like any templating engine, passing an array of variables to the first argument of render
will replace the variables inside the template. Here the default value of websiteName
is replaced with our new value:
echo $syntax->render(['websiteName' => 'Winter CMS']);
// <h1>Winter CMS</h1>
As a bonus feature, calling the toTwig
method will output the template in a prepared state for rendering by the Twig engine.
echo $syntax->toTwig();
// <h1>{{ websiteName }}</h1>
Editor mode
So far the Dynamic Syntax parser is not much different to a regular template engine, however the editor mode is where the utility of Dynamic Syntax becomes more apparent. The editor mode unlocks a new realm of possibility, for example, where layouts inject custom form fields to pages that belong to them or for dynamically built forms used in email campaigns.
To continue with the examples above, calling the toEditor
method on the Parser
object will return a PHP array of properties that define how the variable should be populated, by a form builder for example.
$array = $syntax->toEditor();
// 'websiteName' => [
// 'label' => 'Website name',
// 'default' => 'Our wonderful website',
// 'type' => 'text'
// ]
You may notice the properties closely resemble the options found in form field definitions. This is intentional so the two features compliment each other. We could now easily convert the array above to YAML and write to a fields.yaml
file:
$form = [
'fields' => $syntax->toEditor()
];
File::put('fields.yaml', Yaml::render($form));
Supported tags
There are various tag types that can be used with the Dynamic Syntax parser, these are designed to match common form field types.
NOTE: Every tag except for the
{variable}
tag will render its value when in view mode. If you just want to store the value to be used elsewhere on your page in view mode, then it's recommended to use the{variable}
tag instead.
Variable
Renders the form field type exactly as defined in the type
attribute. This tag will simply set a variable and will render in view mode as an empty string.
{variable type="text" name="name" label="Name"}John{/variable}
Color picker
Color picker widget for color selection. This tag will contain the selected hexadecimal value. You may optionally provide an availableColors
attribute to define the available colours for selection.
{colorpicker name="bg_color" label="Background colour" allowEmpty="true" availableColors="#ffffff|#000000"}{/colorpicker}
Dropdown
Renders a dropdown form field.
{dropdown name="dropdown" label="Pick one" options="One|Two"}{/dropdown}
Renders a dropdown form field with independent values and labels.
{dropdown name="dropdown" label="Pick one" options="one:One|two:Two"}{/dropdown}
Renders a dropdown form field with an array returned by a static class method (the class must be a fully namespaced class).
{dropdown name="dropdown" label="Pick one" options="\Path\To\Class::method"}{/dropdown}
File upload
File uploader input for files. This tag value will contain the full path to the file.
{fileupload name="logo" label="Logo"}defaultlogo.png{/fileupload}
Markdown
Text input for Markdown content.
{markdown name="content" label="Markdown content"}Default text{/markdown}
Renders in Twig as
{{ content | md }}
Media finder
File selector for media library items. This tag value will contain the relative path to the file.
{mediafinder name="logo" label="Logo"}defaultlogo.png{/mediafinder}
Renders in Twig as
{{ logo | media }}
Radio
Renders a radio form field.
{radio name="radio" label="Thoughts?" options="y:Yes|n:No|m:Maybe"}{/radio}
Repeater
Renders a repeating section with other fields inside.
{repeater name="content_sections" prompt="Add another content section"}
<h2>{text name="title" label="Title"}Title{/text}</h2>
<p>{textarea name="content" label="Content"}Content{/textarea}</p>
{/repeater}
Renders in Twig as
{% for fields in repeater %}
<h2>{{ fields.title }}</h2>
<p>{{ fields.content | raw }}</p>
{% endfor %}
Calling $syntax->toEditor
will return a different array for a repeater field:
'repeater' => [
'label' => 'Website name',
'type' => 'repeater',
'fields' => [
'title' => [
'label' => 'Title',
'default' => 'Title',
'type' => 'text'
],
'content' => [
'label' => 'Content',
'default' => 'Content',
'type' => 'textarea'
]
]
]
The repeater field also supports group mode, to be used with the dynamic syntax parser as follows:
{variable name="sections" type="repeater" prompt="Add another section" tab="Sections"
groups="$/author/plugin/repeater_fields.yaml"}{/variable}
This is an example of the repeater_fields.yaml group configuration file:
quote:
name: Quote
description: Quote item
icon: icon-quote-right
fields:
quote_position:
span: auto
label: Quote Position
type: radio
options:
left: Left
center: Center
right: Right
quote_content:
span: auto
label: Details
type: textarea
For more information about the repeater group mode see Repeater Widget.
Rich editor
Text input for rich content (WYSIWYG).
{richeditor name="content" label="Main content"}Default text{/richeditor}
Renders in Twig as
{{ content | raw }}
Text
Single line input for smaller blocks of text.
{text name="websiteName" label="Website Name"}Our wonderful website{/text}
Textarea
Multiple line input for larger blocks of text.
{textarea name="websiteDescription" label="Website Description"}
This is our vision for things to come
{/textarea}
Data File Parser: Array
Winter CMS uses PHP array files (PHP files that do nothing except return a single array) for managing configuration and translation data files. In order to simplify working with these files programatically, Winter provides the Winter\Storm\Parse\PHP\ArrayFile
parser in the core.
ArrayFile
Load The ArrayFile
class can be used to modify a PHP array file. The ArrayFile::open()
method will initialize the ArrayFile
parser with the contents of the provided path (if the path does not exist it will be created on a call to $arrayFile->write()
).
use Winter\Storm\Parse\PHP\ArrayFile;
$arrayFile = ArrayFile::open('/path/to/file.php');
$arrayFile->set('foo', 'bar');
$arrayFile->write();
The ArrayFile::open()
method accepts a second argument $throwIfMissing
that defaults to false
. If true
, a \InvalidArgumentException
will be thrown if the provided $filePath
does not point to an existing file.
Set Array File values
Setting values can be chained or multiple values can be set by passing an array
ArrayFile::open('/path/to/file.php')
->set('foo', 'bar')
->set('bar', 'foo')
->write();
// or
ArrayFile::open('/path/to/file.php')->set([
'foo' => 'bar',
'bar' => 'foo'
])->write();
Multidimensional arrays
Multidimensional arrays can be set via dot notation, or by passing an array.
ArrayFile::open('/path/to/file.php')->set([
'foo.bar.a' => 'bar',
'foo.bar.b' => 'foo'
])->write();
// or
ArrayFile::open('/path/to/file.php')->set([
'foo' => [
'bar' => [
'a' => 'bar',
'b' => 'foo'
]
]
])->write();
Will output:
<?php
return [
'foo' => [
'bar' => [
'a' => 'bar',
'b' => 'foo',
]
]
];
env()
helper
Default values for If an array file has a env()
function call for a given key, setting the value of that key will set the default argument for the call to env()
rather than replacing the env()
call altogether.
For example, if the array file looks like:
<?php
return [
'foo' => [
'bar' => env('EXAMPLE_KEY'),
]
];
And then the following code is used to set the foo.bar
property:
ArrayFile::open('/path/to/file.php')->set([
'foo.bar' => 'Winter CMS',
])->write();
Will result in:
<?php
return [
'foo' => [
'bar' => env('EXAMPLE_KEY', 'Winter CMS'),
]
];
Function values
Function calls can be added to your config either via the PHPFunction
class or using the function()
helper method
on the ArrayFile
object.
use Winter\Storm\Parse\PHP\ArrayFile;
use Winter\Storm\Parse\PHP\PHPFunction;
ArrayFile::open('/path/to/file.php')->set([
'foo.bar' => new PHPFunction('env', ['argument1', 'argument1']),
])->write();
// or
$arrayFile = ArrayFile::open('/path/to/file.php');
$arrayFile->set([
'foo.bar' => $arrayFile->function('env', ['argument1', 'argument1']),
]);
$arrayFile->write();
Constant values
Constants can be added to your config either via the PHPConstant
class or using the constant()
helper method
on the ArrayFile
object.
use Winter\Storm\Parse\PHP\ArrayFile;
use Winter\Storm\Parse\PHP\PHPConstant;
ArrayFile::open('/path/to/file.php')->set([
'foo.bar' => new PHPConstant('PHP_OS'),
])->write();
// or
$arrayFile = ArrayFile::open('/path/to/file.php');
$arrayFile->set([
'foo.bar' => $arrayFile->constant('\Path\To\Class::VALUE'),
]);
$arrayFile->write();
Key sorting
The ArrayFile
object supports sorting the keys used in the file before rendering.
$arrayFile = ArrayFile::open('/path/to/file.php');
$arrayFile->set([
'b' => 'is awesome'
'a.b' => 'CMS',
'a.a' => 'Winter',
]);
$arrayFile->sort(ArrayFile::SORT_ASC);
$arrayFile->write();
Will write out:
<?php
return [
'a' => [
'a' => 'Winter',
'b' => 'CMS',
],
'b' => 'is awesome',
];
The sort method supports the following options:
-
ArrayFile::SORT_ASC
-
ArrayFile::SORT_DESC
- a callable function
By default, sort()
will use ArrayFile::SORT_ASC
.
ArrayFile
Write By default, calling $arrayFile->write()
will write the current state of the ArrayFile
to the path provided when it was initialized with ArrayFile::open($path)
.
If desired, you can specify a different path to write to as the first argument provided to the write($path)
method:
ArrayFile::open('/path/to/file.php')->set([
'foo.bar' => 'Winter CMS',
])->write('/path/to/another.file.php');
Render contents
If you require the PHP ArrayFile
contents as a string instead of writing directly to a file with write()
, the render()
method can be used.
$phpConfigString = ArrayFile::open('/path/to/file.php')->set([
'foo.bar' => 'Winter CMS',
])->render();
.env
Data File Parser: Winter supports the use of DotEnv files (.env
) to manage environment specific variables.
Getting these values is as easy as using the env()
helper function. Winter also provides a way to programmatically set the values in the .env
file through the use of the Winter\Storm\Parse\EnvFile
parser in the core.
EnvFile
Load The EnvFile
class can be used to modify a PHP array file. The EnvFile::open()
method will initialize the EnvFile
parser with the contents of the provided path (if the path does not exist it will be created on a call to $envFile->write()
).
By default, the .env
file interacted with will be base_path('.env')
, this can be changed if required by passing the path to the open()
method.
Set values in Env file
Values can be set either one at a time or by passing an array of values to set.
$env = EnvFile::open();
$env->set('FOO', 'bar');
$env->set('BAR', 'foo');
$env->write();
// or
EnvFile::open()->set([
'FOO' => 'bar'
'BAR' => 'foo'
])->write();
NOTE: Array dot notation and nested arrays are not supported by
EnvFile
use Winter\Storm\Config\EnvFile;
$env = EnvFile::open();
$env->set('FOO', 'bar');
$env->write();
NOTE: Values are set in the order they are provided, automatic sorting is not currently supported because comments and empty lines make that a lot more complex.
Empty lines
It is also possible to add empty lines into the env file, usually for organizational purposes:
$env = EnvFile::open();
$env->set('FOO', 'bar');
$env->addEmptyLine();
$env->set('BAR', 'foo');
$env->write();
Will output:
FOO="bar"
BAR="foo"
EnvFile
Write By default, calling $envFile->write()
will write the current state of the EnvFile
to the path provided when it was initialized with EnvFile::open($path)
. (defaulting to base_path('.env')
when no $path
is provided).
If desired, you can specify a different path to write to as the first argument provided to the write($path)
method:
EnvFile::open()->set([
'FOO' => 'bar',
])->write('/path/to/.env.alternative');
EnvFile
contents
Render If you require the EnvFile
contents as a string; instead of writing directly to a file with write()
, the render()
method can be used.
$envFileString = EnvFile::open()->set([
'APP_NAME' => 'Winter CMS',
])->render();
Get variables
The EnvFile
parser provides a getVariables()
method that can be used to get an associative array of the current variables excluding any comments or empty lines.
$envVariables = EnvFile::open()->set([
'APP_NAME' => 'Winter CMS',
])->getVariables();
echo $envVariables['APP_NAME']; // echos "Winter CMS"