Passed
Push — master ( 96fbaa...4c2459 )
by Théo
02:40
created

Compile::logCommandResourcesUsage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Console\Command;
16
17
use Amp\MultiReasonException;
18
use Assert\Assertion;
19
use DateTimeImmutable;
20
use DateTimeZone;
21
use KevinGH\Box\Box;
22
use KevinGH\Box\Compactor;
23
use KevinGH\Box\Configuration;
24
use KevinGH\Box\Console\Logger\BuildLogger;
25
use KevinGH\Box\MapFile;
26
use KevinGH\Box\PhpSettingsHandler;
27
use KevinGH\Box\RequirementChecker\RequirementsDumper;
28
use KevinGH\Box\StubGenerator;
29
use RuntimeException;
30
use stdClass;
31
use Symfony\Component\Console\Helper\QuestionHelper;
32
use Symfony\Component\Console\Input\InputInterface;
33
use Symfony\Component\Console\Input\InputOption;
34
use Symfony\Component\Console\Logger\ConsoleLogger;
35
use Symfony\Component\Console\Output\OutputInterface;
36
use Symfony\Component\Console\Question\Question;
37
use Symfony\Component\Console\Style\SymfonyStyle;
38
use Symfony\Component\VarDumper\Cloner\VarCloner;
39
use Symfony\Component\VarDumper\Dumper\CliDumper;
40
use const DATE_ATOM;
41
use function array_shift;
42
use function count;
43
use function decoct;
44
use function explode;
45
use function function_exists;
46
use function get_class;
47
use function implode;
48
use function ini_get;
49
use function KevinGH\Box\disable_parallel_processing;
50
use function KevinGH\Box\FileSystem\chmod;
51
use function KevinGH\Box\FileSystem\dump_file;
52
use function KevinGH\Box\FileSystem\make_path_relative;
53
use function KevinGH\Box\FileSystem\remove;
54
use function KevinGH\Box\FileSystem\rename;
55
use function KevinGH\Box\formatted_filesize;
56
use function KevinGH\Box\get_phar_compression_algorithms;
57
use function putenv;
58
use function strlen;
59
use function strtolower;
60
use function substr;
61
use function trim;
62
63
/**
64
 * @final
65
 * @private
66
 * TODO: make final when Build is removed
67
 */
68
class Compile extends Configurable
69
{
70
    use ChangeableWorkingDirectory;
71
72
    private const HELP = <<<'HELP'
73
The <info>%command.name%</info> command will compile code in a new PHAR based on a variety of settings.
74
<comment>
75
  This command relies on a configuration file for loading
76
  PHAR packaging settings. If a configuration file is not
77
  specified through the <info>--config|-c</info> option, one of
78
  the following files will be used (in order): <info>box.json</info>,
79
  <info>box.json.dist</info>
80
</comment>
81
The configuration file is actually a JSON object saved to a file. For more
82
information check the documentation online:
83
<comment>
84
  https://github.com/humbug/box
85
</comment>
86
HELP;
87
88
    private const DEBUG_OPTION = 'debug';
89
    private const NO_PARALLEL_PROCESSING_OPTION = 'no-parallel';
90
    private const NO_RESTART_OPTION = 'no-restart';
91
    private const DEV_OPTION = 'dev';
92
    private const NO_CONFIG_OPTION = 'no-config';
93
94
    private const DEBUG_DIR = '.box_dump';
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected function configure(): void
100
    {
101
        parent::configure();
102
103
        $this->setName('compile');
104
        $this->setDescription('Compile an application into a PHAR');
105
        $this->setHelp(self::HELP);
106
107
        $this->addOption(
108
            self::DEBUG_OPTION,
109
            null,
110
            InputOption::VALUE_NONE,
111
            'Dump the files added to the PHAR in a `'.self::DEBUG_DIR.'` directory'
112
        );
113
        $this->addOption(
114
            self::NO_PARALLEL_PROCESSING_OPTION,
115
            null,
116
            InputOption::VALUE_NONE,
117
            'Disable the parallel processing'
118
        );
119
        $this->addOption(
120
            self::NO_RESTART_OPTION,
121
            null,
122
            InputOption::VALUE_NONE,
123
            'Do not restart the PHP process. Box restarts the process by default to disable xdebug and set `phar.readonly=0`'
124
        );
125
        $this->addOption(
126
            self::DEV_OPTION,
127
            null,
128
            InputOption::VALUE_NONE,
129
            'Skips the compression step'
130
        );
131
        $this->addOption(
132
            self::NO_CONFIG_OPTION,
133
            null,
134
            InputOption::VALUE_NONE,
135
            'Ignore the config file even when one is specified with the --config option'
136
        );
137
138
        $this->configureWorkingDirOption();
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    protected function execute(InputInterface $input, OutputInterface $output): void
145
    {
146
        $io = new SymfonyStyle($input, $output);
147
148
        if ($input->getOption(self::NO_RESTART_OPTION)) {
149
            putenv('BOX_ALLOW_XDEBUG=1');
150
        }
151
152
        if ($debug = $input->getOption(self::DEBUG_OPTION)) {
153
            $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
154
        }
155
156
        self::checkPhpSettings($io, $output);
157
158
        if ($input->getOption(self::NO_PARALLEL_PROCESSING_OPTION)) {
159
            disable_parallel_processing();
160
            $io->writeln('<info>[debug] Disabled parallel processing</info>', OutputInterface::VERBOSITY_DEBUG);
161
        }
162
163
        $this->changeWorkingDirectory($input);
164
165
        $io->writeln($this->getApplication()->getHelp());
166
        $io->writeln('');
167
168
        $config = $input->getOption(self::NO_CONFIG_OPTION)
169
            ? Configuration::create(null, new stdClass())
170
            : $this->getConfig($input, $output, true)
171
        ;
172
        $path = $config->getOutputPath();
173
174
        $logger = new BuildLogger($io);
175
176
        $startTime = microtime(true);
177
178
        $this->removeExistingArtefacts($config, $logger, $debug);
179
180
        $logger->logStartBuilding($path);
181
182
        $this->createPhar($config, $input, $output, $logger, $io, $debug);
183
184
        $this->correctPermissions($path, $config, $logger);
185
186
        $logger->log(
187
            BuildLogger::STAR_PREFIX,
188
            'Done.'
189
        );
190
191
        $this->logCommandResourcesUsage($io, $path, $startTime);
192
    }
193
194
    /**
195
     * Taken from Composer.
196
     *
197
     * @see https://github.com/composer/composer/blob/34c371f5f23e25eb9aa54ccc65136cf50930612e/bin/composer#L20-L50
198
     */
199
    private static function checkPhpSettings(SymfonyStyle $io, OutputInterface $output): void
200
    {
201
        if (function_exists('ini_set')) {
202
            $memoryInBytes = function (string $value): int {
203
                $unit = strtolower($value[strlen($value) - 1]);
204
205
                $value = (int) $value;
206
                switch ($unit) {
207
                    case 'g':
208
                        $value *= 1024;
209
                    // no break (cumulative multiplier)
210
                    case 'm':
211
                        $value *= 1024;
212
                    // no break (cumulative multiplier)
213
                    case 'k':
214
                        $value *= 1024;
215
                }
216
217
                return $value;
218
            };
219
220
            $memoryLimit = trim(ini_get('memory_limit'));
221
222
            // Increase memory_limit if it is lower than 500MB
223
            if ('-1' !== $memoryLimit && $memoryInBytes($memoryLimit) < 1024 * 1024 * 512 && false === getenv('BOX_MEMORY_LIMIT')) {
224
                @ini_set('memory_limit', '512M');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

224
                /** @scrutinizer ignore-unhandled */ @ini_set('memory_limit', '512M');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
225
226
                $io->writeln(
227
                    sprintf(
228
                        '<info>[debug] Bumped the memory limit from "%s" to "%s".</info>',
229
                        $memoryLimit,
230
                        '512M'
231
                    ),
232
                    OutputInterface::VERBOSITY_DEBUG
233
                );
234
            }
235
236
            // Set user defined memory limit
237
            if ($newMemoryLimit = getenv('BOX_MEMORY_LIMIT')) {
238
                @ini_set('memory_limit', $newMemoryLimit);
239
240
                $io->writeln(
241
                    sprintf(
242
                        '<info>[debug] Bumped the memory limit from "%s" to BOX_MEMORY_LIMIT="%s".</info>',
243
                        $memoryLimit,
244
                        $newMemoryLimit
245
                    ),
246
                    OutputInterface::VERBOSITY_DEBUG
247
                );
248
            }
249
        }
250
251
        (new PhpSettingsHandler(new ConsoleLogger($output)))->check();
252
    }
253
254
    private function createPhar(
255
        Configuration $config,
256
        InputInterface $input,
257
        OutputInterface $output,
258
        BuildLogger $logger,
259
        SymfonyStyle $io,
260
        bool $debug
261
    ): void {
262
        $box = Box::create(
263
            $config->getTmpOutputPath()
264
        );
265
        $box->getPhar()->startBuffering();
266
267
        $this->setReplacementValues($config, $box, $logger);
268
        $this->registerCompactors($config, $box, $logger);
269
        $this->registerFileMapping($config, $box, $logger);
270
271
        // Registering the main script _before_ adding the rest if of the files is _very_ important. The temporary
272
        // file used for debugging purposes and the Composer dump autoloading will not work correctly otherwise.
273
        $main = $this->registerMainScript($config, $box, $logger);
274
275
        $check = $this->registerRequirementsChecker($config, $box, $logger);
276
277
        $this->addFiles($config, $box, $logger, $io);
278
279
        $this->registerStub($config, $box, $main, $check, $logger);
280
        $this->configureMetadata($config, $box, $logger);
281
282
        $this->configureCompressionAlgorithm($config, $box, $input->getOption(self::DEV_OPTION), $logger);
283
284
        $box->getPhar()->stopBuffering();
285
286
        if ($debug) {
287
            $box->getPhar()->extractTo(self::DEBUG_DIR, null, true);
288
        }
289
290
        $this->signPhar($config, $box, $config->getTmpOutputPath(), $input, $output, $logger);
291
292
        if ($config->getTmpOutputPath() !== $config->getOutputPath()) {
293
            rename($config->getTmpOutputPath(), $config->getOutputPath());
294
        }
295
    }
296
297
    private function removeExistingArtefacts(Configuration $config, BuildLogger $logger, bool $debug): void
298
    {
299
        $path = $config->getOutputPath();
300
301
        if ($debug) {
302
            remove(self::DEBUG_DIR);
303
304
            $date = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format(DATE_ATOM);
305
            $file = null !== $config->getFile() ? $config->getFile() : 'No config file';
306
307
            remove(self::DEBUG_DIR);
308
309
            dump_file(
310
                self::DEBUG_DIR.'/.box_configuration',
311
                <<<EOF
312
//
313
// Processed content of the configuration file "$file" dumped for debugging purposes
314
// Time: $date
315
//
316
317
318
EOF
319
                .(new CliDumper())->dump(
320
                    (new VarCloner())->cloneVar($config),
321
                    true
322
                )
323
            );
324
        }
325
326
        if (false === file_exists($path)) {
327
            return;
328
        }
329
330
        $logger->log(
331
            BuildLogger::QUESTION_MARK_PREFIX,
332
            sprintf(
333
                'Removing the existing PHAR "%s"',
334
                $path
335
            )
336
        );
337
338
        remove($path);
339
    }
340
341
    private function setReplacementValues(Configuration $config, Box $box, BuildLogger $logger): void
342
    {
343
        $values = $config->getProcessedReplacements();
344
345
        if ([] === $values) {
346
            return;
347
        }
348
349
        $logger->log(
350
            BuildLogger::QUESTION_MARK_PREFIX,
351
            'Setting replacement values'
352
        );
353
354
        foreach ($values as $key => $value) {
355
            $logger->log(
356
                BuildLogger::PLUS_PREFIX,
357
                sprintf(
358
                    '%s: %s',
359
                    $key,
360
                    $value
361
                )
362
            );
363
        }
364
365
        $box->registerPlaceholders($values);
366
    }
367
368
    private function registerCompactors(Configuration $config, Box $box, BuildLogger $logger): void
369
    {
370
        $compactors = $config->getCompactors();
371
372
        if ([] === $compactors) {
373
            $logger->log(
374
                BuildLogger::QUESTION_MARK_PREFIX,
375
                'No compactor to register'
376
            );
377
378
            return;
379
        }
380
381
        $logger->log(
382
            BuildLogger::QUESTION_MARK_PREFIX,
383
            'Registering compactors'
384
        );
385
386
        $logCompactors = function (Compactor $compactor) use ($logger): void {
387
            $compactorClassParts = explode('\\', get_class($compactor));
388
389
            if ('_HumbugBox' === substr($compactorClassParts[0], 0, strlen('_HumbugBox'))) {
390
                array_shift($compactorClassParts);
391
            }
392
393
            $logger->log(
394
                BuildLogger::PLUS_PREFIX,
395
                implode('\\', $compactorClassParts)
396
            );
397
        };
398
399
        array_map($logCompactors, $compactors);
400
401
        $box->registerCompactors($compactors);
402
    }
403
404
    private function registerFileMapping(Configuration $config, Box $box, BuildLogger $logger): void
405
    {
406
        $fileMapper = $config->getFileMapper();
407
408
        $this->logMap($fileMapper, $logger);
409
410
        $box->registerFileMapping(
411
            $config->getBasePath(),
412
            $fileMapper
413
        );
414
    }
415
416
    private function addFiles(Configuration $config, Box $box, BuildLogger $logger, SymfonyStyle $io): void
417
    {
418
        $logger->log(BuildLogger::QUESTION_MARK_PREFIX, 'Adding binary files');
419
420
        $count = count($config->getBinaryFiles());
421
422
        $box->addFiles($config->getBinaryFiles(), true);
423
424
        $logger->log(
425
            BuildLogger::CHEVRON_PREFIX,
426
            0 === $count
427
                ? 'No file found'
428
                : sprintf('%d file(s)', $count)
429
        );
430
431
        $logger->log(BuildLogger::QUESTION_MARK_PREFIX, 'Adding files');
432
433
        $count = count($config->getFiles());
434
435
        try {
436
            $box->addFiles($config->getFiles(), false, null !== $config->getComposerJson());
437
        } catch (MultiReasonException $exception) {
438
            // This exception is handled a different way to give me meaningful feedback to the user
439
            foreach ($exception->getReasons() as $reason) {
440
                $io->error($reason);
441
            }
442
443
            throw $exception;
444
        }
445
446
        $logger->log(
447
            BuildLogger::CHEVRON_PREFIX,
448
            0 === $count
449
                ? 'No file found'
450
                : sprintf('%d file(s)', $count)
451
        );
452
    }
453
454
    private function registerMainScript(Configuration $config, Box $box, BuildLogger $logger): string
455
    {
456
        $main = $config->getMainScriptPath();
457
458
        $logger->log(
459
            BuildLogger::QUESTION_MARK_PREFIX,
460
            sprintf(
461
                'Adding main file: %s',
462
                $main
463
            )
464
        );
465
466
        $localMain = $box->addFile(
467
            $main,
468
            $config->getMainScriptContents()
469
        );
470
471
        $relativeMain = make_path_relative($main, $config->getBasePath());
472
473
        if ($localMain !== $relativeMain) {
474
            $logger->log(
475
                BuildLogger::CHEVRON_PREFIX,
476
                $localMain
477
            );
478
        }
479
480
        return $localMain;
481
    }
482
483
    private function registerRequirementsChecker(Configuration $config, Box $box, BuildLogger $logger): bool
484
    {
485
        if (false === $config->checkRequirements()) {
486
            return false;
487
        }
488
489
        $logger->log(
490
            BuildLogger::QUESTION_MARK_PREFIX,
491
            'Adding requirements checker'
492
        );
493
494
        $checkFiles = RequirementsDumper::dump($config->getComposerLockDecodedContents(), null !== $config->getCompressionAlgorithm());
495
496
        foreach ($checkFiles as $fileWithContents) {
497
            [$file, $contents] = $fileWithContents;
498
499
            $box->addFile('.box/'.$file, $contents, true);
500
        }
501
502
        return true;
503
    }
504
505
    private function registerStub(Configuration $config, Box $box, string $main, bool $checkRequirements, BuildLogger $logger): void
506
    {
507
        if ($config->isStubGenerated()) {
508
            $logger->log(
509
                BuildLogger::QUESTION_MARK_PREFIX,
510
                'Generating new stub'
511
            );
512
513
            $stub = $this->createStub($config, $main, $checkRequirements, $logger);
514
515
            $box->getPhar()->setStub($stub->generate());
516
        } elseif (null !== ($stub = $config->getStubPath())) {
517
            $logger->log(
518
                BuildLogger::QUESTION_MARK_PREFIX,
519
                sprintf(
520
                    'Using stub file: %s',
521
                    $stub
522
                )
523
            );
524
525
            $box->registerStub($stub);
526
        } else {
527
            // TODO: add warning that the check requirements could not be added
528
            $aliasWasAdded = $box->getPhar()->setAlias($config->getAlias());
529
530
            Assertion::true(
531
                $aliasWasAdded,
532
                sprintf(
533
                    'The alias "%s" is invalid. See Phar::setAlias() documentation for more information.',
534
                    $config->getAlias()
535
                )
536
            );
537
538
            $box->getPhar()->setDefaultStub($main);
539
540
            $logger->log(
541
                BuildLogger::QUESTION_MARK_PREFIX,
542
                'Using default stub'
543
            );
544
        }
545
    }
546
547
    private function configureMetadata(Configuration $config, Box $box, BuildLogger $logger): void
548
    {
549
        if (null !== ($metadata = $config->getMetadata())) {
550
            $logger->log(
551
                BuildLogger::QUESTION_MARK_PREFIX,
552
                'Setting metadata'
553
            );
554
555
            $logger->log(
556
                BuildLogger::MINUS_PREFIX,
557
                is_string($metadata) ? $metadata : var_export($metadata, true)
558
            );
559
560
            $box->getPhar()->setMetadata($metadata);
561
        }
562
    }
563
564
    private function configureCompressionAlgorithm(Configuration $config, Box $box, bool $dev, BuildLogger $logger): void
565
    {
566
        if (null !== ($algorithm = $config->getCompressionAlgorithm())) {
567
            $logger->log(
568
                BuildLogger::QUESTION_MARK_PREFIX,
569
                sprintf(
570
                    'Compressing with the algorithm "<comment>%s</comment>"',
571
                    array_search($algorithm, get_phar_compression_algorithms(), true)
1 ignored issue
show
Bug introduced by
It seems like array_search($algorithm,...ion_algorithms(), true) can also be of type false; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

571
                    /** @scrutinizer ignore-type */ array_search($algorithm, get_phar_compression_algorithms(), true)
Loading history...
572
                )
573
            );
574
575
            $box->getPhar()->compressFiles($algorithm);
576
        } else {
577
            $logger->log(
578
                BuildLogger::QUESTION_MARK_PREFIX,
579
                $dev
580
                    ? 'No compression'
581
                    : '<error>No compression</error>'
582
            );
583
        }
584
    }
585
586
    private function signPhar(
587
        Configuration $config,
588
        Box $box,
589
        string $path,
590
        InputInterface $input,
591
        OutputInterface $output,
592
        BuildLogger $logger
593
    ): void {
594
        // sign using private key, if applicable
595
        //TODO: check that out
596
        remove($path.'.pubkey');
597
598
        $key = $config->getPrivateKeyPath();
599
600
        if (null === $key) {
601
            if (null !== ($algorithm = $config->getSigningAlgorithm())) {
0 ignored issues
show
introduced by
The condition null !== $algorithm = $c...->getSigningAlgorithm() is always true.
Loading history...
602
                $box->getPhar()->setSignatureAlgorithm($algorithm);
603
            }
604
605
            return;
606
        }
607
608
        $logger->log(
609
            BuildLogger::QUESTION_MARK_PREFIX,
610
            'Signing using a private key'
611
        );
612
613
        $passphrase = $config->getPrivateKeyPassphrase();
614
615
        if ($config->isPrivateKeyPrompt()) {
616
            if (false === $input->isInteractive()) {
617
                throw new RuntimeException(
618
                    sprintf(
619
                        'Accessing to the private key "%s" requires a passphrase but none provided. Either '
620
                        .'provide one or run this command in interactive mode.',
621
                        $key
622
                    )
623
                );
624
            }
625
626
            /** @var $dialog QuestionHelper */
627
            $dialog = $this->getHelper('question');
628
629
            $question = new Question('Private key passphrase:');
630
            $question->setHidden(false);
631
            $question->setHiddenFallback(false);
632
633
            $passphrase = $dialog->ask($input, $output, $question);
634
635
            $output->writeln('');
636
        }
637
638
        $box->signUsingFile($key, $passphrase);
639
    }
640
641
    private function correctPermissions(string $path, Configuration $config, BuildLogger $logger): void
642
    {
643
        if (null !== ($chmod = $config->getFileMode())) {
644
            $logger->log(
645
                BuildLogger::QUESTION_MARK_PREFIX,
646
                sprintf(
647
                    'Setting file permissions to <comment>%s</comment>',
648
                    '0'.decoct($chmod)
649
                )
650
            );
651
652
            chmod($path, $chmod);
653
        }
654
    }
655
656
    private function createStub(Configuration $config, ?string $main, bool $checkRequirements, BuildLogger $logger): StubGenerator
657
    {
658
        $stub = StubGenerator::create()
659
            ->alias($config->getAlias())
660
            ->index($main)
661
            ->intercept($config->isInterceptFileFuncs())
662
            ->checkRequirements($checkRequirements)
663
        ;
664
665
        if (null !== ($shebang = $config->getShebang())) {
666
            $logger->log(
667
                BuildLogger::MINUS_PREFIX,
668
                sprintf(
669
                    'Using shebang line: %s',
670
                    $shebang
671
                )
672
            );
673
674
            $stub->shebang($shebang);
675
        } else {
676
            $logger->log(
677
                BuildLogger::MINUS_PREFIX,
678
                'No shebang line'
679
            );
680
        }
681
682
        if (null !== ($bannerPath = $config->getStubBannerPath())) {
683
            $logger->log(
684
                BuildLogger::MINUS_PREFIX,
685
                sprintf(
686
                    'Using custom banner from file: %s',
687
                    $bannerPath
688
                )
689
            );
690
691
            $stub->banner($config->getStubBannerContents());
692
        } elseif (null !== ($banner = $config->getStubBannerContents())) {
693
            $logger->log(
694
                BuildLogger::MINUS_PREFIX,
695
                'Using banner:'
696
            );
697
698
            $bannerLines = explode("\n", $banner);
699
700
            foreach ($bannerLines as $bannerLine) {
701
                $logger->log(
702
                    BuildLogger::CHEVRON_PREFIX,
703
                    $bannerLine
704
                );
705
            }
706
707
            $stub->banner($banner);
708
        }
709
710
        return $stub;
711
    }
712
713
    private function logMap(MapFile $fileMapper, BuildLogger $logger): void
714
    {
715
        $map = $fileMapper->getMap();
716
717
        if ([] === $map) {
718
            return;
719
        }
720
721
        $logger->log(
722
            BuildLogger::QUESTION_MARK_PREFIX,
723
            'Mapping paths'
724
        );
725
726
        foreach ($map as $item) {
727
            foreach ($item as $match => $replace) {
728
                if ('' === $match) {
729
                    $match = '(all)';
730
                    $replace .= '/';
731
                }
732
733
                $logger->log(
734
                    BuildLogger::MINUS_PREFIX,
735
                    sprintf(
736
                        '%s <info>></info> %s',
737
                        $match,
738
                        $replace
739
                    )
740
                );
741
            }
742
        }
743
    }
744
745
    private function logCommandResourcesUsage(SymfonyStyle $io, string $path, float $startTime)
746
    {
747
        return $io->comment(
0 ignored issues
show
Bug introduced by
Are you sure the usage of $io->comment(sprintf('<i...rue) - $startTime, 2))) targeting Symfony\Component\Consol...SymfonyStyle::comment() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
748
            sprintf(
749
                "<info>PHAR size: %s\nMemory usage: %.2fMB (peak: %.2fMB), time: %.2fs<info>",
750
                formatted_filesize($path),
751
                round(memory_get_usage() / 1024 / 1024, 2),
752
                round(memory_get_peak_usage() / 1024 / 1024, 2),
753
                round(microtime(true) - $startTime, 2)
754
            )
755
        );
756
    }
757
}
758