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 Closure s. 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

See nikic's article on PHP over XML for a great argument for using pure PHP for configuration.

Alternatives

There's a few PHP task runners popping up: