Application::__construct()   B
last analyzed

Complexity

Conditions 6
Paths 8

Size

Total Lines 49
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 49
rs 8.5906
c 0
b 0
f 0
cc 6
eloc 31
nc 8
nop 1
1
<?php
2
3
/**
4
 * This file is part of tenside/core-bundle.
5
 *
6
 * (c) Christian Schiffler <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * This project is provided in good faith and hope to be usable by anyone.
12
 *
13
 * @package    tenside/core-bundle
14
 * @author     Christian Schiffler <[email protected]>
15
 * @copyright  2015 Christian Schiffler <[email protected]>
16
 * @license    https://github.com/tenside/core-bundle/blob/master/LICENSE MIT
17
 * @link       https://github.com/tenside/core-bundle
18
 * @filesource
19
 */
20
21
namespace Tenside\CoreBundle\Console;
22
23
use Composer\Command as ComposerCommand;
24
use Composer\Command\ScriptAliasCommand;
25
use Composer\Factory as ComposerFactory;
26
use Composer\IO\ConsoleIO;
27
use Composer\IO\IOInterface;
28
use Psr\Log\LoggerInterface;
29
use Symfony\Component\Console\Application as SymfonyApplication;
30
use Symfony\Component\Console\Command\Command;
31
use Symfony\Component\Console\Formatter\OutputFormatter;
32
use Symfony\Component\Console\Input\InputInterface;
33
use Symfony\Component\Console\Input\InputOption;
34
use Symfony\Component\Console\Output\BufferedOutput;
35
use Symfony\Component\Console\Output\ConsoleOutput;
36
use Symfony\Component\Console\Output\OutputInterface;
37
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
38
use Symfony\Component\HttpKernel\Bundle\Bundle;
39
use Symfony\Component\HttpKernel\KernelInterface;
40
use Tenside\Core\Composer\ComposerJson;
41
use Tenside\Core\Tenside;
42
use Tenside\Core\Util\RuntimeHelper;
43
use Tenside\CoreBundle\Command\SelfUpdateCommand;
44
45
/**
46
 * The console application that handles the commands.
47
 */
48
class Application extends SymfonyApplication
49
{
50
    /**
51
     * The kernel in use.
52
     *
53
     * @var KernelInterface
54
     */
55
    private $kernel;
56
57
    /**
58
     * Flag if commands have been registered.
59
     *
60
     * @var bool
61
     */
62
    private $commandsRegistered = false;
63
64
    /**
65
     * The io interface in use.
66
     *
67
     * @var IOInterface
68
     */
69
    private $inputOutput;
70
71
    /**
72
     * Out logo, will get concatenated with the composer logo.
73
     *
74
     * @var string
75
     */
76
    private static $logo = '
77
 _____               _     _
78
/__   \___ _ __  ___(_) __| | ___
79
  / /\/ _ \ \'_ \/ __| |/ _` |/ _ \
80
 / / |  __/ | | \__ \ | (_| |  __/
81
 \/   \___|_| |_|___/_|\__,_|\___|
82
';
83
84
    /**
85
     * Constructor.
86
     *
87
     * @param KernelInterface $kernel A KernelInterface instance.
88
     */
89
    public function __construct(KernelInterface $kernel)
90
    {
91
        if (function_exists('ini_set') && extension_loaded('xdebug')) {
92
            ini_set('xdebug.show_exception_trace', false);
93
            ini_set('xdebug.scream', false);
94
        }
95
96
        $this->ensurePath();
97
98
        if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) {
99
            date_default_timezone_set(date_default_timezone_get());
100
        }
101
102
        $this->kernel = $kernel;
103
104
        parent::__construct(Tenside::DISTRIBUTION, Tenside::VERSION);
105
106
        $definition = $this->getDefinition();
107
        $definition->addOption(new InputOption('--shell', null, InputOption::VALUE_NONE, 'Launch the shell.'));
108
        $definition->addOption(
109
            new InputOption(
110
                '--process-isolation',
111
                null,
112
                InputOption::VALUE_NONE,
113
                'Launch commands from shell as a separate process.'
114
            )
115
        );
116
117
        if (!\Phar::running()) {
118
            $definition->addOption(
119
                new InputOption(
120
                    '--env',
121
                    '-e',
122
                    InputOption::VALUE_REQUIRED,
123
                    'The Environment name.',
124
                    $kernel->getEnvironment()
125
                )
126
            );
127
128
            $definition->addOption(
129
                new InputOption(
130
                    '--no-debug',
131
                    null,
132
                    InputOption::VALUE_NONE,
133
                    'Switches off debug mode.'
134
                )
135
            );
136
        }
137
    }
138
139
    /**
140
     * Gets the Kernel associated with this Console.
141
     *
142
     * @return KernelInterface A KernelInterface instance
143
     */
144
    public function getKernel()
145
    {
146
        return $this->kernel;
147
    }
148
149
    /**
150
     * {@inheritDoc}
151
     */
152
    public function doRun(InputInterface $input, OutputInterface $output)
153
    {
154
        $this->kernel->boot();
155
156
        if (!$this->commandsRegistered) {
157
            $this->registerCommands($output);
158
159
            $this->commandsRegistered = true;
160
        }
161
162
        $container = $this->kernel->getContainer();
163
164
        foreach ($this->all() as $command) {
165
            if ($command instanceof ContainerAwareInterface) {
166
                $command->setContainer($container);
167
            }
168
        }
169
170
        $this->setDispatcher($container->get('event_dispatcher'));
171
172
        RuntimeHelper::setupHome($container->get('tenside.home')->homeDir());
173
174
        $this->inputOutput = new ConsoleIO($input, $output, $this->getHelperSet());
175
176
177
        $this->isUpdateNeeded($input, $output);
178
179
        return parent::doRun($input, $output);
180
    }
181
182
    /**
183
     * {@inheritDoc}
184
     */
185
    protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
186
    {
187
        if ($command instanceof \Composer\Command\BaseCommand) {
188
            if (!$command instanceof SelfUpdateCommand) {
189
                $command->setComposer(ComposerFactory::create($this->inputOutput));
190
            }
191
            $command->setIO(new ConsoleIO($input, $output, $this->getHelperSet()));
192
        }
193
194
        return parent::doRunCommand($command, $input, $output);
195
    }
196
197
    /**
198
     * Register all commands from the container and bundles in the application.
199
     *
200
     * @param OutputInterface $output The output handler to use.
201
     *
202
     * @return void
203
     */
204
    protected function registerCommands(OutputInterface $output)
205
    {
206
        $container = $this->kernel->getContainer();
207
208
        foreach ($this->kernel->getBundles() as $bundle) {
209
            if ($bundle instanceof Bundle) {
210
                $bundle->registerCommands($this);
211
            }
212
        }
213
214
        if ($container->hasParameter('console.command.ids')) {
215
            foreach ($container->getParameter('console.command.ids') as $id) {
216
                $this->add($container->get($id));
217
            }
218
        }
219
220
        $this->addComposerCommands();
221
222
        /** @var ComposerJson $file */
223
        $file = $container->get('tenside.composer_json');
224
225
        // Add non-standard scripts as own commands - keep this last to ensure we do not override internal commands.
226
        if ($file->has('scripts')) {
227
            foreach (array_keys($file->get('scripts')) as $script) {
228
                if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
229
                    if ($this->has($script)) {
230
                        $output->writeln(
231
                            sprintf(
232
                                '<warning>' .
233
                                'A script named %s would override a native function and has been skipped' .
234
                                '</warning>',
235
                                $script
236
                            )
237
                        );
238
                        continue;
239
                    }
240
                    $this->add(new ScriptAliasCommand($script));
241
                }
242
            }
243
        }
244
    }
245
246
    /**
247
     * Add the composer base commands.
248
     *
249
     * @return void
250
     */
251
    protected function addComposerCommands()
252
    {
253
        $this->add(new ComposerCommand\AboutCommand());
254
        $this->add(new ComposerCommand\ConfigCommand());
255
        $this->add(new ComposerCommand\DependsCommand());
256
        $this->add(new ComposerCommand\InitCommand());
257
        $this->add(new ComposerCommand\InstallCommand());
258
        $this->add(new ComposerCommand\CreateProjectCommand());
259
        $this->add(new ComposerCommand\UpdateCommand());
260
        $this->add(new ComposerCommand\SearchCommand());
261
        $this->add(new ComposerCommand\ValidateCommand());
262
        $this->add(new ComposerCommand\ShowCommand());
263
        $this->add(new ComposerCommand\SuggestsCommand());
264
        $this->add(new ComposerCommand\RequireCommand());
265
        $this->add(new ComposerCommand\DumpAutoloadCommand());
266
        $this->add(new ComposerCommand\StatusCommand());
267
        $this->add(new ComposerCommand\ArchiveCommand());
268
        $this->add(new ComposerCommand\DiagnoseCommand());
269
        $this->add(new ComposerCommand\RunScriptCommand());
270
        $this->add(new ComposerCommand\LicensesCommand());
271
        $this->add(new ComposerCommand\GlobalCommand());
272
        $this->add(new ComposerCommand\ClearCacheCommand());
273
        $this->add(new ComposerCommand\RemoveCommand());
274
        $this->add(new ComposerCommand\HomeCommand());
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280
    public function run(InputInterface $input = null, OutputInterface $output = null)
281
    {
282
        if (null === $output) {
283
            $styles    = ComposerFactory::createAdditionalStyles();
284
            $formatter = new OutputFormatter(null, $styles);
285
            $output    = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, null, $formatter);
286
        }
287
288
        return parent::run($input, $output);
289
    }
290
291
    /**
292
     * {@inheritDoc}
293
     */
294
    public function renderException(\Exception $exception, OutputInterface $output)
295
    {
296
        // Preserve plain echoing to console...
297
        parent::renderException($exception, $output);
298
299
        // ... but pass to logger as well.
300
        if ($container = $this->kernel->getContainer()) {
301
            /** @var LoggerInterface $logger */
302
            $logger = $container->get('logger');
303
304
            // We want stack traces, therefore be very verbose.
305
            $buffer = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE);
306
            parent::renderException($exception, $buffer);
307
            $logger->error('--------------------------------------------------------');
308
            foreach (explode("\n", str_replace("\n\n", "\n", $buffer->fetch())) as $line) {
309
                if ('' !== $line) {
310
                    $logger->error($line);
311
                }
312
            }
313
            $logger->error('--------------------------------------------------------');
314
        }
315
    }
316
317
    /**
318
     * Return the help string.
319
     *
320
     * @return string
321
     */
322
    public function getHelp()
323
    {
324
        return self::$logo . parent::getHelp();
325
    }
326
327
    /**
328
     * {@inheritDoc}
329
     */
330
    public function getLongVersion()
331
    {
332
        return parent::getLongVersion() . ' ' . Tenside::RELEASE_DATE;
333
    }
334
335
    /**
336
     * Check if updating is needed.
337
     *
338
     * @param InputInterface  $input  The input interface.
339
     * @param OutputInterface $output The output interface.
340
     *
341
     * @return bool
342
     *
343
     * @SuppressWarnings(PHPMD.Superglobals)
344
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
345
     */
346
    protected function isUpdateNeeded(InputInterface $input, OutputInterface $output)
347
    {
348
        if ('@warning_time@' !== Tenside::WARNING_TIME) {
349
            $commandName = '';
350
            if ($name = $this->getCommandName($input)) {
351
                try {
352
                    $commandName = $this->find($name)->getName();
353
                } catch (\InvalidArgumentException $e) {
354
                    // Swallow the exception.
355
                }
356
            }
357
            if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
358
                if (time() > Tenside::WARNING_TIME) {
359
                    $output->writeln(
360
                        sprintf(
361
                            '<warning>Warning: This development build is over 30 days old. ' .
362
                            'It is recommended to update it by running "%s self-update" to get the latest version.' .
363
                            '</warning>',
364
                            $_SERVER['PHP_SELF']
365
                        )
366
                    );
367
368
                    return true;
369
                }
370
            }
371
        }
372
373
        return false;
374
    }
375
376
    /**
377
     * Ensure we have a PATH environment variable.
378
     *
379
     * @return void
380
     */
381
    protected function ensurePath()
382
    {
383
        // "git" binary not found when no PATH environment is present.
384
        // https://github.com/contao-community-alliance/composer-client/issues/54
385
        if (!getenv('PATH')) {
386
            if (defined('PHP_WINDOWS_VERSION_BUILD')) {
387
                putenv('PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem');
388
            } else {
389
                putenv('PATH=/opt/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin');
390
            }
391
        }
392
    }
393
}
394