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

Git push to multiple repositories

I have a couple of projects where I need to push to more than one repo all the time.

I have been using this command line to do so:

git push origin && git push other-remote

However, I recently discovered that I can create a remote that points to more than one repository using these commands:

git remote add all git@github.com:akrabat/projectname.git
git remote set-url --add all ssh://example.com/path/to/projectname.git

I now have a remote called all that will push to both repositories!

There's no automatic way to go the other way and fetch from multiple repositories though as apparently it makes less sense to fetch the same branch identifier from multiple places.

Further details in this email by Linus in 2006.

If you want to script the creation of the all remote, then you could use this script which manipulates the remote configuration settings directly:

#!/bin/bash

if [ "`git remote| grep all`" == "all" ] ; then
    git remote remove all
fi

for r in `git remote`
do
    git config --add remote.all.url `git config remote.$r.url`
done

Create this as /usr/local/bin/git-add-push-all.sh and then you can just run it in the root of your project.

Kim

Today is Ada Lovelace day which celebrates the achievements of women in science, technology, engineering and maths. I have many role models in my technical life and many are women who inspire and encourage me to do better. There are too many to list, but I am thankful every one of them, both men and women.

I want to talk today about one person who inspires me: Kim. Kim is relatively new to development. Fortunately she works for an organisation that recognised her abilities and enabled her to to transition to a new position where she could work professionally as a developer for them. I've watched her grow from someone who knew very little to someone I now ask questions of.

She has three things going for her:

  • enthusiasm
  • eagerness to learn
  • willingness to ask questions

I feel these are her key strengths when it comes to growing so quickly into a competent developer. They work very well together.

Firstly, Kim is so enthusiastic to everything that she puts her mind to. It doesn't matter if it's updating HTML to ensure that information is displayed clearly for users, writing PHP or even testing Android code, Kim is excited to be doing the work.

Tied with this enthusiasm is an eagerness to learn. Just because she hasn't done it before doesn't seem to be a massive impediment. Just point her in the right direction and she'll go off and research the topic area and then comes back asking questions. The key thing here is that she has educated herself before she asks questions. As a result, the questions are nuanced, specific and answerable. If only everyone asked questions this way!

What do I want to learn from Kim? Most obviously, I have become lazy. I tend to ask questions first, so others do the "hard work" of research for me. This is disrespectful of their time and I am perfectly capable of doing enough research so then I can ask the specific questions to the people who freely give of their time to help me.

Jerry-rigging pygments to support new PHP keywords

I use rst2pdf to create my presentations and noticed that the syntax highlighter wasn't highlighting instanceof.

rst2pdf uses pygments for syntax highlighting, so I wondered what was going on. A short investigation led to me realise that the current stable version of pigments is 1.6 and they are working on 2.0. It seems that 2.0 has a number of changes to the PHP lexer, which aren't in 1.6.

While I'm waiting, I modified my local copy of pigments 1.6 directly!

On my Mac, the file I'm interested in is in the egg file at /Library/Python/2.7/site-packages/Pygments-1.6-py2.7.egg. The .egg file is simply a directly, so within there, I edited pygments/lexer/web.py which is where the PHP lexer is.

Open web.py and look for the PhpLexer class (it's around line 759 at the moment!). Scrolling further down, you come to the tokens section and then with the 'php' array, I found:

(r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|'
             r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|'
             r'FALSE|print|for|require|continue|foreach|require_once|'
             r'declare|return|default|static|do|switch|die|stdClass|'
             r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|'
             r'virtual|endfor|include_once|while|endforeach|global|__FILE__|'
             r'endif|list|__LINE__|endswitch|new|__sleep|endwhile|not|'
             r'array|__wakeup|E_ALL|NULL|final|php_user_filter|interface|'
             r'implements|instanceof|public|private|protected|abstract|clone|try|'
             r'catch|throw|this|use|namespace|trait)\b', Keyword),

This is the list of words that will be highlighted as keywords. Simply add the new ones that you need.

I then deleted the web.pyc file that was in the lexer directory (though I'm unsure if I needed to as it may auto-recreate itself) and then ran rst2pdf again to create my pdf with instanceof, yield & finally now correctly highlighted!

Obviously, when the next version of pygments is released, hopefully it'll be up to date, so jerry-rigging won't be required. Until then, this is working for me, at least.

Context specific history at the bash prompt

One change I made recently to my .profile is this:

# up & down map to history search once a command has been started.
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'

These two bind command change the way that the up and down arrow keys work once you start typing a command to only search the history for lines that start with what you've typed so far.

This means that I type, say, git and then press ↑ & ↓ to go through all the times I've typed a git command without having to go through all the other commands.

It's quite handy and I find it easier to use than ctrl+r.

Setting up mailcatcher as a service in Debian/Ubuntu

I've recently been changing joind.in's Vagrant system to use Debian and one issue I came across was getting Mailcatcher to start on boot and integrate property with the service command.

To do this, I created an init script which is based off the skeleton and then stored this in /etc/init.d and then ran update-rc.d mailcatcher defaults to set up the correct links in the various rc.d directories.

This is the init script:

/etc/init.d/mailcatcher:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          mailcatcher
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/rvm/gems/ruby-1.9.3-p547/bin
DESC="Super simple SMTP server"
NAME=mailcatcher
DAEMON=/usr/local/rvm/wrappers/default/$NAME
DAEMON_ARGS=" --http-ip 0.0.0.0"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
    # Return
    #   0 if daemon has been started
    #   1 if daemon was already running
    #   2 if daemon could not be started
    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
        || return 1
    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
        $DAEMON_ARGS \
        || return 2
    # Add code here, if necessary, that waits for the process to be ready
    # to handle requests from services started subsequently which depend
    # on this one.  As a last resort, sleep for some time.
     
    # Create the PIDFILE
    pidof mailcatcher >> $PIDFILE
}

#
# Function that stops the daemon/service
#
do_stop()
{
    # Return
    #   0 if daemon has been stopped
    #   1 if daemon was already stopped
    #   2 if daemon could not be stopped
    #   other if a failure occurred
    
    if [ -f "$PIDFILE" ]
    then
        kill `cat $PIDFILE`
        rm -f $PIDFILE
        return 0
    else
        return 1
    fi
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
    #
    # If the daemon can reload its configuration without
    # restarting (for example, when it is sent a SIGHUP),
    # then implement that here.
    #
    start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
    return 0
}

case "$1" in
  start)
    [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
    do_start
    case "$?" in
        0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
    esac
    ;;
  stop)
    [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
    do_stop
    case "$?" in
        0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
    esac
    ;;
  status)
    status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
    ;;
  #reload|force-reload)
    #
    # If do_reload() is not implemented then leave this commented out
    # and leave 'force-reload' as an alias for 'restart'.
    #
    #log_daemon_msg "Reloading $DESC" "$NAME"
    #do_reload
    #log_end_msg $?
    #;;
  restart|force-reload)
    #
    # If the "reload" option is implemented then remove the
    # 'force-reload' alias
    #
    log_daemon_msg "Restarting $DESC" "$NAME"
    do_stop
    case "$?" in
      0|1)
        do_start
        case "$?" in
            0) log_end_msg 0 ;;
            1) log_end_msg 1 ;; # Old process is still running
            *) log_end_msg 1 ;; # Failed to start
        esac
        ;;
      *)
        # Failed to stop
        log_end_msg 1
        ;;
    esac
    ;;
  *)
    #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
    echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
    exit 3
    ;;
esac

:

As you can probably tell, it's very obviously a tweaked version of /etc/init.d/skeleton, but there are some important changes:

Path to binary
As Mailcatcher is a ruby app, the correct path to the binary is actually /usr/local/rvm/wrappers/default/mailcatcher which is not where which tells you it is. I suspect that my lack of knowledge about Ruby environments is showing…

do_start()
The do_start() function calls through to start-stop-daemon to start mailcatcher. However this doesn't create a pid file in /var/run for us, so we create it ourselves using:

pidof mailcatcher >> $PIDFILE

do_stop()
Mailcatcher is intended to be stopped by pressing the Quit button in the HTML interface, so the default code in skeleton doesn't work. I rewrote it to simply kill the process if the pid file exists:

    if [ -f "$PIDFILE" ]
    then
        kill `cat $PIDFILE`
        rm -f $PIDFILE
        return 0
    else
        return 1
    fi

That's it. The most important thing about these changes is that service mailcatcher status now works as expected and so Puppet's ensure => 'running' test actually works correctly.

Codes of conduct

As my mind turns towards the conferences that I'm attending this autumn, I came across Why you want a code of conduct & how we made one by Erin Kissane. I highly recommend that you read it and the links within it.

The part that struck me most was the thoughts on a plan of action. It's all very well to have a code of conduct, but if the event's organisers haven't got a plan on how to deal with reports and actual enforce the code, then it's a waste of time and doesn't help anyone.

I can imagine that reporting, in particular, can be more complicated than initial thoughts would imply. "Just talk to a member of staff" doesn't work very well for everyone as they always seem busy.If they aren't busy, then they are usually surrounded by people; not everyone is confident enough to interrupt and say "can I have word, privately". I can see why the really good codes of conduct have a telephone number on them. One code of conduct I read says that the report should preferably be in writing. That may be useful legally, but I'm not sure that that sends the right message.

Similarly, I wonder if the organisers with a code of conduct have a plan of action and know what they will do in advance if a report is made. Have the organisers discussed how they will react if it's a close friend that's caused the problem? Have they talked about what actions will result in a warning & which will require the attendee to leave the conference? Most importantly is it clear how any such decisions will be made and who will actually talk to the harassing attendee? I really hope so as it's incredibly hard to sort these things out during an actual incident.

I think the visibility of any code of conduct says a lot about how important inclusiveness is to the conference organisers. A conference I attended recently called out the code of conduct right at the start of the introduction. I liked this a lot.

Inclusiveness is important to me. Going forwards, this will be a factor I take into account when deciding to attend a conference. The conference organisers set the tone they expect and then it is for us, the attendees, to call out any unacceptable behaviour we see before it becomes an incident needing to be reported.

Using ZF2 Forms with Twig

Following on from looking at how to integrate Zend Framework 2 forms into Slim Framework, let's look at the changes required if you also happen to want to use Twig.

When it comes to rendering the form, we would want our template to look like this:

    <form method="POST" role="form">
        <div class="form-group">
            {{ formRow(form.get('email')) }}
        </div>
        {{ formElement(form.get('submit')) }}
    </form>

The ZF2 view helpers, formRow and formElement now look like Twig functions, however we don't want to have to rewrite all our ZF2 view helpers into Twig. Fortunately, Twig supports the concept of a undefined function callback is called whenever Twig encounters a function that it doesn't know how to call. We can use this to proxy through to the Zend\View system and get it to render the ZF2 view helpers.

Let's look at how we do this.

Starting with the work in the previous article, we merely need to change the view layer. That is, we don't need a custom PHP view (\RKA\View), but instead use the Slim-Views Twig component.

To add Twig to a Slim project we update composer.json and add "twig/twig": "1.16.*" and "slim/views": "0.1.*" to the require section and then run composer update.

We then update index.php to add the view class when instantiating the Slim and configure it.:

$app = new \Slim\Slim([
    'view' => new \Slim\Views\Twig()
]);

// Configure Twig
$view = $app->view();
$view->parserOptions = [
    'debug' => true,
    'cache' => false,
];
$view->parserExtensions = array(
    new \Slim\Views\TwigExtension(),
);

Now, whenever we call $app->render(), Twig will be used. The view templates have a .twig extension, so we rename home.php to home.twig and it's rendered like this:

$app->render('home.twig', [
    'form' => $form
]);

To integrate the ZF2 Form view helpers without having to rewrite them all as Twig extensions, we just need to register an undefined callback function that uses Zend Framework 2's PhpRenderer to render the view helper.

This is done with a few lines in index.php:

$viewHelperManager = $app->serviceManager->get('ViewHelperManager');
$renderer = new \Zend\View\Renderer\PhpRenderer();
$renderer->setHelperPluginManager($viewHelperManager);

$view->getInstance()->registerUndefinedFunctionCallback(
    function ($name) use ($viewHelperManager, $renderer) {
        if (!$viewHelperManager->has($name)) {
            return false;
        }

        $callable = [$renderer->plugin($name), '__invoke'];
        $options  = ['is_safe' => ['html']];
        return new \Twig_SimpleFunction(null, $callable, $options);
    }
);

Let's break this down.

Firstly, we create a PhpRenderer and set the helper plugin manager to ViewHelperManager that's already defined in the service manager.

We can access the underlying Twig_Environment object using $view->getInstance() and then call registerUndefinedFunctionCallback with a closure for our callback.

The callback code is this bit:

    function ($name) use ($viewHelperManager, $renderer) {
        if (!$viewHelperManager->has($name)) {
            return false;
        }

        $callable = [$renderer->plugin($name), '__invoke'];
        $options  = ['is_safe' => ['html']];
        return new \Twig_SimpleFunction(null, $callable, $options);
    }

Within the callback we check the ViewHelperManager to see if we have a view helper that we can call. If we don't then we return false so that Twig can try a difference undefined function callback in its list.

If we do know about this view helper, then we instantiate a Twig_SimpleFunction that will call the __invoke method of the view helper which we retrieve via the plugin method of the renderer. Note that we also tell Twig that our view helper is safe to use with HTML so it doesn't escape the HTML tags created by the view helper.

That's all there is to it.

The code that's shown here is on GitHub in the twig branch of the slim-zendform project, so you can download it and play around yourself.