Completed
Push — master ( 502445...002137 )
by Andrii
17:02
created

StartController   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 68.75%

Importance

Changes 5
Bugs 2 Features 0
Metric Value
wmc 48
c 5
b 2
f 0
lcom 1
cbo 6
dl 0
loc 205
ccs 66
cts 96
cp 0.6875
rs 8.4864

14 Methods

Rating   Name   Duplication   Size   Complexity  
A hasAlias() 0 6 2
A includeMainConfig() 0 7 2
A actionUpdate() 0 6 2
A actionMake() 0 12 1
A addAutoloader() 0 9 2
B addAliases() 0 18 9
D requireAll() 0 26 9
C needsComposerInstall() 0 22 7
B includeAll() 0 13 5
A loadConfig() 0 7 2
A setRootDir() 0 4 1
A getRootDir() 0 8 2
A findRootDir() 0 11 3
A buildRootPath() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like StartController 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 StartController, 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\base\File;
14
use hidev\helpers\ConfigPlugin;
15
use Yii;
16
use yii\base\InvalidParamException;
17
use yii\helpers\ArrayHelper;
18
19
/**
20
 * Start goal.
21
 * Chdirs to the project's root directory and loads dependencies and configs.
22
 */
23
class StartController extends CommonController
24
{
25
    /**
26
     * @var string absolute path to the project root directory
27
     */
28
    protected $_rootDir;
29
30
    /**
31
     * @var bool hidev already started flag
32
     */
33
    public static $started = false;
34
35
    /**
36
     * Make action.
37
     */
38 2
    public function actionMake()
39
    {
40 2
        self::$started = true;
41 2
        $this->getRootDir();
42 2
        $this->includeMainConfig();
43 2
        $this->addAliases();
44 2
        $this->addAutoloader();
45 2
        $this->requireAll();
46 2
        $this->includeAll();
47 2
        $this->loadConfig();
48 2
        $this->includeMainConfig();
49 2
    }
50
51 2
    public function includeMainConfig()
52
    {
53 2
        $this->takeConfig()->includeConfig('.hidev/config.yml');
54 2
        if (file_exists('.hidev/config-local.yml')) {
55
            $this->takeConfig()->includeConfig('.hidev/config-local.yml');
56
        }
57 2
    }
58
59 2
    public function addAutoloader()
60
    {
61 2
        $autoloader = Yii::getAlias('@root/vendor/autoload.php');
62 2
        if (file_exists($autoloader)) {
63
            spl_autoload_unregister(['Yii', 'autoload']);
64
            require $autoloader;
65
            spl_autoload_register(['Yii', 'autoload'], true, true);
66
        }
67 2
    }
68
69
    /**
70
     * Update action.
71
     * @return int exit code
72
     */
73
    public function actionUpdate()
74
    {
75
        if (file_exists('.hidev/composer.json')) {
76
            return $this->passthru('composer', ['update', '-d', '.hidev', '--prefer-source', '--ansi']);
77
        }
78
    }
79
80
    /**
81
     * Adds aliases:
82
     * - @root alias to current project root dir
83
     * - current package namespace for it could be used from hidev.
84
     */
85 2
    public function addAliases()
86
    {
87 2
        Yii::setAlias('@root', $this->getRootDir());
88 2
        $config = $this->takeConfig()->rawItem('package');
89 2
        $alias  = isset($config['namespace']) ? strtr($config['namespace'], '\\', '/') : '';
90 2
        if ($alias && !Yii::getAlias('@' . $alias, false)) {
91
            $srcdir = Yii::getAlias('@root/' . ($config['src'] ?: 'src'));
92
            Yii::setAlias($alias, $srcdir);
0 ignored issues
show
Bug introduced by
It seems like $srcdir defined by \Yii::getAlias('@root/' ...onfig['src'] ?: 'src')) on line 91 can also be of type boolean; however, yii\BaseYii::setAlias() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
93
        }
94 2
        $aliases = $this->takeConfig()->rawItem('aliases');
95 2
        if (!empty($aliases) && is_array($aliases)) {
96 2
            foreach ($aliases as $alias => $path) {
97 2
                if (!$this->hasAlias($alias)) {
98
                    Yii::setAlias($alias, $path);
99
                }
100
            }
101
        }
102 2
    }
103
104 2
    public function hasAlias($alias, $exact = true)
0 ignored issues
show
Unused Code introduced by
The parameter $exact 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...
105
    {
106 2
        $pos = strpos($alias, '/');
107
108 2
        return $pos === false ? isset(Yii::$aliases[$alias]) : isset(Yii::$aliases[substr($alias, 0, $pos)][$alias]);
109
    }
110
111
    /**
112
     * Require all configured requires.
113
     */
114 2
    protected function requireAll()
115
    {
116 2
        $plugins = $this->takeConfig()->rawItem('plugins');
117 2
        $vendors = [];
118 2
        if ($plugins) {
119
            $file = File::create('.hidev/composer.json');
120
            $data = ArrayHelper::merge($file->load(), ['require' => $plugins]);
121
            if ($file->save($data) || !is_dir('.hidev/vendor')) {
122
                $this->runAction('update');
123
            }
124
            $vendors[] = $this->getRootDir() . '/.hidev/vendor';
125 2
        } elseif ($this->needsComposerInstall()) {
126
            if ($this->passthru('composer', ['install', '--ansi'])) {
127
                throw new InvalidParamException('Failed initialize project with composer install');
128
            }
129
        }
130 2
        $localVendor = $this->getRootDir() . '/vendor';
131 2
        if (file_exists(ConfigPlugin::path($localVendor, 'hidev'))) {
132
            $vendors[] = $localVendor;
133
        }
134 2
        if (!empty($vendors)) {
135
            foreach (array_unique($vendors) as $dir) {
136
                $this->module->loadExtraVendor($dir);
137
            }
138
        }
139 2
    }
140
141 2
    public function needsComposerInstall()
142
    {
143 2
        if (file_exists('vendor')) {
144
            return false;
145
        }
146 2
        if (!file_exists('composer.json')) {
147 2
            return false;
148
        }
149
        $data = File::create('composer.json')->load();
150
        foreach (['require', 'require-dev'] as $key) {
151
            if (isset($data[$key])) {
152
                foreach ($data[$key] as $package => $version) {
153
                    list(, $name) = explode('/', $package);
154
                    if (strncmp($name, 'hidev-', 6) === 0) {
155
                        return true;
156
                    }
157
                }
158
            }
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * Include all configs.
166
     */
167 2
    public function includeAll()
168
    {
169 2
        $still = true;
170 2
        while ($still) {
171 2
            $still = false;
172 2
            $include = $this->takeConfig()->rawItem('include');
173 2
            if ($include) {
174
                foreach ($include as $path) {
175
                    $still = $still || $this->takeConfig()->includeConfig($path);
176
                }
177
            }
178
        }
179 2
    }
180
181
    /**
182
     * Load project's config if configured.
183
     */
184 2
    public function loadConfig()
185
    {
186 2
        $path = $this->takeConfig()->rawItem('config');
187 2
        if ($path) {
188 2
            $this->module->loadExtraConfig($path);
189
        }
190 2
    }
191
192
    public function setRootDir($value)
193
    {
194
        $this->_rootDir = $value;
195
    }
196
197 2
    public function getRootDir()
198
    {
199 2
        if ($this->_rootDir === null) {
200 2
            $this->_rootDir = $this->findRootDir();
201
        }
202
203 2
        return $this->_rootDir;
204
    }
205
206
    /**
207
     * Chdirs to project's root by looking for config file in the current directory and up.
208
     * @throws InvalidParamException when failed to find
209
     * @return string path to the root directory of hidev project
210
     */
211 2
    protected function findRootDir()
212
    {
213 2
        $configDir = '.hidev';
214 2
        for ($i = 0; $i < 9; ++$i) {
215 2
            if (is_dir($configDir)) {
216 2
                return getcwd();
217
            }
218
            chdir('..');
219
        }
220
        throw new InvalidParamException("Not a hidev project (or any of the parent directories).\nUse `hidev init` to initialize hidev project.");
221
    }
222
223
    public function buildRootPath($subpath)
224
    {
225
        return $this->getRootDir() . DIRECTORY_SEPARATOR . $subpath;
226
    }
227
}
228