Task

Another PHP task runner.

View project onGitHub

Build Status Scrutinizer Code Quality Code Coverage Latest Stable Version Total Downloads Latest Unstable Version License

Got a PHP project? Heard of Grunt and Gulp but don't use NodeJS? Task is a pure PHP task runner.

  • Leverage PHP as a scripting language, and as your platform of choice.
  • Use loads of nice features inspired by Grunt and Gulp (and Phing).
  • Employ Symfony components for effortless CLI goodness.
  • Extend with plugins.

Ask us anything on Twitter at @taskphp.

Example

<?php

use Task\Plugin;

require 'vendor/autoload.php';

$project = new Task\Project('wow');

$project->inject(function ($container) {
    $container['phpspec'] = new Plugin\PhpSpecPlugin;
    $container['fs'] = new Plugin\FilesystemPlugin;
    $container['sass'] = (new Plugin\Sass\ScssPlugin)
        ->setPrefix('sass');
    $container['watch'] = new Plugin\WatchPlugin;
});

$project->addTask('welcome', function () {
    $this->getOutput()->writeln('Hello!');
});

$project->addTask('test', ['phpspec', function ($phpspec) {
    $phpspec->command('run')
        ->setFormat('pretty')
        ->setVerbose(true)
        ->pipe($this->getOutput());
}]);

$project->addTask('css', ['fs', 'sass', function ($fs, $sass) {
    $fs->open('my.scss')
        ->pipe($sass)
        ->pipe($fs->touch('my.css'));
}]);

$project->addTask('css.watch', ['watch', function ($watch) {
    $watch->init('my.scss')
        ->addListener('modify', function ($event) {
            $this->runTask('css', $this->getOutput());
        })
        ->start();
}]);

return $project;

Installation

Add to your composer.json:

...
    "require": {
        "task/task": "~0.5"
    }
...

This will allow you to instantiate a Task\Project. To run tasks from the command line, install task/cli. You should probably do this now!

There are 3 options for installation:

#1 Composer global (recommended)

$> composer global require task/cli ~0.2

If you haven't installed anything this way before you'll need to update your PATH:

export PATH=$PATH:$HOME/.composer/vendor/bin

#2 Phar

Download from Github:

$> wget -O /usr/bin/task https://github.com/taskphp/cli/releases/download/v0.3.0/task.phar
$> chmod +x /usr/bin/task

#3 Composer

...
"require-dev": {
    "task/cli": "~0.2"
}
...

Run at ./vendor/bin/task.

Usage

The only requirements are that you implement a Taskfile that returns a Task\Project:

<?php

# Include the task/task library and your dependencies.
require 'vendor/autoload.php';

# Instantiate a project by giving it a name.
$project = new Task\Project('foo');

# Return the project!
return $project;

We suggest putting the Taskfile in the root of your project. The CLI package will look for a Taskfile in the current working directory, so cd in to your project and run:

$> task
foo version 

  --verbose        -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.

Available commands:
  help    Displays help for a command
  list    Lists commands
  shell

If you've used Symfony's Console component before this will look familiar! Your Task\Project is a Symfony\Component\Console\Application and so you have a pretty CLI application out of the box.

Add a task:

<?php

# Include the task/task library and your dependencies.
require 'vendor/autoload.php';

# Instantiate a project by giving it a name.
$project = new Task\Project('foo');

# Add a task
$project->addTask('greet', function () {
    # Write to stdout
    $this->getOutput()->writeln('Hello, World!');
});

# Return the project!
return $project;

As you can see, tasks are just Closures. To run it:

$> task greet
Hello, World!

Plugins

Plugins are where the real work gets done.

...
    "require": {
        "task/task": "~0.1",
        "task/process": "~0.1"
    }
...
<?php

use Task\Plugin\ProcessPlugin;

# Include the task/task library and your dependencies.
require 'vendor/autoload.php';

# Instantiate a project by giving it a name.
$project = new Task\Project('foo');

# Add your plugins to the project's DI container.
$project->inject(function ($container) {
    $container['ps'] = new ProcessPlugin;
});

# Add a task.
$project->addTask('greet', function () {
    # Write to stdout.
    $this->getOutput()->writeln('Hello, World!');
});

# Use the handy array syntax to inject plugins into tasks.
$project->addTask('whoami', ['ps', function ($ps) {
    # Use streams to pass data between plugins and interfaces.
    $ps->build('whoami')->pipe($this->getOutput());
}]);

# Return the project!
return $project;
$> task whoami
mbfisher

This is a totally pointless example but it demonstrates some core concepts.

DI

Dependency injection is used to setup plugins and inject them into tasks. Project::inject allows you to fill a Pimple container up with anything you like.

Injection

Plugins are injected into tasks using Task\Injector. Instead of a Closure, pass Project::addTask an array with your task as the last element. The preceding elements should be IDs stored in the container; they will be retrieved and passed as arguments to the task.

Streams

Plugins are encouraged to use NodeJS-style streams for handling data flows. Task\Plugin\Stream\ReadableInterface provides read and pipe methods, Task\Plugin\Stream\WritableInterface provides a write method. In the example above ProcessPlugin::build returns a Task\Plugin\Process\ProcessBuilder, which implements ReadableInterface, allowing us to pipe a Task\Plugin\Console\Output\Output instance to it, which implements WritableInterface.

Discussion

Alternatives

There's a few PHP task runners popping up:

  • Bldr - http://bldr.io/
  • Robo - http://codegyre.github.io/Robo/