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
- 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:
- Bldr - http://bldr.io/
- Robo - http://codegyre.github.io/Robo/





