---
title: "Tutorial: Create a console command from scratch"
manual: "TYPO3 Explained"
version: "12.4"
permalink: "https://docs.typo3.org/permalink/t3coreapi:console-command-tutorial@12.4"
source: "ApiOverview/CommandControllers/Tutorial.rst"
rendered: "2026-10-21T15:29:37+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v12 has reached end-of-life as of April 30th 2026 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 v12 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# Tutorial: Create a console command from scratch {#console-command-tutorial}

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

**Table of contents**

-   [Creating a basic command](https://docs.typo3.org/permalink/t3coreapi:creating-a-basic-command@12.4)
-   [Example console command implementations](https://docs.typo3.org/permalink/t3coreapi:example-console-command-implementations@12.4)

## Creating a basic command {#console-command-tutorial-create}

<!-- TODO: no Markdown rendering for "versionadded" -->

The Symfony PHP attribute \Symfony\Component\Console\Attribute\AsCommand
is now accepted to register console commands.
See the section PHP attribute AsCommand
for more details.

In this section we will create an empty command skeleton with no parameters or user
interaction.

> [!NOTE]
> **See also**
>
> -   [Create a new console command with "Make"](https://docs.typo3.org/permalink/t3coreapi:extension-make-console-command@12.4).

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

**EXT:my_extension/Classes/Command/MyCommand.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
    name: 'myextension:dosomething',
    description: 'A command that does nothing and always succeeds.',
    aliases: ['examples:dosomethingalias'],
)]
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
    {
        $io = new SymfonyStyle($input, $output);
        $io->info('Command needs to be implemented. ');
        return Command::SUCCESS;
    }
}

```

The following two methods should be overridden by your class:

-   **`configure()`**

    As the name suggests, this is where the command can be configured.
    Add a help text and/or define arguments and options.

-   **`execute()`**

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

The above example can be run via the command line. If a newly created or edited
command is not found, clear the cache first:

**Composer mode**

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

**Classic mode**

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

The command will return without a message as it does nothing but state that it
has succeeded.

## Example console command implementations {#console-command-tutorial-example}

### A command with parameters and arguments {#console-command-tutorial-parameters}

**packages/my_extension/Classes/Command/SendFluidMailCommand.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use T3docs\Examples\Exception\InvalidWizardException;

#[AsCommand(
    name: 'myextension:createwizard',
)]
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',
            );
    }
    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;
    }

    private function doMagic(SymfonyStyle $io, mixed $wizardName, bool $bruteForce): void
    {
        // do your magic here
    }
}

```

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

**Composer mode**

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

**Classic mode**

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

### Sending a FluidMail via command {#console-command-tutorial-fluidmail}

**packages/my_extension/Classes/Command/SendFluidMailCommand.php**

```php
<?php

declare(strict_types=1);

namespace T3docs\Examples\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
use TYPO3\CMS\Core\Http\ServerRequest;
use TYPO3\CMS\Core\Mail\FluidEmail;
use TYPO3\CMS\Core\Mail\MailerInterface;
use TYPO3\CMS\Core\Site\SiteFinder;

#[AsCommand(
    name: 'myextension:sendmail',
)]
class SendFluidMailCommand extends Command
{
    public function __construct(
        private readonly SiteFinder $siteFinder,
        private readonly MailerInterface $mailer,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        Bootstrap::initializeBackendAuthentication();

        // The site has to have a fully qualified domain name
        $site = $this->siteFinder->getSiteByPageId(1);
        $request = (new ServerRequest())
            ->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE)
            ->withAttribute('site', $site);
        $GLOBALS['TYPO3_REQUEST'] = $request;
        // Send some mails with FluidEmail
        $email = new FluidEmail();
        $email->setRequest($request);
        // Set receiver etc
        $this->mailer->send($email);
        return Command::SUCCESS;
    }
}

```
