Pyrus plugins: custom file tasks
Introduction
If you are not familiar with how file tasks work, read the introduction to file tasks first.
Custom file tasks can implement a single task, and are defined by
an xml file that is noted in package.xml with the customtask
file role. The XML format is defined and validated by pyrus with the
customtask-2.0.xsd
XSchema file.
Here is a human-readable custom file task xml file definition. Optional elements are enclosed in [brackets].
<?xml version="1.0" encoding="UTF-8"?> <task version="2.0" xmlns="http://pear2.php.net/dtd/customtask-2.0"> <name>taskname</name> <class>MyPlugin_Taskname</class> [<autoloadpath>relative/path/to/MyPlugin</autoloadpath>] </task>
Telling Pyrus how to load your task: <class> and <autoloadpath>
This is the same method used for all plugins, and is documented here.
Defining the task: the task class
A custom file task must extend the \PEAR2\Pyrus\Task\Common
class, and must implement a few methods:
- validateXml()
- startSession()
Optionally, __construct() may be used to do special pre-processing, storing of information or other setup tasks.
A task must also define two class constants, TYPE
and PHASE. TYPE should be one of
'simple'
or
'script'
. 'script'
tasks are
post-install scripts, and are executed by the
run-scripts command.
'simple'
tasks are executed while installation is
in progress.
PHASE should be one of:
- \PEAR2\Pyrus\Task\Common::PACKAGE
- \PEAR2\Pyrus\Task\Common::INSTALL
- \PEAR2\Pyrus\Task\Common::PACKAGEANDINSTALL
- \PEAR2\Pyrus\Task\Common::POSTINSTALL
\PEAR2\Pyrus\Task\Common::POSTINSTALL is only used by
post-install scripts, the other 3 are used to determine at what time the
task should be called. If your custom task only works at installation-time,
use \PEAR2\Pyrus\Task\Common::INSTALL. If the task can
perform only at package time (this is exceedingly rare), use
\PEAR2\Pyrus\Task\Common::PACKAGE. Most tasks should use
\PEAR2\Pyrus\Task\Common::PACKAGEANDINSTALL. If your task
can perform its task at packaging time, then you should override the
isPreProcessed() method and return true. Some tasks, such
as the replace
task, perform some of their actions at
package time, and the rest at install time. The isPreProcessed()
method should only return true if the XML of the specific task could be
processed at package time.
The startSession() method is used to actually execute the
task, and is passed a read/write file resource that is set to the beginning
of the file being installed. The full path of the file's final installation
location is also passed in, and can be used for throwing exceptions. All
errors should be handled by throwing a PEAR2\Pyrus\Task\Exception
or one of its descendants.
The validateXml() method should validate the XML as
represented in an array format. The array uses associative arrays for tags,
the attribs
index for attributes, and for a tag with both
attributes and content, the _content
index contains the
content. If present, the tasks:
namespace is removed
from the tags prior to passing to validateXml(). On
encountering validation errors, the method should throw one of the four
exceptions in the example below.
Example Custom Task
<?php
class MyPlugin_Taskname extends \PEAR2\Pyrus\Task\Common
{
const TYPE = 'simple';
const PHASE = \PEAR2\Pyrus\Task\Common::PACKAGEANDINSTALL;
/**
* Initialize a task instance with the parameters
* @param array raw, parsed xml
* @param array attributes from the <file> tag containing this task
* @param string|null last installed version of this package
*/
function __construct($pkg, $phase, $xml, $attribs, $lastversion)
{
parent::__construct($pkg, $phase, $xml, $attribs, $lastversion);
// do any special parsing of attributes/contents here
// phase is one of \PEAR2\Pyrus\Task\Common::PACKAGE,
// \PEAR2\Pyrus\Task\Common::INSTALL, or
// \PEAR2\Pyrus\Task\Common::POSTINSTALL
}
/**
* Validate the basic contents of a <taskname> tag
* @param PEAR_Pyrus_IPackageFile
* @param array
* @param array the entire parsed <file> tag
* @param string the filename of the package.xml
* @throws \PEAR2\Pyrus\Task\Exception\InvalidTask
* @throws \PEAR2\Pyrus\Task\Exception\NoAttributes
* @throws \PEAR2\Pyrus\Task\Exception\MissingAttribute
* @throws \PEAR2\Pyrus\Task\Exception\WrongAttributeValue
*/
static function validateXml(PEAR2\Pyrus\IPackage $pkg, $xml, $fileXml, $file)
{
if (!isset($xml['attribs'])) {
throw new \PEAR2\Pyrus\Task\Exception\NoAttributes('taskname', $file);
}
if (!isset($xml['attribs']['attributename'])) {
throw new \PEAR2\Pyrus\Task\Exception\MissingAttribute('taskname',
$attrib, $file);
}
if ($xml['attribs']['attributename'] != 'hi'
&& $xml['attribs']['attributename'] != 'bye') {
throw new \PEAR2\Pyrus\Task\Exception\WrongAttributeValue('taskname',
'attributename',
$xml['attribs']['attributename'],
$file,
array('hi', 'bye'));
}
}
if (MyPlugin_Other_Class::somecondition()) {
throw new \PEAR2\Pyrus\Task\Exception\InvalidTask(
'general validation errors should use this exception');
}
return true;
}
/**
* Perform the taskname task
* @param \PEAR2\Pyrus\IPackage
* @param resource open file pointer, set to the beginning of the file
* @param string the eventual final file location (informational only)
* @return string|false
*/
function startSession($fp, $dest)
{
// use \PEAR2\Pyrus\Logger::log() to send messages to the user
\PEAR2\Pyrus\Logger::log(3, 'doing stuff');
\PEAR2\Pyrus\Logger::log(0, 'doing more important stuff');
// operate directly on the file pointer
$contents = stream_get_contents($fp);
$contents .= 'hi';
rewind($fp);
ftruncate($fp, 0);
fwrite($fp, $contents);
return true;
}
/**
* This is used to tell the installer to skip a task that has already been
* executed.
*
* For example, package-info replacement tasks are performed at packaging
* time, and do not need to be re-applied on installation
* @return bool true if task has already been executed on the file at
* packaging time
*/
function isPreProcessed()
{
// do not implement this method unless your task does its processing
// at package-time!
return true;
}
}
?>