---
title: "Tutorial"
manual: "TYPO3 Explained"
version: "11.5"
permalink: "https://docs.typo3.org/permalink/t3coreapi:console-command-tutorial@11.5"
source: "ApiOverview/CommandControllers/Tutorial.rst"
rendered: "2026-10-21T15:30:00+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v11 has reached end-of-life as of October 31th 2024 and
> is no longer being maintained. Use the version switcher on the top
> left of this page to select documentation for a supported version of TYPO3.
>
> Need more time before upgrading?
> You can purchase Extended Long Term Support (ELTS) for TYPO3 v11 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# Tutorial {#console-command-tutorial}

## Create a console command from scratch {#create-a-console-command-from-scratch}

A console command is always situated in an extension. If you want to create
one, [kickstart a custom extension](https://docs.typo3.org/permalink/t3coreapi:extension-kickstart@11.5) or use your
sitepackage extension.

## Creating a basic command {#creating-a-basic-command}

The extension kickstarter "[Make](https://docs.typo3.org/permalink/t3coreapi:extension-make@11.5)" offers a convenient
console command that creates a new command in an extension of your choice:
[Create a new console command with "Make"](https://docs.typo3.org/permalink/t3coreapi:extension-make-console-command@11.5).
You can use "Make" to create a console command even if your extension was
created by different means.

This command can be found in the
[Examples extension](https://github.com/TYPO3-Documentation/t3docs-examples).

<a id="schedulable"></a>
### 1\. Register the command {#deactivating-the-command-in-scheduler}

Register the command in `Configuration/Services.yaml` by adding the service
definition for your class as tag `console.command`:

**EXT:examples/Configuration/Services.yaml**

```yaml
services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false

  T3docs\Examples\:
    resource: '../Classes/*'
    exclude: '../Classes/Domain/Model/*'

  T3docs\Examples\Command\DoSomethingCommand:
    tags:
      - name: console.command
        command: 'examples:dosomething'
        description: 'A command that does nothing and always succeeds.'
      # Also an alias for the command can be configured
      - name: console.command
        command: 'examples:dosomethingalias'
        alias: true
```

The following attributes are available:

-   **`command`**

    The name under which the command is available.

-   **`description`**

    Give a short description. It will be displayed in the list of commands and
    the help information of the command.

-   **`schedulable`**

    By default, a command can be used in the [scheduler](https://docs.typo3.org/c/typo3/cms-scheduler/11.5/en-us/Index.html), too. This can be disabled by setting
    `schedulable` to `false`.

-   **`hidden`**

    A command can be hidden from the command list by setting `hidden` to
    `true`.

-   **`alias`**

    A command can be made available under a different name. Set to `true`,
    if your command name is an alias.

> [!NOTE]
> Despite using `autoconfigure: true` the commands
> have to be explicitly defined in `Configuration/Services.yaml`. It
> is recommended to always supply a description, otherwise there is
> an empty space in the list of commands.

### 2\. Create the command class {#2-create-the-command-class}

Create a class called `DoSomethingCommand` extending
`\Symfony\Component\Console\Command\Command`.

**EXT:examples/Classes/Command/DoSomethingCommand.php**

```php
<?php

declare(strict_types=1);

namespace T3docs\Examples\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class DoSomethingCommand extends Command
{
    protected function configure(): void
    {
        $this->setHelp('This command does nothing. It always succeeds.');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        // Do awesome stuff
        return Command::SUCCESS;
    }
}
```

The following two methods should be overridden by your class:

-   **`configure()`**

    As the name would suggest, allows to configure the command.
    The method allows to add a help text and/or define arguments and options.

-   **`execute()`**

    Contains the logic when executing the command. Must
    return an integer. It is considered best practice to
    return the constants
    `Command::SUCCESS` or `Command::FAILURE`.

> [!NOTE]
> **See also**
>
> A detailed description and an example can be found in
> [the Symfony Command Documentation](https://symfony.com/doc/current/console.html).

### 3\. Run the command {#3-run-the-command}

The above example can be run via command line:

**Composer-based installation**

```bash
vendor/bin/typo3 examples:dosomething
```

**Legacy installation**

```bash
typo3/sysext/core/bin/typo3 examples:dosomething
```

The command will return without a message as it does nothing but stating it
succeeded.

> [!NOTE]
> If a newly created or changed command is not found, clear the cache:
>
> ```bash
> vendor/bin/typo3 cache:flush
> ```

## Create a command with arguments and interaction {#create-a-command-with-arguments-and-interaction}

### Passing arguments {#passing-arguments}

Since a command extends `\Symfony\Component\Console\Command\Command`,
it is possible to define arguments (ordered) and options (unordered) using the Symfony
command API. This is explained in depth on the following Symfony Documentation page:

> [!NOTE]
> **See also**
>
> -   [Symfony: Console Input (Arguments & Options)](https://symfony.com/doc/current/console/input.html)

Add an optional argument and an optional option to your command:

**EXT:examples/Classes/Command/CreateWizardCommand.php**

```php
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

final class CreateWizardCommand extends Command
{
    protected function configure(): void
    {
        $this
            ->setHelp('This command accepts arguments')
            ->addArgument(
                'wizardName',
                InputArgument::OPTIONAL,
                'The wizard\'s name'
            )
            ->addOption(
                'brute-force',
                'b',
                InputOption::VALUE_NONE,
                'Allow the "Wizard of Oz". You can use --brute-force or -b when running command'
            );
    }
}
```

This command takes one optional argument `wizardName` and one optional option,
which can be passed on the command line:

**Composer-based installation**

```bash
vendor/bin/typo3 examples:createwizard [-b] [wizardName]
```

**Legacy installation**

```bash
typo3/sysext/core/bin/typo3 examples:createwizard [-b] [wizardName]
```

This argument can be retrieved with `$input->getArgument()`, the options with
`$input->getOption()`, for example:

**EXT:examples/Classes/Command/CreateWizardCommand.php**

```php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use T3docs\Examples\Exception\InvalidWizardException;

final class CreateWizardCommand extends Command
{
    protected function execute(
        InputInterface $input,
        OutputInterface $output
    ): int {
        $io = new SymfonyStyle($input, $output);
        $wizardName = $input->getArgument('wizardName');
        $bruteForce = (bool)$input->getOption('brute-force');
        try {
            $this->doMagic($io, $wizardName, $bruteForce);
        } catch (InvalidWizardException) {
            return Command::FAILURE;
        }
        return Command::SUCCESS;
    }
}
```

### User interaction on the console {#user-interaction-on-the-console}

You can create a `SymfonyStyle` console user interface from the
`$input` and `$output` parameters to the `execute()` function:

**EXT:examples/Classes/Command/CreateWizardCommand.php**

```php
use Symfony\Component\Console\Style\SymfonyStyle;

final class CreateWizardCommand extends Command
{
    protected function execute(
        InputInterface $input,
        OutputInterface $output
    ): int {
        $io = new SymfonyStyle($input, $output);
        // do some user interaction
        return Command::SUCCESS;
    }
}
```

The `$io` variable can then be used to generate output and prompt for
input:

**EXT:examples/Classes/Command/CreateWizardCommand.php**

```php
use Symfony\Component\Console\Style\SymfonyStyle;
use T3docs\Examples\Exception\InvalidWizardException;

final class CreateWizardCommand extends Command
{
    private function doMagic(
        SymfonyStyle $io,
        mixed $wizardName,
        bool $bruteForce
    ) {
        $io->comment('Trying to create wizard ' . $wizardName . '...');
        if ($wizardName === null) {
            $wizardName = (string)$io->ask(
                'Enter the wizard\'s name (e.g. "Gandalf the Grey")',
                'Lord Voldermort'
            );
        }
        if (!$bruteForce && $wizardName === 'Oz') {
            $io->error('The Wizard of Oz is not allowed. Use --brute-force to allow it.');
            throw new InvalidWizardException();
        }
        $io->success('The wizard ' . $wizardName . ' was created');
    }
}
```

## Dependency injection in console commands {#dependency-injection-in-console-commands}

You can use [dependency injection (DI)](https://docs.typo3.org/permalink/t3coreapi:dependency-injection@11.5) in console
commands by constructor injection or method injection:

**EXT:examples/Classes/Command/MeowInformationCommand.php**

```php
use T3docs\Examples\Http\MeowInformationRequester;

final class MeowInformationCommand extends Command
{
    public function __construct(
        private readonly MeowInformationRequester $requester,
    ) {
        parent::__construct();
    }
}
```

## Initialize backend user {#initialize-backend-user}

A backend user can be initialized with this call inside `execute()` method:

**EXT:some_extension/Classes/Command/DoBackendRelatedThingsCommand.php**

```php
use TYPO3\CMS\Core\Core\Bootstrap;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class DoBackendRelatedThingsCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        Bootstrap::initializeBackendAuthentication();
        // Do backend related stuff

        return Command::SUCCESS;
    }
}
```

This is necessary when using [DataHandler](https://docs.typo3.org/permalink/t3coreapi:datahandler-basics@11.5)
or other backend permission handling related tasks.

## More information {#more-information}

-   See implementation of existing command controllers in the Core:
    `typo3/sysext/*/Classes/Command`
-   [Symfony Command Documentation](https://symfony.com/doc/current/console.html)
-   [Symfony Commands: Console Input (Arguments &
    Options)](https://symfony.com/doc/current/console/input.html)
