2014 in pictures

2014 is coming to an end, so as usual, let's look back at the year as highlighted by the photos that I've taken. This year, I took at least one photo every day, so there's been plenty for me to choose from!

January

As usual, there was flooding in Worcester, but clearly the biggest personal event of the month was breaking my elbow while skateboarding with my son at the end of the month. This clearly impacted my business for February and March as I could barely type for a good six or so weeks.

Still flooded Elbow in a cast

February

My wife and I celebrated her birthday in Barcelona and we discovered Licor 43!

Coffee on La Rambla A glass of Licor 43

March

A quiet month where my cast was removed. I also attended the PHPNE 14 conference in Newcastle.

Removing the cast Staff in their hoodies

April

In April, we went to the Insomnia Gaming Festival where the kids got to test out new games. We also sold our caravan.

At Insomnia 51 2003 Avondale Dart 556-6

May

May is a month of birthdays as both kids and myself celebrate.

New scooter Birthday boy playing on his new 3DS XL

June

The highlights of June were watching Great Britain play Belgium at basketball (and win!) and attending DPC.

Collins "We are screwed"

July

I was very pleased to speak at OSCON in Portland! It's an amazing conference and I would very much like to go to the European edition in Amsterdam next year.

Portland Saturday Market Discussing Atom

August

In August, I went on holiday with my family and, at the end of the month, we celebrated my cousin's marriage.

Playing in the pool Lucy & Dave

September

I attended and presented at the inaugural Endpoint conference where I learned much from Ben Longden. I also attended the Hackference conference day which was filled with interesting content.

Ben Erika Heidi

October

By October, I was heavily into conference season and presented at both PHPNW and ZendCon. Sandwiched between them, I also attended the All Your Base conference. This month, we also bought our own squat rack and cancelled our gym membership.

Khayrattee (aka 7PHP) Completed squat rack! Did you *really* write those tests?

November

I was privileged to be be invited to speak at PHP Argentina! An very well organised conference with a number of well known community people that I was delighted to meet and spend time with. How can you not love a conference where there are deck chairs in the hallway track? I also attended PHPEM's unconference; It turns out that I have opinions on how to give a good talk too! This was a good event and I hope they hold it again next year.

Phil, preparing for his talk The final schedule

December

My eldest son finished building his remote control car kit this month. This was a great project and very enjoyable. At the close of the year, I bought a new camera, so I expect that I'll be taking lots of photos next year too!

The Hornet It's not as big as an Elephpant!

I'm looking forward to 2015!

Recursively deleting elements from an array

I had a need recently to delete items from a nested associative array and also any empty sub-arrays. My initial thought was to use array_walk_recursive, but this doesn't work as you can't unset nested elements and you only have access to the leaves. Clearly I needed a recursive function.

I'm sure that this has been done many times before, but this is my solution:

/**
 * Remove any elements where the callback returns true
 *
 * @param  array    $array    the array to walk
 * @param  callable $callback callback takes ($value, $key, $userdata)
 * @param  mixed    $userdata additional data passed to the callback.
 * @return array
 */
function array_walk_recursive_delete(array &$array, callable $callback, $userdata = null)
{
    foreach ($array as $key => &$value) {
        if (is_array($value)) {
            $value = array_walk_recursive_delete($value, $callback, $userdata);
        }
        if ($callback($value, $key, $userdata)) {
            unset($array[$key]);
        }
    }

    return $array;
}

with this test:

class FunctionsTest extends \PHPUnit_Framework_TestCase
{
    public function testArrayWalkRecursiveDelete()
    {
        $array = [
            'a'=> 'a',
            'b'=> null,
            'c' => [
                'a' => null,
                'b' => 'b',
            ],
            'd' => [
                'a' => null
            ]
        ];

        $result = array_walk_recursive_delete($array, function ($value, $key) {
            if (is_array($value)) {
                return empty($value);
            }
            return ($value === null);
        });

        $expected = [
            'a'=> 'a',
            'c' => [
                'b' => 'b',
            ],
        ];

        $this->assertSame($expected, $result);
    }
}

This is very similar to how array_walk_recursive works except that I return the altered array rather than a boolean as it's a recursive function.

The test shows how I use it:

        $result = array_walk_recursive_delete($array, function ($value, $key) {
            if (is_array($value)) {
                return empty($value);
            }
            return ($value === null);
        });

If the callback returns true, then the element is deleted from the array, so for my case, I return true if the value is an empty array or null.

SSL certificate verification on PHP 5.6

I recently updated my local OS X Zend Server installation to PHP 5.6 and when I ran composer self-update, I got this error message:

[Composer\Downloader\TransportException]                                                                                       
The "https://getcomposer.org/version" file could not be downloaded: SSL operation failed with code 1. OpenSSL Error messages:  
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed                                              
Failed to enable crypto                                                                                                        
failed to open stream: operation failed 

Googling around, I finally worked out that there have been various SSL improvements in PHP 5.6 and that the problem was that it couldn't find any OpenSSL certificates on my system. This isn't a total surprise as OS X has been moving away from using OpenSSL internally in favour of its own libraries.

There's a new PHP function openssl_get_cert_locations that helps with this and so I ran:

$ php -r "print_r(openssl_get_cert_locations());"

on the command line to find out where PHP was looking. On my system, I got this:

Array
(
    [default_cert_file] => /usr/local/openssl-0.9.8zb/ssl/cert.pem
    [default_cert_file_env] => SSL_CERT_FILE
    [default_cert_dir] => /usr/local/openssl-0.9.8zb/ssl/certs
    [default_cert_dir_env] => SSL_CERT_DIR
    [default_private_dir] => /usr/local/openssl-0.9.8zb/ssl/private
    [default_default_cert_area] => /usr/local/openssl-0.9.8zb/ssl
    [ini_cafile] => 
    [ini_capath] => 
)

There is no directory /usr/local/openssl-0.9.8zb on my system and SSL_CERT_FILE and SSL_CERT_DIR are not defined, so it's no surprise that PHP was struggling.

To fix it, I install openssl via homebrew:

brew install openssl

This installs the openssl certificates to /usr/local/etc/openssl/cert.pem, so we can now use the new PHP 5.6 INI setting openssl.cafile to tell PHP where to find the certificates:

Adding

openssl.cafile=/usr/local/etc/openssl/cert.pem

to Zend Server's php.ini solved the problem and I can now use composer once again!

Overriding the built-in Twig date filter

In one project that I'm working on, I'm using Twig and needed to format a date received from an API. The date string received is of the style "YYYYMMDD", however date produced an unexpected output.

Consider this:

{{ "20141216"|date('jS F Y') }}

creates the output:

20th May 1976

This surprised me. Then I thought about it some more and realised that the date filter is treating my date string as a unix timestamp. I investigated and discovered the problem in twig_date_converter:

    $asString = (string) $date;
    if (ctype_digit($asString) 
        || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
        $date = '@'.$date;
    }

    $date = new DateTime($date, $defaultTimezone);

This code tests to see if the dates string provided is a number (positive or negative) and then prepends an '@' symbol to the front. This has the effect of informing DateTime's constructor to treat $date as a unix timestamp.

Unfortunately, twig_date_converter is a function and so I couldn't override it, so I wrote my own extension that registers new date, date_modify filters and a new date function in order to solve my problem:

<?php
/*
 * Extension to provide updated date & date_modify filters along with an
 * updated date function which do not auto-convert strings of numbers to
 * a unix timestamp.
 *
 * Code within dateFilter(), modifyFilter() and dateFromString() extracted
 * from Twig/lib/Twig/Extension/Core.php which is (c) 2009 Fabien Potencier
 * and licensed as per https://github.com/twigphp/Twig/blob/master/LICENSE
 */
namespace My\Twig\Extension;

use Twig_Extension;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
use DateTime;
use DateTimeInterface;
use DateTimeImmutable;

class DateExtension extends Twig_Extension
{
    public function getName()
    {
        return 'my_date';
    }

    public function getFilters()
    {
        return array(
            new Twig_SimpleFilter('date', [$this, 'dateFilter'],
                    ['needs_environment' => true]),
            new Twig_SimpleFilter('date_modify', [$this, 'modifyFilter'],
                    ['needs_environment' => true]),
        );
    }

    public function getFunctions()
    {
        return array(
            new Twig_SimpleFunction('date', [$this, 'dateFromString'],
                    ['needs_environment' => true]),
        );
    }

    public function dateFilter($env, $date, $format = null, $timezone = null)
    {
        if (null === $format) {
            $formats = $env->getExtension('core')->getDateFormat();
            $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
        }

        if ($date instanceof DateInterval) {
            return $date->format($format);
        }

        return $this->dateFromString($env, $date, $timezone)->format($format);
    }

    public function modifyFilter($env, $date, $format = null, $timezone = null)
    {
        $date = $this->dateFromString($env, $date, false);
        $date->modify($modifier);

        return $date;
    }

    public function dateFromString($env, $date, $timezone)
    {
        // determine the timezone
        if (!$timezone) {
            $defaultTimezone = $env->getExtension('core')->getTimezone();
        } elseif (!$timezone instanceof DateTimeZone) {
            $defaultTimezone = new DateTimeZone($timezone);
        } else {
            $defaultTimezone = $timezone;
        }

        // immutable dates
        if ($date instanceof DateTimeImmutable) {
            return false !== $timezone ? $date->setTimezone($defaultTimezone) : $date;
        }

        if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
            $date = clone $date;
            if (false !== $timezone) {
                $date->setTimezone($defaultTimezone);
            }

            return $date;
        }

        $date = new DateTime($date, $defaultTimezone);
        if (false !== $timezone) {
            $date->setTimezone($defaultTimezone);
        }

        return $date;
    }
}

This class simply registers new date, date_modify filters and a new date function to replace the ones in Twig core and then is a direct copy of the functions twig_date_format_filter, twig_date_modify_filter and twig_date_converter with the functionality above removed.

I also needed to register this extension with the Twig_Environment using: $env->addExtension(new \My\Twig\Extension\DateExtension()); and I'm done.

{{ "20141216"|date('jS F Y') }}

now correctly outputs:

16th December 2014

While, it's a shame I can't just override twig_date_converter, I'm glad that I can re-register the relevant Twig filters and function.

Converting databases between MySQL and SQL Server

I regularly deal projects that target SQL Server, but mostly develop against MySQL to avoid having to run a full Windows stack locally all the time. One of the nice things about PHP with DBAL and Migrations is that the database is pretty well abstracted from my code. Of course, this means that I don't target any of the specialist features, but for these projects, this hasn't been an issue.

To convert data from SQL Server to MySQL, I've found Intelligent Converters's MSSQL to MySQL tool to work very well for my needs. It allows me to grab the test database from the client and convert it to a MySQL dump file that I can then import.

Validating JSON with ZF2's Zend\Validator

Let's say that you have an admin form where the user can enter JSON and you'd like to validate that the JSON parses before allowing the user to submit. To do this, you can use the rather excellent jsonlint project by Jordi Boggiano. Obviously, add it via Compser :)

Usage is simple:

use Seld\JsonLint\JsonParser;
use Seld\JsonLint\ParsingException;

$parser = new JsonParser();

$result = $parser->lint($json);
if ($result instanceof ParsingException) {
    // $json is invalid JSON
}

We can wrap this up into a Zend Framework 2 validator quite easily:

<?php

namespace RKA\Validator;

use Zend\Validator\AbstractValidator;
use Seld\JsonLint\JsonParser;
use Seld\JsonLint\ParsingException;


class Json extends AbstractValidator
{
    const INVALID      = 'jsonInvalid';

    /**
     * Json parser
     *
     * @var \Seld\JsonLint\JsonParser
     */
    protected static $parser = null;

    /**
     * Validation failure message template definitions
     *
     * @var array
     */
    protected $messageTemplates = array(
        self::INVALID      => "Json is invalid: '%reason%'",
    );

    /**
     * Additional variables available for validation failure messages
     *
     * @var array
     */
    protected $messageVariables = array(
        'reason' => 'reason',
    );

    /**
     * @var string
     */
    protected $reason;

    /**
     * Returns true if and only if $value is valid JSON
     *
     * @param  string $value
     * @return bool
     */
    public function isValid($value)
    {
        $parser = new JsonParser();

        $result = $parser->lint($value);

        if ($result instanceof ParsingException) {
            $this->reason = $result->getMessage();
            $this->error(self::INVALID);
            return false;
        }

        return true;
    }
}

Simply add RKA\Validator\Json to the input filter of the element in question and we're done.

Autocomplete Phing targets on the command line

Shortly after writing my last post, it crossed my mind that it would be nice if I could autocomplete the targets in my Phing build file on the command line.

In order to do this, I read Buddy Lindsey's Quick and Dirty Write Your Own Bash Autocomplete and made it do what I needed!

Start by creating a new bash completion file in the bash_completion.d directory. This file needs executable permission. This directory can usually be found at /etc/bash_completion.d, but on OS X using Homebrew, it's at /usr/local/etc/bash_completion.d.

This is the file:

# Store this file in /etc/bash_completion.d/phing
 
_phing () {
    local cur prev
 
    COMPREPLY=()
    buildfile=build.xml
    _get_comp_words_by_ref cur prev
 
    [ ! -f $buildfile ] && return 0

    COMPREPLY=( $( compgen -W "$( phing -l | tr -s '\-' | sed s/^-/\|/ | tr -d '\|' \
        | sed s/\ \ .*\// \
        | sed s/Buildfile.*// | sed s/Default\ target:// | sed s/Subtargets:// \
        | sed s/Main\ targets:// \
        | tr -s ' ' \
        | sed 's/[^[:print:]]//g' | sed s/\\[.*// | tr '\n' ' ' | tr -s '\n' 2>/dev/null )" \
        -- "$cur" ) )
}
 
complete -F _phing phing

It's a bit messy because my knowledge of sed/tr isn't too hot. What we do is create a list of words for the -W option to compgen by running phing -l and then manipulating the output to remove everyting that isn't a target name using various sed and tr commands.

Once done, compgen creates the wordlist used by complete when you press tab after the word phing on the command line.

A few Phing tips

Following on from my last post, here's a few other Phing things that I've found helps me.

Hiding targets from Phing -l

I have a number of dependent targets in my build.xml that I don't want listed when I run phing -l. The easiest way to do this is to add the hidden="true" property to the targets I want to hide:

<?xml version="1.0"?>
<project name="buildfile" default="foo">

  <target name="foo" depends="bar,baz" />

  <target name="bar" hidden="true">
    <!-- tasks -->
  </target>

  <target name="baz" hidden="true">
    <!-- tasks -->
  </target>

</project>

The output of phing -l now shows just the foo target as I want:

$ phing -l
Buildfile: /Users/rob/tmp/build.xml
Default target:
-------------------------------------------------------------------------------
 foo

Subtargets:
-------------------------------------------------------------------------------
 foo

Main target vs subtarget

If you set a description on a target, then it becomes a Main target:

<?xml version="1.0"?>
<project name="buildfile" default="foo">

  <target name="foo" depends="bar,baz" description="Run foo to do stuff" />

  <!-- other hidden targets -->

</project>

The output is now:

$ phing -l
Buildfile: /Users/rob/tmp/build.xml
Default target:
-------------------------------------------------------------------------------
 foo

Main targets:
-------------------------------------------------------------------------------
 foo      Run foo to do stuff

It just looks better and provides additional information as well.

List available targets by default

If you run phing without a target, then the default target runs. If you've forgotten what that is, then this may not be the safest thing to happen. Ideally, the default target should do no harm and I've found it helpful to make the default target display a list of the available targets within the build file.

This is done by adding a hidden "list_targets" target and making it the default:

<?xml version="1.0"?>
<project name="buildfile" default="list_targets">

  <target name="list_targets" hidden="true">
    <exec command="phing -q -f ${phing.file} -l" passthru="true"/>
  </target>

  <target name="foo" depends="bar,baz" description="Run foo to do stuff" />

  <!-- other hidden targets -->

</project>

Essentially, we run phing -l against our current build file and set the passthru property so that the output is displayed. This gives us:

$ phing
Buildfile: /Users/rob/tmp/build.xml

buildfile > list:

Default target:
-------------------------------------------------------------------------------
 list

Main targets:
-------------------------------------------------------------------------------
 foo  Run foo to do stuff


BUILD FINISHED

Total time: 0.2176 seconds

Using Phing to SSH into a Vagrant box

Now that I've started using migrations, I've discovered a minor irritant.

I run this project on a Vagrant VM and have discovered that I keep forgetting to ssh into the vagrant box before running the migrations script. The obvious solution is to automate this and I decided to use Phing to do so.

Then add extension=ssh2.so to your /etc/php.ini file.

Firstly, I needed to install the PHP ssh2 extension:

$ brew install libssh2
$ sudo pecl install pecl.php.net/ssh2-beta

I'm on OS X, so installed libssh2 via homebrew and as the PECL ssh2 extension is marked as beta, so we have to specify that. We can accept the default for auto-detecting the libssh2 prefix.

Once this is done, we can now use Phing's SshTask in our build scripts.

This is a simple build.xml file to check it all works:

<?xml version="1.0"?>
<project name="sshtest" default="testssh">
  <target name="testssh">
    <ssh username="vagrant" password="vagrant" host="192.168.123.456"
        command="pwd" property="pwd" display="false" />
    <echo>The current working directory is ${pwd}</echo>
  </target>
</project>

The output looks like:

$ phing
Buildfile: /www/19ft/projectname/build.xml

sshtest > testssh:

     [echo] The current working directory is /home/vagrant


BUILD FINISHED

Total time: 0.3923 seconds

Now that it works, we can write a target that does something useful:

  <target name="migrate"  >
    <ssh username="vagrant" password="vagrant" host="192.168.123.456"
      command="cd /vagrant; php bin/migrations.php migrate"  />
  </target>

Now, I simply run `phing migrate` and the MySQL in my Vagrant box is migrated as I expect.

Obviously Phing isn't the only way to do this, but it worked for me.

My "new" blogging habit

I've just come across Andy Baio's Middling post and this bit really resonated with me:

Twitter and Waxy Links cannibalized all the smaller posts, and as my reach grew, I started reserving blogging for more "serious" stuff — mostly longer-form research and investigative writing.

From around September 2013, I found that I was in this situation. I was only posting here once or twice a month, mainly as I felt that I should only blog when I had something "worthy" to write about. I got of this rut nearly a year later as I started a new project that resulted in a number of fairly frequent "long" posts about Slim and ZF2 components, which rekindled my interest in writing here.

In March 2013, I gave myself permission to write more opinionated posts here but didn't actually do so until June 2014. Since then I've written a few more times on the things I care about and hope to continue to do so.

I've also started giving myself permission to write posts that are shorter tech-notes. These are more aide-mémoira that I want to remember that maybe someone else will find useful.

I would like to write shorter opinion pieces. Say 250ish words on items that catch my interest. I'm currently using Twitter for this, but sometimes 140 characters isn't enough and I can never find what I wrote again. This post is to give myself permission to do so.

Maybe I'll actually do it in less than a year from now!