Completed
Push — composer-script ( c6b262...a92af3 )
by Eric
64:43
created

CKEditorInstallerCommand::info()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
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 13
    public function __construct(CKEditorInstaller $installer = null)
39
    {
40 13
        parent::__construct();
41
42 13
        $this->installer = $installer ?: new CKEditorInstaller();
43 13
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 13
    protected function configure()
49
    {
50 12
        $this
51 13
            ->setName('ckeditor:install')
52 13
            ->setDescription('Install CKEditor')
53 13
            ->addArgument('path', InputArgument::OPTIONAL, 'Where to install CKEditor')
54 13
            ->addOption('release', null, InputOption::VALUE_OPTIONAL, 'CKEditor release (basic, standard or full)')
55 13
            ->addOption('tag', null, InputOption::VALUE_OPTIONAL, 'CKEditor tag (x.y.z or latest)')
56 13
            ->addOption(
57 13
                'clear',
58 13
                null,
59 13
                InputOption::VALUE_OPTIONAL,
60 1
                'How to clear previous CKEditor installation (drop, keep or skip)'
61 12
            )
62 13
            ->addOption(
63 13
                'exclude',
64 13
                null,
65 13
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
66 1
                'Path to exclude when extracting CKEditor'
67 12
            )
68 13
            ->setHelp(<<<'EOF'
69
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=skip</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 12
            );
97 13
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 13
    protected function execute(InputInterface $input, OutputInterface $output)
103
    {
104 13
        $this->title($output);
105
106 13
        $success = $this->installer->install($this->createOptions($input, $output));
107
108 13
        if ($success) {
109 13
            $this->success('CKEditor has been successfully installed...', $output);
110 12
        } else {
111
            $this->info('CKEditor installation has been skipped...', $output);
112
        }
113 13
    }
114
115
    /**
116
     * @param InputInterface  $input
117
     * @param OutputInterface $output
118
     *
119
     * @return mixed[]
120
     */
121 13
    private function createOptions(InputInterface $input, OutputInterface $output)
122
    {
123 13
        $options = ['notifier' => $this->createNotifier($input, $output)];
124
125 13
        if ($input->hasArgument('path')) {
126 13
            $options['path'] = $input->getArgument('path');
127 12
        }
128
129 13
        if ($input->hasOption('release')) {
130 13
            $options['release'] = $input->getOption('release');
131 12
        }
132
133 13
        if ($input->hasOption('tag')) {
134 13
            $options['version'] = $input->getOption('tag');
135 12
        }
136
137 13
        if ($input->hasOption('exclude')) {
138 13
            $options['excludes'] = $input->getOption('exclude');
139 12
        }
140
141 13
        if ($input->hasOption('clear')) {
142 13
            $options['clear'] = $input->getOption('clear');
143 12
        }
144
145 13
        return $options;
146
    }
147
148
    /**
149
     * @param InputInterface  $input
150
     * @param OutputInterface $output
151
     *
152
     * @return \Closure
153
     */
154 13
    private function createNotifier(InputInterface $input, OutputInterface $output)
155
    {
156 13
        $clear = $this->createProgressBar($output);
157 13
        $download = $this->createProgressBar($output);
158 13
        $extract = $this->createProgressBar($output);
159
160 13
        return function ($type, $data) use ($input, $output, $clear, $download, $extract) {
161
            switch ($type) {
162 13
                case CKEditorInstaller::NOTIFY_CLEAR:
163 3
                    $result = $this->choice(
164
                        [
165 3
                            sprintf('CKEditor is already installed in "%s"...', $data),
166 3
                            '',
167 3
                            'What do you want to do?',
168 3
                        ],
169
                        $choices = [
170 3
                            CKEditorInstaller::CLEAR_DROP => 'Drop the directory & reinstall CKEditor',
171 3
                            CKEditorInstaller::CLEAR_KEEP => 'Keep the directory & reinstall CKEditor by overriding files',
172 3
                            CKEditorInstaller::CLEAR_SKIP => 'Skip installation',
173 3
                        ],
174 3
                        CKEditorInstaller::CLEAR_DROP,
175 3
                        $input,
176
                        $output
177 3
                    );
178
179 3
                    if (($key = array_search($result, $choices, true)) !== false) {
180
                        $result = $key;
181
                    }
182
183 3
                    if ($result === CKEditorInstaller::CLEAR_DROP) {
184 3
                        $this->comment(sprintf('Dropping CKEditor from "%s"', $data), $output);
185 3
                    }
186
187 3
                    return $result;
188
189 13
                case CKEditorInstaller::NOTIFY_CLEAR_ARCHIVE:
190 13
                    $this->comment(sprintf('Dropping CKEditor ZIP archive "%s"', $data), $output);
191 13
                    break;
192
193 13
                case CKEditorInstaller::NOTIFY_CLEAR_COMPLETE:
194 3
                    $this->finishProgressBar($clear, $output);
195 3
                    break;
196
197 13
                case CKEditorInstaller::NOTIFY_CLEAR_PROGRESS:
198 3
                    $clear->advance();
199 3
                    break;
200
201 13
                case CKEditorInstaller::NOTIFY_CLEAR_SIZE:
202 3
                    $this->startProgressBar($clear, $output, $data);
203 3
                    break;
204
205 13
                case CKEditorInstaller::NOTIFY_DOWNLOAD:
206 13
                    $this->comment(sprintf('Downloading CKEditor ZIP archive from "%s"', $data), $output);
207 13
                    break;
208
209 13
                case CKEditorInstaller::NOTIFY_DOWNLOAD_COMPLETE:
210 13
                    $this->finishProgressBar($download, $output);
211 13
                    break;
212
213 13
                case CKEditorInstaller::NOTIFY_DOWNLOAD_PROGRESS:
214 13
                    $this->advanceProgressBar($download, $data);
215 13
                    break;
216
217 13
                case CKEditorInstaller::NOTIFY_DOWNLOAD_SIZE:
218 13
                    $this->startProgressBar($download, $output, $data);
219 13
                    break;
220
221 13
                case CKEditorInstaller::NOTIFY_EXTRACT:
222 13
                    $this->comment(sprintf('Extracting CKEditor ZIP archive to "%s"', $data), $output);
223 13
                    break;
224
225 13
                case CKEditorInstaller::NOTIFY_EXTRACT_COMPLETE:
226 13
                    $this->finishProgressBar($extract, $output);
227 13
                    break;
228
229 13
                case CKEditorInstaller::NOTIFY_EXTRACT_PROGRESS:
230 13
                    $extract->advance();
231 13
                    break;
232
233 13
                case CKEditorInstaller::NOTIFY_EXTRACT_SIZE:
234 13
                    $this->startProgressBar($extract, $output, $data);
235 13
                    break;
236
            }
237 13
        };
238
    }
239
240
    /**
241
     * @param OutputInterface $output
242
     */
243 13
    private function title(OutputInterface $output)
244
    {
245 13
        $output->writeln([
246 13
            '----------------------',
247 12
            '| CKEditor Installer |',
248 12
            '----------------------',
249 12
            '',
250 12
        ]);
251 13
    }
252
253
    /**
254
     * @param string|string[] $message
255
     * @param OutputInterface $output
256
     */
257 13
    private function comment($message, OutputInterface $output)
258
    {
259 13
        $output->writeln(' // '.$message);
260 13
        $output->writeln('');
261 13
    }
262
263
    /**
264
     * @param string          $message
265
     * @param OutputInterface $output
266
     */
267 13
    private function success($message, OutputInterface $output)
268
    {
269 13
        $this->block('[OK] - '.$message, $output, 'green', 'black');
270 13
    }
271
272
    /**
273
     * @param string          $message
274
     * @param OutputInterface $output
275
     */
276
    private function info($message, OutputInterface $output)
277
    {
278
        $this->block('[INFO] - '.$message, $output, 'orange', 'black');
279
    }
280
281
    /**
282
     * @param string          $message
283
     * @param OutputInterface $output
284
     * @param string          $background
285
     * @param string          $font
286
     */
287 13
    private function block($message, OutputInterface $output, $background = null, $font = null)
288
    {
289 13
        $options = [];
290
291 13
        if ($background !== null) {
292 13
            $options[] = 'bg='.$background;
293 12
        }
294
295 13
        if ($font !== null) {
296 13
            $options[] = 'fg='.$font;
297 12
        }
298
299 13
        $pattern = ' %s ';
300
301 13
        if (!empty($options)) {
302 13
            $pattern = '<'.implode(';', $options).'>'.$pattern.'</>';
303 12
        }
304
305 13
        $output->writeln($block = sprintf($pattern, str_repeat(' ', strlen($message))));
306 13
        $output->writeln(sprintf($pattern, $message));
307 13
        $output->writeln($block);
308 13
    }
309
310
    /**
311
     * @param string|string[] $question
312
     * @param string[]        $choices
313
     * @param string          $default
314
     * @param InputInterface  $input
315
     * @param OutputInterface $output
316
     *
317
     * @return string|null
318
     */
319 3
    private function choice($question, array $choices, $default, InputInterface $input, OutputInterface $output)
320
    {
321 3
        $helper = new QuestionHelper();
322
323 3
        $result = $helper->ask($input, $output, new ChoiceQuestion(
324 3
            $question,
0 ignored issues
show
Bug introduced by
It seems like $question defined by parameter $question on line 319 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...
325 3
            $choices,
326 3
            $choices[$default]
327 3
        ));
328
329 3
        $output->writeln('');
330
331 3
        return $result;
332
    }
333
334
    /**
335
     * @param OutputInterface $output
336
     *
337
     * @return ProgressBar|ProgressHelper
338
     */
339 13
    private function createProgressBar(OutputInterface $output)
340
    {
341 13
        return class_exists(ProgressBar::class) ? new ProgressBar($output) : new ProgressHelper();
342
    }
343
344
    /**
345
     * @param ProgressBar|ProgressHelper $progress
346
     * @param OutputInterface            $output
347
     * @param int|null                   $max
348
     */
349 13
    private function startProgressBar($progress, OutputInterface $output, $max = null)
350
    {
351 13
        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...
352 13
    }
353
354
    /**
355
     * @param ProgressBar|ProgressHelper $progress
356
     * @param int                        $current
357
     */
358 13
    private function advanceProgressBar($progress, $current)
359
    {
360 13
        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...
361 13
    }
362
363
    /**
364
     * @param ProgressBar|ProgressHelper $progress
365
     * @param OutputInterface            $output
366
     */
367 13
    private function finishProgressBar($progress, OutputInterface $output)
368
    {
369 13
        $progress->finish();
370 13
        $output->writeln(['', '']);
371 13
    }
372
}
373