ConsoleController   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 18
c 6
b 0
f 0
lcom 1
cbo 7
dl 0
loc 162
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setHttpClient() 0 4 1
A dispatch() 0 7 2
A writeLine() 0 6 2
A writeLineError() 0 4 1
A writeLineSuccess() 0 4 1
A getConsole() 0 4 1
A isQuietMode() 0 7 2
A __construct() 0 6 1
B downloadAction() 0 41 6
A getDbResponse() 0 11 1
1
<?php
2
3
namespace ZfSnapGeoip\Controller;
4
5
use Zend\Console\Adapter\AdapterInterface as Console;
6
use Zend\Console\ColorInterface as Color;
7
use Zend\Console\Request as ConsoleRequest;
8
use Zend\Http\Client;
9
use Zend\Http\Client\Exception\RuntimeException;
10
use Zend\Http\Request;
11
use Zend\Http\Response;
12
use Zend\Mvc\Controller\AbstractActionController;
13
use Zend\Stdlib\RequestInterface;
14
use Zend\Stdlib\ResponseInterface;
15
use ZfSnapGeoip\DatabaseConfig;
16
17
/**
18
 * Console Controller
19
 *
20
 * @author Witold Wasiczko <[email protected]>
21
 */
22
class ConsoleController extends AbstractActionController
23
{
24
    /**
25
     * @var Console
26
     */
27
    protected $console;
28
29
    /**
30
     * Is quiet mode enabled?
31
     *
32
     * @var bool
33
     */
34
    protected $isQuiet;
35
36
    /**
37
     * @var DatabaseConfig
38
     */
39
    protected $config;
40
41
    /**
42
     * @var Client
43
     */
44
    protected $httpClient;
45
46
    /**
47
     * @param Console $console
48
     * @param DatabaseConfig $config
49
     * @param Client $httpClient
50
     */
51
    public function __construct(Console $console, DatabaseConfig $config, Client $httpClient)
52
    {
53
        $this->console = $console;
54
        $this->config = $config;
55
        $this->setHttpClient($httpClient);
56
    }
57
58
    /**
59
     * @param Client $httpClient
60
     */
61
    public function setHttpClient(Client $httpClient)
62
    {
63
        $this->httpClient = $httpClient;
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function dispatch(RequestInterface $request, ResponseInterface $response = null)
70
    {
71
        if (!($request instanceof ConsoleRequest)) {
72
            throw new \RuntimeException('You can use this controller only from a console!');
73
        }
74
        return parent::dispatch($request, $response);
75
    }
76
77
    /**
78
     * Download GeoIP data via console
79
     */
80
    public function downloadAction()
81
    {
82
        $datFilePath = $this->config->getDatabasePath();
83
        $events = $this->getEventManager();
84
85
        if ($this->getRequest()->getParam('no-clobber') && is_file($datFilePath)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getParam() does only exist in the following implementations of said interface: Zend\Console\Request.

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...
86
            $events->trigger(__FUNCTION__ . '.exists', $this, [
87
                'path' => $datFilePath,
88
            ]);
89
            $this->writeLine('Database already exist. Skipping...');
90
            return;
91
        }
92
93
        try {
94
            $response = $this->getDbResponse();
95
        } catch (RuntimeException $e) {
96
            $this->writeLineError(sprintf('%s', $e->getMessage()));
97
            return;
98
        }
99
100
        if (!$response instanceof Response || $response->getStatusCode() !== Response::STATUS_CODE_200) {
101
            $this->writeLineError('Error during file download occured');
102
            return;
103
        }
104
105
        $events->trigger(__FUNCTION__ . '.pre', $this, [
106
            'path' => $datFilePath,
107
            'response' => $response,
108
        ]);
109
110
        $this->writeLineSuccess('Download completed');
111
        $this->writeLine('Unzip the downloading data...');
112
113
        file_put_contents($datFilePath, gzdecode($response->getBody()));
114
115
        $events->trigger(__FUNCTION__ . '.post', $this, [
116
            'path' => $datFilePath,
117
        ]);
118
119
        $this->writeLineSuccess(sprintf('Unzip completed (%s)', $datFilePath));
120
    }
121
122
    /**
123
     * @return Response
124
     */
125
    public function getDbResponse()
126
    {
127
        $source = $this->config->getSource();
128
129
        $this->writeLine(sprintf('Downloading %s...', $source));
130
131
        $this->httpClient->setUri($source);
132
        $this->httpClient->setMethod(Request::METHOD_GET);
133
134
        return $this->httpClient->send();
135
    }
136
137
    /**
138
     * @param string $text
139
     * @param int $color
140
     * @param int $bgColor
141
     */
142
    public function writeLine($text, $color = null, $bgColor = null)
143
    {
144
        if (!$this->isQuietMode()) {
145
            $this->getConsole()->writeLine($text, $color, $bgColor);
146
        }
147
    }
148
149
    /**
150
     * @param string $text
151
     */
152
    public function writeLineError($text)
153
    {
154
        $this->writeLine($text, Color::WHITE, Color::RED);
155
    }
156
157
    /**
158
     * @param string $text
159
     */
160
    public function writeLineSuccess($text)
161
    {
162
        $this->writeLine($text, Color::LIGHT_GREEN);
163
    }
164
165
    /**
166
     * @return Console
167
     */
168
    public function getConsole()
169
    {
170
        return $this->console;
171
    }
172
173
    /**
174
     * @return bool
175
     */
176
    public function isQuietMode()
177
    {
178
        if ($this->isQuiet === null) {
179
            $this->isQuiet = (bool) $this->getRequest()->getParam('q', false);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getParam() does only exist in the following implementations of said interface: Zend\Console\Request.

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...
180
        }
181
        return $this->isQuiet;
182
    }
183
}
184