Category Archives: PHP

Using Doctrine Migrations as a standalone tool

My current project has reached the point where a good migrations system is required. As I'm targeting two different database engines (MySQL and MS SQL Server) and we're already using DBAL, it made sense to use Migrations from the Doctrine project.

To do this, I updated my composer.json with the following require statements:

"doctrine/migrations": "1.0.*@dev",
"symfony/console": "~2.5"

in order to install Migrations and also Symfony console component so that I can run it from the command line.

The CLI script

Migrations doesn't come with it's own CLI script, so you have to create your own. Fortunately, we can tweak the supplied phar-cli-stub.php. I placed it in bin/migrations.php:

/**
 * Command line script to run Migrations
 * Inspired by phar-cli-stup.php
 */

use Symfony\Component\Console;
use Doctrine\DBAL\Migrations\MigrationsVersion;
use Doctrine\DBAL\Migrations\Tools\Console\Command as MigrationsCommand;

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

// Set current directory to application root so we can find config files
chdir(__DIR__ . '/..');

// Instantiate console application
$cli = new Console\Application('Doctrine Migrations', MigrationsVersion::VERSION);
$cli->setCatchExceptions(true);

$helperSet = new Console\Helper\HelperSet();
$helperSet->set(new Console\Helper\DialogHelper(), 'dialog');
$cli->setHelperSet($helperSet);

// Add Migrations commands
$commands = array();
$commands[] = new MigrationsCommand\ExecuteCommand();
$commands[] = new MigrationsCommand\GenerateCommand();
$commands[] = new MigrationsCommand\LatestCommand();
$commands[] = new MigrationsCommand\MigrateCommand();
$commands[] = new MigrationsCommand\StatusCommand();
$commands[] = new MigrationsCommand\VersionCommand();

// remove the "migrations:" prefix on each command name
foreach ($commands as $command) {
    $command->setName(str_replace('migrations:', '', $command->getName()));
}
$cli->addCommands($commands);

// Run!
$cli->run();

Note that, by default, the console commands are all prefixed with 'migrations:'. This makes sense as usually they are integrated with the rest of the Doctrine ORM command line application and so it avoids any clashes. In this case, this script is only dealing with migrations and I don't want to type more than I have to, so I remove the prefixes!

Configuration

Migrations requires two separate configuration files to work: migrations.yml and migrations-db.php. These need to be in the current directory, so I used chdir() to ensure that it is in a known location – the application root in this case.

migrations.yml is the config file that tells Migrations the names of things:

migrations.yml:

name: DBAL Migrations
migrations_namespace: Migrations
table_name: migration_versions
migrations_directory: migrations

In my case, I want to use my migrations files to be in a directory called migrations and use the namespace Migrations. The database table name that Migrations uses to keep track of its internal status is called migration_versions. I'm not too imaginative when it comes to naming things!

migrations-db.php must return an array that is used to configure Doctrine\DBAL\DriverManager::getConnection()

migrations-db.php:

<?php
return array(
        'dbname'   => 'albums',
        'user'     => 'rob',
        'password' => '123456',
        'host'     => 'localhost',
        'driver'   => 'pdo_mysql',
);

The documentation will help you find out what can go in here.

We're now ready to go!

On the command line, php bin/migrations.php status should work:

$ php bin/migrations.php 
Doctrine Migrations version 2.0.0-DEV

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v|vv|vvv Increase the verbosity of messages
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.

Available commands:
  execute    Execute a single migration version up or down manually.
  generate   Generate a blank migration class.
  help       Displays help for a command
  latest     Outputs the latest version number
  list       Lists commands
  migrate    Execute a migration to a specified version or the latest available version.
  status     View the status of a set of migrations.
  version    Manually add and delete migration versions from the version table.

Creating a migration

run php bin/migrations.php generate and a new migration class will be created in the migrations directory. It has two methods: up() and down(). Both have an instance of Doctrine\DBAL\Schema\Schema passed and an you can do whatever you like in the function to manipulate your database to its next state. Again, the documentation is helpful!

For instance:

<?php

namespace Migrations;

use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;

class Version20141027161210 extends AbstractMigration
{
    public function up(Schema $schema)
    {
        $myTable = $schema->createTable('artists');
        $myTable->addColumn('id', 'integer',
            ['unsigned' => true, 'autoincrement'=>true]);
        $myTable->addColumn('name', 'string', ['length' => 60]);
        $myTable->setPrimaryKey(['id']);
    }

    public function down(Schema $schema)
    {
        $schema->dropTable('artists');
    }
}

Running the migration

Simply run php bin/migrations.php migrate and Migrations will the up() method for all migration classes that haven't be run yet. In this case it will create a table called artists and update the migration_versions table with the version number of '20141027161210'.

To back out of a migration, simple run php bin/migrations.php migration {version number} and Migrations will run the appropriate down() (or up()) methods to get to that version number.

Finally you can run php bin/migrations.php status to find out the current state.

On the whole, it turns out that it's quite easy to use Migrations as a stand-alone tool outside of Doctrine ORM or Symfony!

substr_in_array

No matter what I want to do with an array, PHP usually has a first class method that does it. I was therefore surprised that in_array() didn't handle substring matches. (I was sure there was a flag, but apparently not!)

No doubt everyone has their own version of this, but here's mine so that I don't have to recreate it next time:

/**
 * A version of in_array() that does a sub string match on $needle
 *
 * @param  mixed   $needle    The searched value
 * @param  array   $haystack  The array to search in
 * @return boolean
 */
function substr_in_array($needle, array $haystack)
{
    $filtered = array_filter($haystack, function ($item) use ($needle) {
        return false !== strpos($item, $needle);
    });

    return !empty($filtered);
}

Is there a better way of doing this?

Setting up PHP & MySQL on OS X Yosemite

It's that time again; Apple has shipped a new version of OS X, 10.10 Yosemite. Apple ships PHP 5.5.14 with Yosemite and this is how to set it up from a clean install.

However, if you don't want to use the built-in PHP or want to use version 5.6, then these are some alternatives:

Let's get started… Continue reading

Alias for the PHP built-in server

I keep forgetting the correct command line syntax for the PHP build-in server, so I've now made an alias for it in my .profile:

alias phps='php -S 0.0.0.0:8888'

Now I can simply type: phps public/index.php to start the built-in web server.

Throw an exception when simplexml_load_string fails

I keep having to look up how to stop the warning that are emitted when simplexml_load_string & simplexml_load_file fail, so this time I've written the world's simplest little class to take care of it for me from now on:

<?php

namespace Rka;

use UnexpectedValueException;

class Xml
{
    /**
     * Load an XML String and convert any warnings to an exception
     *
     * @param string  $string
     * @param string $class_name
     * @param int $options
     * @param string $ns
     * @param bool $is_prefix
     *
     * @throws UnexpectedValueException
     *
     * @return SimpleXMLElement
     */
    public static function loadXMLString(
        $string,
        $class_name = "SimpleXMLElement",
        $options = 0,
        $ns = "",
        $is_prefix = false
    ) {
        $previous = libxml_use_internal_errors(true);

        $xml = simplexml_load_string($string, $class_name, $options, $ns,
                 $is_prefix);

        if (!$xml) {
            $errors = self::getXMLErrorString();
            libxml_use_internal_errors($previous);
            throw new UnexpectedValueException($errors);
        }

        libxml_use_internal_errors($previous);
        return $xml;
    }

    /**
     * Load an XML File and convert any warnings to an exception
     *
     * @param string  $string
     * @param string $class_name
     * @param int $options
     * @param string $ns
     * @param bool $is_prefix
     *
     * @throws UnexpectedValueException
     *
     * @return SimpleXMLElement
     */
    public static function loadXMLFile(
        $string,
        $class_name = "SimpleXMLElement",
        $options = 0,
        $ns = "",
        $is_prefix = false
    ) {
        $previous = libxml_use_internal_errors(true);

        $xml = simplexml_load_file($string, $class_name, $options, $ns,
                 $is_prefix);

        if (!$xml) {
            $errors = self::getXMLErrorString();
            libxml_use_internal_errors($previous);
            throw new UnexpectedValueException($errors);
        }

        libxml_use_internal_errors($previous);
        return $xml;
    }

    /**
     * Helper method to format the XML errors into a string.
     *
     * @return string
     */
    protected static function getXMLErrorString()
    {
        $message = '';
        foreach (libxml_get_errors() as $error) {
            $message .= trim($error->message)
              . " on line: $error->line, column: $error->column.\n";
        }
        libxml_clear_errors();
        return trim($message);
    }
}

Update: The code has been updated based on the comments below. Thanks!

Speaking at DPC & OSCON

Two conferences are coming up that I'm speaking at:

I'm speaking at DPC 2014

The Dutch PHP Conference takes place in Amsterdam and this year is between June 26th and 28. I'm giving a tutorial with Matthew Weier O'Phinney on Apigility which will provide you with a full day's teaching on creating web APIs with Apigility. I'm also giving a talk called Creating Models discussing the options available when creating the model layer of a typical MVC PHP application.

DPC is a fantastic conference full of great content and people and if you can get to Amsterdam at the end of June, it's well worth your time to attend.

I'm speaking at OSCON 2014

The following month, I'm coming to the US to speak at OSCON which runs from the 20th to 24th July in Portland. OSCON is a massive conference that covers the entire spectrum of Open Source and I'm excited to be presenting Creating Models in the PHP track. There's a lot on the schedule and I expect to learn a lot and meet lots of people from communities that I haven't interacted with much before. I've never been to Portland before, so it'll be interesting to see what the city has to offer too.

If you attend either of these conferences, please come and say hello!

Immutable entities

I've been thinking recently about using immutable objects for some of my model-layer entities. That is, an object that cannot be changed once it is created.

Immutable objects have a number of benefits including the fact that they are much easier to understand, simple to use and very easy to test. The main motivation for me is that an immutable object is very predictable as it's state doesn't change. This means that any calculated properties can be done once on creation. This is interesting as one application that I'm currently working on has a lot of calculated properties and it's a source of bugs when one property changes and all the dependent calculations aren't always done.
Continue reading

Use statements

I was having a discussion on IRC about use statements and whether they improved code readability or not.

The choices

Consider this hypothetical code:

$cache = new \User\Service\Cache();
$mapper = new \User\Mapper\User($cache)
$form = new \User\Form\Registration($mapper);
$form->process($request->getPost());

vs

use User\Service\Cache;
use User\Mapper\User;
use User\Form\Registration;

// other code

$cache = new Cache();
$db = new User($cache)
$form = new Registration($mapper);
$form->process($request->getPost());

The first snippet is completely unambiguous at the expense of verbosity. Those longer class names make it a little hard to quickly parse what it going on. The second is clearly less cluttered, but is at the expense of ambiguity. Exactly what class is User? I would have to go to the top of the file to find out. Should I use aliases? If so, how should I name them?
Continue reading