Shells

  • class Cake\Console\Shell

Deprecated since version 3.6.0: Shells are deprecated as of 3.6.0, but will not be removed until 5.x.Use Command Objects instead.

Creating a Shell

Let’s create a shell for use in the Console. For this example, we’ll create asimple Hello world shell. In your application’s src/Shell directory createHelloShell.php. Put the following code inside it:

  1. namespace App\Shell;
  2.  
  3. use Cake\Console\Shell;
  4.  
  5. class HelloShell extends Shell
  6. {
  7. public function main()
  8. {
  9. $this->out('Hello world.');
  10. }
  11. }

The conventions for shell classes are that the class name should match the filename, with the suffix of Shell. In our shell we created a main() method.This method is called when a shell is called with no additional commands. We’lladd some more commands in a bit, but for now let’s just run our shell. From yourapplication directory, run:

  1. bin/cake hello

You should see the following output:

  1. Hello world.

As mentioned before, the main() method in shells is a special method calledwhenever there are no other commands or arguments given to a shell. Since ourmain method wasn’t very interesting let’s add another command that doessomething:

  1. namespace App\Shell;
  2.  
  3. use Cake\Console\Shell;
  4.  
  5. class HelloShell extends Shell
  6. {
  7. public function main()
  8. {
  9. $this->out('Hello world.');
  10. }
  11.  
  12. public function heyThere($name = 'Anonymous')
  13. {
  14. $this->out('Hey there ' . $name);
  15. }
  16. }

After saving this file, you should be able to run the following command and seeyour name printed out:

  1. bin/cake hello hey_there your-name

Any public method not prefixed by an _ is allowed to be called from thecommand line. As you can see, methods invoked from the command line aretransformed from the underscored shell argument to the correct camel-casedmethod name in the class.

In our heyThere() method we can see that positional arguments are providedto our heyThere() function. Positional arguments are also available in theargs property.You can access switches or options on shell applications, which are available at$this->params, but we’ll cover that in a bit.

When using a main() method you won’t be able to use the positionalarguments. This is because the first positional argument or option isinterpreted as the command name. If you want to use arguments, you should usemethod names other than main.

Shell Tasks

There will be times when building more advanced console applications, you’llwant to compose functionality into re-usable classes that can be shared acrossmany shells. Tasks allow you to extract commands into classes. For example thebake command is made almost entirely of tasks. You define a tasks for ashell using the $tasks property:

  1. class UserShell extends Shell
  2. {
  3. public $tasks = ['Template'];
  4. }

You can use tasks from plugins using the standard plugin syntax.Tasks are stored in Shell/Task/ in files named after their classes. So ifwe were to create a new ‘FileGenerator’ task, you would createsrc/Shell/Task/FileGeneratorTask.php.

Each task must at least implement a main() method. The ShellDispatcher,will call this method when the task is invoked. A task class looks like:

  1. namespace App\Shell\Task;
  2.  
  3. use Cake\Console\Shell;
  4.  
  5. class FileGeneratorTask extends Shell
  6. {
  7. public function main()
  8. {
  9. }
  10. }

A shell can also access its tasks as properties, which makes tasks great formaking re-usable chunks of functionality similar toComponents:

  1. // Found in src/Shell/SeaShell.php
  2. class SeaShell extends Shell
  3. {
  4. // Found in src/Shell/Task/SoundTask.php
  5. public $tasks = ['Sound'];
  6.  
  7. public function main()
  8. {
  9. $this->Sound->main();
  10. }
  11. }

You can also access tasks directly from the command line:

  1. $ cake sea sound

Note

In order to access tasks directly from the command line, the taskmust be included in the shell class’ $tasks property.

Also, the task name must be added as a sub-command to the Shell’s OptionParser:

  1. public function getOptionParser()
  2. {
  3. $parser = parent::getOptionParser();
  4. $parser->addSubcommand('sound', [
  5. // Provide help text for the command list
  6. 'help' => 'Execute The Sound Task.',
  7. // Link the option parsers together.
  8. 'parser' => $this->Sound->getOptionParser(),
  9. ]);
  10. return $parser;
  11. }

Loading Tasks On The Fly with TaskRegistry

You can load tasks on the fly using the Task registry object. You can load tasksthat were not declared in $tasks this way:

  1. $project = $this->Tasks->load('Project');

Would load and return a ProjectTask instance. You can load tasks from pluginsusing:

  1. $progressBar = $this->Tasks->load('ProgressBar.ProgressBar');

Using Models in Your Shells

You’ll often need access to your application’s business logic in shellutilities; CakePHP makes that super easy. You can load models in shells, just asyou would in a controller using loadModel(). The loaded models are set asproperties attached to your shell:

  1. namespace App\Shell;
  2.  
  3. use Cake\Console\Shell;
  4.  
  5. class UserShell extends Shell
  6. {
  7. public function initialize(): void
  8. {
  9. parent::initialize();
  10. $this->loadModel('Users');
  11. }
  12.  
  13. public function show()
  14. {
  15. if (empty($this->args[0])) {
  16. return $this->abort('Please enter a username.');
  17. }
  18. $user = $this->Users->findByUsername($this->args[0])->first();
  19. $this->out(print_r($user, true));
  20. }
  21. }

The above shell, will fetch a user by username and display the informationstored in the database.

Shell Helpers

If you have complex output generation logic, you can useCommand Helpers to encapsulate this logic in a re-usable way.

Invoking Other Shells from Your Shell

  • Cake\Console\Shell::dispatchShell($args)

There are still many cases where you will want to invoke one shell from another though.Shell::dispatchShell() gives you the ability to call other shells by providing theargv for the sub shell. You can provide arguments and options eitheras var args or as a string:

  1. // As a string
  2. $this->dispatchShell('schema create Blog --plugin Blog');
  3.  
  4. // As an array
  5. $this->dispatchShell('schema', 'create', 'Blog', '--plugin', 'Blog');

The above shows how you can call the schema shell to create the schema for a pluginfrom inside your plugin’s shell.

Passing extra parameters to the dispatched Shell

It can sometimes be useful to pass on extra parameters (that are not shell arguments)to the dispatched Shell. In order to do this, you can now pass an array todispatchShell(). The array is expected to have a command key as wellas an extra key:

  1. // Using a command string
  2. $this->dispatchShell([
  3. 'command' => 'schema create Blog --plugin Blog',
  4. 'extra' => [
  5. 'foo' => 'bar'
  6. ]
  7. ]);
  8.  
  9. // Using a command array
  10. $this->dispatchShell([
  11. 'command' => ['schema', 'create', 'Blog', '--plugin', 'Blog'],
  12. 'extra' => [
  13. 'foo' => 'bar'
  14. ]
  15. ]);

Parameters passed through extra will be merged in the Shell::$paramsproperty and are accessible with the Shell::param() method.By default, a requested extra param is automatically added when a Shellis dispatched using dispatchShell(). This requested parameter preventsthe CakePHP console welcome message from being displayed on dispatched shells.

Parsing CLI Options

Shells use Option Parsers to define their options,arguments and automate help generation.

Interacting with Input/Output

Shells allow you to access a ConsoleIo instance via the getIo() method.See the Command Input/Output section for more information.

In addition to the ConsoleIo object, Shell classes offer a suite of shortcutmethods. These methods are shortcuts and aliases to those found on ConsoleIo:

  1. // Get arbitrary text from the user.
  2. $color = $this->in('What color do you like?');
  3.  
  4. // Get a choice from the user.
  5. $selection = $this->in('Red or Green?', ['R', 'G'], 'R');
  6.  
  7. // Create a file
  8. $this->createFile('bower.json', $stuff);
  9.  
  10. // Write to stdout
  11. $this->out('Normal message');
  12.  
  13. // Write to stderr
  14. $this->err('Error message');
  15.  
  16. // Write to stderr and raise a stop exception
  17. $this->abort('Fatal error');

It also provides two convenience methods regarding the output level:

  1. // Would only appear when verbose output is enabled (-v)
  2. $this->verbose('Verbose message');
  3.  
  4. // Would appear at all levels.
  5. $this->quiet('Quiet message');

Shell also includes methods for clearing output, creating blank lines, ordrawing a line of dashes:

  1. // Output 2 newlines
  2. $this->out($this->nl(2));
  3.  
  4. // Clear the user's screen
  5. $this->clear();
  6.  
  7. // Draw a horizontal line
  8. $this->hr();

Stopping Shell Execution

When your shell commands have reached a condition where you want execution tostop, you can use abort() to raise a StopException that will halt theprocess:

  1. $user = $this->Users->get($this->args[0]);
  2. if (!$user) {
  3. // Halt with an error message and error code.
  4. $this->abort('User cannot be found', 128);
  5. }

Status and Error Codes

Command-line tools should return 0 to indicate success, or a non-zero value toindicate an error condition. Since PHP methods usually return true orfalse, the Cake Shell dispatch function helps to bridge these semanticsby converting your null and true return values to 0, and all othervalues to 1.

The Cake Shell dispatch function also catches the StopException anduses its exception code value as the shell’s exit code. As described above, youcan use the abort() method to print a message and exit with a specificcode, or raise the StopException directly as shown in the example:

  1. namespace App\Shell\Task;
  2.  
  3. use Cake\Console\Shell;
  4.  
  5. class ErroneousShell extends Shell
  6. {
  7. public function main()
  8. {
  9. return true;
  10. }
  11.  
  12. public function itFails()
  13. {
  14. return false;
  15. }
  16.  
  17. public function itFailsSpecifically()
  18. {
  19. throw new StopException("", 2);
  20. }
  21. }

The example above will return the following exit codes when executed on acommand-line:

  1. $ bin/cake erroneousshell ; echo $?
  2. 0
  3. $ bin/cake erroneousshell itFails ; echo $?
  4. 1
  5. $ bin/cake erroneousshell itFailsSpecifically ; echo $?
  6. 2

Tip

Avoid exit codes 64 - 78, as they have specific meanings described bysysexits.h.Avoid exit codes above 127, as these are used to indicate process exitby signal, such as SIGKILL or SIGSEGV.

Note

You can read more about conventional exit codes in the sysexit manual pageon most Unix systems (man sysexits), or the System Error Codes helppage in Windows.

Hook Methods

  • Cake\Console\Shell::initialize()
  • Initializes the Shell, acts as constructor for subclasses and allowsconfiguration of tasks prior to shell execution.
  • Cake\Console\Shell::startup()
  • Starts up the Shell and displays the welcome message. Allows for checkingand configuring prior to command or main execution.

Tip

Override the startup() method if you want to remove the welcomeinformation, or otherwise modify the pre-command flow.

Avoid exit codes 64 - 78, as they have specific meanings described bysysexits.h.Avoid exit codes above 127, as these are used to indicate process exitby signal, such as SIGKILL or SIGSEGV.