Parse YAML in PHP Using Symfony YAML

Convert data in YAML format into a PHP array.

I do a lot of work in WordPress. I also build a lot of static websites – both for rapid design in-the-browser and as a low-cost small-business website solution. I mainly use the excellent Jekyll static site generator.

Jekyll uses YAML for config and data files (it can also use CSV format, but that’s another story). WordPress doesn’t use YAML.

I like YAML because it is very human friendly – the whole team (including non-developers) can easily build YAML config files in a way that you’re not going to see with formats like JSON or XML. This is an example of a YAML array used to create Javascript variables for use in a Google map:


# Map centre Latitude & Longitude
# ------------------------------------------------------------------------------
latitude: 52.7157856867271
longitude: -8.8741735070805

zoom: 15
#height: 548px

# Set custom colour variables
# ------------------------------------------------------------------------------
waterColour: "#398A8D"
landColour: "#dec7c7"
mainRoadColour: "#777777"
minorRoadColour: "#a9a9a9"

# A nested array
# ------------------------------------------------------------------------------
test:
- One
- Two
- Three

In the context of Jekyll, you could place this data in a data file – e.g./_data/map.yml – writing Javascript variables into <head> something like this:


<script>
var cwCentre = {
latitude:{{ site.data.map.latitude }},
longitude:{{ site.data.map.longitude }},
zoom:{{ page.map-zoom }},
mainMarker:"{{ site.baseurl}}/{{ site.data.map.mainMarker }}",
secondaryMarker:"{{ site.baseurl}}/{{ site.data.map.secondaryMarker }}",
waterColour:"{{ site.data.map.waterColour }}",
landColour:"{{ site.data.map.landColour }}",
mainRoadColour:"{{ site.data.map.mainRoadColour }}",
minorRoadColour:"{{ site.data.map.minorRoadColour }}",
title: "{{ site.data.map.title | escape }}",
description:'{{ map_description | markdownify | strip_newlines }}',
};
{% if "multi-centre" == page.map-type %}
var markers = [
{% for location in site.data.secondary-map-coords %}
[
'{{ location.name | escape }}',
{{ location.latitude }},
{{ location.longitude }},
'{{ location.description | escape }}'
]
{% unless forloop.last %},{% endunless %}
{% endfor %}
];
{% endif %}
</script>

YAML in WordPress

I recently needed to convert a Jekyll site to a WordPress theme. Moving the map config settings required parsing YAML data into a PHP array. Fortunately this can be achieved pretty easily thanks to the Symfony YAML component.

I’m a recent convert to Composer, and find it amazingly powerful. You can add the Symfony YAML component with a single composer command.

Add Symfony/YAML Using Composer


composer require symfony/yaml

When you run this, composer will add a new `symfony/yaml` directory under the project ‘vendor’ directory. It will also add the relevant namespace to the ‘autoload_psr4.php’ file, so that the new class will be autoloaded.

Using the YAML parser

To read the YAML contents of the config fields into a PHP array:


<?php
use Symfony\Component\Yaml\Parser;

$yaml = new Parser();

$value = $yaml->parse( file_get_contents( get_template_directory() . '/assets/map.yml' ) );

For the YAML content presented above, the following will be output:


$value = array (
'latitude' => 52.715785686727102,
'longitude' => -8.8741735070804992,
'zoom' => 15,
'waterColour' => '#398A8D',
'landColour' => '#dec7c7',
'mainRoadColour' => '#777777',
'minorRoadColour' => '#a9a9a9',
'test' => array (
0 => 'One',
1 => 'Two',
2 => 'Three',
),
);

This array can be passed to wp_localize_script() when enqueuing the map script.

The WordPress/PHP way would be to collect such data from a form on an admin page, storing the data in the wp_options table. However taking variables from YAML files can be a good way to quickly port settings, which might even be used as defaults. It might also be a good way to configure certain project settings.

Adding a .htaccess File to a Jekyll Site

How to have Jekyll build a .htaccess file into your project

If you serve your site with Apache, adding a .htaccess file to your document root allows fine control over access permissions.

Amongst other things, .htaccess rules can set:

  • In-browser caching
  • Access – you could allow/disallow access from certain IP addresses or ranges
  • Redirects
  • Rewrites

You can also prevent modification of code over 3G on some European providers (I’ve experienced UK providers in particular totally mangling site styles).

When setting up WordPress sites, I would typically lock down access to the entire admin area by IP address as a security measure. While this isn’t necessary for Jekyll sites (where there is no login), .htaccess rules can be a useful way of controlling how your site resources are cached. This has the potential to speed up site loading times.

.htaccess in Jekyll

Update: by default, Jekyll includes .htaccess files – so explicitly including your .htaccess file is unnecessary.

By default Jekyll excludes dotfiles – but you can easily override this behaviour.

In the project config.yml (or config_prod.yml if you have a production environment config file), add this line:


include: ['.htaccess']

Jekyll will now build the .htaccess file – which is much more convenient than editing the file on the server.

Create .htaccess file in the root of your Jekyll project.

When you build  the project, the .htaccess file will be included in the project root.

Sample .htaccess for Cache Control

The following .htaccess directive tells browsers to use cached content for the specified files. Just add the directive to the project .htaccess file:


# Set browser caching
# ------------------------------------------------------------------------------
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 month"
ExpiresByType text/html "access 1 month"
ExpiresByType application/pdf "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresByType application/x-shockwave-flash "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 month"
</IfModule>
# End caching block