Ian P. Christian's Personal Blog Random witterings from pookey

30Mar/091

YAML in PHP – fun with sfYaml

In a recent post about Symfony components - Fabien wrote about how the Symfony project (yes, that's right - a capital S now!) will be releasing more and more components. In this post I introduce sfYaml, a YAML parser for PHP - building on my previous blog post about routing.

Our goal within this post is to convert a routing.yml configuration file into an collection of objects we could use to handle routing URLs to actions. First, we need to get a copy of the sfYaml code - we'll get this from SVN.

$ svn co http://svn.symfony-project.com/components/yaml/trunk/ .
A    test
A    test/sfYamlInlineTest.php
... <snipped> ...
A    lib/sfYamlDumper.php
A    lib/sfYamlParser.php
 
Fetching external item into 'test/lime'
A    test/lime/lime.php
Checked out external at revision 16768.
 
Checked out revision 16768.

Now we will create a very simple YAML file, which we will use to define routing rules. Our config file in the example will be identical to symfony's routing.yml - so should be familiar with symfony users, but straight forward enough for people who aren't familiar with symfony.

# default rules
homepage:
  url:   /  
  param: { module: main, action: index }
 
default_index:
  url:   /:module
  param: { action: index }
 
default:
  url:   /:module/:action/*

We will use sfYaml to read this file, and then create a sfPatternRouting object containing all our routes. First, let us see just how easy it is to read parse a YAML file:

<?php
require_once('lib/sfYaml.php');
 
$data = sfYaml::load('routing.yml');
var_dump($data);

The output:

array(3) {
  ["homepage"]=>
  array(2) {
    ["url"]=>
    string(1) "/"
    ["param"]=>
    array(2) {
      ["module"]=>
      string(4) "main"
      ["action"]=>
      string(5) "index"
    }
  }
  ["default_index"]=>
  array(2) {
    ["url"]=>
    string(8) "/:module"
    ["param"]=>
    array(1) {
      ["action"]=>
      string(5) "index"
    }
  }
  ["default"]=>
  array(1) {
    ["url"]=>
    string(18) "/:module/:action/*"
  }
}

Now we'll build our sfPatternRouting object up, which is pretty much just a case of looping though this array:

<?php
 
require_once('lib/sfYaml.php');
$sfpath = '../lib/vendor/symfony/lib/';
require_once($sfpath.'routing/sfRoute.class.php');
require_once($sfpath.'routing/sfRouting.class.php');
require_once($sfpath.'routing/sfPatternRouting.class.php');
require_once($sfpath.'event/sfEvent.class.php');
require_once($sfpath.'event/sfEventDispatcher.class.php');
 
 
$data = sfYaml::load('routing.yml');
 
$routing = new sfPatternRouting(new sfEventDispatcher());
foreach($data as $routeName => $routeData)
{
  if (!array_key_exists('url', $routeData))
  {
    throw InvalidArgumentException(sprintf('Route name %s is missing the "url" value',
      $routeName));
  }
  // if no params defined, default to empty array
  $params = isset($routeData['params']) ? $routeData['params'] : array();
  $route = new sfRoute($routeData['url'], $params);
  $routing->connect($routeName, $route);
}

This code is really simple, and to prove it works, we can test it as below:

ret = $routing->parse('/moo/cow');
foreach($ret as $key => $val)
{
  if (!is_object($val))
  {
    echo "{$key} :: {$val}\n";
  }
}

Which shows the output you might expect:

module :: moo
action :: cow

sfYaml can do a little more than just this of course, and I suggest you look at the test code and the class source itself. As always, the best way to learn the features of code, is to actually read the source. This article should have given you a taste for just how easy it is, and got you up and running sufficiently to play with the classes yourself.

Note for symfony users: You might look at this example, and think that this is how symfony handles it's routing - but actually, it's not! symfony uses the sfRoutingConfigHandler class, which handles generating PHP code from the YAML, which is written out into the cache folder. This means that the YAML file is not parsed on each request. Below is the code that symfony 1.2 (yes, lower case 's' for symfony 1.2! ;) ) generates from our routing.yml - just so you can see how it does it. After this code is generated, it can simply be included:

<?php
// auto-generated by sfRoutingConfigHandler
// date: 2009/03/30 18:57:48
return array(
'homepage' => new sfRoute('/', array (
  'module' => 'main',
  'action' => 'index',
), array (
), array (
)),
'default_index' => new sfRoute('/:module', array (
  'action' => 'index',
), array (
), array (
)),
'default' => new sfRoute('/:module/:action/*', array (
), array (
), array (
)),
);
Filed under: php, symfony Leave a comment
Comments (1) Trackbacks (0)
  1. Thanks, your post is interesting :)

    Till now I’m using sfYaml just to create and manage easy config files, and adapted to them a class of mine to dialogue with mysql db. I’m thinkin’ on new uses like you did.

    Cheers.


Leave a comment

No trackbacks yet.