Školení Videa Blog

Symfony Console from the Scratch

Tomáš Votruba  
Symfony Console is the one package you will probably use to build a PHP CLI app. It's of one the easiest Symfony components. Why? You only create Application class, add your Command class and you are ready to go.

Main feature of Symfony Console

This package helps you to create applications like Composer, PHP-CS-Fixer, PHP_CodeSniffer or Statie, that generates this website.

So in general, to build applications where you:

2 Classes to Learn

Application - This is the entry point. It contains all commands and routes arguments to them. Something like Application is in Nette or HttpKernel is in Symfony.

Command - Handles input data, processes them and return result to the output. Something like Presenter or Controller. One application can have many commands.

What Belongs to Command?

Before diving into our first command, there is important rule that I want to share with you. In many tutorials you find business logic inside Commands. That is convenient in the begging, but difficult to unlearn later building more commands.

When I wrote commands is something like Presenter or Controller, I was talking about Delegator Pattern. Like Controller, it should only delegate arguments to other services and return result to the output.

This rule will help you to easily avoid:

Very common, very coupled. You would never use controller inside another controller, right?

Ok, now you know this. So lets create your first command!

Create First Command in 3 Steps

1. Install via Composer

$ composer require symfony/console

2. Create Console Application

Conventional location is bin/console (having .php suffix is also fine):

#!/usr/bin/env php
<?php

require_once __DIR__ . '/../vendor/autoload.php';

// Create the Application
$application = new Symfony\Component\Console\Application;

// Run it
$application->run();

Now we can run app and see that it's ready:

$ php bin/console

All good?

3. Create and Register Command

Let's create command, that will safely hash any password you enter.

$ composer require nette/security
<?php
// src/Command/HashPasswordCommand.php

namespace App\Command;

use Nette\Security\Passwords;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class HashPasswordCommand extends Command
{
    /**
     * In this method setup command, description and its parameters
     */
    protected function configure()
    {
        $this->setName('hash-password');
        $this->setDescription('Hashes provided password with BCRYPT and prints to output.');
        $this->addArgument('password', InputArgument::REQUIRED, 'Password to be hashed.');
    }

    /**
     * Here all logic happens
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $password = $input->getArgument('password');

        $hashedPassword = Passwords::hash($password);

        $output->writeln(sprintf(
            'Your hashed password is: %s', $hashedPassword
        ));

        // return value is important when using CI
        // to fail the build when the command fails
        // 0 = success, other values = fail
        return 0;
    }
}

Configure autoloading, add the following to the composer.json file:

"autoload": {
        "psr-4": {"App\\": "src/"}
}

Dump the autoloader

$ composer dump-autoload

And update our bin/console file:

#!/usr/bin/env php
<?php

require_once __DIR__ . '/../vendor/autoload.php';

// Create the Application
$application = new Symfony\Component\Console\Application;

// Register all Commands
$application->add(new App\Command\HashPasswordCommand);

// Run it
$application->run();

Now you can run it from CLI with your password as argument:

$ php bin/console hash-password heslo123
Your hashed password is: $2y$10$NZVuDpvFbqhsBhR1AZZzX.xUHKhr5qtP1qGKjqRM4S9Xakxn1Xgy2

You Are One Step Further

Now you should:

Where to go next?

Still hungry for knowledge? Go check Symfony documentation then.