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