Completed
Push — master ( c37ad9...8221f3 )
by Marko
129:14 queued 114:10
created

CKEditorInstallerCommand::info()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
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 FOS\CKEditorBundle\Command;
13
14
use FOS\CKEditorBundle\Installer\CKEditorInstaller;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Helper\ProgressBar;
17
use Symfony\Component\Console\Helper\ProgressHelper;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Helper\ProgressHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
    public function __construct(CKEditorInstaller $installer = null)
39
    {
40
        parent::__construct();
41
42
        $this->installer = $installer ?: new CKEditorInstaller();
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    protected function configure()
49
    {
50
        $this
51
            ->setName('ckeditor:install')
52
            ->setDescription('Install CKEditor')
53
            ->addArgument('path', InputArgument::OPTIONAL, 'Where to install CKEditor')
54
            ->addOption(
55
                'release',
56
                null,
57
                InputOption::VALUE_OPTIONAL,
58
                'CKEditor release (basic, standard or full)'
59
            )
60
            ->addOption('tag', null, InputOption::VALUE_OPTIONAL, 'CKEditor tag (x.y.z or latest)')
61
            ->addOption(
62
                'clear',
63
                null,
64
                InputOption::VALUE_OPTIONAL,
65
                'How to clear previous CKEditor installation (drop, keep or skip)'
66
            )
67
            ->addOption(
68
                'exclude',
69
                null,
70
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
71
                'Path to exclude when extracting CKEditor'
72
            )
73
            ->setHelp(
74
                <<<'EOF'
75
The <info>%command.name%</info> command install CKEditor in your application:
76
77
  <info>php %command.full_name%</info>
78
  
79
You can install it at a specific path (absolute):
80
81
  <info>php %command.full_name% path</info>
82
  
83
You can install a specific release (basic, standard or full):
84
85
  <info>php %command.full_name% --release=full</info>
86
  
87
You can install a specific version:
88
89
  <info>php %command.full_name% --tag=4.7.0</info>
90
91
If there is a previous CKEditor installation detected, 
92
you can control how it should be handled in non-interactive mode:
93
94
  <info>php %command.full_name% --clear=drop</info>
95
  <info>php %command.full_name% --clear=keep</info>
96
  <info>php %command.full_name% --clear=skip</info>
97
  
98
You can exclude path(s) when extracting CKEditor:
99
100
  <info>php %command.full_name% --exclude=samples --exclude=adapters</info>
101
EOF
102
            );
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    protected function execute(InputInterface $input, OutputInterface $output)
109
    {
110
        $this->title($output);
111
112
        $success = $this->installer->install($this->createOptions($input, $output));
113
114
        if ($success) {
0 ignored issues
show
introduced by
The condition $success is always true.
Loading history...
115
            $this->success('CKEditor has been successfully installed...', $output);
116
        } else {
117
            $this->info('CKEditor installation has been skipped...', $output);
118
        }
119
    }
120
121
    /**
122
     * @param InputInterface  $input
123
     * @param OutputInterface $output
124
     *
125
     * @return mixed[]
126
     */
127
    private function createOptions(InputInterface $input, OutputInterface $output)
128
    {
129
        $options = ['notifier' => $this->createNotifier($input, $output)];
130
131
        if ($input->hasArgument('path')) {
132
            $options['path'] = $input->getArgument('path');
133
        }
134
135
        if ($input->hasOption('release')) {
136
            $options['release'] = $input->getOption('release');
137
        }
138
139
        if ($input->hasOption('tag')) {
140
            $options['version'] = $input->getOption('tag');
141
        }
142
143
        if ($input->hasOption('exclude')) {
144
            $options['excludes'] = $input->getOption('exclude');
145
        }
146
147
        if ($input->hasOption('clear')) {
148
            $options['clear'] = $input->getOption('clear');
149
        }
150
151
        return array_filter($options);
152
    }
153
154
    /**
155
     * @param InputInterface  $input
156
     * @param OutputInterface $output
157
     *
158
     * @return \Closure
159
     */
160
    private function createNotifier(InputInterface $input, OutputInterface $output)
161
    {
162
        $clear = $this->createProgressBar($output);
163
        $download = $this->createProgressBar($output);
164
        $extract = $this->createProgressBar($output);
165
166
        return function ($type, $data) use ($input, $output, $clear, $download, $extract) {
167
            switch ($type) {
168
                case CKEditorInstaller::NOTIFY_CLEAR:
169
                    $result = $this->choice(
170
                        [
171
                            sprintf('CKEditor is already installed in "%s"...', $data),
172
                            '',
173
                            'What do you want to do?',
174
                        ],
175
                        $choices = [
176
                            CKEditorInstaller::CLEAR_DROP => 'Drop the directory & reinstall CKEditor',
177
                            CKEditorInstaller::CLEAR_KEEP => 'Keep the directory & reinstall CKEditor by overriding files',
178
                            CKEditorInstaller::CLEAR_SKIP => 'Skip installation',
179
                        ],
180
                        CKEditorInstaller::CLEAR_DROP,
181
                        $input,
182
                        $output
183
                    );
184
185
                    if (($key = array_search($result, $choices, true)) !== false) {
186
                        $result = $key;
187
                    }
188
189
                    if ($result === CKEditorInstaller::CLEAR_DROP) {
190
                        $this->comment(sprintf('Dropping CKEditor from "%s"', $data), $output);
191
                    }
192
193
                    return $result;
194
195
                case CKEditorInstaller::NOTIFY_CLEAR_ARCHIVE:
196
                    $this->comment(sprintf('Dropping CKEditor ZIP archive "%s"', $data), $output);
197
                    break;
198
199
                case CKEditorInstaller::NOTIFY_CLEAR_COMPLETE:
200
                    $this->finishProgressBar($clear, $output);
201
                    break;
202
203
                case CKEditorInstaller::NOTIFY_CLEAR_PROGRESS:
204
                    $clear->advance();
205
                    break;
206
207
                case CKEditorInstaller::NOTIFY_CLEAR_SIZE:
208
                    $this->startProgressBar($clear, $output, $data);
209
                    break;
210
211
                case CKEditorInstaller::NOTIFY_DOWNLOAD:
212
                    $this->comment(sprintf('Downloading CKEditor ZIP archive from "%s"', $data), $output);
213
                    break;
214
215
                case CKEditorInstaller::NOTIFY_DOWNLOAD_COMPLETE:
216
                    $this->finishProgressBar($download, $output);
217
                    break;
218
219
                case CKEditorInstaller::NOTIFY_DOWNLOAD_PROGRESS:
220
                    $this->advanceProgressBar($download, $data);
221
                    break;
222
223
                case CKEditorInstaller::NOTIFY_DOWNLOAD_SIZE:
224
                    $this->startProgressBar($download, $output, $data);
225
                    break;
226
227
                case CKEditorInstaller::NOTIFY_EXTRACT:
228
                    $this->comment(sprintf('Extracting CKEditor ZIP archive to "%s"', $data), $output);
229
                    break;
230
231
                case CKEditorInstaller::NOTIFY_EXTRACT_COMPLETE:
232
                    $this->finishProgressBar($extract, $output);
233
                    break;
234
235
                case CKEditorInstaller::NOTIFY_EXTRACT_PROGRESS:
236
                    $extract->advance();
237
                    break;
238
239
                case CKEditorInstaller::NOTIFY_EXTRACT_SIZE:
240
                    $this->startProgressBar($extract, $output, $data);
241
                    break;
242
            }
243
        };
244
    }
245
246
    /**
247
     * @param OutputInterface $output
248
     */
249
    private function title(OutputInterface $output)
250
    {
251
        $output->writeln(
252
            [
253
                '----------------------',
254
                '| CKEditor Installer |',
255
                '----------------------',
256
                '',
257
            ]
258
        );
259
    }
260
261
    /**
262
     * @param string|string[] $message
263
     * @param OutputInterface $output
264
     */
265
    private function comment($message, OutputInterface $output)
266
    {
267
        $output->writeln(' // '.$message);
0 ignored issues
show
Bug introduced by
Are you sure $message of type string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
        $output->writeln(' // './** @scrutinizer ignore-type */ $message);
Loading history...
268
        $output->writeln('');
269
    }
270
271
    /**
272
     * @param string          $message
273
     * @param OutputInterface $output
274
     */
275
    private function success($message, OutputInterface $output)
276
    {
277
        $this->block('[OK] - '.$message, $output, 'green', 'black');
278
    }
279
280
    /**
281
     * @param string          $message
282
     * @param OutputInterface $output
283
     */
284
    private function info($message, OutputInterface $output)
285
    {
286
        $this->block('[INFO] - '.$message, $output, 'yellow', 'black');
287
    }
288
289
    /**
290
     * @param string          $message
291
     * @param OutputInterface $output
292
     * @param string          $background
293
     * @param string          $font
294
     */
295
    private function block($message, OutputInterface $output, $background = null, $font = null)
296
    {
297
        $options = [];
298
299
        if ($background !== null) {
300
            $options[] = 'bg='.$background;
301
        }
302
303
        if ($font !== null) {
304
            $options[] = 'fg='.$font;
305
        }
306
307
        $pattern = ' %s ';
308
309
        if (!empty($options)) {
310
            $pattern = '<'.implode(';', $options).'>'.$pattern.'</>';
311
        }
312
313
        $output->writeln($block = sprintf($pattern, str_repeat(' ', strlen($message))));
314
        $output->writeln(sprintf($pattern, $message));
315
        $output->writeln($block);
316
    }
317
318
    /**
319
     * @param string|string[] $question
320
     * @param string[]        $choices
321
     * @param string          $default
322
     * @param InputInterface  $input
323
     * @param OutputInterface $output
324
     *
325
     * @return string|null
326
     */
327
    private function choice($question, array $choices, $default, InputInterface $input, OutputInterface $output)
328
    {
329
        $helper = new QuestionHelper();
330
331
        if (is_array($question)) {
332
            $question = implode("\n", $question);
333
        }
334
335
        $result = $helper->ask(
336
            $input,
337
            $output,
338
            new ChoiceQuestion($question, $choices, $choices[$default])
339
        );
340
341
        $output->writeln('');
342
343
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type boolean which is incompatible with the documented return type null|string.
Loading history...
344
    }
345
346
    /**
347
     * @param OutputInterface $output
348
     *
349
     * @return ProgressBar|ProgressHelper
350
     */
351
    private function createProgressBar(OutputInterface $output)
352
    {
353
        return class_exists(ProgressBar::class) ? new ProgressBar($output) : new ProgressHelper();
354
    }
355
356
    /**
357
     * @param ProgressBar|ProgressHelper $progress
358
     * @param OutputInterface            $output
359
     * @param int|null                   $max
360
     */
361
    private function startProgressBar($progress, OutputInterface $output, $max = null)
362
    {
363
        class_exists(ProgressBar::class) ? $progress->start($max) : $progress->start($output, $max);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Component\Consol...er\ProgressBar::start() has too many arguments starting with $max. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

363
        class_exists(ProgressBar::class) ? $progress->start($max) : $progress->/** @scrutinizer ignore-call */ start($output, $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. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
$output of type Symfony\Component\Console\Output\OutputInterface is incompatible with the type null|integer expected by parameter $max of Symfony\Component\Consol...er\ProgressBar::start(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

363
        class_exists(ProgressBar::class) ? $progress->start($max) : $progress->start(/** @scrutinizer ignore-type */ $output, $max);
Loading history...
364
    }
365
366
    /**
367
     * @param ProgressBar|ProgressHelper $progress
368
     * @param int                        $current
369
     */
370
    private function advanceProgressBar($progress, $current)
371
    {
372
        class_exists(ProgressBar::class) ? $progress->setProgress($current) : $progress->setCurrent($current);
0 ignored issues
show
Bug introduced by
The method setCurrent() does not exist on Symfony\Component\Console\Helper\ProgressBar. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

372
        class_exists(ProgressBar::class) ? $progress->setProgress($current) : $progress->/** @scrutinizer ignore-call */ setCurrent($current);

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...
373
    }
374
375
    /**
376
     * @param ProgressBar|ProgressHelper $progress
377
     * @param OutputInterface            $output
378
     */
379
    private function finishProgressBar($progress, OutputInterface $output)
380
    {
381
        $progress->finish();
382
        $output->writeln(['', '']);
383
    }
384
}
385