Mastering Yii
上QQ阅读APP看书,第一时间看更新

Creating console commands

Now that we know what built-in commands Yii2 provides, let's start adding our own commands. In Yii2, any custom commands we write are going to be stored in the /commands subfolder of our application. If this folder doesn't exist yet, go ahead and create it:

mkdir commands

Now, let's write a basic console command that just outputs some text:

  1. First, we'll create a new file called BasicController.php in the commands folder:
    touch commands/BasicController.php
    
  2. Now, let's write some PHP code. First, we need to declare the namespace that our BasicController lives in. This namespace directly corresponds to the controllerNamespace parameter we defined in config/console.php:
    <?php
    
    namespace app\commands;
  3. Then, we'll want to declare that we want to use the \yii\console\Controller class in our new controller:
    use \yii\console\Controller;
  4. Next, we'll declare our controller class as follows:
    class BasicController extends Controller { }
  5. Finally, inside our class, we'll create an actionIndex() method that will simply output HelloWorld and then gracefully return with a successful error code. By default, the actionIndex() method is the method that is called when an action is not specified to a controller:
    public function actionIndex()
    {
      echo "HelloWorld";
      return 0;
    }

We have our first console command! Now, if we run the help command, you can see that our command appears in the list of available commands:

$ ./yii help

Moreover, we can now execute our command to verify that it functions properly:

$ ./yii basic

This is the output:

HelloWorld

Generating help information

While we can now run our commands, the help command for both the global help menu and the action help menu currently doesn't provide any useful information. In Yii2, this information is extracted directly from the document block comments (also known as DocBlock comments) that are used before our BasicController class and our actionIndex() method. For instance, consider that we add the following before our class declaration:

/**
 * A basic controller for our Yii2 Application
 */
class BasicController extends \yii\console\Controller {}

We could also provide more information to our actionIndex() method by specifying a DocBlock comment before the method:

/**
 * Outputs HelloWorld
 */
public function actionIndex() {}

Running the help command on the basic controller would then display the following:

$ ./yii help basic

Passing command-line arguments

Like our web controllers (yii\web\Controller), we can also pass arguments through the command line to our console commands. Rather than using $_GET parameters to determine the arguments in use, Yii2 will pull the arguments directly from the command-line interface. Take, for instance, the following method of our BasicController:

/**
 * Outputs "$name lives in $city"
 * @param string $name    The name of the person
 * @param string $city  The city $name lives in
 * @return 0
 */
public function actionLivesIn($name, $city="Chicago")
{
  echo "$name lives in $city.\n";
  return 0;
}

The help command now shows us what arguments are required and what arguments are optional for this new method:

$ ./yii help basic/lives-in

Tip

By now, you may have noticed that console commands can accept two types of input: arguments, (in this example, name and city), and options. Arguments serve as the data that we provide to your actions. On the other hand, options allow us to specify additional configuration for our controller in general. For instance, as previously shown, we can run our commands noninteractively by passing the --interactve=0 flag option. Each console application we create and use may have separate options that we can set. Ensure that you reference the Yii2 documentation for that class and use the help command to determine what options are available for each command.

Without any arguments, this command will throw the following error, indicating that the name parameter is required:

Error: Missing required arguments: name

Once we provide the name, the console outputs the result as expected:

$ ./yii basic/lives-in Alice

This is the output:

Alice lives in Chicago.

By providing a default value to the city parameter, that option is not required for our command to be executed. However, if we passed a value as the second parameter, it would override our default value as expected:

$ ./yii basic/lives-in Alice California

Here's the output:

Alice lives in California.

Tip

Depending upon how your shell is configured, you may not be able to pass certain characters (such as $ or *) from the command line. Ensure that you wrap any strings that use special characters in quotes to ensure that the full argument is passed to your application.

In addition to simple strings, Yii2 will also accept arrays in the form of comma-separated lists. Take, for instance, the following method:

/**
 * Outputs each element of the input $array on a new line
 * @param array $array A comma separated list of elements
 * @return 0
 */
public function actionListElements(array $array)
{
  foreach ($array as $$k)
    echo "$$k\n";

  return 0;
}

By type-hinting the first parameter using the array type-hint, we can notify Yii to convert the command-line arguments into a usable PHP array. From the command line, we can specify an element as an array by representing it as a comma-separated list:

$ ./yii basic/list-elements these,are,separate,items

This is the output that will appear:

these
are
separate
items

Tip

Yii2 does not support the use of multidimensional arrays from the command line. If you need to pass a multidimensional array of data from the command line, you can pass a path to a configuration file instead and then load that file inside your controller action.

The options to store this data range from a PHP file, which returns an array of data, to a JSON- or YAML-formatted file, which would be loaded and converted to a PHP array within your controller action.

Exit codes

As shown in our previous examples, each action we've written thus far has a return value of 0. While returning from our controller action isn't strictly necessary, it's considered a best practice so that our shell can be notified whether our console command has been executed successfully or not. By convention, an exit code of 0 indicates that our command ran without errors, whereas any positive integer greater than zero would indicate that a specific error occurred. The number returned will be the error code that is returned to the shell, and it can be used by our end users to reference our application documentation or support forum to identify what went wrong.

Suppose, for instance, that we wanted to validate one of our inputs without diving into custom forms and validators. In this example, we want our input of $shouldRun to be a positive nonzero integer. If that integer is less than zero, we could return an error code that our documentation would be able to reference:

/**
 * Returns successfully IFF $shouldRun is set to any 
 * positive integer greater than 0
 *
 * @param integer $shouldRun
 * @return integer
 */
public function actionConditionalExit($shouldRun=0)
{
  if ((int)$shouldRun < 0)
  {
    echo 'The $shouldRun argument must be an positive non-zero integer' . "\n";
    return 1;
  }

  return 0;
}

Additionally, Yii2 provides some predefined constants for us to work with: Controller::EXIT_CODE_NORMAL, which has a value of 0, and Controller::EXIT_CODE_ERROR, which has a value of 1. If you have more than one return code, it is considered a good practice to define meaningful constants in your controller to identify your error code.

Formatting

Yii2 provides support for the formatting of the output of our console commands. This is provided through the yii\helpers\Console helper. Before we can use this helper, we need to import it into our class:

<?php

namespace app\commands;
use yii\helpers\Console;

With this helper loaded, we can now use either the stdout() method from \yii\console\Controller or the ansiFormat() method. While both methods will format text, the ansiFormat() method can be used to dynamically combine multiple strings with different formats:

/**
 * Outputs text in bold and cyan
 * @return 0
 */
public function actionColors()
{
  $this->stdout("Waiting on important thing to happen...\n", Console::BOLD);

  $yay = $this->ansiFormat('Yay', Console::FG_CYAN);
  echo "$yay! We're done!\n";
  return 0;
}

Then, if we run our new console command, we can see how our output text changes:

$ ./yii basic/colors

Note

A complete list of available constants is available in the Yii2 documentation at http://www.yiiframework.com/doc-2.0/yii-helpers-baseconsole.html.