Automate PHP with Phake – Introduction

This entry is part 1 of 2 in the series Automating PHP with Phake

Automating PHP with Phake

As developers, we often have to do repetitive tasks such as updating the database structure, seeding the database, writing CRUD code, running tests, and uploading files to a server. Wouldn’t it be great if we could automate these mundane tasks and proceed with solving the more important problems such as making our app more secure or more usable to our users?

Phake, an automation tool written for PHP, can do those tasks for you. If you’re familiar with Ruby, it’s basically a Rake clone. In this two-part series I’m going to walk you through integrating Phake into your workflow. I will walk you through the installation, some of the basics in using Phake and then finally some real-world examples.

Installation

Phake should be installed globally via Composer:

composer global require 'jaz303/phake=*'

This makes Phake accessible from any folder, and doesn’t require you to change your project’s composer.json file.

If you don’t have access to the ‘composer’ command, install Composer globally.

Basics

To execute Phake tasks, you need to create a Phakefile. The Phakefile contains the configuration for the tasks that you want to execute. If you’ve used Grunt before, a Phakefile is similar to a Gruntfile.

An important note about the Phakefile is that it’s just a PHP file so you can just write it the same way as you do with your PHP projects.

Creating Tasks

You can create tasks by calling the task() method. This takes up the name of the task as the first argument, and the function to execute as the last argument.

<?php
task('task_a', function(){
  echo "Hi I'm task A!\n"; 
});

You can then execute it with the following command:

phake task_a

This will then return the following output:

Hi I'm task A!

Dependencies

If one task depends on another task, you can supply the name of that task right after the main task:

<?php
task('task_a', function(){
  echo "Hi I'm task A!\n"; 
});

task('task_b', 'task_a', function(){
  echo "Hi I'm task B! I need task A to execute first before I can do my thing!\n";
});

To execute the tasks in order you just have to call the task which has a dependency first. In this case task_b depends on task_a so we call it first:

phake task_b

Executing it will return the following output:

Hi I'm task A!
Hi I'm task B! I need task A to execute first before I can do my thing!

And you can keep on adding dependencies:

<?php
task('task_a', function(){
  echo "I get to execute first!\n"; 
});


task('task_b', 'task_a', function(){
  echo "Second here!\n";
});

task('task_c', 'task_b', function(){
  echo "I'm the last one!\n";
});

Execute them by calling the the final task that needs to be called. In this case, it’s task_c that we want to execute last:

phake task_c

It will then return the following output:

I get to execute first!
Second here!
I'm the last one!

Note that with this method of declaring dependencies, calling task_b would result in task_a being called first. If you don’t want this to happen and you still want to execute a specific task separately without executing its dependencies first then you can declare it using the following method:

task('task_a', function(){
  echo "I get to execute first!\n"; 
});

task('task_b', function(){
  echo "Second here!\n";
});

task('task_c', 'task_a', 'task_b', function(){
  echo "I'm the last one!\n";
});

In the example above we are setting task_a, and task_b as the dependencies of task_c. Note that the order matters here. So the task right after the main task (task_a) will get executed first, and the one right next to it (task_b) will be the second and then finally the main task (task_c) is executed.

With Phake there’s another way of defining your dependencies: through the use of before or after blocks right after defining the main task. In this case our main task is to eat so we define the tasks that we want to execute before and after it under its declaration:

task('eat', function(){
  echo "Yum!";
});

before('eat', function(){
  echo "Wash your hands before you eat\n";
});

after('eat', function(){
  echo "Brushy brush! brush!\n";
});

When you execute eat you get the following output:

Wash your hands before you eat
Yum!
Brushy brush! brush!

Grouping Tasks

With Phake you can also group related tasks together:

group('clean_the_house', function(){
  task('polish_furniture', function(){..});
  task('wash_the_clothes', function(){..});
  task('mop_the_floor', function(){..}); 
});

Grouped tasks can be called using the group name that you specified, followed by a colon, then the name of the task that you want to execute:

phake clean_the_house:polish_furniture

If you want to execute all of the tasks in a group you can just make the final task depend on the first and second task. In the example below, the final task that we want to execute is the mop_the_floor task so we make it depend on the polish_furniture and wash_the_clothes task:

group('clean_the_house', function(){
  task('polish_furniture', function(){..});
  task('wash_the_clothes', function(){..});
  task('mop_the_floor', 'polish_furniture', 'wash_the_clothes' function(){..}); 
});

Then we simply call the mop_the_floor task from the terminal:

phake clean_the_house:mop_the_floor

This will then call the tasks in the following order:

polish_furniture
wash_the_clothes
mop_the_floor

Describing Tasks

After some time of using Phake, you might accumulate a bunch of tasks in your Phakefile, so its a good idea to have some sort of documentation. Lucky for us, Phake comes with a utility that allows us to describe what a specific Phake task does. You can use it by calling the desc method right before the declaration of the task that you want to describe:

desc('Allows you to water the plants');
task('hose', function(){..}); 

desc('Allows you to wash the dish');
task('dish_washer', function(){..});

You can then list out the available tasks in your Phakefile with the following command:

phake -T

It will return an output similar to the following:

hose              Allows you to water the plants
dish_washer       Allows you to wash the dish

Passing Arguments to Tasks

To make the tasks more flexible we can also pass in arguments. This can be done by declaring a parameter in the function. This can then be used to access individual arguments that you pass in to the task:

task('brush_teeth', function($args){
  $motion = (!empty($args['motion'])) ? $args['motion'] : 'circular';
  $includes = (!empty($args['includes'])) ? $args['includes'] : '';

  brush($motion, $includes); 

});

Arguments can be passed by including a name-value pair right after the name of the task. If you wish to pass in more than 1 argument you can separate them by using a single space between the value of the first argument and the name of the second argument:

phake brush_teeth motion=horizontal includes=tongue,teeth,gums

If you need to pass in arguments with spaces between them you can simply wrap it up in single or double quotes:

phake brush_teeth motion="circular horizontal and vertical"

Conclusion

Now that we’ve seen what Phake is for and how we can execute tasks with it, we’ve readied the terrain for some real world applications in part two. Stay tuned!

Automating PHP with Phake

Automate PHP with Phake – Real World Examples >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Mike Mx Kowalski

    Consider this real-world scenario for job queue: user uploads a picture. Job queue is created to create picture thumbnails. Should I create a task with the task() function, then immediately run it using the exec() function?

    • http://www.adrianmiu.ro/ Adrian Miu

      This tool is more appropriate for command-line tasks.

    • http://yapf.blogspot.nl/ vinny42

      You could, but you would just be adding another level of complexity. Just call the thumbnail routine directly from the upload.

      And that ‘extra level of complexity’ seems a recurring theme in Phake, looking at the example on GitHub. The second part of this article is going to have to be pretty amazing to get me interested :-)

  • Matthew Turland

    I think the Composer package reference is incorrect. phake/phake is a mocking library.

    • Mike Mx Kowalski

      Yeah, it should be jaz303/phake, author just went to packagist and copied first Phake result obviously. The article looks like copy&paste from the official documentation as well.

      • http://www.bitfalls.com/ Bruno Skvorc

        Sorry about the misreference, fixed, thanks.
        The article is a preparation for an advanced followup, so stay tuned!

  • gggeek

    So far I have used another tool, called pake. From 10-miles up, they look pretty similar. Do you know about any pros/cons of each?

  • M S

    “Phake should be installed globally via Composer:”

    Is that supposed to mean something?

    • http://www.bitfalls.com/ Bruno Skvorc

      Yes, that it gets installed as an executable bin callable from anywhere, with that command that follows. What’s not clear?

  • Maxence

    On Windows, use double quotes to install:

    composer global require “jaz303/phake=*”

  • Ian Simmons

    This looks very interesting. I’m thinking it could possibly be used locally instead of globally in a project where you want to convert the whole project into a standalone phar. Much like creating a symfony console command and then making it into a phar. Gonna look into it. Thanks for the intro :-)

  • Cryode

    Would be nice if there was a link to the actual Phake project in this article. And on an unrelated note, there are two PHP packages named Phake that do two very different things. Name your things appropriately, folks!