Completed
Push — master ( c86a14...cd5fc7 )
by Eric
29:59
created

CKEditorInstallerCommand::createNotifier()   D

Complexity

Conditions 16
Paths 1

Size

Total Lines 85
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 60
CRAP Score 16.0276

Importance

Changes 0
Metric Value
dl 0
loc 85
ccs 60
cts 63
cp 0.9524
rs 4.8736
c 0
b 0
f 0
cc 16
eloc 59
nc 1
nop 2
crap 16.0276

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Ivory CKEditor package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\CKEditorBundle\Command;
13
14
use Ivory\CKEditorBundle\Installer\CKEditorInstaller;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Helper\ProgressBar;
17
use Symfony\Component\Console\Helper\ProgressHelper;
18
use Symfony\Component\Console\Helper\QuestionHelper;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ChoiceQuestion;
24
25
/**
26
 * @author GeLo <[email protected]>
27
 */
28
class CKEditorInstallerCommand extends Command
29
{
30
    /**
31
     * @var CKEditorInstaller
32
     */
33
    private $installer;
34
35
    /**
36
     * @param CKEditorInstaller|null $installer
37
     */
38 10
    public function __construct(CKEditorInstaller $installer = null)
39
    {
40 10
        parent::__construct();
41
42 10
        $this->installer = $installer ?: new CKEditorInstaller();
43 10
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 10
    protected function configure()
49
    {
50 8
        $this
51 10
            ->setName('ckeditor:install')
52 10
            ->setDescription('Install CKEditor')
53 10
            ->addArgument('path', InputArgument::OPTIONAL, 'Where to install CKEditor')
54 10
            ->addOption('release', null, InputOption::VALUE_OPTIONAL, 'CKEditor release (basic, standard or full)')
55 10
            ->addOption('tag', null, InputOption::VALUE_OPTIONAL, 'CKEditor tag (x.y.z or latest)')
56 10
            ->addOption(
57 10
                'clear',
58 10
                null,
59 10
                InputOption::VALUE_OPTIONAL,
60 2
                'How to clear previous CKEditor installation (drop, keep or abort)'
61 8
            )
62 10
            ->addOption(
63 10
                'exclude',
64 10
                null,
65 10
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
66 2
                'Path to exclude when extracting CKEditor'
67 8
            )
68 10
            ->setHelp(<<<'EOF'
69 1
The <info>%command.name%</info> command install CKEditor in your application:
70
71
  <info>php %command.full_name%</info>
72
  
73
You can install it at a specific path (absolute):
74
75
  <info>php %command.full_name% path</info>
76
  
77
You can install a specific release (basic, standard or full):
78
79
  <info>php %command.full_name% --release=full</info>
80
  
81
You can install a specific version:
82
83
  <info>php %command.full_name% --tag=4.7.0</info>
84
85
If there is a previous CKEditor installation detected, 
86
you can control how it should be handled in non-interactive mode:
87
88
  <info>php %command.full_name% --clear=drop</info>
89
  <info>php %command.full_name% --clear=keep</info>
90
  <info>php %command.full_name% --clear=abort</info>
91
  
92
You can exclude path(s) when extracting CKEditor:
93
94 1
  <info>php %command.full_name% --exclude=samples --exclude=adapters</info>
95
EOF
96 8
            );
97 10
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 10
    protected function execute(InputInterface $input, OutputInterface $output)
103
    {
104 10
        $this->title($output);
105
106 10
        $success = $this->installer->install($this->createOptions($input, $output));
107
108 10
        if ($success) {
109 10
            $this->success('CKEditor has been successfully installed...', $output);
110 8
        }
111 10
    }
112
113
    /**
114
     * @param InputInterface  $input
115
     * @param OutputInterface $output
116
     *
117
     * @return mixed[]
118
     */
119 10
    private function createOptions(InputInterface $input, OutputInterface $output)
120
    {
121 10
        $options = ['notifier' => $this->createNotifier($input, $output)];
122
123 10
        if ($input->hasArgument('path')) {
124 10
            $options['path'] = $input->getArgument('path');
125 8
        }
126
127 10
        if ($input->hasOption('release')) {
128 10
            $options['release'] = $input->getOption('release');
129 8
        }
130
131 10
        if ($input->hasOption('tag')) {
132 10
            $options['version'] = $input->getOption('tag');
133 8
        }
134
135 10
        if ($input->hasOption('exclude')) {
136 10
            $options['excludes'] = $input->getOption('exclude');
137 8
        }
138
139 10
        if ($input->hasOption('clear')) {
140 10
            $options['clear'] = $input->getOption('clear');
141 8
        }
142
143 10
        return $options;
144
    }
145
146
    /**
147
     * @param InputInterface  $input
148
     * @param OutputInterface $output
149
     *
150
     * @return \Closure
151
     */
152 10
    private function createNotifier(InputInterface $input, OutputInterface $output)
153
    {
154 10
        $clear = $this->createProgressBar($output);
155 10
        $download = $this->createProgressBar($output);
156 10
        $extract = $this->createProgressBar($output);
157
158 10
        return function ($type, $data) use ($input, $output, $clear, $download, $extract) {
159
            switch ($type) {
160 10
                case CKEditorInstaller::NOTIFY_CLEAR:
161 1
                    $result = $this->choice(
162
                        [
163 1
                            sprintf('CKEditor is already installed in "%s"...', $data),
164 1
                            '',
165 1
                            'What do you want to do?',
166 1
                        ],
167
                        $choices = [
168 1
                            CKEditorInstaller::CLEAR_DROP  => 'Drop the directory & reinstall CKEditor',
169 1
                            CKEditorInstaller::CLEAR_KEEP  => 'Keep the directory & reinstall CKEditor by overriding files',
170 1
                            CKEditorInstaller::CLEAR_ABORT => 'Abort installation',
171 1
                        ],
172 1
                        CKEditorInstaller::CLEAR_DROP,
173 1
                        $input,
174
                        $output
175 1
                    );
176
177 1
                    if (($key = array_search($result, $choices, true)) !== false) {
178
                        $result = $key;
179
                    }
180
181 1
                    if ($result === CKEditorInstaller::CLEAR_DROP) {
182 1
                        $this->comment(sprintf('Dropping CKEditor from "%s"', $data), $output);
183 1
                    }
184
185 1
                    return $result;
186
187 10
                case CKEditorInstaller::NOTIFY_CLEAR_ARCHIVE:
188 10
                    $this->comment(sprintf('Dropping CKEditor ZIP archive "%s"', $data), $output);
189 10
                    break;
190
191 10
                case CKEditorInstaller::NOTIFY_CLEAR_COMPLETE:
192 1
                    $this->finishProgressBar($clear, $output);
193 1
                    break;
194
195 10
                case CKEditorInstaller::NOTIFY_CLEAR_PROGRESS:
196 1
                    $clear->advance();
197 1
                    break;
198
199 10
                case CKEditorInstaller::NOTIFY_CLEAR_SIZE:
200 1
                    $this->startProgressBar($clear, $output, $data);
201 1
                    break;
202
203 10
                case CKEditorInstaller::NOTIFY_DOWNLOAD:
204 10
                    $this->comment(sprintf('Downloading CKEditor ZIP archive from "%s"', $data), $output);
205 10
                    break;
206
207 10
                case CKEditorInstaller::NOTIFY_DOWNLOAD_COMPLETE:
208 10
                    $this->finishProgressBar($download, $output);
209 10
                    break;
210
211 10
                case CKEditorInstaller::NOTIFY_DOWNLOAD_PROGRESS:
212 10
                    $this->advanceProgressBar($download, $data);
213 10
                    break;
214
215 10
                case CKEditorInstaller::NOTIFY_DOWNLOAD_SIZE:
216 10
                    $this->startProgressBar($download, $output, $data);
217 10
                    break;
218
219 10
                case CKEditorInstaller::NOTIFY_EXTRACT:
220 10
                    $this->comment(sprintf('Extracting CKEditor ZIP archive to "%s"', $data), $output);
221 10
                    break;
222
223 10
                case CKEditorInstaller::NOTIFY_EXTRACT_COMPLETE:
224 10
                    $this->finishProgressBar($extract, $output);
225 10
                    break;
226
227 10
                case CKEditorInstaller::NOTIFY_EXTRACT_PROGRESS:
228 10
                    $extract->advance();
229 10
                    break;
230
231 10
                case CKEditorInstaller::NOTIFY_EXTRACT_SIZE:
232 10
                    $this->startProgressBar($extract, $output, $data);
233 10
                    break;
234
            }
235 10
        };
236
    }
237
238
    /**
239
     * @param OutputInterface $output
240
     */
241 10
    private function title(OutputInterface $output)
242
    {
243 10
        $output->writeln([
244 10
            '----------------------',
245 8
            '| CKEditor Installer |',
246 8
            '----------------------',
247 8
            '',
248 8
        ]);
249 10
    }
250
251
    /**
252
     * @param string|string[] $message
253
     * @param OutputInterface $output
254
     */
255 10
    private function comment($message, OutputInterface $output)
256
    {
257 10
        $output->writeln(' // '.$message);
258 10
        $output->writeln('');
259 10
    }
260
261
    /**
262
     * @param string          $message
263
     * @param OutputInterface $output
264
     */
265 10
    private function success($message, OutputInterface $output)
266
    {
267 10
        $this->block('[OK] - '.$message, $output, 'green', 'black');
268 10
    }
269
270
    /**
271
     * @param string          $message
272
     * @param OutputInterface $output
273
     * @param string          $background
274
     * @param string          $font
275
     */
276 10
    private function block($message, OutputInterface $output, $background = null, $font = null)
277
    {
278 10
        $options = [];
279
280 10
        if ($background !== null) {
281 10
            $options[] = 'bg='.$background;
282 8
        }
283
284 10
        if ($font !== null) {
285 10
            $options[] = 'fg='.$font;
286 8
        }
287
288 10
        $pattern = ' %s ';
289
290 10
        if (!empty($options)) {
291 10
            $pattern = '<'.implode(';', $options).'>'.$pattern.'</>';
292 8
        }
293
294 10
        $output->writeln($block = sprintf($pattern, str_repeat(' ', strlen($message))));
295 10
        $output->writeln(sprintf($pattern, $message));
296 10
        $output->writeln($block);
297 10
    }
298
299
    /**
300
     * @param string|string[] $question
301
     * @param string[]        $choices
302
     * @param string          $default
303
     * @param InputInterface  $input
304
     * @param OutputInterface $output
305
     *
306
     * @return string|null
307
     */
308 1
    private function choice($question, array $choices, $default, InputInterface $input, OutputInterface $output)
309
    {
310 1
        $helper = new QuestionHelper();
311
312 1
        $result = $helper->ask($input, $output, new ChoiceQuestion(
313 1
            $question,
0 ignored issues
show
Bug introduced by
It seems like $question defined by parameter $question on line 308 can also be of type array<integer,string>; however, Symfony\Component\Consol...Question::__construct() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
314 1
            $choices,
315 1
            $choices[$default]
316 1
        ));
317
318 1
        $output->writeln('');
319
320 1
        return $result;
321
    }
322
323
    /**
324
     * @param OutputInterface $output
325
     *
326
     * @return ProgressBar|ProgressHelper
327
     */
328 10
    private function createProgressBar(OutputInterface $output)
329
    {
330 10
        return class_exists(ProgressBar::class) ? new ProgressBar($output) : new ProgressHelper();
331
    }
332
333
    /**
334
     * @param ProgressBar|ProgressHelper $progress
335
     * @param OutputInterface            $output
336
     * @param int|null                   $max
337
     */
338 10
    private function startProgressBar($progress, OutputInterface $output, $max = null)
339
    {
340 10
        class_exists(ProgressBar::class) ? $progress->start($max) : $progress->start($output, $max);
0 ignored issues
show
Documentation introduced by
$output is of type object<Symfony\Component...Output\OutputInterface>, but the function expects a integer|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to ProgressBar::start() has too many arguments starting with $max.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
341 10
    }
342
343
    /**
344
     * @param ProgressBar|ProgressHelper $progress
345
     * @param int                        $current
346
     */
347 10
    private function advanceProgressBar($progress, $current)
348
    {
349 10
        class_exists(ProgressBar::class) ? $progress->setProgress($current) : $progress->setCurrent($current);
0 ignored issues
show
Bug introduced by
The method setCurrent() does not seem to exist on object<Symfony\Component...ole\Helper\ProgressBar>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
350 10
    }
351
352
    /**
353
     * @param ProgressBar|ProgressHelper $progress
354
     * @param OutputInterface            $output
355
     */
356 10
    private function finishProgressBar($progress, OutputInterface $output)
357
    {
358 10
        $progress->finish();
359 10
        $output->writeln(['', '']);
360 10
    }
361
}
362