Skip to content

Pestle Arguments and Options

astorm edited this page Apr 30, 2016 · 2 revisions

Assuming you've created a command

<?php
namespace Pulsestorm\Magento2\Cli\Testbed;
use function Pulsestorm\Pestle\Importer\pestle_import;
pestle_import('Pulsestorm\Pestle\Library\output');

/**
* Test Command
* @command testbed
*/
function pestle_cli($argv)
{
    output("Hello World");
}

your next question may be

How do I pass arguments into a pestle command line program.

Things passed to pestle on the command line are split into two categories. Arguments are just that -- arguments to the command

# We're going to use the `runner.php` script in this
# tutorial -- pestle.phar equivalent shown here for
# context
$ php runner.php testbed argument_1 argument_2
$ pestle.phar testbed argument_1 argument_2

If, however, you prepend an argument with two dashes (--), pestle will consider this an option.

$ php runner.php testbed argument_1 argument_2 --option_1=foo --option_2 foo argument_3
$ pestle.phar testbed argument_1 argument_2 --option_1=foo --option_2 foo argument_3

By default, pestle passes all arguments to the pestle_cli function as the first parameter, and all options as the second parameter. Pestle passes the parameters as native PHP arrays. For example, the following command

<?php
namespace Pulsestorm\Magento2\Cli\Testbed;
use function Pulsestorm\Pestle\Importer\pestle_import;
pestle_import('Pulsestorm\Pestle\Library\output');

/**
* Test Command
* @command testbed
*/
function pestle_cli($arguments, $options)
{
    foreach($arguments as $key=>$value)
    {
        output($key . '::' . $value);
    }
    
    foreach($options as $key=>$value)
    {
        output($key . '::' . $value);
    }        
}

Will produce the following output

$ php runner.php testbed argument_1 argument_2 --option_1=foo --option_2 foo argument_3    
0::argument_1
1::argument_2
2::argument_3
option_1::foo
option_2::foo    

As you can see, arguments are passed as an numerically indexed array, and options as a string-indexed array.

Pestle Input

Pestle has a simple function for accepting user input. You can import it from the module library with

pestle_import('Pulsestorm\Pestle\Library\input');

and then use it like this

function pestle_cli($arguments, $options)
{
    $ask = input("Enter a value");
    output("You entered: " . $ask);
}        

Running the following program

<?php
namespace Pulsestorm\Magento2\Cli\Testbed;
use function Pulsestorm\Pestle\Importer\pestle_import;
pestle_import('Pulsestorm\Pestle\Library\output');
pestle_import('Pulsestorm\Pestle\Library\input');

/**
* Test Command
* @command testbed
*/
function pestle_cli($arguments, $options)
{
    $ask = input("Enter a value",'default');
    output("You entered: " . $ask);
}

will product the following output, with the program pausing for user input.

$ php runner.php testbed
Enter a value (default)] Great!
You entered: Great!

Most other PHP command line frameworks have something similar. However, one of the high level goals for pestle's commands is that all commands be runnable without the need to pause for user input -- i.e., anything that and end user might input should be available as a command option. To help facilitate this, pestle parses the PHP Doc Block at the top of the pestle_cli function for arguments and options.

Configuring Automatic Input Arguments

You can add an arguments to your program using the @argument identifier

/**
* Test Command
* @command testbed
* @argument argument_name Please enter a value for argument_name [the default value]
*/
function pestle_cli($arguments, $options)
{
    output($arguments['argument_name']);
}

If you run the above program with no arguments, pestle will prompt you for the missing argument

$ php runner.php testbed 
Please enter a value for argument_name (the default value)]     

However, if you pass in an argument, pestle will run without prompting.

$ php runner.php testbed 'First argument'
First argument

When using the @argument syntax, pestle will pass in $arguments as a string indexed PHP array.

output($arguments['argument_name']);

As for the @argument value itself

* @argument argument_name Please enter a value for argument_name [the default value]    

The first bit of text (argument_name) is the key you want for your argument. The next bit of text is the question pestle uses when asking end-users for a missing argument value. If you include a value inside two brackets ([]), pestle will use that value for the default.

If you're using @argument identifiers, pestle will ignore any additional un-configured arguments passed in. Consider the following program

/**
* Test Command
* @command testbed
* @argument argument_name Please enter a value for argument_name [the default value]
*/
function pestle_cli($arguments, $options)
{
    foreach($arguments as $key=>$value)
    {
        output($key . '::' . $value);
    }    
}

//...  

and the output from that program

$ php runner.php testbed first_argument second_argument
argument_name::first_argument

As you can see, pestle ignores the second_argument. If you want your program to recognize this, you'll need to add a second @argument.

/**
* Test Command
* @command testbed
* @argument argument_name Please enter a value for argument_name [the default value]
* @argument argument_foo Don't forget the other value [the default value]
*    
*/

Run the program with the above doc block, and should see the following

$ php runner.php testbed first_argument second_argument
argument_name::first_argument
argument_foo::second_argument

Callback Arguments

Pestle features a special callback argument type. Consider the following program.

function exampleOfACallback($arguments, $index)
{
    return 'Value of Argument';
}

/**
* Test Command
* @command testbed
* @argument foobar @callback exampleOfACallback
*/
function pestle_cli($arguments, $options)
{
    output($arguments['foobar']);
}

Here we've configured a argument named foobar. Immediately after the argument's name, we've entered the string @callback. This lets pestle know foobar is a callback argument. Then, the final string, exampleOfACallback is a function local to this program's module. Pestle will call the exampleOfACallback function to get the value for the foobar argument.

This is a powerful feature, most useful if you want to ask a complex series of questions to determine the value of a single argument in pestle. You can see an example of this in the generate_menu command.

* @argument parent @callback selectParentMenu

//...

function selectParentMenu($arguments, $index)
{
    if(array_key_exists($index, $arguments))
    {
        return $arguments[$index];
    }
    
    $parent     = '';
    $continue   = input('Is this a new top level menu? (Y/N)','N');
    if(strToLower($continue) === 'n')
    {
        $parent = choseMenuFromTop();
    }
    return $parent;
}

Here we have a @argument named parent, configured as a @callback, with the function selectParentMenu. In selectParentMenu, you can see we're using the input function to ask the user a question, and then take different code paths depending on the answers.

While this feature is powerful, it does introduce the possibility of creating a command that requires user interaction, so use it with caution. One of pestle's goals is all interactive questions in a command should be passable @arguments. That's why pestle, when it calls the callback, will pass in the entire list of command arguments, as well as the index of the current argument.

function selectParentMenu($arguments, $index)

This allows you, if you wish, to just return the value a user passed in. You can see this with the selectParentMenu callback above.

function selectParentMenu($arguments, $index)
{
    if(array_key_exists($index, $arguments))
    {
        return $arguments[$index];
    }
    //...
}