Yearly Archives: 2012

2012 in pictures

As another year draws to a close, I continue my tradition of showing off some photos that recap our year.

January

January 2012 was a quiet month. I released Daily Jotter 1.3 and started using Sublime Text as my editor of choice for programming. Photographically, the highlight of the month was the Worcester Flickr Group’s scavenger hunt.

Coffee

February

It snowed in Feburary and I started publishing articles on ZF2. Again, a Worcester Flickr Group outing to the Panorama Tower, Croome D’abitot in Worcestershire provided the only interesting photographs that I took all month.

Panoramic Tower

March

I was very much into Instagram and didn’t take any photos with my DSLR this month!

Concentration

April

In April, I went to the inaugural Whisky Web conference as an attendee, which was fun, especially as Josh used one of my photos in his keynote.

Josh Holmes

May

Both sons have their birthdays in May and a big family get together were the highlights of May.

Out-take

June

Nick and Kerry were married this month.

Nick & Kerry's wedding

July

A quiet month. I visited the PHPNW user group to see Jenny’s first talk.

Jenny presenting at #phpnw

August

In August we went to the Isle of Wight on holiday and I managed to visit the steam railway.

Old and newer

September

Eldest’s first day at high school!

First day at high school

October

October saw me speak at both the PHPNW and ZendCon conferences. I gave a tutorial with Evan Coury at both events which seem to be appreciated by the attendees.

The bar was popular in the evening

Whilst in California, I managed to take a photo of the Golden Gate bridge.

Golden Gate Bridge

November

After the busy-ness of October’s travel, I had a quiet month in Worcester. I also got some Toyella business cards!

I have @toyella cards!

December

Last month of the year and the last month that my company’s offices were in Birmingham as we moved to Worcester for the start for 2013. There was also a lot of rain!

Flooded!

All in all, another pretty good year. Next year, I intend to take more photos with my DSLR though.

Zend Framework 1 is not dead; ensure you upgrade!

I’m delighted to announce that Zend Framework 1.12.1 has been released! This release fixes 50 issues which is a great result. I’d like to thank everyone who submitted a patch to ZF1 and to Matthew Weier O’Phinney, Frank Br├╝ckner and Mike Willibanks in particular for their work on this release.

There’s a few important things to note:

  • There’s a security fix in 1.12.1. Please read ZF2012-05 if you use Zend_Feed_Rss or Zend_Feed_Atom.
  • The minimum PHP version for ZF 1.12.0 and ZF 1.11.12 is 5.2.11 due to security fixes for ZF2012-03.
  • Zend_Markup_Parser_Textile has been removed from 1.12.1 as it was significantly broken.

As I said in the title of this post, ZF1 isn’t dead: we have committed to supporting it until at least 2014 so we will have more releases as required either due to security issues found or because we’ve accumulated enough bug fixes in the codebase. With that in mind, please continue to raise issues on the issue tracker if you find any issues in 1.12.1. We’d also appreciate your patches if you can fix any of the issues! I’m on IRC if you want any help (Akrabat in the #zftalk.dev channel on Freenode).

Some notes on git

This set of notes covers the main things that I think you need to know about working with git. It is not comprehensive and mainly serves as a reminder for myself.

Remotes

In a typical open source workflow using GitHub or BitBucket, you would fork the main repository into your own and then clone that copy to your local computer:

    git clone git@github.com:akrabat/joind.in.git

You then need to connect your local repository to the main repository. By convention, the main repository is known as upstream:

    cd joind.in
    git remote add upstream git://github.com/joindin/joind.in.git

To sync your local repository with upstream and update your copy back on BitBucket/GitHub:

    git checkout master
    git fetch upstream
    git merge --ff-only upstream/master
    git push origin

If the merge fails, then something bad has happened and you’ll need Google! One option is to force your master to match upstream using git reset --hard upstream/master and then git push --force origin.

Branching

The main branch in the repository is called master. Never ever code directly on master. Always create a branch and code on that and then merge back to master when complete.

(Note that the push command is also used throughout this document to sync your local repository with your remote on BitBucket/GitHub. If don’t want to publish, then don’t push.)

Create a branch:

    git checkout -b my-branch-name
    git push origin my-branch-name

List all branches:

    git branch -v

To list all remote branches too use the -a switch:

    git branch -v -a

Change from one branch to another:

    git checkout another-branch-name

Delete a branch:

    git branch -D my-branch-name
    git push origin :my-branch-name

Rebase master onto a branch:

If lots of people are working on the project, then master has probably changed a lot since you started. It’s going to make your merge back to master much easier if you update your branch so that all your changes appear to be after all the changes on master. This is known as rebasing:

    git fetch upstream
    git checkout my-branch-name
    git rebase -f upstream/master
    git push -f origin my-branch

Bring in a remote branch to your local repository:

If you want to work with a branch that’s on a remote repository, then you need to create your own tracking branch:

    git fetch upstream
    git checkout master
    git branch -t upstream/remote-branch-name
    git push origin remote-branch-name

The name of your local branch will match the remote branch name.

Sync remote branch with local one

    git fetch upstream
    git checkout remote-branch-name
    git merge upstream/remote-branch-name
    git push origin

Committing

To commit a change:

To commit a change, you first need to stage the files that you want to commit to the index:

    git add filename

You can then commit:

    git commit -m "my commit message"
    git push origin

Note that if you change a filename after adding to the index, then the change will not be committed unless your git add the file again. For information about a good commit message, read A Note About Git Commit Messages by Tim Pope.

Merging

Merge a branch into master:

    git checkout master
    git fetch upstream && git merge --ff-only upstream/master
    git merge --no-ff my-branch-name

Note that you don’t need to commit anything. You do need push though:

    git push

If there were conflicts, then you need to resolve them by editing the files appropriately (look for <<<<<<<). At this point, you do need commit your changes:

    git add .
    git commit
    git push

Back out a conflicted merge:

    git reset --hard HEAD

Working with your repository

To find out what’s happened:

    git reflog -10

This provides a list of the last 10 things that you’ve done on this repository across all branches.

    git log --oneline -10

This provides a list of the last 10 commits that’s happened on this branch

In both cases, the first column contains the commit hash reference that uniquely identifies each commit.

To find out current commit:

    git log -1

To undo all working changes

    git checkout .

To revert a commit:

Find the commit that you want to go back to via log or reflog:

    git reset --hard abcdef0

You can also use the ‘HEAD’ number from reflog:

    git reset --hard HEAD@{1}

To amend last commit message:

    git commit --amend -m "New commit message"

It’s best to do this before pushing. If you have pushed, then you need to force push using git push --force and expect to get lots of hassle from your co-workers.

Differences

To find out the differences between your edits and the last commit:

All files:

    git diff

One file:

    git diff -- my-filename

To find out the differences between current branch and master:

    git diff master..HEAD my-filename

To find out the differences between local master and origin’s master:

    git diff HEAD...origin/master

Some git aliases

Aliases provide a way to create new git commands and are usually used for creating shortcuts:

Type these from the command line:

    $ git config --global alias.st status
    $ git config --global alias.staged 'diff --staged'
    $ git config --global alias.unstage 'reset HEAD --'
    $ git config --global alias.last 'log -1 HEAD'

This gives you some new git commands:

  • git st => view current status
  • git staged => view the diff of what is currently staged
  • git unstage filename => Remove filename from the staging area
  • git last => view last commit

Git export from BitBucket

One of the consequences of moving to git is that I needed to get our deployment scripts working with it. The first requirement was an equivalent to svn export. This is simple enough, but, as usual, I wanted to save myself some effort and created a script that wrapped it up for me.

git-export.sh:

#!/bin/sh

#
# USAGE: export.sh REPO_NAME BRANCH_NAME [DIRECTORY_NAME] [BITBUCKET_ACCOUNT_NAME]
#
#

BITBUCKET_ACCOUNT_NAME=akrabat
GITROOT=git@bitbucket.org:${BITBUCKET_ACCOUNT_NAME}

if [ -z "$1" ]; then
    echo "Usage is git-export.sh {repo_name} [{branch_name = master}] [{dir_name = repo_name}]"
    exit 1
fi


REPO_NAME=$1
BRANCH_NAME=$2
THIS_DIR=$3

if [ -z "$BRANCH_NAME" ]; then
    BRANCH_NAME=master
fi
if [ -z "$THIS_DIR" ]; then
    THIS_DIR=$REPO_NAME
fi

if [ -d "$THIS_DIR" ]; then
    echo "Error: directory '$THIS_DIR' already exists."
    exit 1
fi

echo "Exporting $BRANCH_NAME branch of $REPO_NAME into ./$THIS_DIR..."
mkdir $THIS_DIR
git archive --format=tar --remote=$GITROOT/$REPO_NAME $BRANCH_NAME | tar -xf - -C $THIS_DIR

echo "Done"
echo ""

Not the most complicated script in the world, but I never need to think about it again and it’s easy to call from Phing and other deployment scripts and doesn’t require a local clone to exist.

Migrating to BitBucket from Subversion

I’ve recently started the process of moving all of my Subversion repositories to BitBucket. BitBucket is my preferred git hosting supplier as its pricing structure suits me much better; that is, I have lots of private repositories and GitHub is too expensive for my case!

As I needed to migrate over one hundred repositories from Subversion to BitBucket, I automated it via a simple shell script. Rather usefully, BitBucket has an API that let me create the repository using curl and then use git to do the rest.

I’ve put this script here in the hope that it may be useful to someone else. It’s certainly been useful to me!

#!/bin/sh

# Inspired by :
# * http://blogs.atlassian.com/2012/01/moving-confluence-from-subversion-to-git/
# * http://john.albin.net/git/git-svn-migrate
# * http://blog.woobling.org/2009/06/git-svn-abandon.html
# * http://stackoverflow.com/questions/10637378/how-do-i-convert-a-bare-git-repository-into-a-normal-one-in-place

# usage: svn2git.sh REPO
# NOTE that we assume that REPO lives underneath SVN_URL and is a standard subversion layout

# SET THESE CORRECTLY!
USERNAME=my_bitbucket_username
PASSWORD=my_bitbucket_password
AUTHORS_TEXT_FILE=~/authors.txt
SVN_URL=http://svn.example.com/svn


REPO=$1
if [ -z "$REPO" ]; then
    echo ""
    echo "USAGE: svn2git.sh {repo} [{delete_repo_first =1|0}]"
    echo ""
    exit;
fi
DELETE_REPO_FIRST=$2
if [ -z "$DELETE_REPO_FIRST" ]; then
    DELETE_REPO_FIRST=0
fi

BASE_DIR=/tmp
TEMP_REPO=${REPO}_tmp
BARE_REPO=${REPO}.git

echo ""
if [ $DELETE_REPO_FIRST -eq 1 ]; then
    echo "Delete repository on BitBucket first"
    curl -L --silent -X DELETE -u ${USERNAME}:${PASSWORD} https://api.bitbucket.org/1.0/repositories/${USERNAME}/${REPO}
fi
echo "Create repository on BitBucket"
r=`curl -L --silent -X POST -u ${USERNAME}:${PASSWORD} https://api.bitbucket.org/1.0/repositories/ -d name=${REPO} -d scm=git`
t=${r:0:11}
if [ "${r:0:11}" = "Bad Request" ]; then
    echo ${r}
    exit 1
fi

echo ""
echo "Clone from subversion into temporary repository"
cd ${BASE_DIR}
git svn clone --stdlayout --no-metadata 
 -A ${AUTHORS_TEXT_FILE} 
 ${SVN_URL}/${REPO}/ ${TEMP_REPO}

if [ "$?" -ne "0" ]; then
    echo "ERROR: Failed to convert $REPO from subversion to git"
    exit 1
fi

echo ""
echo "Create bare repository"
git init --bare ${BARE_REPO}
cd ${BASE_DIR}/${BARE_REPO}
git symbolic-ref HEAD refs/heads/trunk

echo ""
echo "Push temporary repository into bare one"
cd ${BASE_DIR}/${TEMP_REPO}
git remote add bare ${BASE_DIR}/${BARE_REPO}
git config remote.bare.push 'refs/remotes/*:refs/heads/*'
git push bare

echo ""
echo "Rename 'trunk' to 'master' as that's the git norm"
cd ${BASE_DIR}/${BARE_REPO}
git branch -m trunk master


echo ""
echo "Clean up branches and tags"
git for-each-ref --format='%(refname)' refs/heads/tags |
cut -d / -f 4 |
while read ref
do
  git tag "$ref" "refs/heads/tags/$ref";
  git branch -D "tags/$ref";
done

echo ""
echo "Convert to 'normal' repository from bare"
mkdir .git
mv * .git
git config --local --bool core.bare false


echo ""
echo "Push branches & tags to BitBucket"
git remote add upstream git@bitbucket.org:${USERNAME}/${REPO}
git push --all upstream
git push --tags upstream


echo ""
echo "Clean up"
cd ${BASE_DIR}
rm -rf ${BARE_REPO}
rm -rf ${TEMP_REPO}

echo ""
echo "All done"

Note that at the top there’s this section:

# SET THESE CORRECTLY!
USERNAME=my_bitbucket_username
PASSWORD=my_bitbucket_password
AUTHORS_TEXT_FILE=~/authors.txt
SVN_URL=http://svn.example.com/svn

You need to put your information here if you want to use this script :)

The authors.txt file is a simple text file that maps Subversion user names to git names and emails. It should look something like this:

rob = Rob Allen <rob@akrabat.com>
jsmith = John Smith <jsmith@example.com>

To use this script, I simply call:

svn2bitbucket.sh reponame

and it does its thing. Note that it’s not especially fast for a repository that has lots of history. You can also do svn2bitbucket.sh reponame 1 which will delete the repository on BitBucket first which allows you to re-run the transfer. Note that if you do this, then you will lose any commits you independently pushed to the git repository!

Using ZendSession

This is a quick note on how to use ZendSession.

Although the component name is ZendSession, you actually interact with ZendSessionContainer to store and retrieve session data:

use ZendSessionContainer;

$session = new Container('SomeKeyName');

ZendSessionContainer‘s constructor takes a string argument which is the name for this container (‘SomeKeyName’ in this case). It’s optional and if you don’t set it, then it is set to ‘Default’. The name allows you to use the same session keys in different containers.

To set data into the session:

$session->pageNumber = 2;

and to retrieve it again:

$pageNumber = $session->pageNumber;

Behind the scenes, ZendSession has replaced _SESSION with an instance of ZendSessionStorageSessionStorage. Fortunately this object extends ArrayObject, so you can still access $_SESSION as if it was an array. Our particular piece of data is at $_SESSION['SomeKeyName']['pageNumber'] and is set it to 2.

Integrating BjyAuthorize with ZendNavigation

If you are using BjyAuthorize for ACL configuration and want to use ZendNavigation‘s ZendAcl integration features, then you need to set the Acl and Role information into ZendNavigation.

The easiest way to do this is to add the following to ApplicationModule::onBoostrap():

        $sm = $e->getApplication()->getServiceManager();

        // Add ACL information to the Navigation view helper
        $authorize = $sm->get('BjyAuthorizeServiceAuthorize');
        $acl = $authorize->getAcl();
        $role = $authorize->getIdentity();
        ZendViewHelperNavigation::setDefaultAcl($acl);
        ZendViewHelperNavigation::setDefaultRole($role);

This assumes that you’ve set up BjyAuthorize with some resources and rules. For example, in my config/autoload/bjyauthorize.global.php, I have a ‘bug’ resource and have a rule that allows the reporter role access to the list and add privileges:

        'resource_providers' => array(
            'BjyAuthorizeProviderResourceConfig' => array(
                'bug' => array(),
            ),
        ),

        'rule_providers' => array(
            'BjyAuthorizeProviderRuleConfig' => array(
                'allow' => array(
                    array(array('reporter'), 'bug', array('list', 'add')),
                ),
            ),
        ),

My ZendNavigation configuration for the bug menu item is in my Bug module’s module.config.php and it looks like:

    'navigation' => array(
        'site' => array(
            'bug' => array(
                'label' => 'Bugs',
                'route' => 'bug',
                'resource' => 'bug',
                'privilege' => 'list',
                'pages' => array(
                    'create' => array(
                        'label' => 'Create new project',
                        'route' => 'bug/create',
                        'resource' => 'bug',
                        'privilege' => 'add',
                    ),                        
                ),
            ),
        ),        
    ),    

That’s all there is to it.

Introducing AkrabatSession

One of the requirements for a new app that I’m writing is that it has a specific session name. In Zend Framework 2, this is done by creating a SessionManager with the correct configuration and then setting the default manager on the Session Container:

use ZendSessionConfigSessionConfig;
use ZendSessionSessionManager;
use ZendSessionContainer;

$sessionConfig = new SessionConfig();
$sessionConfig->setOptions(array('name'=>'MY_SESSION_NAME');
$sessionManager = new SessionManager($config);
Container::setDefaultManager($sessionManager);

Obviously, I need to be able to configure the name (and potentially other session configuration options) from my config/autoload/global.php file and this is a generically useful requirement, so I created the AkrabatSession module.

This is a really simple module that simply allows you to configure the SessionManager with minimal effort:

  1. Install AkrabatSession.
  2. Enable it as the first module in application.config.php
  3. Add the following to your configuration array in global.php:
        'session' => array(
            'name' => 'MY_SESSION_NAME_HERE',
        ),

Further details are in the README file and of course, it’s available on Packagist.

Zend Framework 1 or 2 training in Belgium in January 2013?

I will be in Belgium in the last week of January (from the 28th). If your company could benefit from one or two days training or consultancy in Zend Framework 1 or 2, get in touch and let’s see if we can make it happen.

I’m able to provide training on ZF1/ZF2 to small groups of developers at your offices and can customise the course to suit your needs and the experience of your team. I can also provide consultancy advice on your current code and provide expert, outside advice as required. I’m happy to travel anywhere within a reasonable commute from Brussels.

Send me an email if you’re interested in exploring the options.