Completed
Push — master ( 8ee32e...efc44e )
by Pablo
02:33
created

ComposerToolHandler   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 8
c 0
b 0
f 0
lcom 1
cbo 7
dl 0
loc 86
rs 10
ccs 26
cts 26
cp 1

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A handle() 0 4 1
A execute() 0 16 2
A executeTool() 0 8 3
A getComposerFilesResponse() 0 4 1
1
<?php
2
3
namespace PhpGitHooks\Module\Composer\Contract\Command;
4
5
use Bruli\EventBusBundle\CommandBus\CommandHandlerInterface;
6
use Bruli\EventBusBundle\CommandBus\CommandInterface;
7
use Bruli\EventBusBundle\QueryBus\QueryBus;
8
use PhpGitHooks\Module\Composer\Contract\Exception\ComposerFilesNotFoundException;
9
use PhpGitHooks\Module\Files\Contract\Query\ComposerFilesExtractor;
10
use PhpGitHooks\Module\Files\Contract\Response\ComposerFilesResponse;
11
use PhpGitHooks\Module\Git\Contract\Response\BadJobLogoResponse;
12
use PhpGitHooks\Module\Git\Service\PreCommitOutputWriter;
13
use Symfony\Component\Console\Output\OutputInterface;
14
15
class ComposerToolHandler implements CommandHandlerInterface
16
{
17
    const CHECKING_MESSAGE = 'Checking composer files';
18
    /**
19
     * @var PreCommitOutputWriter
20
     */
21
    private $outputMessage;
22
    /**
23
     * @var OutputInterface
24
     */
25
    private $output;
26
    /**
27
     * @var QueryBus
28
     */
29
    private $queryBus;
30
    /**
31
     * ComposerTool constructor.
32
     *
33
     * @param QueryBus        $queryBus
34
     * @param OutputInterface $output
35
     */
36 3
    public function __construct(
37
        QueryBus $queryBus,
38
        OutputInterface $output
39
    ) {
40 3
        $this->output = $output;
41 3
        $this->queryBus = $queryBus;
42 3
    }
43
44
    /**
45
     * @param CommandInterface|ComposerTool $command
46
     */
47 3
    public function handle(CommandInterface $command)
48
    {
49 3
        $this->execute($command->getFiles(), $command->getErrorMessage());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Bruli\EventBusBundle\CommandBus\CommandInterface as the method getFiles() does only exist in the following implementations of said interface: PhpGitHooks\Module\Compo...ct\Command\ComposerTool, PhpGitHooks\Module\JsonL...and\JsonLintToolCommand, PhpGitHooks\Module\PhpCs...d\PhpCsFixerToolCommand, PhpGitHooks\Module\PhpCs...ommand\PhpCsToolCommand, PhpGitHooks\Module\PhpLi...mand\PhpLintToolCommand, PhpGitHooks\Module\PhpMd...ommand\PhpMdToolCommand.

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...
Bug introduced by
It seems like you code against a concrete implementation and not the interface Bruli\EventBusBundle\CommandBus\CommandInterface as the method getErrorMessage() does only exist in the following implementations of said interface: PhpGitHooks\Module\Compo...ct\Command\ComposerTool, PhpGitHooks\Module\JsonL...and\JsonLintToolCommand, PhpGitHooks\Module\PhpCs...d\PhpCsFixerToolCommand, PhpGitHooks\Module\PhpCs...ommand\PhpCsToolCommand, PhpGitHooks\Module\PhpLi...mand\PhpLintToolCommand, PhpGitHooks\Module\PhpMd...ommand\PhpMdToolCommand, PhpGitHooks\Module\PhpUn...mand\PhpUnitToolCommand, PhpGitHooks\Module\PhpUn...d\StrictCoverageCommand.

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...
50 2
    }
51
52
    /**
53
     * @param array  $files
54
     * @param string $errorMessage
55
     *
56
     * @throws ComposerFilesNotFoundException
57
     */
58 3
    private function execute(array $files, $errorMessage)
59
    {
60 3
        $composerFilesResponse = $this->getComposerFilesResponse($files);
61
62 3
        $this->outputMessage = new PreCommitOutputWriter(self::CHECKING_MESSAGE);
63 3
        if (true === $composerFilesResponse->isExists()) {
64 2
            $this->output->write($this->outputMessage->getMessage());
65
66 2
            $this->executeTool(
67 2
                $composerFilesResponse->isJsonFile(),
68 2
                $composerFilesResponse->isLockFile(),
69 2
                $errorMessage
70
            );
71 1
            $this->output->writeln($this->outputMessage->getSuccessfulMessage());
72
        }
73 2
    }
74
75
    /**
76
     * @param bool   $jsonFile
77
     * @param bool   $lockFile
78
     * @param string $errorMessage
79
     *
80
     * @throws ComposerFilesNotFoundException *
81
     */
82 2
    private function executeTool($jsonFile, $lockFile, $errorMessage)
83
    {
84 2
        if (true === $jsonFile && false === $lockFile) {
85 1
            $this->output->writeln($this->outputMessage->getFailMessage());
86 1
            $this->output->writeln(BadJobLogoResponse::paint($errorMessage));
87 1
            throw new ComposerFilesNotFoundException();
88
        }
89 1
    }
90
91
    /**
92
     * @param array $files
93
     *
94
     * @return ComposerFilesResponse
95
     */
96 3
    private function getComposerFilesResponse(array $files)
97
    {
98 3
        return $this->queryBus->handle(new ComposerFilesExtractor($files));
99
    }
100
}
101