Loïc Faugeron Technical Blog

Memio v1.0 06/05/2015

TL;DR: Memio is a PHP code generator library, the main repository provides integration with Twig templates, but by using the smaller package pretty-printer on its own it is possible to be decoupled from any template engines.

Memio has finally be released in version 1.0 (stable). In this article, we'll see how the different packages are assembled in the main repository.

Standard Edition

Memio is composed of small packages (linter, model, pretty-printer, twig-template-engine, validator), the main repository (memio/memio) is a standard selection of those to make their usage easier:

Package diagram

It can be installed using Composer:

composer require memio/memio:~1.0

It provides two services, which can be instanciated as follow:

<?php
// File: memio.php

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

use Memio\Memio\Config\Build;

$linter = Build::linter();
$prettyPrinter = Build::prettyPrinter();

To see how to use them, let's describe a request handler method:

// ...

use Memio\Model\Method;
use Memio\Model\Argument;

$handle = Method::make('handle')
    ->addArgument(Argument::make('Request', 'request'))
    ->addArgument(Argument::make('int', 'type')
        ->setDefaultValue('self::MASTER_REQUEST')
    )
    ->addArgument(Argument::make('bool', 'catch')
        ->setDefaultValue('true')
    )
;

Note: Each Model can be constructed using new or the static constructor make. The last one has the advantage to allow method chaining (e.g. Method::make('doHandle')->makePrivate()).

We can lint this model:

// ...

$linter->validate($handler); // @throws Memio\Validator\Exception\InvalidModelException if the model contains syntax errors.

And we can generate the corresponding PHP code:

// ...

$generatedCode = $prettyPrinter->generateCode($handle);

We can check in our console's output the result:

// ...

echo $generatedCode;

This should print:

    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
    {
    }

Limitations

Currently it is possible to describe:

It is possible to describe a method Body, but only with a string:

// ...

$handle->setBody(<<<BODY
        try {
            $this->requestHandler->handle($request);
        } catch (\Exception $e) {
            if (!$catch) {
                throw $e;
            }
        }
BODY
);

Use cases

In real life, models wouldn't be built manually as in the above examples. They would be built dynamically:

// ...

array_shift($argv); // remove binary name (php)
array_shift($argv); // remove script name (memio.php)
$methodName = array_shift($argv); // first argument = method name
$arguments = $argv; // all other arguments = argument types (e.g. `int`, `bool`, `DateTime`, etc)

$method = new Method($methodName);
$index = 1;
foreach ($arguments as $argumentType) {
    $argumentName = 'argument'.$index++;
    $method->addArgument(new Argument($argumentType, $argumentName));
}

echo $prettyPrinter->generatedCode($method);

Have a try by running php memio.php handle Request int bool, it should print the following:

    public function handle(Request $argument1, $argument2, $argument3)
    {
    }

With this we can already improve phpspec generator (generate typehinted arguments, PHPdoc, etc). This is going to be the next Memio package, a phpspec extension.

Extension points

The coding style can be changed by creating our custom templates. Those can be loaded as follow:

// ...

$prettyPrinter->addTemplatePath(__DIR__.'/our-custom-templates-dir');

Custom constraints can be written to check more things, for example we can ensure that arguments are always object.

Those steps, just like the rest, are heavily described in the official documentation.

Conclusion

Memio is a library that provides a PHP Code Generator:

  1. First we describe what we want by building Models (e.g. new Method('__construct'))
  2. Optionally we can Lint them to check if we introduced syntax errors (e.g. Method cannot be both abstract and final)
  3. Then we use a PrettyPrinter to get the generated code (returns a string, can be displayed on the output or saved in a file, etc)

You can read more about it with the following articles:

It also has an official documentation.

The next step is to create a phpspec extension to improve its code generator.