Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to build Console Applications @ phplx

How to build Console Applications @ phplx

In some cases you feel the need to have some specific command-line commands for deployment, testing some code or do any other specific task.

The Symfony2 Console component is a tool that gives you in a simple and easy way to build a console application with your own command-line commands.

This talk is an introduction to the Console component and a walkthrough on how to use it in an effective way.

Daniel Gomes

October 17, 2013
Tweet

More Decks by Daniel Gomes

Other Decks in Programming

Transcript

  1. Daniel Gomes
    @danielcsgomes
    October 17, 2013
    icon by Austin Andrews © http://thenounproject.com/Templarian
    How to Build
    Console Applications

    View Slide

  2. • Software Engineer @ GuestCentric Systems
    • ZCE PHP 5.3, OCP MySQL 5 Developer, CSM
    • @danielcsgomes
    about me

    View Slide

  3. Lots of scripts
    Too many di!erent tools
    Tedious and boring

    View Slide

  4. Centralize your tools
    Have interactive tools
    Easy to use by everyone
    Usage manual
    what about

    View Slide

  5. Meet
    symfony
    Console Component

    View Slide

  6. • Application
    • Command
    • Input
    • Output
    • Helpers
    • Events
    • Formatters
    console component toolset

    View Slide

  7. APPLICATION
    COMMAND A
    COMMAND B
    COMMAND C
    plug-in & run

    View Slide

  8. {
    "require": {
    "symfony/console": "2.3.*@dev"
    }
    }
    install via composer
    zero dependencies

    View Slide

  9. // app.php
    require_once __DIR__ . '/vendor/autoload.php';
    use Symfony\Component\Console\Application;
    $app = new Application('Clear Cache App', '0.0.1');
    $app->run();
    Bootstrapping the application

    View Slide

  10. $ php app.php

    View Slide

  11. basic command

    View Slide

  12. // Command/CacheClearCommand.php
    namespace DCSG\Command;
    use Symfony\Component\Console\Command\Command;
    class CacheClearCommand extends Command
    {
    protected function configure()
    {
    $this
    ->setName('cache:clear')
    ->setDescription('Clears Application Cache')
    ->setHelp(<<The cache:clear command removes all files inside
    the cache directory.
    EOF
    );
    }
    // ...
    }

    View Slide

  13. require_once __DIR__ . '/vendor/autoload.php';
    use DCSG\Command\CacheClearCommand;
    use Symfony\Component\Console\Application;
    $app = new Application('Clear Cache App', '0.0.1');
    $app->add(new CacheClearCommand());
    $app->run();
    adding to application

    View Slide

  14. $ php app.php

    View Slide

  15. $ php app.php help cache:clear

    View Slide

  16. add arguments

    View Slide

  17. • ordered
    • input type:
    • optional
    • required
    • array
    arguments are:

    View Slide

  18. // Command/CacheClearCommand.php
    $this->addArgument(
    'directory', // name
    InputArgument::OPTIONAL, // input type
    'The cache directory', // description
    './cache' // default value
    );
    adding an argument

    View Slide

  19. add options

    View Slide

  20. • unordered
    • input type:
    • optional
    • required
    • array
    • none (no input)
    options are:

    View Slide

  21. // CacheClearCommand.php
    $this->addOption(
    'force', // name
    'f', // shortname
    InputOption::VALUE_NONE, // input type
    'Forces to clear the cache.' // description
    );
    adding an option

    View Slide

  22. add logic

    View Slide

  23. // Command/CacheClearCommand.php
    protected function execute(
    InputInterface $input,
    OutputInterface $output)
    {
    // Logic here
    }
    execute the command

    View Slide

  24. // Command/CacheClearCommand.php
    protected function execute(InputInterface $input, OutputInterface $output)
    {
    // Reads the Input
    $directory = $input->getArgument('directory');
    // Removes all files inside the directory
    $counter = 0;
    $files = glob($cacheDir . '/*');
    foreach ($files as $file) {
    if (is_file($file)) {
    unlink($file);
    $counter++;
    }
    }
    // Writes the Output
    $output->writeln(sprintf('Cache cleared. Removed %d files.', $counter));
    }

    View Slide

  25. always
    validate the input

    View Slide

  26. // Command/CacheClearCommand.php
    // Reads the Input
    $directory = $input->getArgument('directory');
    // Validate the argument input
    if (false === $cacheDir = realpath($directory)) {
    throw new \InvalidArgumentException('Directory does't
    exist.');
    }
    // InputOption::VALUE_NONE does not need to be validate
    // since it does not accept parameters
    // in this case will return true or false
    $force = $input->getOption('force');

    View Slide

  27. $ php app.php cache:clear /foo/bar

    View Slide

  28. interact
    with the user

    View Slide

  29. • Dialog helper
    • Formatter helper
    • Progress helper
    • Table helper

    View Slide

  30. dialog HELPER

    View Slide

  31. // Symfony/Component/Console/Helper/DialogHelper.php
    public function select(...){...}
    public function ask(...){...}
    public function askConfirmation(...){...}
    public function askHiddenResponse(...){...}
    public function askAndValidate(...){...}
    public function askHiddenResponseAndValidate (...){...}

    View Slide

  32. // Command/CacheClearCommand.php
    // ...
    // Ask the user if it's the right directory
    $dialog = $this->getHelperSet()->get('dialog');
    $answer = $dialog->askConfirmation(
    $output,
    sprintf('Remove all files in: %s ? (no)', $cacheDir),
    false // default value
    );
    // ...
    Remove all files in: ./cache ? (no) yes
    OUTPUT

    View Slide

  33. OUTPUT
    // examples/Helpers/select.php
    $colors = array('Red', 'Blue', 'Green', 'Yellow');
    // Ask the user the favorite color
    $dialog = $this->getHelperSet()->get('dialog');
    $colorIndex = $dialog->select(
    $output,
    'What color do you like?',
    $colors
    );
    // Writes the Output
    $output->writeln(
    sprintf('The color you like is %s.', $colors[$colorIndex])
    );

    View Slide

  34. formatter HELPER

    View Slide

  35. OUTPUT
    $formatter = $this->getHelperSet()->get('formatter');
    $formattedLine = $formatter->formatSection(
    'SomeSection',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    $msg = array('Something went wrong');
    $formattedBlock = $formatter->formatBlock($msg, 'error');
    $output->writeln($formattedBlock);
    $msg = array('Custom Colors');
    $formattedBlock = $formatter->formatBlock($msg, 'bg=blue;fg=white');
    $output->writeln($formattedBlock);

    View Slide

  36. progress helper

    View Slide

  37. 05/50 [== ] 10%
    25/50 [============= ] 50%
    50/50 [============================] 100%
    $progress = $this->getHelperSet()->get('progress');
    $progress->start($output, 50);
    $i = 0;
    while ($i++ < 50) {
    $progress->advance();
    }
    $progress->finish();
    OUTPUT

    View Slide

  38. table helper

    View Slide

  39. $table = $this->getHelperSet()->get('table');
    $table
    ->setHeaders(array('Color', 'HEX'))
    ->setRows(
    array(
    array('Red', '#ff0000'),
    array('Blue', '#0000ff'),
    array('Green', '#008000'),
    array('Yellow', '#ffff00')
    )
    );
    $table->render($output);
    OUTPUT

    View Slide

  40. command events

    View Slide

  41. • command - before run
    • exception - on exceptions
    • terminate - before return exit code
    the events

    View Slide

  42. testing Commands

    View Slide

  43. public function testCacheClear()
    {
    $application = new Application();
    $application->add(new CacheClearCommand());
    // Mock the DialogHelper to skip interact
    // ...
    $command = $application->find('cache:clear');
    // Add Mock to command
    $commandTester = new CommandTester($command);
    $commandTester->execute(array(
    'command' => 'cache:clear',
    'directory' => './cache'
    ));
    $this->assertFileNotExists($filename);
    }

    View Slide

  44. // Symfony/Component/Console/Tester/CommandTester.php
    class CommandTester
    {
    private $command;
    private $input;
    private $output;
    public function __construct(Command $command){..}
    public function execute(array $input, array $options=array()){..}
    public function getDisplay($normalize = false){..}
    public function getInput(){..}
    public function getOutput(){..}
    }
    command tester

    View Slide

  45. $ ./vendor/bin/phpunit

    View Slide

  46. dependency injection
    in your application

    View Slide

  47. Application
    Container
    CacheClearCommand
    FileSystem
    Finder
    Services
    setContainer(...)
    getContainer()
    addCommand()
    ...
    Other Commands

    View Slide

  48. namespace Symfony\Component\DependencyInjection;
    interface ContainerAwareInterface
    {
    public function setContainer(
    ContainerInterface $container = null
    );
    }
    container aware interface

    View Slide

  49. // DCSG/Application.php
    class Application extends BaseApplication
    implements ContainerAwareInterface
    {
    private $container;
    public function setContainer(..){..}
    public function getContainer(){..}
    }
    implement in the application

    View Slide

  50. // DCSG/Command/CacheClearCommand.php
    // ...
    $container = $this->getApplication()->getContainer();
    $fs = $container->get('filesystem');
    $finder = $container->get('finder');
    $files = $finder->in($cacheDir);
    $fs->remove($files);
    // ...
    usage in the command

    View Slide

  51. http://goo.gl/h0Xcfe
    http://symfony.com/doc/current/components/process.html
    Long running processes
    resources
    http://symfony.com/doc/current/components/console/index.html
    http://symfony.com/doc/current/cookbook/console/index.html
    http://symfony.com/doc/current/cookbook/service_container/index.html
    http://symfony.com/doc/current/components/dependency_injection/index.html
    Symfony2 Docs
    Cron jobs
    http://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/
    Source Code
    https://github.com/danielcsgomes/phplx-how-to-build-console-app
    https://github.com/symfony/Console

    View Slide

  52. @danielcsgomes | [email protected] | http://danielcsgomes.com
    Photo by Jian Awe © http://www."ickr.com/photos/qqjawe/6511141237
    Please give feedback:
    https://joind.in/9615
    Questions?

    View Slide