Shells
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:
- namespace App\Shell;
- use Cake\Console\Shell;
- class HelloShell extends Shell
- {
- public function main()
- {
- $this->out('Hello world.');
- }
- }
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:
- bin/cake hello
You should see the following output:
- 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:
- namespace App\Shell;
- use Cake\Console\Shell;
- class HelloShell extends Shell
- {
- public function main()
- {
- $this->out('Hello world.');
- }
- public function heyThere($name = 'Anonymous')
- {
- $this->out('Hey there ' . $name);
- }
- }
After saving this file, you should be able to run the following command and seeyour name printed out:
- 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:
- class UserShell extends Shell
- {
- public $tasks = ['Template'];
- }
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:
- namespace App\Shell\Task;
- use Cake\Console\Shell;
- class FileGeneratorTask extends Shell
- {
- public function main()
- {
- }
- }
A shell can also access its tasks as properties, which makes tasks great formaking re-usable chunks of functionality similar toComponents:
- // Found in src/Shell/SeaShell.php
- class SeaShell extends Shell
- {
- // Found in src/Shell/Task/SoundTask.php
- public $tasks = ['Sound'];
- public function main()
- {
- $this->Sound->main();
- }
- }
You can also access tasks directly from the command line:
- $ 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:
- public function getOptionParser()
- {
- $parser = parent::getOptionParser();
- $parser->addSubcommand('sound', [
- // Provide help text for the command list
- 'help' => 'Execute The Sound Task.',
- // Link the option parsers together.
- 'parser' => $this->Sound->getOptionParser(),
- ]);
- return $parser;
- }
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:
- $project = $this->Tasks->load('Project');
Would load and return a ProjectTask instance. You can load tasks from pluginsusing:
- $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:
- namespace App\Shell;
- use Cake\Console\Shell;
- class UserShell extends Shell
- {
- public function initialize(): void
- {
- parent::initialize();
- $this->loadModel('Users');
- }
- public function show()
- {
- if (empty($this->args[0])) {
- return $this->abort('Please enter a username.');
- }
- $user = $this->Users->findByUsername($this->args[0])->first();
- $this->out(print_r($user, true));
- }
- }
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
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:
- // As a string
- $this->dispatchShell('schema create Blog --plugin Blog');
- // As an array
- $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:
- // Using a command string
- $this->dispatchShell([
- 'command' => 'schema create Blog --plugin Blog',
- 'extra' => [
- 'foo' => 'bar'
- ]
- ]);
- // Using a command array
- $this->dispatchShell([
- 'command' => ['schema', 'create', 'Blog', '--plugin', 'Blog'],
- 'extra' => [
- 'foo' => 'bar'
- ]
- ]);
Parameters passed through extra
will be merged in the Shell::$params
property 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
:
- // Get arbitrary text from the user.
- $color = $this->in('What color do you like?');
- // Get a choice from the user.
- $selection = $this->in('Red or Green?', ['R', 'G'], 'R');
- // Create a file
- $this->createFile('bower.json', $stuff);
- // Write to stdout
- $this->out('Normal message');
- // Write to stderr
- $this->err('Error message');
- // Write to stderr and raise a stop exception
- $this->abort('Fatal error');
It also provides two convenience methods regarding the output level:
- // Would only appear when verbose output is enabled (-v)
- $this->verbose('Verbose message');
- // Would appear at all levels.
- $this->quiet('Quiet message');
Shell also includes methods for clearing output, creating blank lines, ordrawing a line of dashes:
- // Output 2 newlines
- $this->out($this->nl(2));
- // Clear the user's screen
- $this->clear();
- // Draw a horizontal line
- $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:
- $user = $this->Users->get($this->args[0]);
- if (!$user) {
- // Halt with an error message and error code.
- $this->abort('User cannot be found', 128);
- }
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:
- namespace App\Shell\Task;
- use Cake\Console\Shell;
- class ErroneousShell extends Shell
- {
- public function main()
- {
- return true;
- }
- public function itFails()
- {
- return false;
- }
- public function itFailsSpecifically()
- {
- throw new StopException("", 2);
- }
- }
The example above will return the following exit codes when executed on acommand-line:
- $ bin/cake erroneousshell ; echo $?
- 0
- $ bin/cake erroneousshell itFails ; echo $?
- 1
- $ bin/cake erroneousshell itFailsSpecifically ; echo $?
- 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.