RunCommand   A
last analyzed

Complexity

Total Complexity 6

Size/Duplication

Total Lines 77
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 0
Metric Value
wmc 6
c 0
b 0
f 0
lcom 2
cbo 5
dl 0
loc 77
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A configure() 0 18 1
A execute() 0 15 3
A askForTheService() 0 11 1
1
<?php
2
3
namespace Dock\Cli;
4
5
use Dock\Docker\Compose\Config;
6
use Dock\Docker\Compose\NotWithinServiceException;
7
use Dock\IO\ProcessRunner;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Console\Input\InputArgument;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Input\InputOption;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Console\Question\ChoiceQuestion;
14
15
class RunCommand extends Command
16
{
17
    /**
18
     * @var ProcessRunner
19
     */
20
    private $processRunner;
21
22
    /**
23
     * @var Config
24
     */
25
    private $config;
26
27
    /**
28
     * @param ProcessRunner $processRunner
29
     * @param Config        $config
30
     */
31
    public function __construct(ProcessRunner $processRunner, Config $config)
32
    {
33
        parent::__construct();
34
35
        $this->processRunner = $processRunner;
36
        $this->config = $config;
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    protected function configure()
43
    {
44
        $this
45
            ->setName('run')
46
            ->setDescription('Run a command on a service')
47
            ->addOption(
48
                'service',
49
                's',
50
                InputOption::VALUE_REQUIRED,
51
                'Service to run the command on'
52
            )
53
            ->addArgument(
54
                'service_command',
55
                InputArgument::IS_ARRAY,
56
                'Command to run on the current service'
57
            )
58
        ;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    protected function execute(InputInterface $input, OutputInterface $output)
65
    {
66
        if ($input->getOption('service') !== null) {
67
            $service = $input->getOption('service');
68
        } else {
69
            try {
70
                $service = $this->config->getCurrentService();
71
            } catch (NotWithinServiceException $e) {
72
                $service = $this->askForTheService($input, $output);
73
            }
74
        }
75
76
        $command = implode(' ', $input->getArgument('service_command'));
77
        $this->processRunner->run("docker-compose run $service $command");
78
    }
79
80
    private function askForTheService(InputInterface $input, OutputInterface $output)
81
    {
82
        $services = $this->config->getServices();
83
84
        $question = new ChoiceQuestion(
85
            'Please select the service to run your command on',
86
            array_combine($services, $services) // work around a bug in symfony/console 2.7
87
        );
88
89
        return $this->getHelper('question')->ask($input, $output, $question);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method ask() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper, Symfony\Component\Console\Helper\QuestionHelper, Symfony\Component\Consol...r\SymfonyQuestionHelper.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
90
    }
91
}
92