Pyrus plugins: custom commands
Introduction
Custom commands add new functionality to pyrus.phar. An example of a
plugin that implements custom commands is the
PEAR2_Pyrus_Developer
package, which implements the
make,
package,
pickle, and
run-phpt commands.
Custom command plugins can implement multiple commands, and are defined by
an xml file that is noted in package.xml with the customcommand
file role. The XML format is defined and validated by pyrus with the
customcommand.xsd
XSchema file.
Command Arguments and Options: a brief primer
Commands can have arguments and options. Here is an example of a command with a single argument:
php pyrus.phar install PackageName
The command is install
, and the argument is
PackageName
.
Here is an example of a command with multiple arguments:
php pyrus.phar install PackageName package.xml http://example.com/Foo.tgz
The command is install
, and the arguments are
PackageName
, package.xml
and
http://example.com/Foo.tgz
.
An option is a special argument that is preceded by 1-2 dashes (-
or
--
). Short arguments are single letters preceded by a dash,
and long arguments are words preceded by two dashes. Here is an example
of a command with both a short and a long option
php pyrus.phar package -p --tar
The command is package
, the short option is
-p
and the long option is --tar
. Short
options are aliases for long options. For the package
command,
the -p
short
option is an alias to the --phar
long option.
Options can also accept arguments in 2 formats. If an option accepts an argument, there are two ways of passing that argument. Short options consider the next argument to be their argument:
php pyrus.phar install -r /path/to/packagingroot PackageName
/path/to/packagingroot
is the argument to the short
option -r
, PackageName
is the argument
to the install
command.
Long options require an =
sign in between the option and
the argument as in:
php pyrus.phar install --packagingroot=/path/to/packagingroot PackageName
/path/to/packagingroot
is the argument to the long
option --packagingroot
, PackageName
is the argument
to the install
command.
Overview of the Custom Command XML Format
Here is a human-readable overview of a custom command's XML definition file.
Optional tags are enclosed in [brackets]. If there is a choice of
tags, they are separated by a vertical line |
and
enclosed in parentheses like (<this>|<example>)
.
<?xml version="1.0" encoding="UTF-8"?> <commands version="2.0" xmlns="http://pear2.php.net/dtd/customcommand-2.0"> <command> <name>commandname</name> <class>CommandClass\Name</class> <function>makePackageXml</function> [ <webfunction>webCommand</webfunction>] [ <gtkfunction>gtkCommand</gtkfunction>] [ <autoloadpath>CommandClass</autoloadpath>] <summary>Short description of command purpose</summary> <shortcut>CN</shortcut> <options> [<option> <name>optionname</name> <shortopt>O</shortopt> <type>(<bool/>|<string/>|<int/>|<float/>|<counter/>| <callback>optionProcessorCallback</callback>| <set><value>value1</value><value>value2</value>...</set>)</type> [<default>defaultvalue</default>] <doc>Short description of option purpose</doc> </option>] </options> <arguments> [<argument> <name>argname</name> <multiple>0</multiple> <optional>1</optional> <doc>Short argument description</doc> </argument>] </arguments> <doc> Long description of command usage for help output </doc> </command> </commands>
A command need not require or accept any arguments or options, and can accept multiple arguments and multiple options. In addition, a custom command definition XML file may declare multiple commands.
Commands can also implement a shortcut, which is a shorter alias. The
upgrade
command, for instance, has a shortcut of
up
.
By convention, all command names of external projects should be prefixed with
the vendor name. For instance, if the vendor is Zend Framework, commands should be
prefixed with something like zf-
or with zend-
.
An install command would be zf-install
.
In addition, all shortcuts from external vendors should be upper-cased and
consist of the first letter of the vendor, and the first two letters
of the command. zf-install
would have a shortcut of
Zin
. This helps to avoid name collisions between
vendors.
Telling Pyrus how to load your command: <class> and <autoloadpath>
This is the same method used for all plugins, and is documented here.
The three frontend command handlers are discussed in the CLI section, Web section, and the Gtk section.
Defining arguments
Pyrus recognizes both optional and required arguments, as well as the ability to specify repeating arguments. The logic is very similar to function signatures in PHP. Each argument is placed by name into an associative array and passed to the function. Note that invalid input is not passed to custom commands, so a command can assume valid input if it is called.
Here is a sample argument definition and the way that Pyrus will handle different valid inputs for the hypothetical foo command:
<arguments> <argument> <name>argname</name> <multiple>0</multiple> <optional>0</optional> <doc>required arg</doc> </argument> <argument> <name>argname2</name> <multiple>0</multiple> <optional>1</optional> <doc>optional arg</doc> </argument> <argument> <name>argname3</name> <multiple>1</multiple> <optional>1</optional> <doc>optional arg, multiple</doc> </argument> </arguments>
php pyrus.phar foo arg1
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1')
}
?>
php pyrus.phar foo arg1 arg2
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2')
}
?>
php pyrus.phar foo arg1 arg2 arg3
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2', 'argname3' => array('arg3'))
}
?>
php pyrus.phar foo arg1 arg2 arg3 arg4
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2', 'argname3' => array('arg3', 'arg4'))
}
?>
Defining options
Options must define a short and a long option, a type for the option, and a short description of the option for help text.
Options fall into two categories, those that accept arguments, and those that don't.
Options that don't accept arguments are of type <bool/>
and <counter/>
.
The types recognized are:
<bool/>
<counter/>
<string/>
<int/>
<float/>
<callback></callback>
<set></set>
<bool/>
A <bool/>
option sets its value to TRUE if
present, and FALSE if not.
<options> <option> <name>nocompatible</name> <shortopt>n</shortopt> <type><bool/></type> <doc>Do not generate package_compatible.xml</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => false)
}
?>
php pyrus.phar foo -n
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => true)
}
?>
php pyrus.phar foo --nocompatible
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => true)
}
?>
<counter/>
A <counter/>
option sets its value to 0
if not present, and to the number of times the option is present if it is.
This is the only option type for which multiple occurrences of the option
are allowed.
<options> <option> <name>verbose</name> <shortopt>v</shortopt> <type><counter/></type> <doc>How verbose to be with output</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 0)
}
?>
php pyrus.phar foo -v
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 1)
}
?>
php pyrus.phar foo --verbose -vv --verbose
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 4)
}
?>
<string/>
A <string/>
option sets its value to the
argument passed in if present, or to NULL if not present.
<options> <option> <name>name</name> <shortopt>n</shortopt> <type><string/></type> <doc>Name of foo</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => null)
}
?>
php pyrus.phar foo -n myname
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => 'myname')
}
?>
php pyrus.phar foo --name=myname
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => 'myname')
}
?>
<int/>
An <int/>
option sets its value to the
integer argument passed in if present, or to NULL if not present.
<options> <option> <name>namecount</name> <shortopt>n</shortopt> <type><int/></type> <doc>Number of foo names</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => null)
}
?>
php pyrus.phar foo -n 3
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => 3)
}
?>
php pyrus.phar foo --namecount=3
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => 3)
}
?>
<float/>
A <float/>
option sets its value to the
float argument passed in if present, or to NULL if not present.
<options> <option> <name>foopercent</name> <shortopt>f</shortopt> <type><float/></type> <doc>Foo percent in decimal notation</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => null)
}
?>
php pyrus.phar foo -f .2
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => 0.2)
}
?>
php pyrus.phar foo --foopercent=0.3
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => 0.3)
}
?>
<callback></callback>
A <callback/>
option calls the specified
callback with the value passed in by the user as a string.
The callback must be a static method in the same class that
implements the command.
<options> <option> <name>foocallback</name> <shortopt>f</shortopt> <type><callback>processfoo</callback></type> <doc>Foo date</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => null)
}
static function processfoo($value)
{
// $value = null
return null;
}
?>
php pyrus.phar foo -f 2009-12-31
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => new DateTime('2009-12-31', new DateTimeZone('UTC')))
}
static function processfoo($value)
{
// $value = '2009-12-31';
$date = new DateTime($value, new DateTimeZone('UTC'));
return $date;
}
?>
php pyrus.phar foo --foocallback=2009-12-31
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => new DateTime('2009-12-31', new DateTimeZone('UTC')))
}
static function processfoo($value)
{
// $value = '2009-12-31';
$date = new DateTime($value, new DateTimeZone('UTC'));
return $date;
}
?>
<set></set>
A <set/>
option sets its value to NULL if not
present. Otherwise it ensures that the value passed in by the user is within
a pre-defined acceptable list of values.
<options> <option> <name>numbah</name> <shortopt>n</shortopt> <type> <set> <value>one</value> <value>two</value> <value>three</value> <value>four</value> </set> </type> <doc>Numbers less than five</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => null)
}
?>
php pyrus.phar foo -n four
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => 'four')
}
?>
php pyrus.phar foo --numbah=one
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => 'one')
}
?>
Declaring CLI command method
A class must implement a command-line handler, which should have this method signature:
<?php
function commandhandler($frontend, $args, $options)
{
// perform command here
}
?>
The first argument is the CLI frontend, and can be used for asking the user
a question with the ask() method, or passing control
to a built-in command. $args
is an associative array of
arguments. Only required arguments are guaranteed to be present, all optional
arguments must be verified as present before using. $options
is an associative array of options. All options will be present, those not present
will be initialized to NULL.
Options should be accessed using their long names, and arguments using their names (see examples in the documentation sections for arguments and options above).
The ask() method accepts 3 arguments:
-
string
$question
the question to ask the user. It should end in a question mark -
(optional) array
$choices
an array of possible answers -
(optional) string
$default
the default answer
Declaring Web command method
Although a Web frontend has not yet been written for Pyrus, the XML command
format supports the eventual addition of a Web frontend. A separate method
should be used for the Web handling, and is named via the
<webfunction>
tag.
Declaring GTK command method
Although a GTK frontend has not yet been written for Pyrus, the XML command
format supports the eventual addition of a GTK frontend. A separate method
should be used for the GTK handling, and is named via the
<gtkfunction>
tag.