Emma Goodwin is a Drupal developer based in Bristol. She specialises in backend development, Linux server administration and making the perfect cup of tea.

She's currently working with a luxury travel agency in London, helping them port their old legacy site over to Drupal.

This site tumblr feed will focus on Emma's biggest interests:
Bash scripts
Drupal 6, 7 & 8
Continuous Integration
Behaviour Driven Development

Profiling PHP with XDebug, QCacheGrind (KCacheGrind) and Vagrant on Mountain Lion

Installing XDebug and KCacheGrind is normally a straightforward task. However, I found it particularly fiddly to get working on a Mac (running Mountain Lion) with Vagrant.

A few VMs later, I figured out the quickest way to achieve this setup:

SSH onto Vagrant VM and install XDebug
cd vbox
vagrant ssh 
sudo apt-get install php5-xdebug

Create a writable directory for cachegrind files
mkdir /vagrant/cachegrind
chmod 777 /vagrant/cachegrind

Add the following to php.ini to enable XDebug profiler
sudo vim /etc/php5/apache2/php.ini

xdebug.profiler_enable = 1
xdebug.profiler_append = 1
xdebug.profiler_output_dir = "/vagrant/cachegrind"
xdebug.profiler_output_name = "cachegrind.out.%t.%p"

Restart apache
sudo service apache2 restart

Install QCacheGrind on Mac
brew install qcachegrind
brew linkapps

Install Graphviz (req. to produce graphs)
brew install graphviz

By default, Graphviz installs the dot binary to /usr/local/bin/dot, which QCachGrind can’t find. To get the graphs working, you need to add the following symlink:
sudo ln -s /usr/local/bin/dot /usr/bin/dot

Refresh website, fire up QCacheGrind.app, open cachegrind file and you’re good to go:


A quicker way to revert all Drupal features

Reverting all features seems to take a very long time. Intrigued whether any time could be saved by only reverting the features marked as ‘Overridden’, I created a quick bash script:

Reverting only the ‘Overridden’ features is around 45 seconds quicker (when compared to executing drush fra -y):


On average, the Drupal site I am working on is rebuilt around 20 times a day. That’s a 1¼ hr time saving across a working week!

I don’t know if this method will provide time savings as large as this to all versions of features. This has only been tested with 7.x-1.0-rc3. However, it is definitely worth trying.

Quick < 10 second guide to installing and using Slate (window manager for OSX)

I will be the first to admit that I am a bit of a ‘productivity geek’ when it comes to my mac workflow. I believe that having the right tools to multi task well and using your mouse less, are key to a productive day in the office.

I have used both Optimal Layout and Divvy for some time, but I have been left somewhat dissatisfied with the lack of configuration available. Slate on the other hand is a programmer’s dream.

cd /Applications && curl http://www.ninjamonkeysoftware.com/slate/versions/slate-latest.tar.gz | tar -xz && curl https://raw.github.com/jigish/slate/master/Slate/default.slate > ~/.slate && echo -e "\n # Show Grid \n bind g:cmd grid\n" >> ~/.slate && echo "Slate is now installed"

Launch Slate, hit CMD + G and you’re good to go!


Slate is not only more configurable than the popular alternatives, it is also free!

This post is only intended as a quick guide to get you up and running. To configure Slate further, I highly recommend reading Tristan Hume’s post.

How to delete / remove a field from a content type stored in a feature (Drupal)

When you remove a field from a content type and update the feature, this does not remove the fieldRemoving a field which is shared across several content types presents an even bigger problem. Normally you can just delete a field using field_delete_fieldTo remove a shared field, you need to remove an instance of the field.

After looking up how Drush perfoms this task, it appears that you have to first mark the field for deletion, then call cron or field_purge_batch to actually remove the field.

What worked for me
After removing the field from the feature (by deleting it from the content type and executing drush fu my_feature -y),  adding the following gist in an update hook did the trick for me.

Give your CMD + TAB keys a break and use terminal-notifier

If a lot of your work is terminal based, you will probably find yourself jumping between your browser and terminal many times a day, to check on the status of commands executed. Give your keyboard a break and save some time by using terminal-notifier instead:

sudo gem install terminal-notifier

Once installed, you can add notifications to existing bash scripts or append notifications to your command one liners like so:

rsync -avz Foo Bar && terminal-notifier -message "File transfer complete" -title "Rsync"

You can also configure notifications for when your commands fail:

(rsync Foo Bar && cap staging deploy:local && terminal-notifier -message "The latest changes have been deployed" -title "Deploy to stage" ) || terminal-notifier -message "An error has occurred" -title "Deploy to stage"

Programmatically create validated .htaccess rewrite rules in batch (using Bash)

Equipped with a CSV file containing a list of old and new URLs (produced by a batch script and completed by the client), I needed a quick and reliable method of turning this data into .htaccess rewrite rules.

In total, there’s probably less than 200 redirects that I needed to create. I could have created these rules manually. However, I chose to script this task for the following reasons:

  • Save time
  • Test both URLs for any 404s
  • Validate incomplete values in the CSV file
  • Eliminate any ‘copying and pasting’ errors
  • Create something that is ‘reusable’, more rules will be added later

To programmatically create these rules, I wrote a bash script which reads in a CSV value and performs the following tasks:

Assign the CSV column variables to real names
Even though this is only a very small script, as a matter of standard practice, I normally assign generic variable names to real names. For larger scripts, this helps improve code readability and reduces the need for heavy comments. It also means I only have a few lines to modify if the client decides re-arrange the order of the CSV columns (which used to happen quite a lot with some of my older clients).

Skip the CSV row if the input does not meet the required length
As the client had entered different values such as “none”, “incomplete” or “not finished”, it was easier and quicker to simply test the string length.

Check the HTTP response code for both URLs
When wget is invoked with the  --spider option, this instructs wget to behave like a web spider. Instead of downloading the page, this simply checks to see if the pages exist.

Print the .htaccess RewriteRule
If the HTTP response is “200 OK", the old URL is cleaned up (first forward slash removed) and the rewrite rule is printed:

RewriteRule ^old(/?.*) /new [R=301,L]

Excellence is not an act but a habit. The things you do the most are the things you will do the best.

Marva Collins