Completed
Push — develop ( 3d2d65...50fd95 )
by Tom
07:00 queued 02:46
created

ReportCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
c 1
b 0
f 0
lcom 2
cbo 3
dl 0
loc 153
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A inject() 0 7 1
B configure() 0 42 1
C execute() 0 61 13
A isTagFiltered() 0 9 1
1
<?php
2
3
namespace N98\Magento\Command\Cache;
4
5
use Magento\Framework\App\CacheInterface;
6
use Magento\PageCache\Model\Cache\Type as FullPageCache;
7
use N98\Magento\Command\AbstractMagentoCommand;
8
use N98\Util\Console\Helper\Table\Renderer\RendererFactory;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Validator\Exception\RuntimeException;
13
14
class ReportCommand extends AbstractMagentoCommand
15
{
16
    /**
17
     * @var CacheInterface
18
     */
19
    private $cache;
20
21
    /**
22
     * @var FullPageCache
23
     */
24
    private $fpc;
25
26
    /**
27
     * @param CacheInterface $cache
28
     * @param FullPageCache $fpc
29
     */
30
    public function inject(
31
        CacheInterface $cache,
32
        FullPageCache $fpc
33
    ) {
34
        $this->cache = $cache;
35
        $this->fpc = $fpc;
36
    }
37
38
    /**
39
     * @return void
40
     */
41
    protected function configure()
42
    {
43
        $this
44
            ->setName('cache:report')
45
            ->addOption(
46
                'fpc',
47
                null,
48
                InputOption::VALUE_NONE,
49
                'Use full page cache instead of core cache'
50
            )
51
            ->addOption(
52
                'tags',
53
                't',
54
                InputOption::VALUE_NONE,
55
                'Output tags'
56
            )
57
            ->addOption(
58
                'mtime',
59
                'm',
60
                InputOption::VALUE_NONE,
61
                'Output last modification time'
62
            )
63
            ->addOption(
64
                'filter-id',
65
                '',
66
                InputOption::VALUE_OPTIONAL,
67
                'Filter output by ID (substring)'
68
            )
69
            ->addOption(
70
                'filter-tag',
71
                '',
72
                InputOption::VALUE_OPTIONAL,
73
                'Filter output by TAG (separate multiple tags by comma)'
74
            )
75
            ->addOption(
76
                'format',
77
                null,
78
                InputOption::VALUE_OPTIONAL,
79
                'Output Format. One of [' . implode(', ', RendererFactory::getFormats()) . ']'
80
            )
81
            ->setDescription('View inside the cache');
82
    }
83
84
    /**
85
     * @param InputInterface $input
86
     * @param OutputInterface $output
87
     * @return int|void
88
     * @throws RuntimeException
89
     */
90
    protected function execute(InputInterface $input, OutputInterface $output)
91
    {
92
        $this->detectMagento($output, true);
93
        if (!$this->initMagento()) {
94
            return;
95
        }
96
97
        /** @var \Zend_Cache_Core $lowLevelFrontend */
98
        if ($input->hasOption('fpc') && $input->getOption('fpc')) {
99
            $lowLevelFrontend = $this->fpc->getLowLevelFrontend();
100
        } else {
101
            $lowLevelFrontend = $this->cache->getFrontend()->getLowLevelFrontend();
102
        }
103
104
        $table = [];
105
        $cacheIds = $lowLevelFrontend->getIds();
106
        $filterId = $input->getOption('filter-id');
107
        $filterTag = $input->getOption('filter-tag');
108
        $mTime = $input->getOption('mtime');
109
        $tags = $input->getOption('tags');
110
        foreach ($cacheIds as $cacheId) {
111
            if ($filterId !== null &&
112
                !stristr($cacheId, $filterId)) {
113
                continue;
114
            }
115
116
            $metadata = $lowLevelFrontend->getMetadatas($cacheId);
117
            if ($filterTag !== null &&
118
                !$this->isTagFiltered($metadata, $input)) {
119
                continue;
120
            }
121
122
            $row = [
123
                $cacheId,
124
                date('Y-m-d H:i:s', $metadata['expire']),
125
            ];
126
127
            if ($mTime) {
128
                $row[] = date('Y-m-d H:i:s', $metadata['mtime']);
129
            }
130
131
            if ($tags) {
132
                $row[] = implode(', ', $metadata['tags']);
133
            }
134
135
            $table[] = $row;
136
        }
137
138
        $headers = ['ID', 'EXPIRE'];
139
        if ($mTime) {
140
            $headers[] = 'MTIME';
141
        }
142
        if ($tags) {
143
            $headers[] = 'TAGS';
144
        }
145
146
        $this
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 setHeaders() does only exist in the following implementations of said interface: N98\Util\Console\Helper\TableHelper, Symfony\Component\Console\Helper\TableHelper.

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...
147
            ->getHelper('table')
148
            ->setHeaders($headers)
149
            ->renderByFormat($output, $table, $input->getOption('format'));
150
    }
151
152
    /**
153
     * @param array $metadata
154
     * @param InputInterface $input
155
     * @return bool
156
     */
157
    private function isTagFiltered($metadata, $input)
158
    {
159
        return (bool) count(
160
            array_intersect(
161
                $metadata['tags'],
162
                explode(',', $input->getOption('filter-tag'))
163
            )
164
        );
165
    }
166
}
167