Convert PHP Warnings and notices into fatal errors

Xdebug version 2.3 was released last week and includes a feature improvement that I requested back in 2013! Issue 1004 asked for the ability to halt on warnings and notices and I'm delighted that Derick implemented the feature and that it's now in the general release version.

It works really simply too.

Turn on the feature by setting xdebug.halt_level either in your php.ini or via ini_set():

<php
ini_set('xdebug.halt_level', E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);

Now cause a warning:

<php
echo "Before";
imagecreatefromstring(null); // Don't pass null into imagecreatefromstring()!
echo "After";

The result is that "Before" is displayed and then we get the standard Xdebug notice, but "After" is not displayed as the script is halted on the due to the warning.

Xdebug halt-level example

I used to have a error handler in place solely to do this, but now I don't need it!

The most common use-case I have for it is a warning that occurs during a POSTed request that redirects to another page. Other people are dedicated log-checkers, but I'm not and vastly prefer seeing the big orange box telling what's gone wrong.

As I said, I'm really happy to see this feature; it's worth considering turning on your development set up too!

Git submodules cheat sheet

Note: run these from the top level of your repo.

Clone a repo with submodules:

    $ git clone git@bitbucket.org:akrabat/dotvim.git .vim
    $ git submodule update --init

View status of all submodules:

    $ git submodule status

Update submodules after switching branches:

    $ git submodule update

Add a submodule:

    $ git submodule add git://github.com/tpope/vim-sensible.git bundle/vim-sensible

Update all submodules to latest remote version

    $ git submodule update --remote --merge
    $ git commit -m "Update submodules"

Update a specific submodule to the latest version (explicit method):

    cd bundle/vim-sensible
    git pull origin master
    cd ../..
    git add bundle/vim-sensible
    git commit -m "update vim-sensible"

Remove a submodule:

    edit .gitmodules and remove this submodule's section
    $ git rm --cached bundle/vim-sensible
    $ rm -rf bundle/vim-sensible
    $ git commit -m "Remove vim-sensible"

or

    git submodule deinit bundle/vim-sensible    
    git rm bundle/vim-sensible
    git commit -m "Remove vim-sensible"

Docs:

Routing to a controller with Slim

In a couple of projects that I've written using Slim Framework, I've found it beneficial to organise my code into controllers with injected dependencies; probably because that's how I'm used to working with ZF2.

To make this easier, I've written an extension to the main Slim class and packaged it into rka-slim-controller which will dynamically instantiate controllers for you for each route.

Defining routes is exactly the same as normal for a Slim controller, except that instead of a closure, you set a string containing the controller's classname and method that you want to be called, separated by a colon:

$app = new \RKA\Slim();
$app->get('/hello/:name', 'App\IndexController:hello');
$app->map('/contact', 'App\ContactController:contact')->via('GET', 'POST');

Behind the scenes, this will create a closure for you that will lazily instantiate the controller class only if this route is matched. It will also try to retrieve the controller via Slim's DI container which allows me to inject relevant dependencies into my controller class.

For example, you could group the functionality for authentication:

$app->get('/login', 'User\AuthController:login')->name('login');
$app->post('/login', 'User\AuthController:postLogin');
$app->get('/logout', 'User\AuthController:logout')->name('logout');

The controller needs to interact with a service class, say UserService, which is injected into the controller:

namespace User;

final class AuthController extends \RKA\AbstractController
{
    private $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    public function login()
    {
        // display login form
    }

    public function postLogin()
    {
        // authentication & redirect
    }

    public function logout()
    {
        // logout functionality
    }
}

In order to inject the service, we define a factory for the DI container and we're done:

$app->container->singleton('User\AuthController', function ($container) {
    return new \User\AuthController($container['UserService']);
});

The nice thing about this approach is that I can group functionality that requires the same dependencies into a single class and be sure that I only instantiate the classes that I need in order to service the request.

Sending test emails from PHP

I've written before about redirecting email while developing using a trap mail script on my development system. Other people also like MailCatcher.

I've recently switched to using a simple PHP script that creates a elm file on disk that can be opened by Apple's Mail client. This solution is much faster as there is no SMTP processing involved.

Adam Royle came up with this solution in his article Setup a testing mail server using PHP on Mac OS X, so thank you Adam!

In summary:

  • Create ~/smtp_out
  • Create ~/smtp_out/smtp_catcher.php with these contents:
    #!/usr/bin/php
    *lt;?php
    
    // create a filename for the emlx file
    list($ms, $time) = explode(' ', microtime());
    $filename = __DIR__ . '/' . date('Y-m-d h.i.s,', $time) . substr($ms,2,3) . '.eml';
    
    // write the email contents to the file
    $email_contents = fopen('php://stdin', 'r');
    $fstat = fstat($email_contents);
    file_put_contents($filename, $fstat['size'] . "\n");
    file_put_contents($filename, $email_contents, FILE_APPEND);
    
    // open up the eml file (using Apple Mail)
    exec('open ' . escapeshellarg($filename));
    
  • Make ~/smtp_out/smtp_catcher.php executable
  • Update php.ini and set:
    sendmail_path = sudo -u rob /Users/rob/smtp_out/smtp_catcher.php

    (Change rob to your username!)

  • Restart Apache
  • Give Apache permission to open Mail, by updating sudoers:
    • sudo visudo
    • Add this to the end:
      %www    ALL=(ALL)   NOPASSWD: /Users/rob/smtp_out/smtp_catcher.php

      (Again, change rob to your username!)

Read Adam's post for the full details on what's going on.

It's a nice solution and I'm liking it!

Customising Bootstrap 3

I'm sure everyone already knows this, but it turns out that you can customise Bootstrap 3 without having to understand Less.

Part of the reason that I didn't realise this is that I run my web browser windows quite small and regularly don't see the main menu of getbootstrap.com as it's hidden being the "three dashes" button. However, there's an option called Customize on it.

This page gives you a massive form where you can configure lots of Bootstrap settings.

For one project, I have tightened the spacing to suit the customer's requirements. This was easily done by changing:

	@grid-gutter-width: 16px
	@modal-inner-padding: 10px
	@modal-title-padding: 10px
	@panel-body-padding: 10px
	@panel-heading-padding: 10px 10px

The Compile and Download button at the bottom rather helpfully puts your configuration file into a gist so you can find it again too.

Dependency injection in Slim framework

Slim framework comes with a Dependency Injection container called Set.

The basics

The DIC is accessed via the container property of $app.

To set, you use the set() method:

$app->container->set('foobar', function() { return new Foo\Bar(); } );

If you need a given resource to be shared, then use the singleton method:

$app->container->singleton('foobar', function() { return new Foo\Bar(); } );

And then to retrieve from the container, there are multiple ways to do it:

$fooBar = $app->container->get('foobar');
$fooBar = $app->container['foobar'];
$fooBar = $app->foobar;

The shortcut version ($app->foobar) only works if the key is a valid PHP variable name.

Dependent dependencies

The callback is passed the container instance, you can also retrieve dependencies and use them to configure the object that you are instantiating as shown in this example:

$app = new \Slim\Slim(
    [
        'db.dsn' => 'mysql:host=localhost;dbname=something',
        'db.username' => 'user',
        'db.password' => 'pass',
    ]
);

// Add dependencies
$app->container->singleton('PDO', function ($container) {
    $settings = $container['settings'];
    return new PDO($settings['db.dsn'], $settings['db.username'], $settings['db.password']);
});

$app->container->singleton('UserMapper', function ($container) {
    $pdo = $container['PDO'];
    return new User\UserMapper($pdo);
});

// Routes
$app->get('/', function ($name) use ($app) {
    // (Using the DIC as a service locator here...)
    $userMapper = $app->container['UserMapper'];
    // etc...
});

In this example the '/' route uses an instance of User\Mapper, which requires a PDO object. We therefore add two resources to the container: one for PDO and one for the UserMapper.

Slim uses its own DIC

Note that Slim uses it's own DIC for all its dependencies which means that you can override them. It even uses it for the configuration settings that you pass in to the Slim constructor which is accessible via $app->container['settings'] (or $app->settings).

The classic example is that Slim creates its own instance of Slim\Log and attaches it to the log key in the container. If you wanted to use monolog instead, you can simply do:

$app->container->singleton('log', function () {
    $log = new \Monolog\Logger();
    $log->pushHandler(new \Monolog\Handler\StreamHandler('path/to/log.txt'));
    return $log;
});

We have now replaced Slim's own log system with monolog.

Fin

I know that it's tempting to grab $app directly, but Slim makes is nearly as easy to configure your objects using dependency injection which will make your life much easier in the long-run!

Setup Doctrine Migrations to update MySQL timestamp on update

One project I'm working on uses MySQL exclusively and is also using Doctrine Migrations.

I wanted to set up a column called updated that was automatically set to the timestamp of the last time the row was changed.

This is done in SQL like this:

CREATE TABLE foo (
    id INT AUTO_INCREMENT NOT NULL,
    bar VARCHAR(100) NOT NULL,
    updated timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    PRIMARY KEY(id)
);

It's not quite obvious how to do this in Migrations as it is designed to be portable across database engines and clearly this is a MySQL-ism.

To do it, you use the columnDefinition option to addColumn() within your up() method, like this:

public function up(Schema $schema)
{
  $myTable = $schema->createTable('foo');
  $myTable->addColumn('id', 'integer', ['autoincrement'=>true]);
  $myTable->addColumn('bar', 'string', ['length' => 100]);
  $myTable->addColumn(
    'updated',
    'datetime',
    ['columnDefinition' => 'timestamp default current_timestamp on update current_timestamp']
  );

  $myTable->setPrimaryKey(['id']);
}

The columnDefinition option replaces the type (which you must still set it to a valid portable option) and so you can easily put in database-specific definitions when you need to.

Shorter directory text in Bash prompt

Rather helpfully, David Goodwin left a comment about how he shortens the space taken up by the directory section of his terminal's PS1 prompt by using a Bash script to remove the middle portion.

This is a really good idea, so I ported it into my PS1 set up which resulted in some rearranging and thought I'd share here as I modified for OS X and I don't want to lose it!

The relevant portion of my .profile is:

# Git information for prompt
if [ -f $(brew --prefix)/etc/bash_completion.d/git-prompt.sh ]; then
    . $(brew --prefix)/etc/bash_completion.d/git-prompt.sh
fi
GIT_PS1_SHOWDIRTYSTATE=true
GIT_PS1_SHOWUNTRACKEDFILES=true

# Shorten current directory - Based on function by David Goodwin
function shorten_pwd()
{
    LENGTH="40"
    PART1="10"
    PART2="27"

    DIR=`echo "${PWD}" | sed "s/\\/home\\/$USER/~/" | sed "s/\\/Users\\/$USER/~/"`

    if [ ${#DIR} -gt $(($LENGTH)) ]; then
        echo "${DIR:0:$(($PART1))}...${DIR:$((${#DIR}-$PART2)):$PART2}"
    else
        echo "$DIR"
    fi
}


# Set prompt
prompt_cmd () {
    LAST_STATUS=$?

    local COLOUR_RESET='\[\e[0m\]'
    local BLACK='\[\e[0;30m\]'
    local RED='\[\e[0;31m\]'
    local GREEN='\[\e[0;32m\]'
    local YELLOW='\[\e[0;33m\]'
    local BLUE='\[\e[0;34m\]'
    local PURPLE='\[\e[0;35m\]'
    local CYAN='\[\e[0;36m\]'
    local WHITE='\[\e[0;37m\]'
    local BOLD_BLACK='\[\e[1;30m\]'
    local BOLD_RED='\[\e[1;31m\]'
    local BOLD_GREEN='\[\e[1;32m\]'
    local BOLD_YELLOW='\[\e[1;33m\]'
    local BOLD_BLUE='\[\e[1;34m\]'
    local BOLD_PURPLE='\[\e[1;35m\]'
    local BOLD_CYAN='\[\e[1;36m\]'
    local BOLD_WHITE='\[\e[1;37m\]'

    PS1="$BLACK\u@\h"     # user@host
    PS1+=" "
    PS1+="$BLUE"
    PS1+=$(shorten_pwd)   # current directory (usually \w)
    PS1+=" "
    PS1+="$RED"
    PS1+=$(__git_ps1)     # git status
    PS1+="$COLOUR_RESET"
    PS1+='\$ '
}

PROMPT_COMMAND='prompt_cmd && tab_title'

There are three sections here. Firstly we ensure that git-prompt.sh is loaded and configure a couple of settings for it. Then we write a function called shorten_cwd() based on David's script. The main changes here are that I also look for /Users/$USER as that's where OS X stores home directories and that I don't split in the middle. Finally we define prompt_cmd() to set PS1 in a way that I understand and assign it to PROMPT_COMMAND along with tab_title.

The end result looks like this:

Shorter prompt

Setting OS X's Terminal Tab to the current directory

I use many tabs in a Terminal window quite frequently, and while the window title will show the current directory name, the tab title doesn't. You can manually change it using shift+cmd+i, but who can be bothered?

Automating it so that the tab title always matches the current directory turns out to be really easy and just requires a few lines in ~/.profile.

Firstly, we need a function that sets the tab's title to the last segment of the current working directory:

function tab_title {
  echo -n -e "\033]0;${PWD##*/}\007"
}

We then just need to automate calling it whenever we change directory. The easiest way to do this is to change PROMPT_COMMAND:

PROMPT_COMMAND="tab_title ; $PROMPT_COMMAND"

That's it. Whenever I change directory, or open a new terminal tab, then the tab's title now matches the last segment of the directory for that tab. Much more useful!

Terminal tabs

Exclude elements from Zend\Form's getData()

If you need to exclude some elements from a Zend\Form's getData(), the easiest way is to use a validation group.

For example:

class SomeForm extends \Zend\Form\Form implements
    \Zend\InputFilter\InputFilterProviderInterface
{
    public function init()
    {
        $this->add([
            'name' => 'name',
            'options' => ['label' => 'Name'],
        ]);
        
        $this->add([
            'name' => 'email',
            'options' => ['label' => 'Email'],
        ]);

        $this->add([
            'name' => 'submit',
            'type' => 'button',
            'options' => [
                'label' => 'Filter',
            ],
        ]);

        $this->setValidationGroup(['name', 'email']);
    }

    public function getInputFilterSpecification()
    {
        // return input filter specification here
    }
}

The call to setValidationGroup() contains an array of all the elements you want to be validated. In this case we list all elements except the submit button. The side-effect is that when you called $form->getData(), only the values for those elements are returned which can sometimes be useful.

Generalise

Rather than having to re-list all the elements again, it's more usually to only want to exclude one or two elements. The easiest way to do this is to extend Zend\Form\Form and override isValid() to set up a validation group based on an option called 'exclude' that you apply to the relevant elements:

<?php
namespace My;

class Form extends \Zend\Form\Form
{
    /**
     * Override isValid() to set an validation group of all elements that do not
     * have an 'exclude' option, if at least one element has this option set.
     *
     * @return boolean
     */
    public function isValid()
    {
        if ($this->hasValidated) {
            return $this->isValid;
        }

        if ($this->getValidationGroup() === null) {
            // Add all non-excluded elements to the validation group
            $validationGroup = null;
            foreach ($this->getElements() as $element) {
                if ($element->getOption('exclude') !== true) {
                    $validationGroup[] = $element->getName();
                }
            }
            if ($validationGroup) {
                $this->setValidationGroup($validationGroup);
            }
        }
        
        return parent::isValid();
    }
}
?>

Then, to exclude an element, we extend SomeForm from \My\Form and just add a new 'exclude' option to the elements we wish to exclude. For example:

// in SomeForm::init()
$this->add([
    'name' => 'submit',
    'type' => 'button',
    'options' => [
        'label' => 'Filter',
        'exclude' => true,
    ],
]);

and this element will be automatically excluded when you call $form->getData().