GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Job   C
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 312
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 6
loc 312
rs 5.8364
wmc 64
lcom 1
cbo 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 24 3
B configureVariables() 0 26 1
B offsetSet() 0 20 5
A addTask() 0 8 2
D addVariablesFromTask() 0 43 20
A camelCaseToLowerCaseDashed() 0 10 1
C getDefinition() 6 51 16
A isDryRun() 0 4 1
C run() 0 35 15

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 Job 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 Job, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * See class comment
4
 *
5
 * PHP Version 5
6
 *
7
 * @category Netresearch
8
 * @package  Netresearch\Kite
9
 * @author   Christian Opitz <[email protected]>
10
 * @license  http://www.netresearch.de Netresearch Copyright
11
 * @link     http://www.netresearch.de
12
 */
13
14
namespace Netresearch\Kite;
15
16
17
use Netresearch\Kite\Service\Console;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputDefinition;
20
use Symfony\Component\Console\Input\InputOption;
21
22
/**
23
 * Job - the outermost task object
24
 *
25
 * @category Netresearch
26
 * @package  Netresearch\Kite
27
 * @author   Christian Opitz <[email protected]>
28
 * @license  http://www.netresearch.de Netresearch Copyright
29
 * @link     http://www.netresearch.de
30
 */
31
class Job extends Tasks
32
{
33
    /**
34
     * @var array
35
     */
36
    protected $definitions = array();
37
38
    /**
39
     * @var bool
40
     */
41
    protected $dryRun = false;
42
43
    /**
44
     * @var bool
45
     */
46
    private $started = false;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
47
48
    /**
49
     * @var bool
50
     */
51
    private $initialized = false;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
52
53
    /**
54
     * Job constructor.
55
     *
56
     * @param Console $console Console
57
     */
58
    public function __construct(Console $console)
59
    {
60
        static $kite, $composer;
61
62
        $this->console = $console;
63
64
        $this->offsetSet('job', $this);
65
        $this->offsetSet('config', $console->getConfig());
66
        if (!$kite) {
67
            $kite = array(
68
                'path' => $path = dirname(__DIR__),
69
                'dir' => $console->getFilesystem()->findShortestPath(getcwd(), $path)
70
            );
71
        }
72
        $this->offsetSet('kite', $kite);
73
74
        parent::__construct($this);
75
76
        if (!$composer) {
77
            $composer = $this->factory->createTask('Netresearch\\Kite\\Service\\Composer', $this);
78
        }
79
80
        $this->offsetSet('composer', $composer);
81
    }
82
83
    /**
84
     * Variable configuration
85
     *
86
     * @return array
87
     */
88
    protected function configureVariables()
89
    {
90
        return [
91
            'options' => [
92
                'type' => 'array',
93
                'label' => 'Options to expose on the console app - keys are camelCase '
94
                    . " variable names, which are used as option names lowercase-dashed.\n"
95
                    . "Values contain variable configuration with keys:\n"
96
                    . "  'type': Variable type (array, string, etc.)\n"
97
                    . "  'label': The label to show on --help\n"
98
                    . "  'default': Default value (if any, invalid for booleans without explicit mode)\n"
99
                    . "  'mode': Mode for the \\Symfony\\Component\\Console\\Input\\InputOption\n"
100
                    . "  'shortcut': Shortcut for the \\Symfony\\Component\\Console\\Input\\InputOption"
101
            ],
102
            'arguments' => [
103
                'type' => 'array',
104
                'label' => 'Arguments to expose on the console app - keys are camelCase '
105
                    . " variable names, which are used as argument names lowercase-dashed.\n"
106
                    . "Values contain variable configuration with keys:\n"
107
                    . "  'type': Variable type (array, string, etc.)\n"
108
                    . "  'label': The label to show on --help\n"
109
                    . "  'default': Default value (if any, invalid for booleans without explicit mode)\n"
110
                    . "  'mode': Mode for the \\Symfony\\Component\\Console\\Input\\InputArgument"
111
            ]
112
        ] + parent::configureVariables();
113
    }
114
115
    /**
116
     * Configure options and arguments
117
     *
118
     * @param string $name  The name
119
     * @param mixed  $value The value
120
     *
121
     * @return void
122
     */
123
    public function offsetSet($name, $value)
124
    {
125
        if ($name === 'options' || $name === 'arguments') {
126
            $type = substr($name, 0, -1);
127
            if (!is_array($value)) {
128
                throw new Exception($name . ' must be array');
129
            }
130
            foreach ($value as $variable => $config) {
131
                $from = $this->camelCaseToLowerCaseDashed($variable);
132
                $this->definitions[$from] = array(
133
                    'context' => $this,
134
                    'variable' => $variable,
135
                    'type' => $type,
136
                    'config' => $config
137
                );
138
            }
139
            return;
140
        }
141
        parent::offsetSet($name, $value);
142
    }
143
144
145
    /**
146
     * Run an array of tasks
147
     *
148
     * @return $this
149
     */
150
    public function run()
151
    {
152
        $this->started = true;
153
154
        if (isset($this->console->getConfig()['workspace'])) {
155
            $this->console->getFilesystem()->ensureDirectoryExists($this->expand('{config["workspace"]}'));
156
        }
157
158
        $input = $this->console->getInput();
159
        $this->dryRun = $input->getOption('dry-run');
160
        foreach ($this->definitions as $from => $info) {
161
            $config = $info['config'];
162
            if ($info['type'] === 'option') {
163
                if ($input->hasOption($from)) {
164
                    $value = $input->getOption($from);
165
                    if (($value === null || is_array($value) && !isset($value[0]) && count($value) <= 1)
166
                        && preg_match('/(^|\|)bool(ean)?($|\|)/', $config['type']) && strpos($config['type'], '|')
167
                    ) {
168
                        if ($input->hasParameterOption($opt = '--' . $from)
169
                            || array_key_exists('shortcut', $config)
170
                            && $input->hasParameterOption($opt = '-' . $config['shortcut'])
171
                        ) {
172
                            // Set value to true, when option has no value
173
                            $value = true;
174
                        }
175
                    }
176
                    $info['context']->set($info['variable'], $value);
177
                }
178
            } elseif ($input->hasArgument($from)) {
179
                $info['context']->set($info['variable'], $input->getArgument($from));
180
            }
181
        }
182
183
        return parent::run();
184
    }
185
186
    /**
187
     * Override get the input options and arguments for the JobCommand
188
     *
189
     * @param \Netresearch\Kite\Task $task The task
190
     *
191
     * @return $this|mixed $this or the task return value when this is running
192
     */
193
    public function addTask(Task $task)
194
    {
195
        if (!$this->started) {
196
            $this->addVariablesFromTask($task);
197
        }
198
199
        return parent::addTask($task);
200
    }
201
202
    /**
203
     * Add variables from the task to the job
204
     *
205
     * @param Task      $task             The task
206
     * @param Variables $context          Context to set the variable to, when job is run.
207
     *                                    When null, variable is set on task
208
     * @param bool|null $overrideExisting Whether to override existing args (true),
209
     *                                    don't override them (false) or throw an
210
     *                                    exception (null)
211
     *
212
     * @return void
213
     */
214
    public function addVariablesFromTask(Task $task, $context = null, $overrideExisting = null)
215
    {
216
        if ($task instanceof Job) {
217
            foreach ($task->definitions as $from => $definition) {
218
                if ($context) {
219
                    $definition['context'] = $context;
220
                }
221
                if (!array_key_exists($from, $this->definitions)) {
222
                    $this->definitions[$from] = $definition;
223
                }
224
            }
225
            return;
226
        }
227
228
        $context = $context ?: $task;
229
        foreach ($task->get('_variableConfiguration') as $variable => $config) {
230
            if (!is_array($config)) {
231
                continue;
232
            }
233
            $option = array_key_exists('option', $config) && $config['option'];
234
            $argument = array_key_exists('argument', $config) && $config['argument'];
235
            if ($argument && $option) {
236
                throw new Exception('Variable can not be option and argument at the same time');
237
            }
238
            if ($argument || $option) {
239
                $setting = $argument ?: $option;
240
                $from = is_string($setting) ? $setting : $this->camelCaseToLowerCaseDashed($variable);
241
                if (array_key_exists($from, $this->definitions)) {
242
                    if ($overrideExisting === null) {
243
                        throw new Exception('Argument/option definitions must be unique');
244
                    } elseif ($overrideExisting === false) {
245
                        continue;
246
                    }
247
                }
248
                $this->definitions[$from] = array(
249
                    'context' => $context,
250
                    'variable' => $variable,
251
                    'type' => $option ? 'option' : 'argument',
252
                    'config' => $config
253
                );
254
            }
255
        }
256
    }
257
258
    /**
259
     * camelCase to lower-case-dashed
260
     *
261
     * @param string $string String
262
     *
263
     * @return string
264
     */
265
    protected function camelCaseToLowerCaseDashed($string)
266
    {
267
        $string = preg_replace('/(?<=\\w)([A-Z])/', '-\\1', $string);
268
269
        // Converts string to lowercase
270
        // The function converts all Latin characters (A-Z, but no accents, etc) to
271
        // lowercase. It is safe for all supported character sets (incl. utf-8).
272
        // Unlike strtolower() it does not honour the locale.
273
        return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
274
    }
275
276
    /**
277
     * Add the retrieved options and arguments to a definition
278
     *
279
     * @return InputDefinition
280
     */
281
    public function getDefinition()
282
    {
283
        if (!$this->initialized) {
284
            $this->initialize();
285
        }
286
287
        $definition = new InputDefinition();
288
289
        foreach ($this->definitions as $from => $info) {
290
            $config = $info['config'];
291
            $required = array_key_exists('required', $config) && $config['required'];
292
            if ($info['type'] === 'option') {
293
                $mode = array_key_exists('mode', $config) ? $config['mode'] : InputOption::VALUE_OPTIONAL;
294
                if ($required) {
295
                    $mode |= InputOption::VALUE_REQUIRED;
296
                }
297 View Code Duplication
                if (in_array('array', explode('|', $config['type']), true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
298
                    $mode |= InputOption::VALUE_IS_ARRAY;
299
                }
300
                if (preg_match('/(^|\|)bool(ean)?($|\|)/', $config['type'])) {
301
                    if (strpos($config['type'], '|')) {
302
                        $mode |= InputOption::VALUE_NONE;
303
                    } else {
304
                        $mode = InputOption::VALUE_NONE;
305
                    }
306
                }
307
                $definition->addOption(
308
                    new InputOption(
309
                        $from,
310
                        array_key_exists('shortcut', $config) ? $config['shortcut'] : null,
311
                        $mode,
312
                        $config['label'],
313
                        array_key_exists('default', $config) ? $config['default'] : null
314
                    )
315
                );
316
            } else {
317
                $mode = array_key_exists('mode', $config) ? $config['mode'] : InputArgument::OPTIONAL;
318
                if ($required) {
319
                    $mode |= InputArgument::REQUIRED;
320
                }
321 View Code Duplication
                if (in_array('array', explode('|', $config['type']), true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
322
                    $mode |= InputArgument::IS_ARRAY;
323
                }
324
                $definition->addArgument(
325
                    new InputArgument($from, $mode, $config['label'], array_key_exists('default', $config) ? $config['default'] : null)
326
                );
327
            }
328
        }
329
330
        return $definition;
331
    }
332
333
    /**
334
     * Determine wether this job is dry run
335
     *
336
     * @return boolean
337
     */
338
    public function isDryRun()
339
    {
340
        return $this->dryRun;
341
    }
342
}
343
?>
344