Completed
Pull Request — master (#32)
by Théo
02:37
created

Build   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 908
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 51
dl 0
loc 908
rs 7.4418
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 8 1
B createPhar() 0 26 1
B execute() 0 43 3
A removeExistingPhar() 0 18 2
B registerCompactors() 0 31 2
A loadBootstrapFile() 0 18 2
B registerStub() 0 30 3
B createStub() 0 50 4
A addFilesToBox() 0 19 4
B registerMappedFiles() 0 33 5
B registerMainScript() 0 36 3
B signPhar() 0 56 6
B addFiles() 0 60 5
A configureMetadata() 0 16 3
B registerPlaceholders() 0 27 3
A correctPermissions() 0 10 2
A configureCompressionAlgorithm() 0 18 2

How to fix   Complexity   

Complex Class

Complex classes like Build often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Build, and based on these observations, apply Extract Interface, too.

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\Command;
16
17
use KevinGH\Box\Box;
18
use KevinGH\Box\Compactor;
19
use KevinGH\Box\Configuration;
20
use KevinGH\Box\Logger\BuildLogger;
21
use KevinGH\Box\StubGenerator;
22
use RuntimeException;
23
use SplFileInfo;
24
use Symfony\Component\Console\Helper\QuestionHelper;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Symfony\Component\Console\Question\Question;
28
use Symfony\Component\Console\Style\SymfonyStyle;
29
use Symfony\Component\Filesystem\Filesystem;
30
use function KevinGH\Box\formatted_filesize;
31
use function KevinGH\Box\get_phar_compression_algorithms;
32
33
final class Build extends Configurable
34
{
35
    /**
36
     * {@inheritdoc}
37
     */
38
    protected function configure(): void
39
    {
40
        parent::configure();
41
42
        $this->setName('build');
43
        $this->setDescription('Builds a new PHAR');
44
        $this->setHelp(
45
            <<<HELP
46
The <info>%command.name%</info> command will build a new Phar based on a variety of settings.
47
<comment>
48
  This command relies on a configuration file for loading
49
  Phar packaging settings. If a configuration file is not
50
  specified through the <info>--configuration|-c</info> option, one of
51
  the following files will be used (in order): <info>box.json,
52
  box.json.dist</info>
53
</comment>
54
The configuration file is actually a JSON object saved to a file.
55
Note that all settings are optional.
56
<comment>
57
  {
58
    "algorithm": ?,
59
    "alias": ?,
60
    "banner": ?,
61
    "banner-file": ?,
62
    "base-path": ?,
63
    "blacklist": ?,
64
    "bootstrap": ?,
65
    "chmod": ?,
66
    "compactors": ?,
67
    "compression": ?,
68
    "datetime": ?,
69
    "datetime_format": ?,
70
    "directories": ?,
71
    "directories-bin": ?,
72
    "extract": ?,
73
    "files": ?,
74
    "files-bin": ?,
75
    "finder": ?,
76
    "finder-bin": ?,
77
    "git-version": ?,
78
    "intercept": ?,
79
    "key": ?,
80
    "key-pass": ?,
81
    "main": ?,
82
    "map": ?,
83
    "metadata": ?,
84
    "mimetypes": ?,
85
    "mung": ?,
86
    "not-found": ?,
87
    "output": ?,
88
    "replacements": ?,
89
    "shebang": ?,
90
    "stub": ?,
91
    "web": ?
92
  }
93
</comment>
94
95
96
97
The <info>algorithm</info> <comment>(string, integer)</comment> setting is the signing algorithm to
98
use when the Phar is built <comment>(Phar::setSignatureAlgorithm())</comment>. It can an
99
integer value (the value of the constant), or the name of the Phar
100
constant. The following is a list of the signature algorithms listed
101
on the help page:
102
<comment>
103
  - MD5 (Phar::MD5)
104
  - SHA1 (Phar::SHA1)
105
  - SHA256 (Phar::SHA256)
106
  - SHA512 (Phar::SHA512)
107
  - OPENSSL (Phar::OPENSSL)
108
</comment>
109
The <info>alias</info> <comment>(string)</comment> setting is used when generating a new stub to call
110
the <comment>Phar::mapPhar()</comment> method. This makes it easier to refer to files in
111
the Phar.
112
113
The <info>annotations</info> <comment>(boolean, object)</comment> setting is used to enable compacting
114
annotations in PHP source code. By setting it to <info>true</info>, all Doctrine-style
115
annotations are compacted in PHP files. You may also specify a list of
116
annotations to ignore, which will be stripped while protecting the
117
remaining annotations:
118
<comment>
119
  {
120
      "annotations": {
121
          "ignore": [
122
              "author",
123
              "package",
124
              "version",
125
              "see"
126
          ]
127
      }
128
  }
129
</comment>
130
You may want to see this website for a list of annotations which are
131
commonly ignored:
132
<comment>
133
  https://github.com/herrera-io/php-annotations
134
</comment>
135
The <info>banner</info> <comment>(string)</comment> setting is the banner comment that will be used when
136
a new stub is generated. The value of this setting must not already be
137
enclosed within a comment block, as it will be automatically done for
138
you.
139
140
The <info>banner-file</info> <comment>(string)</comment> setting is like <info>stub-banner</info>, except it is a
141
path to the file that will contain the comment. Like <info>stub-banner</info>, the
142
comment must not already be enclosed in a comment block.
143
144
The <info>base-path</info> <comment>(string)</comment> setting is used to specify where all of the
145
relative file paths should resolve to. This does not, however, alter
146
where the built Phar will be stored <comment>(see: <info>output</info>)</comment>. By default, the
147
base path is the directory containing the configuration file.
148
149
The <info>blacklist</info> <comment>(string, array)</comment> setting is a list of files that must
150
not be added. The files blacklisted are the ones found using the other
151
available configuration settings: <info>directories, directories-bin, files,
152
files-bin, finder, finder-bin</info>. Note that directory separators are
153
automatically corrected to the platform specific version.
154
155
Assuming that the base directory path is <comment>/home/user/project</comment>:
156
<comment>
157
  {
158
      "blacklist": [
159
          "path/to/file/1"
160
          "path/to/file/2"
161
      ],
162
      "directories": ["src"]
163
  }
164
</comment>
165
The following files will be blacklisted:
166
<comment>
167
  - /home/user/project/src/path/to/file/1
168
  - /home/user/project/src/path/to/file/2
169
</comment>
170
But not these files:
171
<comment>
172
  - /home/user/project/src/another/path/to/file/1
173
  - /home/user/project/src/another/path/to/file/2
174
</comment>
175
The <info>bootstrap</info> <comment>(string)</comment> setting allows you to specify a PHP file that
176
will be loaded before the <info>build</info> or <info>add</info> commands are used. This is
177
useful for loading third-party file contents compacting classes that
178
were configured using the <info>compactors</info> setting.
179
180
The <info>chmod</info> <comment>(string)</comment> setting is used to change the file permissions of
181
the newly built Phar. The string contains an octal value: <comment>0755</comment>. You
182
must prefix the mode with zero if you specify the mode in decimal.
183
184
The <info>compactors</info> <comment>(string, array)</comment> setting is a list of file contents
185
compacting classes that must be registered. A file compacting class
186
is used to reduce the size of a specific file type. The following is
187
a simple example:
188
<comment>
189
  use Herrera\\Box\\Compactor\\CompactorInterface;
190
191
  class MyCompactor implements CompactorInterface
192
  {
193
      public function compact(\$contents)
194
      {
195
          return trim(\$contents);
196
      }
197
198
      public function supports(\$file)
199
      {
200
          return (bool) preg_match('/\.txt/', \$file);
201
      }
202
  }
203
</comment>
204
The following compactors are included with Box:
205
<comment>
206
  - Herrera\\Box\\Compactor\\Json
207
  - Herrera\\Box\\Compactor\\Php
208
</comment>
209
The <info>compression</info> <comment>(string, integer)</comment> setting is the compression algorithm
210
to use when the Phar is built. The compression affects the individual
211
files within the Phar, and not the Phar as a whole <comment>(Phar::compressFiles())</comment>.
212
The following is a list of the signature algorithms listed on the help
213
page:
214
<comment>
215
  - BZ2 (Phar::BZ2)
216
  - GZ (Phar::GZ)
217
  - NONE (Phar::NONE)
218
</comment>
219
The <info>directories</info> <comment>(string, array)</comment> setting is a list of directory paths
220
relative to <info>base-path</info>. All files ending in <comment>.php</comment> will be automatically
221
compacted, have their placeholder values replaced, and added to the
222
Phar. Files listed in the <info>blacklist</info> setting will not be added.
223
224
The <info>directories-bin</info> <comment>(string, array)</comment> setting is similar to <info>directories</info>,
225
except all file types are added to the Phar unmodified. This is suitable
226
for directories containing images or other binary data.
227
228
The <info>extract</info> <comment>(boolean)</comment> setting determines whether or not the generated
229
stub should include a class to extract the phar. This class would be
230
used if the phar is not available. (Increases stub file size.)
231
232
The <info>files</info> <comment>(string, array)</comment> setting is a list of files paths relative to
233
<info>base-path</info>. Each file will be compacted, have their placeholder files
234
replaced, and added to the Phar. This setting is not affected by the
235
<info>blacklist</info> setting.
236
237
The <info>files-bin</info> <comment>(string, array)</comment> setting is similar to <info>files</info>, except that
238
all files are added to the Phar unmodified. This is suitable for files
239
such as images or those that contain binary data.
240
241
The <info>finder</info> <comment>(array)</comment> setting is a list of JSON objects. Each object key
242
is a name, and each value an argument for the methods in the
243
<comment>Symfony\\Component\\Finder\\Finder</comment> class. If an array of values is provided
244
for a single key, the method will be called once per value in the array.
245
Note that the paths specified for the "in" method are relative to
246
<info>base-path</info>.
247
248
The <info>finder-bin</info> <comment>(array)</comment> setting performs the same function, except all
249
files found by the finder will be treated as binary files, leaving them
250
unmodified.
251
<comment>
252
It may be useful to know that Box imports files in the following order:
253
254
 - finder
255
 - finder-bin
256
 - directories
257
 - directories-bin
258
 - files
259
 - files-bin
260
</comment>
261
The <info>datetime</info> <comment>(string)</comment> setting is the name of a placeholder value that
262
will be replaced in all non-binary files by the current datetime.
263
264
Example: <comment>2015-01-28 14:55:23</comment>
265
266
The <info>datetime_format</info> <comment>(string)</comment> setting accepts a valid PHP date format. It can be used to change the format for the <info>datetime</info> setting.
267
268
Example: <comment>Y-m-d H:i:s</comment>
269
270
The <info>git-commit</info> <comment>(string)</comment> setting is the name of a placeholder value that
271
will be replaced in all non-binary files by the current Git commit hash
272
of the repository.
273
274
Example: <comment>e558e335f1d165bc24d43fdf903cdadd3c3cbd03</comment>
275
276
The <info>git-commit-short</info> <comment>(string)</comment> setting is the name of a placeholder value
277
that will be replaced in all non-binary files by the current Git short
278
commit hash of the repository.
279
280
Example: <comment>e558e33</comment>
281
282
The <info>git-tag</info> <comment>(string)</comment> setting is the name of a placeholder value that will
283
be replaced in all non-binary files by the current Git tag of the
284
repository.
285
286
Examples:
287
<comment>
288
 - 2.0.0
289
 - 2.0.0-2-ge558e33
290
</comment>
291
The <info>git-version</info> <comment>(string)</comment> setting is the name of a placeholder value that
292
will be replaced in all non-binary files by the one of the following (in
293
order):
294
295
  - The git repository's most recent tag.
296
  - The git repository's current short commit hash.
297
298
The short commit hash will only be used if no tag is available.
299
300
The <info>intercept</info> <comment>(boolean)</comment> setting is used when generating a new stub. If
301
setting is set to <comment>true</comment>, the <comment>Phar::interceptFileFuncs();</comment> method will be
302
called in the stub.
303
304
The <info>key</info> <comment>(string)</comment> setting is used to specify the path to the private key
305
file. The private key file will be used to sign the Phar using the
306
<comment>OPENSSL</comment> signature algorithm. If an absolute path is not provided, the
307
path will be relative to the current working directory.
308
309
The <info>key-pass</info> <comment>(string, boolean)</comment> setting is used to specify the passphrase
310
for the private <info>key</info>. If a <comment>string</comment> is provided, it will be used as is as
311
the passphrase. If <comment>true</comment> is provided, you will be prompted for the
312
passphrase.
313
314
The <info>main</info> <comment>(string)</comment> setting is used to specify the file (relative to
315
<info>base-path</info>) that will be run when the Phar is executed from the command
316
line. If the file was not added by any of the other file adding settings,
317
it will be automatically added after it has been compacted and had its
318
placeholder values replaced. Also, the #! line will be automatically
319
removed if present.
320
321
The <info>map</info> <comment>(array)</comment> setting is used to change where some (or all) files are
322
stored inside the phar. The key is a beginning of the relative path that
323
will be matched against the file being added to the phar. If the key is
324
a match, the matched segment will be replaced with the value. If the key
325
is empty, the value will be prefixed to all paths (except for those
326
already matched by an earlier key).
327
328
<comment>
329
  {
330
    "map": [
331
      { "my/test/path": "src/Test" },
332
      { "": "src/Another" }
333
    ]
334
  }
335
</comment>
336
337
(with the files)
338
339
<comment>
340
  1. my/test/path/file.php
341
  2. my/test/path/some/other.php
342
  3. my/test/another.php
343
</comment>
344
345
(will be stored as)
346
347
<comment>
348
  1. src/Test/file.php
349
  2. src/Test/some/other.php
350
  3. src/Another/my/test/another.php
351
</comment>
352
353
The <info>metadata</info> <comment>(any)</comment> setting can be any value. This value will be stored as
354
metadata that can be retrieved from the built Phar <comment>(Phar::getMetadata())</comment>.
355
356
The <info>mimetypes</info> <comment>(object)</comment> setting is used when generating a new stub. It is
357
a map of file extensions and their mimetypes. To see a list of the default
358
mapping, please visit:
359
360
  <comment>http://www.php.net/manual/en/phar.webphar.php</comment>
361
362
The <info>mung</info> <comment>(array)</comment> setting is used when generating a new stub. It is a list
363
of server variables to modify for the Phar. This setting is only useful
364
when the <info>web</info> setting is enabled.
365
366
The <info>not-found</info> <comment>(string)</comment> setting is used when generating a new stub. It
367
specifies the file that will be used when a file is not found inside the
368
Phar. This setting is only useful when <info>web</info> setting is enabled.
369
370
The <info>output</info> <comment>(string)</comment> setting specifies the file name and path of the newly
371
built Phar. If the value of the setting is not an absolute path, the path
372
will be relative to the current working directory.
373
374
The <info>replacements</info> <comment>(object)</comment> setting is a map of placeholders and their
375
values. The placeholders are replaced in all non-binary files with the
376
specified values.
377
378
The <info>shebang</info> <comment>(string)</comment> setting is used to specify the shebang line used
379
when generating a new stub. By default, this line is used:
380
381
  <comment>#!/usr/bin/env php</comment>
382
383
The shebang line can be removed altogether if <comment>false</comment> or an empty string
384
is provided.
385
386
The <info>stub</info> <comment>(string, boolean)</comment> setting is used to specify the location of a
387
stub file, or if one should be generated. If a path is provided, the stub
388
file will be used as is inside the Phar. If <comment>true</comment> is provided, a new stub
389
will be generated. If <comment>false (or nothing)</comment> is provided, the default stub
390
used by the Phar class will be used.
391
392
The <info>web</info> <comment>(boolean)</comment> setting is used when generating a new stub. If <comment>true</comment> is
393
provided, <comment>Phar::webPhar()</comment> will be called in the stub.
394
HELP
395
        );
396
    }
397
398
    /**
399
     * {@inheritdoc}
400
     */
401
    protected function execute(InputInterface $input, OutputInterface $output): void
402
    {
403
        $io = new SymfonyStyle($input, $output);
404
405
        $io->writeln($this->getApplication()->getHelp());
406
        $io->writeln('');
407
408
        $config = $this->getConfig($input);
409
        $path = $config->getOutputPath();
410
411
        $logger = new BuildLogger($io);
412
413
        $startTime = microtime(true);
414
415
        $this->loadBootstrapFile($config, $logger);
416
        $this->removeExistingPhar($config, $logger);
417
418
        $logger->logStartBuilding($path);
419
420
        $this->createPhar($path, $config, $input, $output, $logger);
421
422
        $this->correctPermissions($path, $config, $logger);
423
424
        $logger->log(
425
            BuildLogger::STAR_PREFIX,
426
            'Done.'
427
        );
428
429
        if ($io->getVerbosity() >= OutputInterface::VERBOSITY_NORMAL) {
430
            $io->comment(
431
                sprintf(
432
                    "<info>Size: %s\nMemory usage: %.2fMB (peak: %.2fMB), time: %.2fs<info>",
433
                    formatted_filesize($path),
434
                    round(memory_get_usage() / 1024 / 1024, 2),
435
                    round(memory_get_peak_usage() / 1024 / 1024, 2),
436
                    round(microtime(true) - $startTime, 2)
437
                )
438
            );
439
        }
440
441
        if (false === file_exists($path)) {
442
            //TODO: check that one
443
            $io->warning('The archive was not generated because it did not have any contents');
444
        }
445
    }
446
447
    private function createPhar(
448
        string $path,
449
        Configuration $config,
450
        InputInterface $input,
451
        OutputInterface $output,
452
        BuildLogger $logger
453
    ): void {
454
        $box = Box::create($path);
455
456
        $box->getPhar()->startBuffering();
457
458
        $this->registerPlaceholders($config, $box, $logger);
459
        $this->registerCompactors($config, $box, $logger);
460
        $this->registerMappedFiles($config, $box, $logger);
461
462
        $this->addFiles($config, $box, $logger);
463
464
        $main = $this->registerMainScript($config, $box, $logger);
465
466
        $this->registerStub($config, $box, $main, $logger);
467
        $this->configureMetadata($config, $box, $logger);
468
        $this->configureCompressionAlgorithm($config, $box, $logger);
469
470
        $box->getPhar()->stopBuffering();
471
472
        $this->signPhar($config, $box, $path, $input, $output, $logger);
473
    }
474
475
    private function loadBootstrapFile(Configuration $config, BuildLogger $logger): void
476
    {
477
        $file = $config->getBootstrapFile();
478
479
        if (null === $file) {
480
            return;
481
        }
482
483
        $logger->log(
484
            BuildLogger::QUESTION_MARK_PREFIX,
485
            sprintf(
486
                'Loading the bootstrap file "%s"',
487
                $file
488
            ),
489
            OutputInterface::VERBOSITY_VERBOSE
490
        );
491
492
        $config->loadBootstrap();
493
    }
494
495
    private function removeExistingPhar(Configuration $config, BuildLogger $logger): void
496
    {
497
        $path = $config->getOutputPath();
498
499
        if (false === file_exists($path)) {
500
            return;
501
        }
502
503
        $logger->log(
504
            BuildLogger::QUESTION_MARK_PREFIX,
505
            sprintf(
506
                'Removing the existing PHAR "%s"',
507
                $path
508
            ),
509
            OutputInterface::VERBOSITY_VERBOSE
510
        );
511
512
        (new Filesystem())->remove($path);
513
    }
514
515
    private function registerPlaceholders(Configuration $config, Box $box, BuildLogger $logger): void
516
    {
517
        $values = $config->getProcessedReplacements();
518
519
        if ([] === $values) {
520
            return;
521
        }
522
523
        $logger->log(
524
            BuildLogger::QUESTION_MARK_PREFIX,
525
            'Setting replacement values',
526
            OutputInterface::VERBOSITY_VERBOSE
527
        );
528
529
        foreach ($values as $key => $value) {
530
            $logger->log(
531
                BuildLogger::PLUS_PREFIX,
532
                sprintf(
533
                    '%s: %s',
534
                    $key,
535
                    $value
536
                ),
537
                OutputInterface::VERBOSITY_VERBOSE
538
            );
539
        }
540
541
        $box->registerPlaceholders($values);
542
    }
543
544
    private function registerCompactors(Configuration $config, Box $box, BuildLogger $logger): void
545
    {
546
        $compactors = $config->getCompactors();
547
548
        if ([] === $compactors) {
549
            $logger->log(
550
                BuildLogger::QUESTION_MARK_PREFIX,
551
                'No compactor to register',
552
                OutputInterface::VERBOSITY_VERBOSE
553
            );
554
555
            return;
556
        }
557
558
        $logger->log(
559
            BuildLogger::QUESTION_MARK_PREFIX,
560
            'Registering compactors',
561
            OutputInterface::VERBOSITY_VERBOSE
562
        );
563
564
        $logCompactors = function (Compactor $compactor) use ($logger): void {
565
            $logger->log(
566
                BuildLogger::PLUS_PREFIX,
567
                get_class($compactor),
568
                OutputInterface::VERBOSITY_VERBOSE
569
            );
570
        };
571
572
        array_map($logCompactors, $compactors);
573
574
        $box->registerCompactors($compactors);
575
    }
576
577
    private function registerMappedFiles(Configuration $config, Box $box, BuildLogger $logger): void
578
    {
579
        $map = $config->getMap();
580
581
        if ([] !== $map) {
582
            $logger->log(
583
                BuildLogger::QUESTION_MARK_PREFIX,
584
                'Mapping paths',
585
                OutputInterface::VERBOSITY_VERBOSE
586
            );
587
588
            foreach ($map as $item) {
589
                foreach ($item as $match => $replace) {
590
                    if (empty($match)) {
591
                        $match = '(all)';
592
                    }
593
594
                    $logger->log(
595
                        BuildLogger::MINUS_PREFIX,
596
                        sprintf(
597
                            '%s <info>></info> %s',
598
                            $match,
599
                            $replace
600
                        ),
601
                        OutputInterface::VERBOSITY_VERBOSE
602
                    );
603
                }
604
            }
605
        }
606
607
        $box->registerFileMapping(
608
            $config->createBasePathRetriever(),
609
            $config->getMapper()
610
        );
611
    }
612
613
    private function addFiles(Configuration $config, Box $box, BuildLogger $logger): void
614
    {
615
        if ([] !== ($iterators = $config->getFilesIterators())) {
616
            $logger->log(
617
                BuildLogger::QUESTION_MARK_PREFIX,
618
                'Adding finder files',
619
                OutputInterface::VERBOSITY_VERBOSE
620
            );
621
622
            foreach ($iterators as $iterator) {
623
                $this->addFilesToBox($config, $box, $iterator, null, false, $logger);
624
            }
625
        }
626
627
        if ([] !== ($iterators = $config->getBinaryIterators())) {
628
            $logger->log(
629
                BuildLogger::QUESTION_MARK_PREFIX,
630
                'Adding binary finder files',
631
                OutputInterface::VERBOSITY_VERBOSE
632
            );
633
634
            foreach ($iterators as $iterator) {
635
                $this->addFilesToBox($config, $box, $iterator, null, true, $logger);
636
            }
637
        }
638
639
        $this->addFilesToBox(
640
            $config,
641
            $box,
642
            $config->getDirectoriesIterator(),
643
            'Adding directories',
644
            false,
645
            $logger
646
        );
647
648
        $this->addFilesToBox(
649
            $config,
650
            $box,
651
            $config->getBinaryDirectoriesIterator(),
652
            'Adding binary directories',
653
            true,
654
            $logger
655
        );
656
657
        $this->addFilesToBox(
658
            $config,
659
            $box,
660
            $config->getFilesIterator(),
661
            'Adding files',
662
            false,
663
            $logger
664
        );
665
666
        $this->addFilesToBox(
667
            $config,
668
            $box,
669
            $config->getBinaryFilesIterator(),
670
            'Adding binary files',
671
            true,
672
            $logger
673
        );
674
    }
675
676
    private function registerMainScript(Configuration $config, Box $box, BuildLogger $logger): ?string
677
    {
678
        $main = $config->getMainScriptPath();
679
680
        if (null === $main) {
681
            return null;
682
        }
683
684
        $logger->log(
685
            BuildLogger::QUESTION_MARK_PREFIX,
686
            sprintf(
687
                'Adding main file: %s',
688
                $config->getBasePath().DIRECTORY_SEPARATOR.$main
689
            ),
690
            OutputInterface::VERBOSITY_VERBOSE
691
        );
692
693
        $mapper = $config->getMapper();
694
        $pharPath = $mapper($main);
695
696
        if (null !== $pharPath) {
697
            $logger->log(
698
                BuildLogger::CHEVRON_PREFIX,
699
                $pharPath,
700
                OutputInterface::VERBOSITY_VERBOSE
701
            );
702
703
            $main = $pharPath;
704
        }
705
706
        $box->addFromString(
707
            $main,
708
            $config->getMainScriptContent()
709
        );
710
711
        return $main;
712
    }
713
714
    private function registerStub(Configuration $config, Box $box, ?string $main, BuildLogger $logger): void
715
    {
716
        if (true === $config->isStubGenerated()) {
717
            $logger->log(
718
                BuildLogger::QUESTION_MARK_PREFIX,
719
                'Generating new stub',
720
                OutputInterface::VERBOSITY_VERBOSE
721
            );
722
723
            $stub = $this->createStub($config, $main, $logger);
724
725
            $box->getPhar()->setStub($stub->generate());
726
        } elseif (null !== ($stub = $config->getStubPath())) {
727
            $stub = $config->getBasePath().DIRECTORY_SEPARATOR.$stub;
728
729
            $logger->log(
730
                BuildLogger::QUESTION_MARK_PREFIX,
731
                sprintf(
732
                    'Using stub file: %s',
733
                    $stub
734
                ),
735
                OutputInterface::VERBOSITY_VERBOSE
736
            );
737
738
            $box->registerStub($stub);
739
        } else {
740
            $logger->log(
741
                BuildLogger::QUESTION_MARK_PREFIX,
742
                'Using default stub',
743
                OutputInterface::VERBOSITY_VERBOSE
744
            );
745
        }
746
    }
747
748
    private function configureMetadata(Configuration $config, Box $box, BuildLogger $logger): void
749
    {
750
        if (null !== ($metadata = $config->getMetadata())) {
751
            $logger->log(
752
                BuildLogger::QUESTION_MARK_PREFIX,
753
                'Setting metadata',
754
                OutputInterface::VERBOSITY_VERBOSE
755
            );
756
757
            $logger->log(
758
                BuildLogger::MINUS_PREFIX,
759
                is_string($metadata) ? $metadata : var_export($metadata, true),
760
                OutputInterface::VERBOSITY_VERBOSE
761
            );
762
763
            $box->getPhar()->setMetadata($metadata);
764
        }
765
    }
766
767
    private function configureCompressionAlgorithm(Configuration $config, Box $box, BuildLogger $logger): void
768
    {
769
        if (null !== ($algorithm = $config->getCompressionAlgorithm())) {
770
            $logger->log(
771
                BuildLogger::QUESTION_MARK_PREFIX,
772
                sprintf(
773
                    'Compressing with the algorithm "<comment>%s</comment>"',
774
                    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

774
                    /** @scrutinizer ignore-type */ array_search($algorithm, get_phar_compression_algorithms(), true)
Loading history...
775
                ),
776
                OutputInterface::VERBOSITY_VERBOSE
777
            );
778
779
            $box->getPhar()->compressFiles($algorithm);
780
        } else {
781
            $logger->log(
782
                BuildLogger::QUESTION_MARK_PREFIX,
783
                '<error>No compression</error>',
784
                OutputInterface::VERBOSITY_VERBOSE
785
            );
786
        }
787
    }
788
789
    private function signPhar(
790
        Configuration $config,
791
        Box $box,
792
        string $path,
793
        InputInterface $input,
794
        OutputInterface $output,
795
        BuildLogger $logger
796
    ): void {
797
        // sign using private key, if applicable
798
        //TODO: check that out
799
        if (file_exists($path.'.pubkey')) {
800
            unlink($path.'.pubkey');
801
        }
802
803
        $key = $config->getPrivateKeyPath();
804
805
        if (null === $key) {
806
            if (null !== ($algorithm = $config->getSigningAlgorithm())) {
0 ignored issues
show
introduced by
The condition null !== $algorithm = $c...->getSigningAlgorithm() can never be false.
Loading history...
807
                $box->getPhar()->setSignatureAlgorithm($algorithm);
808
            }
809
810
            return;
811
        }
812
813
        $logger->log(
814
            BuildLogger::QUESTION_MARK_PREFIX,
815
            'Signing using a private key',
816
            OutputInterface::VERBOSITY_VERBOSE
817
        );
818
819
        $passphrase = $config->getPrivateKeyPassphrase();
820
821
        if ($config->isPrivateKeyPrompt()) {
822
            if (false === $input->isInteractive()) {
823
                throw new RuntimeException(
824
                    sprintf(
825
                        'Accessing to the private key "%s" requires a passphrase but none provided. Either '
826
                        .'provide one or run this command in interactive mode.',
827
                        $key
828
                    )
829
                );
830
            }
831
832
            /** @var $dialog QuestionHelper */
833
            $dialog = $this->getHelper('question');
834
835
            $question = new Question('Private key passphrase:');
836
            $question->setHidden(false);
837
            $question->setHiddenFallback(false);
838
839
            $passphrase = $dialog->ask($input, $output, $question);
840
841
            $output->writeln('');
842
        }
843
844
        $box->signUsingFile($key, $passphrase);
845
    }
846
847
    private function correctPermissions(string $path, Configuration $config, BuildLogger $logger): void
848
    {
849
        if (null !== ($chmod = $config->getFileMode())) {
850
            $logger->log(
851
                BuildLogger::QUESTION_MARK_PREFIX,
852
                "Setting file permissions to <comment>$chmod</comment>",
853
                OutputInterface::VERBOSITY_VERBOSE
854
            );
855
856
            chmod($path, $chmod);
857
        }
858
    }
859
860
    /**
861
     * Adds files using an iterator.
862
     *
863
     * @param Configuration          $config
864
     * @param Box                    $box
865
     * @param iterable|SplFileInfo[] $iterator the iterator
866
     * @param string                 $message  the message to announce
867
     * @param bool                   $binary   Should the adding be binary-safe?
868
     * @param BuildLogger            $logger
869
     */
870
    private function addFilesToBox(
871
        Configuration $config,
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed. ( Ignorable by Annotation )

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

871
        /** @scrutinizer ignore-unused */ Configuration $config,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
872
        Box $box,
873
        ?iterable $iterator,
874
        ?string $message,
875
        bool $binary,
876
        BuildLogger $logger
877
    ): void {
878
        if (null === $iterator) {
879
            return;
880
        }
881
882
        if (null !== $message) {
883
            $logger->log(BuildLogger::QUESTION_MARK_PREFIX, $message, OutputInterface::VERBOSITY_VERBOSE);
884
        }
885
886
        $box = $binary ? $box->getPhar() : $box;
887
888
        $box->addFiles($iterator);
0 ignored issues
show
Bug introduced by
The method addFiles() does not exist on Phar. Did you maybe mean addFile()? ( Ignorable by Annotation )

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

888
        $box->/** @scrutinizer ignore-call */ 
889
              addFiles($iterator);

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...
889
    }
890
891
    private function createStub(Configuration $config, ?string $main, BuildLogger $logger): StubGenerator
892
    {
893
        $stub = StubGenerator::create()
894
            ->alias($config->getAlias())
895
            ->extract($config->isExtractable())
896
            ->index($main)
897
            ->intercept($config->isInterceptFileFuncs())
898
            ->mimetypes($config->getMimetypeMapping())
899
            ->mung($config->getMungVariables())
900
            ->notFound($config->getNotFoundScriptPath())
901
            ->web($config->isWebPhar());
902
903
        if (null !== ($shebang = $config->getShebang())) {
904
            $logger->log(
905
                BuildLogger::MINUS_PREFIX,
906
                sprintf(
907
                    'Using custom shebang line: %s',
908
                    $shebang
909
                ),
910
                OutputInterface::VERBOSITY_VERY_VERBOSE
911
            );
912
913
            $stub->shebang($shebang);
914
        }
915
916
        if (null !== ($banner = $config->getStubBanner())) {
917
            $logger->log(
918
                BuildLogger::MINUS_PREFIX,
919
                sprintf(
920
                    'Using custom banner: %s',
921
                    $banner
922
                ),
923
                OutputInterface::VERBOSITY_VERY_VERBOSE
924
            );
925
926
            $stub->banner($banner);
927
        } elseif (null !== ($banner = $config->getStubBannerFromFile())) {
928
            $logger->log(
929
                BuildLogger::MINUS_PREFIX,
930
                sprintf(
931
                    'Using custom banner from file: %s',
932
                    $config->getBasePath().DIRECTORY_SEPARATOR.$config->getStubBannerPath()
933
                ),
934
                OutputInterface::VERBOSITY_VERY_VERBOSE
935
            );
936
937
            $stub->banner($banner);
938
        }
939
940
        return $stub;
941
    }
942
}
943