Symfony2 components overview: Templating

In the 7th post of this series we are going to talk about a very important component for modern PHP frameworks. This component is usually hidden under a few abstractions to make the life of framework users easier, we are talking about the Templating component.

Templating engines allow to create representations of data

The Templating component

The Templating component provides an engine-agnostic infrastructure to load template files and render them using the input variables. Although the component provides methods for creating template systems, we are going to focus on how to integrate third-party template engines like Twig and Smarty, and how to use the right engine based on the templates file extension.

Templates using PHP

Using PHP to generate templates has been common practice in the PHP community and although this approach has a few advantages, such as not having to learn another language and that templates can be executed without having to compile them, there are also some drawbacks. Some of the drawbacks are:

  • PHP templates are usually more verbose, especially when dealing with output escaping:
    <?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
    
  • Missing features such as template inheritance, layouts, filters, and extensions.
  • Difficult for web designers who don’t know PHP.

The component provides a default engine to use PHP for templates improving some of these limitations and providing ways to escape output easily, include subtemplates, and common helpers.

Let’s create the simplest template possible, one that says hello using the variable $name, which is passed by the controller:

Hello <?php echo $name ?>! From a humble PHP template.

Then, to render the template:

<?php

use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\FilesystemLoader;

include_once __DIR__. '/vendor/autoload.php';

$loader = new FilesystemLoader(__DIR__.'/views/%name%');
$templating = new PhpEngine(new TemplateNameParser(), $loader);

echo $templating->render('hello.php', array('name' => 'Raul'));

It outputs:

Hello Raul! From a humble PHP template.

Here’s an overview of what happened under the hood. First, we create the PhpEngine instance with the required parameters, a couple of objects implementing the TemplateNameParserInterface and LoaderInterface interfaces. Don’t be afraid by all this code, it’s much simpler than it seems.

$loader is an instance of FilesystemLoader, it defines where the templates are – in our example, templates are in the view directory. The TemplateNameParser object converts a string containing the template filename to a TemplateReference object.

Finally, we execute the render method, which renders the template ‘views/hello.php’ with the ‘name’ variable set to ‘Raul’.

This complex setup is the price we pay when working with Symfony components, but as we will see in the next section, this design makes adding new templating engines really easy.

Adding other template engines

When adding new template engines, we create bridges that connect the engine itself with the component. This is done implementing the three methods defined in the EngineInterface interface:

  • render($name, array $parameters = array()): It’s the method in charge of actually rendering the template with the name $name passing the array $parameters as variables.
  • exists($name): Checks if the template with the name $name exists.
  • supports($name): Returns true if the engine bridge is able to process the template with the name $name. Usually, this is done checking the file extension. When there are several engine bridges this method is called to determine which one to use.

So, if we want to add support for Twig templates we just have to implement these methods.

<?php

namespace RaulFraile\Templating;

use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Templating\TemplateReferenceInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;

use Twig_Loader_Filesystem;
use Twig_Environment;

class TwigEngine implements EngineInterface
{

    protected $environment;
    protected $parser;

    /**
     * Constructor.
     *
     * @param \Twig_Environment           $environment A \Twig_Environment instance
     * @param TemplateNameParserInterface $parser      A TemplateNameParserInterface instance
     * @param string                      $dir         Templates directory
     */
    public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser, $dir)
    {
        $loader = new Twig_Loader_Filesystem($dir);

        $this->environment = $environment;
        $this->environment->setLoader($loader);

        $this->parser = $parser;
    }

    /**
     * Renders a template.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     * @param array $parameters An array of parameters to pass to the template
     *
     * @return string The evaluated template as a string
     *
     * @throws \RuntimeException if the template cannot be rendered
     *
     * @api
     */
    public function render($name, array $parameters = array())
    {
        return $this->environment->loadTemplate((string) $name)->render($parameters);
    }

    /**
     * Returns true if the template exists.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     *
     * @return Boolean true if the template exists, false otherwise
     *
     * @throws \RuntimeException if the engine cannot handle the template name
     *
     * @api
     */
    public function exists($name)
    {
        $loader = $this->environment->getLoader();

        return $loader->exists($name);
    }

    /**
     * Returns true if this class is able to render the given template.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     *
     * @return Boolean true if this class supports the given template, false otherwise
     *
     * @api
     */
    public function supports($name)
    {
        if ($name instanceof \Twig_Template) {
            return true;
        }

        $template = $this->parser->parse($name);

        return 'twig' === $template->get('engine');
    }

}

To be able to use dependency injection later, the newly created TwigEngine needs an instance of the Twig_Environment and an object that implements TemplateNameParserInterface, which is used to get the engine name – based on the extension. It also accepts a directory where the templates can be found.

The same could be applied to any engine we want to support. For example, a simple engine bridge for Smarty templates would look like this:

<?php

namespace RaulFraile\Templating;

use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Templating\TemplateReferenceInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
use Smarty;

class SmartyEngine implements EngineInterface
{

    protected $smarty;
    protected $parser;

    /**
     * Constructor.
     *
     * @param Smarty                      $environment A Smarty instance
     * @param TemplateNameParserInterface $parser      A TemplateNameParserInterface instance
     * @param string                      $dir         Templates directory
     */
    public function __construct(Smarty $smarty, TemplateNameParserInterface $parser, $dir)
    {
        $this->smarty = $smarty;
        $this->smarty->setTemplateDir($dir);

        $this->parser = $parser;
    }

    /**
     * Renders a template.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     * @param array $parameters An array of parameters to pass to the template
     *
     * @return string The evaluated template as a string
     *
     * @throws \RuntimeException if the template cannot be rendered
     *
     * @api
     */
    public function render($name, array $parameters = array())
    {
        $this->smarty->assign($parameters);

        return $this->smarty->fetch($name);
    }

    /**
     * Returns true if the template exists.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     *
     * @return Boolean true if the template exists, false otherwise
     *
     * @throws \RuntimeException if the engine cannot handle the template name
     *
     * @api
     */
    public function exists($name)
    {
        return $this->smarty->templateExists($name);
    }

    /**
     * Returns true if this class is able to render the given template.
     *
     * @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
     *
     * @return Boolean true if this class supports the given template, false otherwise
     *
     * @api
     */
    public function supports($name)
    {
        $template = $this->parser->parse($name);

        return 'tpl' === $template->get('engine');
    }

}

To use them in a controller:

<?php

use Symfony\Component\Templating\TemplateNameParser;
use RaulFraile\Templating\TwigEngine;
use RaulFraile\Templating\SmartyEngine;

include_once __DIR__. '/vendor/autoload.php';

$templatesDir = __DIR__.'/views/';
$templateNameParser = new TemplateNameParser();

$templating = new TwigEngine(new Twig_Environment(), $templateNameParser, $templatesDir),
// or $templating = new SmartyEngine(new \Smarty(), $templateNameParser, $templatesDir);

echo $templating->render('hello.php', array('name' => 'Raul'));
// or echo $templating->render('hello.tpl', array('name' => 'Raul'));

Choosing the right engine

We have a few engine bridges, but how to choose the right one? Frameworks usually allow us to use the template engine of our choice. For example, in Symfony2 full-stack framework, we can use both Twig and PHP templates simply by changing the filename:

$this->render('AcmeBundle:Hello:index.html.twig', array('name' => $name));

// or

$this->render('AcmeBundle:Hello:index.html.php', array('name' => $name));

Symfony knows which engine to use based on the extension. The component provides a way to follow this approach using the DelegatingEngine class and the supports() method of the engine bridges that we just implemented.

<?php

use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\DelegatingEngine;

use RaulFraile\Templating\TwigEngine;
use RaulFraile\Templating\SmartyEngine;

include_once __DIR__. '/vendor/autoload.php';

$templatesDir = __DIR__.'/views/';
$templateNameParser = new TemplateNameParser();

$engines = new DelegatingEngine(array(
    new PhpEngine($templateNameParser, new FilesystemLoader($templatesDir . '%name%')),
    new TwigEngine(new Twig_Environment(), $templateNameParser, $templatesDir),
    new SmartyEngine(new \Smarty(), $templateNameParser, $templatesDir)
));

echo $engines->render('hello.php', array('name' => 'Raul')) . PHP_EOL;

The render() method of DelegatingEngine will iterate over all the defined engine bridges and call to their supports() method. As soon as it finds an engine capable of handling that template, executes the render() method of that engine.

An alternative for PHP templates: Plates

If you prefer to use native PHP templates but miss some features of compiled ones such as template inheritance, layouts or plugins/extensions, it is worth taking a look at Plates, which is a PHP templating system inspired by Twig.

If we want to provide Plates as the default engine for PHP templates, all we have to do is create a new engine that implements the EngineInterface interface and accepts ‘*.php’ templates.

<?php

namespace RaulFraile\Templating;

use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Templating\TemplateReferenceInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
use League\Plates;

class PlatesEngine implements EngineInterface
{

    protected $engine;
    protected $parser;

    public function __construct(Plates\Engine $engine, TemplateNameParserInterface $parser, $dir)
    {
        $this->engine = $engine;
        $this->engine->setDirectory($dir);
        $this->engine->setFileExtension(null);

        $this->parser = $parser;
    }

    public function render($name, array $parameters = array())
    {
        $template = new Plates\Template($this->engine);
        $template->data($parameters);

        return $template->render($name);
    }

    public function exists($name)
    {
        return $this->engine->pathExists($name);
    }

    public function supports($name)
    {
        $template = $this->parser->parse($name);

        return 'php' === $template->get('engine');
    }
}

Calling the setFileExtension() method with null is necessary in our example as we are already using the extension to choose the template engine, so we want Plates to use hello.php instead of hello.php.php.

Then, we just replace the PhpEngine object by the PlatesEngine one.

<?php

use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\DelegatingEngine;

use RaulFraile\Templating\TwigEngine;
use RaulFraile\Templating\SmartyEngine;
use RaulFraile\Templating\PlatesEngine;

use League\Plates;

include_once __DIR__. '/vendor/autoload.php';

$templatesDir = __DIR__.'/views/';
$templateNameParser = new TemplateNameParser();

$engines = new DelegatingEngine(array(
    new PlatesEngine(new Plates\Engine(), $templateNameParser, $templatesDir),
    new TwigEngine(new Twig_Environment(), $templateNameParser, $templatesDir),
    new SmartyEngine(new \Smarty(), $templateNameParser, $templatesDir)
));

echo $engines->render('hello.php', array('name' => 'Raul')) . PHP_EOL;

Even though the template is the same, we can now use all the features of Plates.

Who’s using it?

Conclusion

The Templating component comes very handy when we need to provide different template engines, especially in frameworks and projects that can be extended somehow with templates, such as static blog generators or plugin systems.

More info

Photo: Matt Cornock

marzo 11 / 2014

New TLDs

NewTLDs

Upcoming Conferences

CraftingTour

CraftingTour

PHP New Zealand









Symfony2 components overview: Validator

In the first five posts of this series we have been talking about key components for any PHP framework from the point of view of their internals, such as HttpFoundation to abstract the HTTP protocol, HttpKernel to convert a Request into a Response, Routing to map requests to controllers, EventDispatcher to add reusability and extensibility, and Config to load and validate configuration values. This time we’ll delve deeper in the user space to describe a component that is for specific apps rather than just for frameworks. Today’s topic will be the Validator component.

It is not always easy to know what and how to validate a given input

The problem

Every single application that accepts data from a source must implement some sort of validation. It does not matter that your data comes from users or from an online API, you always run the risk your data might be tainted.

We all know the catastrophic consequences we can face if we don’t filter input data, from inconsistency in our database to security disasters caused by SQL injections, Cross-Site Scripting (XSS) or Cross-Site Request Forgery (CSRF), to name just a few. Even though the Validator component responsibilities fall under the first set of problems – to keep data consistency -, any non-filtered input data can trigger security issues.

Any application must validate input data. Some of this validations like emails, urls or ISBNs are so common that is a waste of time to code them again, as probably someone else has already made it before. The Validator component provides an extensive list of validations and a way to use them easily.

Architecture

The component it is based on the JSR-303 specification and each validation is composed of one or more constraints, which are validated against a validator. For example, in your database, your employees table must have only email addresses from your company, so you can use the default email constraint to check that the given address is valid and a second constraint that checks the domain. With the Validator component, this is done in two parts:

  • Constraints
    A constraint describes the rule. It is just a PHP object that makes an assertive statement, such as “the email must be valid”.
  • Validators
    Validators implement the validation logic. In the email example, the validator class will use filter_var($email, FILTER_VALIDATE_EMAIL) to check if the email is valid.

Simple example

<?php 

use Symfony\Component\Validator\Validation; 
use Symfony\Component\Validator\Constraints\Range; 

include_once __DIR__. '/vendor/autoload.php'; 

$validator = Validation::createValidator(); 
$constraint = new Range(array(
    'min' => 1,
    'max' => 10
));

$violations = $validator->validateValue(15, $constraint);

The $violations variable will have a ConstraintViolationList object with one ConstraintViolation object for each violation. If there are no violations, the list will be empty, but it is important to remember that for consistency, we will always get a ConstraintViolationList object, not null, false or 0.

In the last example, the $violations variable will contain one violation:

class Symfony\Component\Validator\ConstraintViolationList#10 (1) {
  private $violations =>
  array(1) {
    [0] =>
    class Symfony\Component\Validator\ConstraintViolation#13 (8) {
      private $message =>
      string(32) "This value should be 10 or less."
      private $messageTemplate =>
      string(41) "This value should be {{ limit }} or less."
      private $messageParameters =>
      array(2) {
        ...
      }
      private $messagePluralization =>
      NULL
      private $root =>
      int(11)
      private $propertyPath =>
      string(0) ""
      private $invalidValue =>
      int(11)
      private $code =>
      NULL
    }
  }
}

The ConstraintViolationList class implements Traversable, Countable and ArrayAccess interfaces. That means that we can work with the object like it was an array:

var_dump(count($violations));
var_dump(empty($violations));

foreach ($violations as $violation) {
    var_dump($violation->getMessage());
}
int(1)
bool(false)
string(32) "This value should be 10 or less."

Built-in validations

The component is shipped with the most commonly-needed constraints. Basic constraints to check that an email is valid – even checking host and MX records if needed -, the length of a string, number ranges, list of choices and many more are available out of the box.

Internationalization

We will talk more about the Translation component in future posts, but for now you must know that any object implementing the TranslatorInterface interface can be injected into the validator. The trans method of the object will be called to get the translation. Let’s see an example:

<?php 

use Symfony\Component\Validator\Validation; 
use Symfony\Component\Validator\Constraints\Range; 
use Symfony\Component\Translation\Loader\XliffFileLoader; 
use Symfony\Component\Translation\Translator; 

include_once __DIR__. '/vendor/autoload.php'; 

$translator = new Translator('es_ES'); 
$translator->addLoader('xliff', new XliffFileLoader());
$translator->addResource('xliff', __DIR__ . '/vendor/symfony/validator/Symfony/Component/Validator/Resources/translations/validators.es.xlf', 'es');

$validator = Validation::createValidatorBuilder()
    ->setTranslator($translator)
    ->getValidator();

$rangeConstraint = new Range(array(
    'min' => 1,
    'max' => 10
));

$violations = $validator->validateValue(11, $rangeConstraint);

var_dump($violations[0]->getMessage());

In this example, we want to show error messages in Spanish. To do so, we create the translator object and configure it to use the default XLIFF translation files provided by the component. Obviously, for simplicity, this is a dirty way to reference the file and, based on your application, you should find a better way to do it without relying on a Composer folder called vendor or that the autoload method is going to be PSR-0. Finally, we inject the translator and use the validator as usual. We get as the output the error message in Spanish:

string(35) "Este valor debería ser 10 o menos."

Validating objects

Most of the times, you will need to validate full objects instead of single values before persisting it in the database. How does the validator know what rules apply to each property? We need to create a mapping that links class properties with sets of constraints. It can be done using YAML, XML, annotations or PHP, in our example we will do it using YAML:

RaulFraile\Foo:
    properties:
        email:
            - NotBlank: ~
            - Email: ~
        age:
            - Range:
                min: 18

Once we have the mapping we need to tell the validator what kind of mapping we are using and where to find it:

<?php 

use Symfony\Component\Validator\Validation; 
use Symfony\Component\Validator\Constraints\Range; 
use RaulFraile\Foo; 

include_once __DIR__. '/vendor/autoload.php'; 

$obj = new Foo(); $obj->setEmail('raul@servergrove');
$obj->setAge(17);

$validator = Validation::createValidatorBuilder()
    ->addYamlMapping(__DIR__ . '/src/RaulFraile/validation/Foo.yml')
    ->getValidator();

$violations = $validator->validate($obj);

var_dump($violations);

Finally, we call the validate method – not validateValue – and get the list of violations as usual. In this case, the email is not correct and the age is less than the minimum allowed:

class Symfony\Component\Validator\ConstraintViolationList#11 (1) {
  private $violations =>
  array(2) {
    [0] =>
    class Symfony\Component\Validator\ConstraintViolation#25 (8) {
      private $message =>
      string(40) "This value is not a valid email address."
      private $messageTemplate =>
      string(40) "This value is not a valid email address."
      private $messageParameters =>
      array(1) {
        ...
      }
      private $messagePluralization =>
      NULL
      private $root =>
      class RaulFraile\Foo#2 (2) {
        ...
      }
      private $propertyPath =>
      string(5) "email"
      private $invalidValue =>
      string(16) "raul@servergrove"
      private $code =>
      NULL
    }
    [1] =>
    class Symfony\Component\Validator\ConstraintViolation#29 (8) {
      private $message =>
      string(32) "This value should be 18 or more."
      private $messageTemplate =>
      string(41) "This value should be {{ limit }} or more."
      private $messageParameters =>
      array(2) {
        ...
      }
      private $messagePluralization =>
      NULL
      private $root =>
      class RaulFraile\Foo#2 (2) {
        ...
      }
      private $propertyPath =>
      string(3) "age"
      private $invalidValue =>
      int(17)
      private $code =>
      NULL
    }
  }
}

Custom validations

The list of built-in constraints is huge and keeps growing, but if you need custom validations, it’s as easy as creating the corresponding classes for the constraint and the validation logic. For example, let’s create a simple validation to check that a programming language starts with ‘p’, or is in a fixed list of allowed languages – I know it is weird, but it’s just an example :) -.

First, we create the constraint, which defines the error message and the list of available programming languages:

<?php

namespace RaulFraile\Constraints;

use Symfony\Component\Validator\Constraint;

class ProgrammingLanguage extends Constraint
{
    public $message = 'The string "%string%" is not an accepted programming language';
    public $languages = array('php', 'javascript');
}

Then, the validation logic:

<!?php 

namespace RaulFraile\Constraints; 

use Symfony\Component\Validator\Constraint; 
use Symfony\Component\Validator\ConstraintValidator; 

class ProgrammingLanguageValidator extends ConstraintValidator {
    
    public function validate($value, Constraint $constraint) 
    {
        if (!(!empty($value) && $value[0] == 'p') && !in_array($value, $constraint->languages)) {
            $this->context->addViolation(
                $constraint->message,
                array('%string%' => $value)
            );
        }
    }
}

That’s it! If it is not an accepted programming language, we add a new violation with the error message, which will be appended to the list.

If we follow the convention and the validator class has the same name as the constraint class appending “Validator” at the end, it will automatically work. In our previous example, we add a lang property to the Foo class and include the constraint in the class validation definition:

RaulFraile\Foo:
    properties:
        ...
        lang:
            - RaulFraile\Constraints\ProgrammingLanguage: ~

Voila! Our example will work if the lang property is not valid:

<?php

$obj = new Foo(); 
$obj->setEmail('raul@servergrove.com');
$obj->setAge(18);
$obj->setLang('perlang');

$violations = $validator->validate($obj);

Will output:

class Symfony\Component\Validator\ConstraintViolationList#11 (1) {
  private $violations =>
  array(1) {
    [0] =>
    class Symfony\Component\Validator\ConstraintViolation#33 (8) {
      private $message =>
      string(59) "The string "erlang" is not an accepted programming language"
      private $messageTemplate =>
      string(61) "The string "%string%" is not an accepted programming language"
      private $messageParameters =>
      array(1) {
        ...
      }
      private $messagePluralization =>
      NULL
      private $root =>
      class RaulFraile\Foo#2 (3) {
        ...
      }
      private $propertyPath =>
      string(4) "lang"
      private $invalidValue =>
      string(6) "erlang"
      private $code =>
      NULL
    }
  }
}

Who is using it

More info

Photo: Jack Spades – Security Features of the US Twenty Dollar Bill

marzo 03 / 2014

New TLDs

NewTLDs

Upcoming Conferences

CraftingTour

CraftingTour

PHP New Zealand









Symfony2 components overview: Config

After a few weeks, we continue with the Symfony2 components series. This time we are going to be talking about the Config component, which helps you to load and validate configuration values, regardless of their source.

The Nazi’s Enigma Machine had 158,962,555,217,826,360,000 different configurations to cypher messages

Let’s imagine we want to create a blog generator system, which will take a few configuration parameters such as title, description, number of posts shown in the main page, social media icons, and whether the blog will have a RSS channel or not. With that in mind, we could create a YAML configuration file like this:

blog:
    title: My blog
    description: This is just a test blog
    rss: true
    posts_main_page: 2
    social:
        twitter:
            url: http://twitter.com/raulfraile
            icon: twitter.png
        sensiolabs_connect:
            url: https://connect.sensiolabs.com/profile/raulfraile
            icon: sensiolabs_connect.png

Now, we would need to parse the file, check that the required fields are present, set a default value for optional fields and perform by-field validations, such as the rss field must be a boolean or posts_main_page an integer between 1 and 10. In addition, we need to do it every time the script runs, unless we use any sort of caching system. It gets even more trickier if we want to support additional formats like INI, XML or JSON.

To avoid all this we use the config component, so you don’t have to code all of this yourself. It is simple, well tested, and flexible enough to cover the needs of any project.

Architecture

The component is divided in two main parts:

  • Definition of the configuration values hierarchy

    The component permits to define the format of the configuration resource, which basically can be anything, from a simple INI file to a something more esoteric like protocol-specific configuration messages. Using the TreeBuilder builder we can define the types of the values, make them required/optional and set a default value.
  • Locate, load and process configuration resources

    Once the configuration resource has been defined, must be located, loaded and processed to check that the values comply with the defined format. At the end, the component will give us a plain array with the validated values or an exception if there is an error.

Example

Let’s go back to our example. We wanted to create a highly configurable blog generator system, so it’s time to define the configuration values. For that, we use a TreeBuilder instance, which provides an easy DSL for that.

<?php

namespace RaulFraile\Config;

use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('blog');

        $rootNode
            ->children()
                ->scalarNode('title')
                    ->isRequired()
                ->end()
                ->scalarNode('description')
                    ->defaultValue('')
                ->end()
                ->booleanNode('rss')
                    ->defaultValue(false)
                ->end()
                ->integerNode('posts_main_page')
                    ->min(1)
                    ->max(10)
                    ->defaultValue(5)
                ->end()
                ->arrayNode('social')
                    ->prototype('array')
                        ->children()
                            ->scalarNode('url')->end()
                            ->scalarNode('icon')->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ;

        return $treeBuilder;
    }
}

Whoa! Don’t worry too much if it is the first time you see this kind of code in PHP, DSLs always look a bit weird in PHP. What we are doing here is to define a root node, called “blog”, and the format of all the configuration values – nodes -. We expect a scalar value for the “title” and is required, “description” is optional and will be empty if not present, “rss” must be a boolean – true/false – and will be false by default while “posts_main_page” must be an integer between 1 and 10, with 5 by default.

Ok, we have the definition, let’s load and process it. As the configuration resource can be anything, we need to define how to convert that resource into a plain array, that will be validated and processed later using our definition. There must be one class for each format, so if we allow YAML and XML files, we would need to create two classes. In our example we’ll use only YAML for simplicity:

<?php

namespace RaulFraile\Config;

use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Yaml\Yaml;

class YamlConfigLoader extends FileLoader
{
    public function load($resource, $type = null)
    {
        $configValues = Yaml::parse($resource);

        return $configValues;
    }

    public function supports($resource, $type = null)
    {
        return is_string($resource) && 'yml' === pathinfo(
            $resource,
            PATHINFO_EXTENSION
        );
    }
}

As you see, is very simple. The supports method is used by the LoaderResolver to know wether the resource can be load with that specific loader. The load method just converts the YAML file into an array using the Yaml component.

Finally, we make use of the definition and the loader to process the configuration values:

<?php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Definition\Processor;
use RaulFraile\Config\YamlConfigLoader;
use RaulFraile\Config\Configuration;

include_once __DIR__. '/vendor/autoload.php';

// directories of config files
$directories = array(__DIR__.'/config');
$locator = new FileLocator($directories);

// convert the config file into an array
$loader = new YamlConfigLoader($locator);
$configValues = $loader->load($locator->locate('config.yml'));

// process the array using the defined configuration
$processor = new Processor();
$configuration = new Configuration();
try {
    $processedConfiguration = $processor->processConfiguration(
        $configuration,
        $configValues
    );

    // configuration validated
    var_dump($processedConfiguration);
} catch (Exception $e) {
    // validation error
    echo $e->getMessage() . PHP_EOL;
}

Let’s see what this code is doing. First, we define an array of directories where the configuration files could be, and using the FileLocator object look for the config.yml file in those folders. Then, we create the YamlConfigLoader object, which returns an array with the configuration values. Eventually, the array with the raw values is processed using our definition.

The output of this script shows the processed array:

array(5) {
  'title' =>
  string(7) "My blog"
  'description' =>
  string(24) "This is just a test blog"
  'rss' =>
  bool(true)
  'posts_main_page' =>
  int(2)
  'social' =>
  array(2) {
    'twitter' =>
    array(2) {
      'url' =>
      string(29) "http://twitter.com/raulfraile"
      'icon' =>
      string(11) "twitter.png"
    }
    'sensiolabs_connect' =>
    array(2) {
      'url' =>
      string(49) "https://connect.sensiolabs.com/profile/raulfraile"
      'icon' =>
      string(22) "sensiolabs_connect.png"
    }
  }
}

If we change the config.yml and remove the “rss” and “posts_main_page” fields, we will get the default values:

array(5) {
  ...
  'rss' =>
  bool(false)
  'posts_main_page' =>
  int(5)

Caching

If the configuration is complex, having to parse and process the configuration resources in every request can be a time-consuming task. The config component makes caching extremely easy by looking at the “last modified” timestamp of the resources.

To enable caching in our example we only need a few more lines:

<?php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Definition\Processor;
use RaulFraile\Config\YamlConfigLoader;
use RaulFraile\Config\Configuration;

include_once __DIR__. '/vendor/autoload.php';

$cachePath = __DIR__.'/cache/config.php';
$configFile = 'config.yml';

// the second argument is to enable/disable debugging
$cache = new ConfigCache($cachePath, true);

if (!$cache->isFresh()) {
    // directories of config files
    $directories = array(__DIR__.'/config');
    $locator = new FileLocator($directories);

    // convert the config file into an array
    $loader = new YamlConfigLoader($locator);
    $configFilePath = $locator->locate($configFile);
    $configValues = $loader->load($configFilePath);
    $resource = new FileResource($configFilePath);

    // process the array using the defined configuration
    $processor = new Processor();
    $configuration = new Configuration();
    try {
        $processedConfiguration = $processor->processConfiguration(
            $configuration,
            $configValues
        );

        // serialize the config array and save it
        $cache->write(serialize($processedConfiguration), array($resource));
    } catch (Exception $e) {
        // validation error
        echo $e->getMessage() . PHP_EOL;
    }

}

The instance of the ConfigCache class checks if the cache file exists and then compares the last modification timestamp to detect if the cache is fresh or not. Notice that when we write the cache file, we are saving the list of resources too to be able to compare them in next requests.

Multiple loaders

Allowing different formats is as easy as adding a new loader that supports the new format. In our example, if we wanted to support XML configuration files, we would add the loader and use the resolver:

<?php

namespace RaulFraile\Config;

use Symfony\Component\Config\Loader\FileLoader;

class XmlConfigLoader extends FileLoader
{
    public function load($resource, $type = null)
    {
        // parse xml

        return $configValues;
    }

    public function supports($resource, $type = null)
    {
        return is_string($resource) && 'xml' === pathinfo(
            $resource,
            PATHINFO_EXTENSION
        );
    }
}
$loaderResolver = new LoaderResolver(array(
    new YamlConfigLoader($locator),
    new XmlConfigLoader($locator)
));
$delegatingLoader = new DelegatingLoader($loaderResolver);
$configValues = $delegatingLoader->load($locator->locate('config.xml'));

Generate reference configuration

The component provides a couple of dumpers to generate reference configuration to add to your documentation.

<?php
...
use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper;

$dumper = new YamlReferenceDumper();
echo $dumper->dump($configuration);

Will print out:

blog:
    title:                ~ # Required
    description:          ''
    rss:                  false
    posts_main_page:      5
    social:
        url:                  ~
        icon:                 ~

Advantages

You may be thinking that this is too much work and you would do the same with a couple of functions. Well, this is usually the “cost” of having a good OOP design. On the other hand, using this component gives you lots of advantages:

  • The component is well tested (~80% of code coverage) and actively maintained.
  • Allowing new formats is really easy. Just add a new loader that transforms the resource into a plain array. The same definition will be used for any format. In general, to extend any part of the component – node types/validators, loaders, dumpers… – just have to implement the required interface.
  • Caching works out of the box, with different settings for dev and prod environments.
  • Built-in validators.
  • Generates reference configuration.

More info

Photo: Elliot Brown

febrero 21 / 2014

New TLDs

NewTLDs

Upcoming Conferences

CraftingTour

CraftingTour

PHP New Zealand









Summary of SunshinePHP 2014

Last weekend took place the second edition of SunshinePHP and was fantastic, we could not have hoped for a better PHP conference, and we did not even have to travel. For those of you that don’t know, SunshinePHP is hosted by the South Florida PHP User Group, our home base. The event was organised by the tireless Adam Culp, with the help of Pablo Godel, Adrian Cardenas, Diana Espino, and Melissa Billias who became the elephpant model. It was a world class PHP conference and needless to say, we are very proud of it.

hug

The speaker list read like a who’s who of the PHP world and they all brought something new to the conference. Beth Tucker Long, Ryan Weaver, Anthony Ferrara, Keith Casey, Brandon Savage, Michelangelo Van Dam, Evan Coury, Lukas Kahwe Smith, John Coggeshall, Anthony Ferrara, Josh Broton, Chris Hartjes, Ilia Alshanetsky, Paul Jones, Davey Shafik, Jordan Kasper, Jonathan Klein, Jeremy Lindblom, Rowan Merewood, Jeff Carouth, Elizabeth Naramore, Ben Ramsey, Chris Tankersley, Derick Rethans, Alvaro Videla, Jeremy Mikola, Thijs Feryn, Eli White, Ricard Clau and Beau Simensen. So yeah, who would not want to come to the sunny Miami in February to see these people speak? The quality of the talks were top-notch.

Keynotes

But it gets better, the conference had 4 keynotes. Mark Brown and Cal Evans were in charge of opening and closing the conference, while Rasmus Lerdorf, Larry Garfield and Matthew Weier O’Phinney dove into more technical stuff talking about PHP, open source and APIs.

It was great to hear Rasmus’ keynote, where he started giving an overview of new features in PHP, gave some great tips on deploying PHP atomically, and switched to inspire the audience to work on things and projects that make a difference in people’s lives.

photo 2

Talks

The talks were well balanced, not only on skill level, but there was was a good variety of talks, even some excellent non-php talks to help us broaden our horizons. For example, Elizabeth Naramore gave an excellent talk about how Github organises itself using their product and how organisations can do the same. It got us thinking how we organise our company since we also use Github as a communication tool here at ServerGrove.

Elizabeth also gave example of how other people use Github. One that stuck was how Evan Coury used the issue tracker to track his relationship with Alissa. Alas, thanks to Elizabeth, we fear that he might be better known for his relationship tracker than his contributions to Zend Framework.

Other great non-PHP talk were given by Josh Broton on responsive design and Rowan Merewood on being better developers. I must admit, I really hated Josh talk – and Josh itself- for that matter. It made me realize I need to rip apart the front-end of our new control panel and start from scratch. In the other hand, Rowan’s talk was qualified by many attendees as one of the best of the whole conference. He was really entertaining and provided great tips to become a better developer, something we all want and getting to this conferences is just the first step in a long – but satisfying – journey.

On the PHP track, there were also some amazing talks. Anthony Ferrara, as always, did a great job explaining concepts such as cyclomatic complexity and NPath. This last concept is especially interesting as it gives you the minimum number of tests required to completely test a routine. The talk was really funny when Anthony calculated the size needed to test some functions with a high NPath complexity… long story short, to fully test the compose_pm function of phpBB3 at 1 line of code per test, you would require 15,000,000 Milky Way’s of MicroSDs worth of tests. So, keep you NPath complexity low!

It is worth to mention the other talk of Rowan Merewood about sorting and search algorithms. Some members of our team were really happy to see these kind of concepts in PHP conferences.

Organization

photo 1

The conference was well organised and there was plenty of time to meet the speakers, hang out and network. Mostly hang out. People were relaxed, the vibe was friendly and attendees were eager to share their opinions on code. Any conference veteran will tell you that the “hallway track” is one of the best things of any conference. Given the list of speakers hanging out in the hallway or sponsors room, the “hallway track” at SunshinePHP was amazing! With many speakers saying that next year they would like to have more meals with attendees, everyone should take the time to approach the speakers. A simple “hi, my name is ___ and I really appreciate the work you’ve done on ____. May I ask you about…” goes a long way to get the conversation started. So don’t be shy!

 

There was a lot of mentoring going on, something that seems to have been added by design by Adam Culp.

PHPWomen were out in force too. It was inspiring to see such a presence at SunshinePHP.

See you next year!

We look forward to SunshinePHP 2015.

febrero 14 / 2014
Author Kim
Category PHP
Comments No Comments

New TLDs

NewTLDs

Upcoming Conferences

CraftingTour

CraftingTour

PHP New Zealand









Zend Server 6.3 with PHP 5.5 available at ServerGrove

ZS6

Zend released the new version of Zend Server, which includes a lot of great features, the most important one in our opinion is the support for PHP 5.5.

Additionally, they announced their official Long-Term Support (LTS) policy for PHP, enabling users to confidently continue using a given major version of PHP.

We have integrated Zend Server into our control panel making it easy to access and monitor. The Zend Server is only visible in the Applications section of Servers running the Zend Server image or if you installed Zend Server manually.

Order your VPS with the Zend Server pre-installed and get your app up and running in minutes!

What’s new for Zend Server 6.3:

- Expanded version support: Zend Server will now support PHP 5.5, and will be certified to run on Ubuntu 13.10 using Apache 2.4.

- New options for Windows PHP developers coding for the IBM i platform: the XML Toolkit that allows IBM i resources to be leveraged by PHP apps will now be easily deployable on Window-based Zend Server development systems.

- Faster getting up to speed with popular PHP packaged applications: Zend Server comes with sample applications ready for deployment, including Drupal, Magento, WordPress and others.

Related Article:
Preconfigured VPS images with Zend Server

More Information:
Zend.com

febrero 11 / 2014
Author Alex
Category PHP, VPS
Comments No Comments

New TLDs

NewTLDs

Upcoming Conferences

CraftingTour

CraftingTour

PHP New Zealand