Completed
Push — master ( 3204cd...9afd5c )
by Andrii
02:53
created

AbstractController   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 263
Duplicated Lines 9.89 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 5
dl 26
loc 263
ccs 8
cts 8
cp 1
rs 8.5454
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A takeGoal() 0 4 1
A takeConfig() 0 4 1
A takeVendor() 0 4 1
A takePackage() 0 4 1
A takeVcs() 0 4 1
A options() 0 4 1
A perform() 0 4 1
A actionBefore() 0 4 1
A actionMake() 0 4 1
A actionAfter() 0 4 1
A actionLoad() 0 4 1
A actionSave() 0 4 1
A setBefore() 0 4 1
A getBefore() 0 4 1
A setMake() 0 4 1
A getMake() 0 4 1
A setAfter() 0 4 1
A getAfter() 0 4 1
B normalizeTasks() 0 14 6
A runRequests() 13 13 4
A runRequest() 0 4 2
A isNotOk() 0 4 2
A isOk() 0 4 1
A runActions() 13 13 4
A runAction() 0 10 2
A isDone() 0 10 2
A markDone() 0 4 3
A passthru() 0 4 1
A exec() 0 4 1
A execCode() 0 4 1
A readline() 0 4 1
A readpassword() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Automation tool mixed with code generator for easier continuous development
4
 *
5
 * @link      https://github.com/hiqdev/hidev
6
 * @package   hidev
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hidev\controllers;
12
13
use hidev\helpers\Helper;
14
use Yii;
15
use yii\console\Response;
16
17
/**
18
 * Abstract controller.
19
 */
20
abstract class AbstractController extends \hidev\base\Controller
21
{
22
    protected $_before = [];
23
    protected $_after  = [];
24
    protected $_make   = ['load', 'save'];
25
26
    /**
27
     * @var array list of performed actions
28
     */
29
    protected $_done = [];
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function options($actionId)
35
    {
36
        return array_merge(parent::options($actionId), array_keys(Helper::getPublicVars(get_called_class())));
37
    }
38
39 2
    public function perform($action = 'make')
40
    {
41
        return $this->runActions(['before', $action, 'after']);
42 2
    }
43
44
    public function actionBefore()
45
    {
46
        return $this->runRequests($this->getBefore());
47
    }
48
49
    /**
50
     * @return int|Response exit code
51
     */
52
    public function actionMake()
53
    {
54
        return $this->runActions($this->getMake());
55
    }
56
57
    public function actionAfter()
58
    {
59
        return $this->runRequests($this->getAfter());
60
    }
61
62
    public function actionLoad()
63
    {
64
        Yii::trace("Loading nothing for '$this->id'");
65
    }
66
67
    public function actionSave()
68
    {
69
        Yii::trace("Saving nothing for '$this->id'");
70
    }
71
72
    public function setBefore($requests)
73
    {
74
        $this->_before = array_merge($this->getBefore(), $this->normalizeTasks($requests));
75
    }
76
77 3
    public function getBefore()
78
    {
79 3
        return $this->_before;
80
    }
81
82
    public function setMake($requests)
83
    {
84
        $this->_make = array_merge($this->getMake(), $this->normalizeTasks($requests));
85
    }
86
87 5
    public function getMake()
88
    {
89 5
        return $this->_make;
90
    }
91
92
    public function setAfter($requests)
93
    {
94
        $this->_after = array_merge($this->getAfter(), $this->normalizeTasks($requests));
95
    }
96
97 3
    public function getAfter()
98
    {
99 3
        return $this->_after;
100
    }
101
102
    public function normalizeTasks($tasks)
103
    {
104
        if (!$tasks) {
105
            return [];
106
        } elseif (!is_array($tasks)) {
107
            $tasks = [(string) $tasks => 1];
108
        }
109
        $res = [];
110
        foreach ($tasks as $dep => $enabled) {
111
            $res[(string) (is_int($dep) ? $enabled : $dep)] = (bool) (is_int($dep) ? 1 : $enabled);
112
        }
113
114
        return $res;
115
    }
116
117
    /**
118
     * Runs array of requests. Stops on failure and returns exit code.
119
     * @param null|string|array $requests
120
     * @return int|Response exit code
121
     */
122 View Code Duplication
    public function runRequests($requests)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
    {
124
        foreach ($this->normalizeTasks($requests) as $request => $enabled) {
125
            if ($enabled) {
126
                $res = $this->runRequest($request);
127
                if (static::isNotOk($res)) {
128
                    return $res;
129
                }
130
            }
131
        }
132
133
        return 0;
134
    }
135
136
    public function runRequest($request)
137
    {
138
        return $request === null ? null : $this->module->runRequest($request);
139
    }
140
141
    /**
142
     * Is response NOT Ok.
143
     * @param Response|int $res
144
     * @return bool
145
     */
146
    public static function isNotOk($res)
147
    {
148
        return is_object($res) ? $res->exitStatus : $res;
149
    }
150
151
    /**
152
     * Is response Ok.
153
     * @param Response|int $res
154
     * @return bool
155
     */
156
    public static function isOk($res)
157
    {
158
        return !static::isNotOk($res);
159
    }
160
161
    /**
162
     * Runs list of actions.
163
     * TODO: think to redo with runRequests.
164
     * @param null|string|array $actions
165
     * @return int|Response exit code
166
     */
167 View Code Duplication
    public function runActions($actions)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
    {
169
        foreach ($this->normalizeTasks($actions) as $action => $enabled) {
170
            if ($enabled) {
171
                $res = $this->runAction($action);
172
                if (static::isNotOk($res)) {
173
                    return $res;
174
                }
175
            }
176
        }
177
178
        return 0;
179
    }
180
181
    public function runAction($id, $params = [])
182
    {
183
        if ($this->isDone($id)) {
184
            return;
185
        }
186
        $result = parent::runAction($id, $params);
187
        $this->markDone($id);
188
189
        return $result;
190
    }
191
192
    public function isDone($action, $timestamp = null)
0 ignored issues
show
Unused Code introduced by
The parameter $timestamp is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
193
    {
194
        if (isset($this->_done[$action])) {
195
            Yii::trace("Already done: '$this->id/$action'");
196
197
            return true;
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * Mark action as already done.
205
     *
206
     * @param string $action action id
207
     * @param int $time microtime when action was done, false for action was not done
208
     */
209
    public function markDone($action, $time = null)
210
    {
211
        $this->_done[$action] = ($time === null || $time === true) ? microtime(1) : $time;
212
    }
213
214
    /**
215
     * Runs given binary with given arguments. Returns exit code.
216
     * @param string $name
217
     * @param array|string $args
218
     * @return int exit code
219
     */
220
    public function passthru($name, $args = [])
221
    {
222
        return $this->takeGoal('binaries')->passthruBinary($name, $args);
223
    }
224
225
    /**
226
     * Runs given binary with given arguments. Returns stdout array.
227
     * @param string $name
228
     * @param string $args
229
     * @param bool $returnExitCode, default false
0 ignored issues
show
Documentation introduced by
There is no parameter named $returnExitCode,. Did you maybe mean $returnExitCode?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
230
     * @return array|int stdout or exitcode
231
     */
232
    public function exec($name, $args = '', $returnExitCode = false)
233
    {
234
        return $this->takeGoal('binaries')->execBinary($name, $args, $returnExitCode);
235
    }
236
237
    public function execCode($name, $args = '')
238
    {
239
        return $this->takeGoal('binaries')->execBinary($name, $args, true);
240
    }
241
242
    public function readline($prompt)
243
    {
244
        return readline($prompt);
245
    }
246
247
    public function readpassword($prompt)
248
    {
249
        echo $prompt;
250
        system('stty -echo');
251
        $password = rtrim(fgets(STDIN), PHP_EOL);
252
        system('stty echo');
253
        echo "\n";
254
255
        return $password;
256
    }
257
258
    public function takeGoal($id)
259
    {
260
        return $this->module->get('config')->getGoal($id);
261
    }
262
263
    public function takeConfig()
264
    {
265
        return $this->module->get('config');
266
    }
267
268
    public function takeVendor()
269
    {
270
        return $this->takeGoal('vendor');
271
    }
272
273
    public function takePackage()
274
    {
275
        return $this->takeGoal('package');
276
    }
277
278
    public function takeVcs()
279
    {
280
        return $this->takeConfig()->getVcs();
281
    }
282
}
283