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

How to build Console Applications | SymfonyCon 2013

Daniel Gomes
December 13, 2013

How to build Console Applications | SymfonyCon 2013

Source code: https://github.com/danielcsgomes/symfonycon-how-to-build-console-apps

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 and you usually use shell script to do it.

Why not use Symfony2 Console component? You can easily build your own commands and interact with other bundles/components.

The Symfony2 Console component is a tool that gives you to build in a simple and easy way console applications.

This talk will focus on how you can simple build an Console applications and how you can interact with other bundles/components.

Daniel Gomes

December 13, 2013
Tweet

More Decks by Daniel Gomes

Other Decks in Programming

Transcript

  1. How to build
    Console Applications
    Daniel Gomes
    @danielcsgomes
    December 13, 2013

    View Slide

  2. Who am I
    • Senior Software Engineer @ GuestCentric Systems
    • Co-founder & organizer @ phplx
    • Co-founder of a beautiful boy
    • ZCE PHP 5.3, CSM, OCP MySQL 5 Developer

    View Slide

  3. me on the web
    @danielcsgomes
    rate & feedback
    https://joind.in/10376

    View Slide

  4. is easy to create
    console applications?

    View Slide

  5. visualgrover © http://farm4.staticflickr.com/3081/2286086833_91f262868a_o_d.jpg
    are you nuts?

    View Slide

  6. !
    // /path/to/app.php
    require_once __DIR__ . '/vendor/autoload.php';
    !
    use Symfony\Component\Console\Application;
    !
    $app = new Application('My Console App', '0.0.1');
    $app->run();
    Daniel Gomes @danielcsgomes

    View Slide

  7. $ php app.php
    Daniel Gomes @danielcsgomes

    View Slide

  8. Thamara Maura © http://farm9.staticflickr.com/8491/8301588586_1ef18f3d9c_o_d.jpg

    View Slide

  9. What I needed
    •Run background jobs
    •Interactive setup tools
    •Cache clean up / warming
    •Basically to do a specific task
    Daniel Gomes @danielcsgomes

    View Slide

  10. What I got
    • Several script files
    • Written in Bash, PHP, Python, etc
    • No documentation
    • Not very friendly for other users
    Daniel Gomes @danielcsgomes

    View Slide

  11. I had an
    idea
    © Cayusa
    http://farm2.staticflickr.com/1288/981372736_74e2d99d8f_b_d.jpg
    Daniel Gomes @danielcsgomes

    View Slide

  12. Why not …
    • Centralize everything
    • Good documentation
    • Tests
    • User friendly
    Daniel Gomes @danielcsgomes

    View Slide

  13. Hello
    Symfony
    Console Component
    http://symfony.com/doc/current/components/console/introduction.html

    View Slide

  14. Zero Dependencies
    {
    "require": {
    "symfony/console": "~2.4"
    }
    }
    Install via composer

    View Slide

  15. What it brings
    •Application
    •Commands
    •Inputs
    •Outputs
    •Helpers
    •Events
    •Formatters

    View Slide

  16. Let’s start

    View Slide

  17. 1.
    Create
    the application

    View Slide

  18. !
    // /path/to/app.php
    require_once __DIR__ . '/vendor/autoload.php';
    !
    use Symfony\Component\Console\Application;
    !
    $app = new Application('My Console App', '0.0.1');
    $app->run();
    Daniel Gomes @danielcsgomes

    View Slide

  19. $ php app.php
    Daniel Gomes @danielcsgomes

    View Slide

  20. !
    How it works

    View Slide

  21. Standalone Console Component
    APPLICATION
    COMMAND A
    COMMAND B
    COMMAND C
    Plug-in & run
    Daniel Gomes @danielcsgomes

    View Slide

  22. with Symfony Framework
    !
    ├── app
    ├── bin
    ├── src
    └── Acme
    └── DemoBundle
    ├── Command
    │ ├── HelloWorldCommand.php
    │ └──
    ├── vendor
    └── web
    Autoload all commands inside the Bundles Command folder
    Daniel Gomes @danielcsgomes

    View Slide

  23. command <-> controller
    input <-> request
    output <-> response
    CLI <-> Web

    View Slide

  24. $app = new Application();
    $app->add(new MyCommand());
    $app->add(…);

    $app->run();
    Daniel Gomes @danielcsgomes

    View Slide

  25. !
    2.
    Create
    the command

    View Slide

  26. class MyCommand extends Command
    {
    protected function configure(){…}
    !
    protected function execute($input, $output){…}
    !
    protected function interact($input, $output){…}
    }
    Daniel Gomes @danielcsgomes

    View Slide

  27. Workflow
    Command Configure
    Interact
    Execute
    is
    Interactive?
    yes
    no
    Daniel Gomes @danielcsgomes

    View Slide

  28. Configure
    the command

    View Slide

  29. namespace DCSG\Command;
    !
    use Symfony\Component\Console\Command\Command;
    !
    class HelloWorldCommand extends Command
    {
    protected function configure()
    {
    $this->setName('hello:world')
    ->setDescription('Hello World ');
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  30. namespace DCSG\Command;
    !
    use Symfony\Component\Console\Command\Command;
    !
    class HelloWorldCommand extends Command
    {
    protected function configure()
    {
    $this->setName('hello:world')
    ->setDescription('Hello World ');
    }
    }
    Daniel Gomes @danielcsgomes
    namespace

    View Slide

  31. namespace DCSG\Command;
    !
    use Symfony\Component\Console\Command\Command;
    !
    class HelloWorldCommand extends Command
    {
    protected function configure()
    {
    $this->setName('hello:world')
    ->setDescription('Hello World ');
    }
    }
    Daniel Gomes @danielcsgomes
    namespace
    name

    View Slide

  32. $ php app.php
    Daniel Gomes @danielcsgomes

    View Slide

  33. Arguments
    and options

    View Slide

  34. Arguments
    • ordered
    • input type:
    • optional
    • required
    • array
    Daniel Gomes @danielcsgomes

    View Slide

  35. Options
    • unordered
    • input type:
    • optional
    • required
    • array
    • none (no input)
    Daniel Gomes @danielcsgomes

    View Slide

  36. protected function configure()
    {
    // ...
    $this->addArgument('name', InputArgument::REQUIRED);
    $this->addOption('uppercase', 'u');
    }
    Daniel Gomes @danielcsgomes

    View Slide

  37. !
    usage manual

    View Slide

  38. protected function configure()
    {
    // ...
    $this->setHelp(<<This is a simple command that outputs
    “Hello world 'Your Name’.”
    to the console.
    EOF
    );
    }
    Daniel Gomes @danielcsgomes

    View Slide

  39. $ php app.php hello:world —help
    Daniel Gomes @danielcsgomes

    View Slide

  40. $ php app.php hello:world —help
    Daniel Gomes @danielcsgomes

    View Slide

  41. $ php app.php hello:world —help
    Daniel Gomes @danielcsgomes

    View Slide

  42. $ php app.php hello:world —help
    Daniel Gomes @danielcsgomes

    View Slide

  43. $ php app.php hello:world —help
    Daniel Gomes @danielcsgomes

    View Slide

  44. Add logic

    View Slide

  45. class HelloWorldCommand extends Command
    {
    // ...
    !
    protected function execute(
    InputInterface $input,
    OutputInterface $output)
    {
    $name = $input->getArgument(‘name');
    !
    if ($input->getOption('uppercase')) {
    $name = strtoupper($name);
    }
    !
    $output->writeln("Hello World $name.");
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  46. $ php app.php hello:world 'Daniel Gomes'
    Daniel Gomes @danielcsgomes

    View Slide

  47. Call a command
    from other command

    View Slide

  48. protected function execute($input, $output)
    {
    $command = $this->getApplication()->find('hello:world');
    !
    $arguments = array(
    'command' => 'hello:world',
    'name' => 'Daniel Gomes'
    );
    !
    $returnCode = $command->run(
    new ArrayInput($arguments),
    $output
    );
    $output->writeln("Exit code $returnCode");
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  49. protected function execute($input, $output)
    {
    $command = $this->getApplication()->find('hello:world');
    !
    $arguments = array(
    'command' => 'hello:world',
    'name' => 'Daniel Gomes'
    );
    !
    $returnCode = $command->run(
    new ArrayInput($arguments),
    $output
    );
    $output->writeln("Exit code $returnCode");
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  50. protected function execute($input, $output)
    {
    $command = $this->getApplication()->find('hello:world');
    !
    $arguments = array(
    'command' => 'hello:world',
    'name' => 'Daniel Gomes'
    );
    !
    $returnCode = $command->run(
    new ArrayInput($arguments),
    $output
    );
    $output->writeln("Exit code $returnCode");
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  51. protected function execute($input, $output)
    {
    $command = $this->getApplication()->find('hello:world');
    !
    $arguments = array(
    'command' => 'hello:world',
    'name' => 'Daniel Gomes'
    );
    !
    $returnCode = $command->run(
    new ArrayInput($arguments),
    $output
    );
    $output->writeln("Exit code $returnCode");
    }
    }
    Daniel Gomes @danielcsgomes

    View Slide

  52. $ php app.php examples:calling:command
    Daniel Gomes @danielcsgomes

    View Slide

  53. Always validate
    the input

    View Slide

  54. protected function execute(
    InputInterface $input,
    OutputInterface $output)
    {
    $name = $input->getArgument(‘name');
    !
    if (preg_match("/[0-9]+/", $name)) {
    throw new \InvalidArgumentException('Invalid name.');
    }
    // …
    }
    Daniel Gomes @danielcsgomes

    View Slide

  55. $ php app.php hello:world 1
    Daniel Gomes @danielcsgomes

    View Slide

  56. User Interaction
    •Dialog Helper
    •Formatter Helper
    •Progress Helper
    •Table Helper
    Daniel Gomes @danielcsgomes

    View Slide

  57. Dialog Helper

    View Slide

  58. // Symfony/Component/Console/Helper/DialogHelper.php
    !
    public function select(...){...}
    !
    public function ask(...){...}
    !
    public function askConfirmation(...){...}
    !
    public function askHiddenResponse(...){...}
    !
    public function askAndValidate(...){...}
    !
    public function askHiddenResponseAndValidate (...){...}
    Daniel Gomes @danielcsgomes

    View Slide

  59. protected function execute($input, $output)
    {
    $colors = array('Red', 'Yellow', 'Green', 'Blue', 'Black');
    $dialog = $this->getHelperSet()->get('dialog');
    $index = $dialog->select(
    $output,
    'Please select your favorite color:',
    $colors
    );
    $output->writeln("Your favorite color is {$colors[$index]}");
    }
    Daniel Gomes @danielcsgomes

    View Slide

  60. $ php app.php examples:select
    Daniel Gomes @danielcsgomes

    View Slide

  61. $name = $this->getHelper('dialog')->askAndValidate(
    $output,
    'Insert your name: ',
    function ($name) {
    if (empty($name)) {
    throw new \InvalidArgumentException(
    'The name cannot be empty.’
    );
    }
    !
    return $name;
    }
    );
    $output->writeln("Your name is {$name}");
    Daniel Gomes @danielcsgomes

    View Slide

  62. $ php app.php examples:dialog
    Daniel Gomes @danielcsgomes

    View Slide

  63. Formatter Helper

    View Slide

  64. !
    $fmt = $this->getHelperSet()->get('formatter');
    !
    $formattedLine = $fmt->formatSection(
    ‘My Section',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    !
    $msg = array('Something went wrong');
    $fmtBlock = $fmt->formatBlock($msg, 'error');
    $output->writeln($fmtBlock);
    !
    $msg = array('Custom Colors');
    $fmtBlock = $fmt->formatBlock($msg, ‘bg=blue;fg=white');
    $output->writeln($fmtBlock);
    Daniel Gomes @danielcsgomes

    View Slide

  65. !
    $fmt = $this->getHelperSet()->get('formatter');
    !
    $formattedLine = $fmt->formatSection(
    ‘My Section',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    !
    $msg = array('Something went wrong');
    $fmtBlock = $fmt->formatBlock($msg, 'error');
    $output->writeln($fmtBlock);
    !
    $msg = array('Custom Colors');
    $fmtBlock = $fmt->formatBlock($msg, ‘bg=blue;fg=white');
    $output->writeln($fmtBlock);
    Daniel Gomes @danielcsgomes

    View Slide

  66. !
    $fmt = $this->getHelperSet()->get('formatter');
    !
    $formattedLine = $fmt->formatSection(
    ‘My Section',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    !
    $msg = array('Something went wrong');
    $fmtBlock = $fmt->formatBlock($msg, 'error');
    $output->writeln($fmtBlock);
    !
    $msg = array('Custom Colors');
    $fmtBlock = $fmt->formatBlock($msg, ‘bg=blue;fg=white');
    $output->writeln($fmtBlock);
    Daniel Gomes @danielcsgomes

    View Slide

  67. !
    $fmt = $this->getHelperSet()->get('formatter');
    !
    $formattedLine = $fmt->formatSection(
    ‘My Section',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    !
    $msg = array('Something went wrong');
    $fmtBlock = $fmt->formatBlock($msg, 'error');
    $output->writeln($fmtBlock);
    !
    $msg = array('Custom Colors');
    $fmtBlock = $fmt->formatBlock($msg, ‘bg=blue;fg=white');
    $output->writeln($fmtBlock);
    Daniel Gomes @danielcsgomes

    View Slide

  68. !
    $fmt = $this->getHelperSet()->get('formatter');
    !
    $formattedLine = $fmt->formatSection(
    ‘My Section',
    'Here is some message related to that section'
    );
    $output->writeln($formattedLine);
    !
    $msg = array('Something went wrong');
    $fmtBlock = $fmt->formatBlock($msg, 'error');
    $output->writeln($fmtBlock);
    !
    $msg = array('Custom Colors');
    $fmtBlock = $fmt->formatBlock($msg, ‘bg=blue;fg=white');
    $output->writeln($fmtBlock);
    Daniel Gomes @danielcsgomes

    View Slide

  69. $ php app.php examples:formatter
    Daniel Gomes @danielcsgomes

    View Slide

  70. Progress Helper

    View Slide

  71. $progress = $this->getHelperSet()->get('progress');
    !
    $progress->start($output, 50);
    $i = 0;
    while ($i++ < 50) {
    sleep(1);
    $progress->advance();
    }
    !
    $progress->finish();
    Daniel Gomes @danielcsgomes

    View Slide

  72. 1/10 [==>-------------------------] 10%
    !
    10/10 [============================] 100%
    $ php app.php examples:progress
    Daniel Gomes @danielcsgomes

    View Slide

  73. Table Helper

    View Slide

  74. $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);
    Daniel Gomes @danielcsgomes

    View Slide

  75. $ php app.php examples:table
    Daniel Gomes @danielcsgomes

    View Slide

  76. Use case
    Dump Databases

    View Slide

  77. protected function interact($input, $output)
    {
    $isEmpty = function($value) {
    if (empty($value)) {
    throw new \InvalidArgumentException('Value cannot be empty.');
    }
    return $value;
    };
    $dialog = $this->getHelper('dialog');
    $this->host = $dialog->askAndValidate($output, 'host: ', $isEmpty);
    $this->user = $dialog->askAndValidate($output, 'username: ', $isEmpty);
    $this->password = $dialog->askHiddenResponseAndValidate(
    $output, 'password: ', $isEmpty
    );
    $this->dbnames = $dialog->askAndValidate(
    $output, 'databases separate by space: ', $isEmpty
    );
    }
    Daniel Gomes @danielcsgomes

    View Slide

  78. protected function interact($input, $output)
    {
    $isEmpty = function($value) {
    if (empty($value)) {
    throw new \InvalidArgumentException('Value cannot be empty.');
    }
    return $value;
    };
    $dialog = $this->getHelper('dialog');
    $this->host = $dialog->askAndValidate($output, 'host: ', $isEmpty);
    $this->user = $dialog->askAndValidate($output, 'username: ', $isEmpty);
    $this->password = $dialog->askHiddenResponseAndValidate(
    $output, 'password: ', $isEmpty
    );
    $this->dbnames = $dialog->askAndValidate(
    $output, 'databases separate by space: ', $isEmpty
    );
    }
    Daniel Gomes @danielcsgomes

    View Slide

  79. protected function interact($input, $output)
    {
    $isEmpty = function($value) {
    if (empty($value)) {
    throw new \InvalidArgumentException('Value cannot be empty.');
    }
    return $value;
    };
    $dialog = $this->getHelper('dialog');
    $this->host = $dialog->askAndValidate($output, 'host: ', $isEmpty);
    $this->user = $dialog->askAndValidate($output, 'username: ', $isEmpty);
    $this->password = $dialog->askHiddenResponseAndValidate(
    $output, 'password: ', $isEmpty
    );
    $this->dbnames = $dialog->askAndValidate(
    $output, 'databases separate by space: ', $isEmpty
    );
    }
    Daniel Gomes @danielcsgomes

    View Slide

  80. protected function interact($input, $output)
    {
    $isEmpty = function($value) {
    if (empty($value)) {
    throw new \InvalidArgumentException('Value cannot be empty.');
    }
    return $value;
    };
    $dialog = $this->getHelper('dialog');
    $this->host = $dialog->askAndValidate($output, 'host: ', $isEmpty);
    $this->user = $dialog->askAndValidate($output, 'username: ', $isEmpty);
    $this->password = $dialog->askHiddenResponseAndValidate(
    $output, 'password: ', $isEmpty
    );
    $this->dbnames = $dialog->askAndValidate(
    $output, 'databases separate by space: ', $isEmpty
    );
    }
    Daniel Gomes @danielcsgomes

    View Slide

  81. Daniel Gomes @danielcsgomes
    protected function execute(… $input, … $output)
    {
    $mysqldump = $this->composeExecCommand($this->getOptions($input));
    !
    $process = new Process($mysqldump);
    $process->run();
    !
    // executes after the command finishes
    if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
    }
    !
    $output->writeln('Databases dumped with success.');
    }

    View Slide

  82. Daniel Gomes @danielcsgomes
    protected function execute(… $input, … $output)
    {
    $mysqldump = $this->composeExecCommand($this->getOptions($input));
    !
    $process = new Process($mysqldump);
    $process->run();
    !
    // executes after the command finishes
    if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
    }
    !
    $output->writeln('Databases dumped with success.');
    }

    View Slide

  83. Daniel Gomes @danielcsgomes
    protected function execute(… $input, … $output)
    {
    $mysqldump = $this->composeExecCommand($this->getOptions($input));
    !
    $process = new Process($mysqldump);
    $process->run();
    !
    // executes after the command finishes
    if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
    }
    !
    $output->writeln('Databases dumped with success.');
    }

    View Slide

  84. Daniel Gomes @danielcsgomes
    protected function execute(… $input, … $output)
    {
    $mysqldump = $this->composeExecCommand($this->getOptions($input));
    !
    $process = new Process($mysqldump);
    $process->run();
    !
    // executes after the command finishes
    if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
    }
    !
    $output->writeln('Databases dumped with success.');
    }

    View Slide

  85. $ php app.php database:dump
    Daniel Gomes @danielcsgomes

    View Slide

  86. Events
    •command - before run
    •exception - on exceptions
    •terminate - before return exit code
    •extend to add custom event
    note: requires Symfony EventDispatcher Component
    Daniel Gomes @danielcsgomes

    View Slide

  87. !
    3.
    Testing

    View Slide

  88. think in your
    command tests
    as functional tests

    View Slide

  89. CommandTester

    View Slide

  90. namespace Symfony\Component\Console\Tester;
    !
    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(){…}
    }

    View Slide

  91. Test the name output

    View Slide

  92. public function testOutputNameInUppercase()
    {
    $command = new HelloWorldCommand();
    $commandTester = new CommandTester($command);
    $commandTester->execute(
    array(
    'command' => $command->getName(),
    'name' => 'Daniel',
    '--uppercase' => true,
    )
    );
    !
    $this->assertRegExp(
    '/DANIEL/',
    $commandTester->getDisplay()
    );
    }

    View Slide

  93. $ ./vendor/bin/phpunit
    Daniel Gomes @danielcsgomes

    View Slide

  94. 4.
    Dependency Injection
    in your CLI app

    View Slide

  95. Container

    View Slide

  96. Services
    CLI Application
    My Commands
    Container
    DBAL
    Finder
    setContainer(...)
    getContainer()
    Monolog

    Daniel Gomes @danielcsgomes

    View Slide

  97. ContainerAwareInterface

    View Slide

  98. namespace Symfony\Component\DependencyInjection;
    !
    interface ContainerAwareInterface
    {
    /**
    * Sets the Container.
    *
    * @param ContainerInterface|null $container
    */
    public function setContainer(ContainerInterface $container = null);
    }

    View Slide

  99. use Symfony\Component\Console\Command\Command;
    use Symfony\Component\DependencyInjection\ContainerAwareInterface;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    !
    class HelloWorldCommand
    extends Command
    implements ContainerAwareInterface
    {
    private $container;
    !
    public function setContainer(ContainerInterface $container = null)
    {
    $this->container = $container;
    }
    !
    protected function execute($input, $output)
    {
    $this->container->get(‘my_service');
    // ...
    }
    !
    // ...
    }

    View Slide

  100. ContainerAwareCommand

    View Slide

  101. abstract class ContainerAwareCommand
    extends Command implements ContainerAwareInterface
    {
    /** @var ContainerInterface|null */
    private $container;
    !
    /** @return ContainerInterface */
    protected function getContainer()
    {
    if (null === $this->container) {
    $this->container = $this->getApplication()
    ->getKernel()
    ->getContainer();
    }
    return $this->container;
    }
    !
    public function setContainer(ContainerInterface $container = null)
    {
    $this->container = $container;
    }
    }

    View Slide

  102. Example
    Logging
    with monolog

    View Slide

  103. protected function execute($input, $output)
    {
    $logger = $this->container->get('logger');
    $mysqldump = $this->composeExecCommand($this->getOptions($input));
    $exitCode = 0;
    $execOutput = array();
    exec($mysqldump, $execOutput, $exitCode);
    if (0 === $exitCode) {
    $message = "Success";
    $logger->addInfo('Databases dumped with success.’);
    } else {
    $message = "Error with exit code: {$exitCode}";
    $logger->addCritical('Error dumping databases.');
    touch($filename);
    }
    return $filename;
    }

    View Slide

  104. 5.
    Catching Signals

    View Slide

  105. Catching Signals
    • SIGSTOP & SIGKILL cannot be catch
    • Only SIGINT can be triggered by shortcut (ctrl+c)
    • Find the best Tick value that fits your needs
    • Define inside your Commands
    • Create a base command
    • Read “Signaling PHP” book by Cal Evans
    http://www.signalingphp.com
    Daniel Gomes @danielcsgomes

    View Slide

  106. protected function execute($input, $output)
    {
    declare(ticks = 10);
    pcntl_signal(SIGINT, [$this, 'signalHandler']);
    !
    do {
    // do something interesting here.
    $this->write('.');
    } while ($this->continueFlag);
    }
    !
    public function signalHandler($signal)
    {
    !
    echo "Caught a signal" . $signal . PHP_EOL;
    $this->continueFlag = false;
    }
    Daniel Gomes @danielcsgomes

    View Slide

  107. protected function execute($input, $output)
    {
    declare(ticks = 10);
    pcntl_signal(SIGINT, [$this, 'signalHandler']);
    !
    do {
    // do something interesting here.
    $this->write('.');
    } while ($this->continueFlag);
    }
    !
    public function signalHandler($signal)
    {
    !
    echo "Caught a signal" . $signal . PHP_EOL;
    $this->continueFlag = false;
    }
    Daniel Gomes @danielcsgomes

    View Slide

  108. $ ./bin/app examples:catch:signal
    Daniel Gomes @danielcsgomes

    View Slide

  109. Resources
    http://goo.gl/h0Xcfe
    http://symfony.com/doc/current/components/process.html
    Long running processes
    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/symfonycon-how-to-build-console-apps
    https://github.com/symfony/Console
    Catching Signals
    http://signalingphp.com
    https://github.com/Cilex/Cilex
    Cilex

    View Slide

  110. http://conference.phplx.net
    @phplxConf

    View Slide

  111. Questions?
    Daniel Gomes @danielcsgomes

    View Slide

  112. @danielcsgomes | [email protected] | http://danielcsgomes.com
    Photo by Jian Awe © http://www.flickr.com/photos/qqjawe/6511141237
    Please give feedback:
    https://joind.in/10376

    View Slide