occ commands

Nextcloud apps can register custom occ commands that administrators can run from the command line. Commands are built on top of Symfony Console, so the full Symfony Console API is available.

Note

The OCP\Command\ICommand interface is deprecated since Nextcloud 33 and must not be used for new commands. Extend Symfony\Component\Console\Command\Command directly as shown below.

Registering a command

List every command class in appinfo/info.xml under the <commands> element:

appinfo/info.xml
<info>
    ...
    <commands>
        <command>OCA\MyApp\Command\Greet</command>
    </commands>
</info>

Nextcloud reads this list at startup and wires each class through the dependency injection container, so constructor injection works automatically.

Creating a command class

Place command classes in lib/Command/. Each class must extend Symfony\Component\Console\Command\Command and implement two methods:

  • configure() — declare the name, description, arguments, and options.

  • execute() — run the command logic and return an exit code.

lib/Command/Greet.php
<?php

declare(strict_types=1);

namespace OCA\MyApp\Command;

use OCP\IUserManager;
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;

class Greet extends Command {

    public function __construct(
        private IUserManager $userManager,
    ) {
        parent::__construct();
    }

    #[\Override]
    protected function configure(): void {
        $this
            ->setName('myapp:greet')
            ->setDescription('Print a greeting for a Nextcloud user')
            ->addArgument(
                'user-id',
                InputArgument::REQUIRED,
                'The user to greet',
            )
            ->addOption(
                'shout',
                null,
                InputOption::VALUE_NONE,
                'Print the greeting in uppercase',
            );
    }

    #[\Override]
    protected function execute(InputInterface $input, OutputInterface $output): int {
        $userId = $input->getArgument('user-id');
        $user = $this->userManager->get($userId);

        if ($user === null) {
            $output->writeln("<error>User \"$userId\" not found.</error>");
            return self::FAILURE;
        }

        $greeting = 'Hello, ' . $user->getDisplayName() . '!';

        if ($input->getOption('shout')) {
            $greeting = strtoupper($greeting);
        }

        $output->writeln($greeting);
        return self::SUCCESS;
    }
}

Command naming

Use appid:command-name as the command name. For apps with many commands, group them with an extra segment: appid:group:command-name. Names must be lowercase and use hyphens as word separators.

Arguments and options

Arguments are positional. Options are prefixed with --.

Constant

Meaning

InputArgument::REQUIRED

Argument must be provided

InputArgument::OPTIONAL

Argument may be omitted

InputArgument::IS_ARRAY

Argument accepts multiple values

InputOption::VALUE_NONE

Flag — present or absent, no value

InputOption::VALUE_REQUIRED

Option requires a value

InputOption::VALUE_OPTIONAL

Option value is optional

InputOption::VALUE_IS_ARRAY

Option can be repeated

See the Symfony Console documentation for the full reference.

Return codes

execute() must return an integer. Use the constants defined by Symfony\Component\Console\Command\Command:

  • self::SUCCESS (0) — command completed successfully.

  • self::FAILURE (1) — command encountered an error.

  • self::INVALID (2) — command was called with invalid input.

Interactive commands

Commands can ask for confirmation or prompt for values using Symfony’s question helper:

use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\ConfirmationQuestion;

// In execute():
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Are you sure? (y/n) ', false);

if (!$helper->ask($input, $output, $question)) {
    $output->writeln('Aborted.');
    return self::FAILURE;
}

Note

Interactive prompts are skipped when occ is run non-interactively (e.g. from a cron job). Guard against this with $input->isInteractive() or use --yes/--no options so administrators can automate the command.