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

Configuration::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 83
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 83
rs 8.7468
c 0
b 0
f 0
cc 1
eloc 42
nc 1
nop 36

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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;
16
17
use ArrayIterator;
18
use Assert\Assertion;
19
use Closure;
20
use DateTimeImmutable;
21
use Herrera\Annotations\Tokenizer;
22
use Herrera\Box\Compactor\Php as LegacyPhp;
23
use InvalidArgumentException;
24
use KevinGH\Box\Compactor\Php;
25
use Phar;
26
use RuntimeException;
27
use SplFileInfo;
28
use stdClass;
29
use Symfony\Component\Finder\Finder;
30
use Symfony\Component\Process\Process;
31
32
final class Configuration
33
{
34
    private const DEFAULT_ALIAS = 'default.phar';
35
    private const DEFAULT_DATETIME_FORMAT = 'Y-m-d H:i:s';
36
    private const DEFAULT_REPLACEMENT_SIGIL = '@';
37
38
    private $fileMode;
39
    private $alias;
40
    private $basePath;
41
    private $basePathRegex;
42
    private $binaryDirectoriesIterator;
43
    private $binaryFilesIterator;
44
    private $binaryIterators;
45
    private $directoriesIterator;
46
    private $filesIterator;
47
    private $filesIterators;
48
    private $bootstrapFile;
49
    private $compactors;
50
    private $compressionAlgorithm;
51
    private $mainScriptPath;
52
    private $mainScriptContent;
53
    private $map;
54
    private $mapper;
55
    private $metadata;
56
    private $mimetypeMapping;
57
    private $mungVariables;
58
    private $notFoundScriptPath;
59
    private $outputPath;
60
    private $privateKeyPassphrase;
61
    private $privateKeyPath;
62
    private $isPrivateKeyPrompt;
63
    private $processedReplacements;
64
    private $shebang;
65
    private $signingAlgorithm;
66
    private $stubBanner;
67
    private $stubBannerPath;
68
    private $stubBannerFromFile;
69
    private $stubPath;
70
    private $isExtractable;
71
    private $isInterceptFileFuncs;
72
    private $isStubGenerated;
73
    private $isWebPhar;
74
75
    /**
76
     * @param string                     $alias                     TODO: description
77
     * @param string                     $basePath                  Path used as the base for all the relative paths used
78
     * @param string                     $basePathRegex             Base path escaped to be able to easily extract the relative path from a file path
79
     * @param iterable|SplFileInfo[]     $binaryDirectoriesIterator List of directories containing images or other binary data
80
     * @param iterable|SplFileInfo[]     $binaryFilesIterator       List of files containing images or other binary data
81
     * @param iterable[]|SplFileInfo[][] $binaryIterators           List of file iterators returning binary files
82
     * @param iterable|SplFileInfo[]     $directoriesIterator       List of directories
83
     * @param iterable|SplFileInfo[]     $filesIterator             List of files
84
     * @param iterable[]|SplFileInfo[][] $filesIterators            List of file iterators
85
     * @param null|string                $bootstrapFile             The bootstrap file path
86
     * @param Compactor[]                $compactors                List of file contents compactors
87
     * @param null|int                   $compressionAlgorithm      Compression algorithm constant value. See the \Phar class constants
88
     * @param null|int                   $fileMode                  File mode in octal form
89
     * @param null|string                $mainScriptPath            The main script file path
90
     * @param null|string                $mainScriptContent         The processed content of the main script file
91
     * @param string[]                   $map                       The internal file path mapping
92
     * @param callable                   $mapper                    Callable for the configured map
93
     * @param mixed                      $metadata                  The PHAR Metadata
94
     * @param array                      $mimetypeMapping           The file extension MIME type mapping
95
     * @param array                      $mungVariables             The list of server variables to modify for execution
96
     * @param null|string                $notFoundScriptPath        The file path to the script to execute when a file is not found
97
     * @param string                     $outputPath
98
     * @param null|string                $privateKeyPassphrase
99
     * @param null|string                $privateKeyPath
100
     * @param bool                       $isPrivateKeyPrompt        If the user should be prompted for the private key passphrase
101
     * @param array                      $processedReplacements     The processed list of replacement placeholders and their values
102
     * @param null|string                $shebang                   The shebang line
103
     * @param int                        $signingAlgorithm          The PHAR siging algorithm. See \Phar constants
104
     * @param null|string                $stubBanner                The stub banner comment
105
     * @param null|string                $stubBannerPath            The path to the stub banner comment file
106
     * @param null|string                $stubBannerFromFile        The stub banner comment from the fine
107
     * @param null|string                $stubPath                  The PHAR stub file path
108
     * @param bool                       $isExtractable             Wether or not StubGenerator::extract() should be used
109
     * @param bool                       $isInterceptFileFuncs      wether or not Phar::interceptFileFuncs() should be used
110
     * @param bool                       $isStubGenerated           Wether or not if the PHAR stub should be generated
111
     * @param bool                       $isWebPhar                 Wether or not the PHAR is going to be used for the web
112
     */
113
    private function __construct(
114
        string $alias,
115
        string $basePath,
116
        string $basePathRegex,
117
        ?iterable $binaryDirectoriesIterator,
118
        ?iterable $binaryFilesIterator,
119
        array $binaryIterators,
120
        ?iterable $directoriesIterator,
121
        ?iterable $filesIterator,
122
        array $filesIterators,
123
        ?string $bootstrapFile,
124
        array $compactors,
125
        ?int $compressionAlgorithm,
126
        ?int $fileMode,
127
        ?string $mainScriptPath,
128
        ?string $mainScriptContent,
129
        array $map,
130
        callable $mapper,
131
        $metadata,
132
        array $mimetypeMapping,
133
        array $mungVariables,
134
        ?string $notFoundScriptPath,
135
        string $outputPath,
136
        ?string $privateKeyPassphrase,
137
        ?string $privateKeyPath,
138
        bool $isPrivateKeyPrompt,
139
        array $processedReplacements,
140
        ?string $shebang,
141
        int $signingAlgorithm,
142
        ?string $stubBanner,
143
        ?string $stubBannerPath,
144
        ?string $stubBannerFromFile,
145
        ?string $stubPath,
146
        bool $isExtractable,
147
        bool $isInterceptFileFuncs,
148
        bool $isStubGenerated,
149
        bool $isWebPhar
150
    ) {
151
        Assertion::nullOrInArray(
152
            $compressionAlgorithm,
153
            get_phar_compression_algorithms(),
154
            sprintf(
155
                'Invalid compression algorithm "%%s", use one of "%s" instead.',
156
                implode('", "', array_keys(get_phar_compression_algorithms()))
157
            )
158
        );
159
160
        $this->alias = $alias;
161
        $this->basePath = $basePath;
162
        $this->basePathRegex = $basePathRegex;
163
        $this->binaryDirectoriesIterator = $binaryDirectoriesIterator;
164
        $this->binaryFilesIterator = $binaryFilesIterator;
165
        $this->binaryIterators = $binaryIterators;
166
        $this->directoriesIterator = $directoriesIterator;
167
        $this->filesIterator = $filesIterator;
168
        $this->filesIterators = $filesIterators;
169
        $this->bootstrapFile = $bootstrapFile;
170
        $this->compactors = $compactors;
171
        $this->compressionAlgorithm = $compressionAlgorithm;
172
        $this->fileMode = $fileMode;
173
        $this->mainScriptPath = $mainScriptPath;
174
        $this->mainScriptContent = $mainScriptContent;
175
        $this->map = $map;
176
        $this->mapper = $mapper;
177
        $this->metadata = $metadata;
178
        $this->mimetypeMapping = $mimetypeMapping;
179
        $this->mungVariables = $mungVariables;
180
        $this->notFoundScriptPath = $notFoundScriptPath;
181
        $this->outputPath = $outputPath;
182
        $this->privateKeyPassphrase = $privateKeyPassphrase;
183
        $this->privateKeyPath = $privateKeyPath;
184
        $this->isPrivateKeyPrompt = $isPrivateKeyPrompt;
185
        $this->processedReplacements = $processedReplacements;
186
        $this->shebang = $shebang;
187
        $this->signingAlgorithm = $signingAlgorithm;
188
        $this->stubBanner = $stubBanner;
189
        $this->stubBannerPath = $stubBannerPath;
190
        $this->stubBannerFromFile = $stubBannerFromFile;
191
        $this->stubPath = $stubPath;
192
        $this->isExtractable = $isExtractable;
193
        $this->isInterceptFileFuncs = $isInterceptFileFuncs;
194
        $this->isStubGenerated = $isStubGenerated;
195
        $this->isWebPhar = $isWebPhar;
196
    }
197
198
    public static function create(string $file, stdClass $raw): self
199
    {
200
        $alias = $raw->alias ?? self::DEFAULT_ALIAS;
201
202
        $basePath = self::retrieveBasePath($file, $raw);
203
        $basePathRegex = '/'.preg_quote($basePath.DIRECTORY_SEPARATOR, '/').'/';
204
205
        $blacklist = self::retrieveBlacklist($raw);
206
        $blacklistFilter = self::retrieveBlacklistFilter($basePath, $blacklist);
207
208
        $binaryDirectories = self::retrieveBinaryDirectories($raw, $basePath);
209
        $binaryDirectoriesIterator = self::retrieveBinaryDirectoriesIterator($binaryDirectories, $blacklistFilter);
210
211
        $binaryFiles = self::retrieveBinaryFiles($raw, $basePath);
212
        $binaryFilesIterator = self::retrieveBinaryFilesIterator($binaryFiles);
213
214
        $binaryIterators = self::retrieveBinaryIterators($raw, $basePath, $blacklistFilter);
215
216
        $directories = self::retrieveDirectories($raw, $basePath);
217
        $directoriesIterator = self::retrieveDirectoriesIterator($directories, $blacklistFilter);
218
219
        $files = self::retrieveFiles($raw, $basePath);
220
        $filesIterator = self::retrieveFilesIterator($files);
221
222
        $filesIterators = self::retrieveFilesIterators($raw, $basePath, $blacklistFilter);
223
224
        $bootstrapFile = self::retrieveBootstrapFile($raw, $basePath);
225
226
        $compactors = self::retrieveCompactors($raw);
227
        $compressionAlgorithm = self::retrieveCompressionAlgorithm($raw);
228
229
        $fileMode = self::retrieveFileMode($raw);
230
231
        $mainScriptPath = self::retrieveMainScriptPath($raw);
232
        $mainScriptContent = self::retrieveMainScriptContents($mainScriptPath, $basePath);
233
234
        $map = self::retrieveMap($raw);
235
        $mapper = self::retrieveMapper($map);
236
237
        $metadata = self::retrieveMetadata($raw);
238
239
        $mimeTypeMapping = self::retrieveMimetypeMapping($raw);
240
        $mungVariables = self::retrieveMungVariables($raw);
241
        $notFoundScriptPath = self::retrieveNotFoundScriptPath($raw);
242
        $outputPath = self::retrieveOutputPath($raw, $file);
243
244
        $privateKeyPassphrase = self::retrievePrivateKeyPassphrase($raw);
245
        $privateKeyPath = self::retrievePrivateKeyPath($raw);
246
        $isPrivateKeyPrompt = self::retrieveIsPrivateKeyPrompt($raw);
247
248
        $replacements = self::retrieveReplacements($raw);
249
        $processedReplacements = self::retrieveProcessedReplacements($replacements, $raw, $file);
250
251
        $shebang = self::retrieveShebang($raw);
252
253
        $signingAlgorithm = self::retrieveSigningAlgorithm($raw);
254
255
        $stubBanner = self::retrieveStubBanner($raw);
256
        $stubBannerPath = self::retrieveStubBannerPath($raw);
257
        $stubBannerFromFile = self::retrieveStubBannerFromFile($basePath, $stubBannerPath);
258
259
        $stubPath = self::retrieveStubPath($raw);
260
261
        $isExtractable = self::retrieveIsExtractable($raw);
262
        $isInterceptFileFuncs = self::retrieveIsInterceptFileFuncs($raw);
263
        $isStubGenerated = self::retrieveIsStubGenerated($raw);
264
        $isWebPhar = self::retrieveIsWebPhar($raw);
265
266
        return new self(
267
            $alias,
268
            $basePath,
269
            $basePathRegex,
270
            $binaryDirectoriesIterator,
271
            $binaryFilesIterator,
272
            $binaryIterators,
273
            $directoriesIterator,
274
            $filesIterator,
275
            $filesIterators,
276
            $bootstrapFile,
277
            $compactors,
278
            $compressionAlgorithm,
279
            $fileMode,
280
            $mainScriptPath,
281
            $mainScriptContent,
282
            $map,
283
            $mapper,
284
            $metadata,
285
            $mimeTypeMapping,
286
            $mungVariables,
287
            $notFoundScriptPath,
288
            $outputPath,
289
            $privateKeyPassphrase,
290
            $privateKeyPath,
291
            $isPrivateKeyPrompt,
292
            $processedReplacements,
293
            $shebang,
294
            $signingAlgorithm,
295
            $stubBanner,
296
            $stubBannerPath,
297
            $stubBannerFromFile,
298
            $stubPath,
299
            $isExtractable,
300
            $isInterceptFileFuncs,
301
            $isStubGenerated,
302
            $isWebPhar
303
        );
304
    }
305
306
    public function createBasePathRetriever(): callable
307
    {
308
        return new RetrieveBasePath($this->basePathRegex);
309
    }
310
311
    public function getAlias(): string
312
    {
313
        return $this->alias;
314
    }
315
316
    public function getBasePath(): string
317
    {
318
        return $this->basePath;
319
    }
320
321
    /**
322
     * @return null|iterable|SplFileInfo[]
323
     */
324
    public function getBinaryDirectoriesIterator(): ?iterable
325
    {
326
        return $this->binaryDirectoriesIterator;
327
    }
328
329
    /**
330
     * @return null|iterable|SplFileInfo[]
331
     */
332
    public function getBinaryFilesIterator(): ?iterable
333
    {
334
        return $this->binaryFilesIterator;
335
    }
336
337
    /**
338
     * @return iterable[]|SplFileInfo[][]
339
     */
340
    public function getBinaryIterators(): array
341
    {
342
        return $this->binaryIterators;
343
    }
344
345
    /**
346
     * @return null|iterable|SplFileInfo[]
347
     */
348
    public function getDirectoriesIterator(): ?iterable
349
    {
350
        return $this->directoriesIterator;
351
    }
352
353
    /**
354
     * @return null|iterable|SplFileInfo[]
355
     */
356
    public function getFilesIterator(): ?iterable
357
    {
358
        return $this->filesIterator;
359
    }
360
361
    public function getBootstrapFile(): ?string
362
    {
363
        return $this->bootstrapFile;
364
    }
365
366
    public function loadBootstrap(): void
367
    {
368
        $file = $this->bootstrapFile;
369
370
        if (null !== $file) {
371
            include $file;
372
        }
373
    }
374
375
    /**
376
     * @return Compactor[] the list of compactors
377
     */
378
    public function getCompactors(): array
379
    {
380
        return $this->compactors;
381
    }
382
383
    public function getCompressionAlgorithm(): ?int
384
    {
385
        return $this->compressionAlgorithm;
386
    }
387
388
    public function getFileMode(): ?int
389
    {
390
        return $this->fileMode;
391
    }
392
393
    public function getMainScriptPath(): ?string
394
    {
395
        return $this->mainScriptPath;
396
    }
397
398
    public function getMainScriptContent(): ?string
399
    {
400
        return $this->mainScriptContent;
401
    }
402
403
    public function getMimetypeMapping(): array
404
    {
405
        return $this->mimetypeMapping;
406
    }
407
408
    public function getMungVariables(): array
409
    {
410
        return $this->mungVariables;
411
    }
412
413
    public function getNotFoundScriptPath(): ?string
414
    {
415
        return $this->notFoundScriptPath;
416
    }
417
418
    public function getOutputPath(): string
419
    {
420
        return $this->outputPath;
421
    }
422
423
    /**
424
     * @return string[]
425
     */
426
    public function getMap(): array
427
    {
428
        return $this->map;
429
    }
430
431
    public function getMapper(): callable
432
    {
433
        return $this->mapper;
434
    }
435
436
    /**
437
     * @return mixed
438
     */
439
    public function getMetadata()
440
    {
441
        return $this->metadata;
442
    }
443
444
    /**
445
     * @return iterable[]|SplFileInfo[][]
446
     */
447
    public function getFilesIterators()
448
    {
449
        return $this->filesIterators;
450
    }
451
452
    public function getPrivateKeyPassphrase(): ?string
453
    {
454
        return $this->privateKeyPassphrase;
455
    }
456
457
    public function getPrivateKeyPath(): ?string
458
    {
459
        return $this->privateKeyPath;
460
    }
461
462
    public function isPrivateKeyPrompt(): bool
463
    {
464
        return $this->isPrivateKeyPrompt;
465
    }
466
467
    public function getProcessedReplacements(): array
468
    {
469
        return $this->processedReplacements;
470
    }
471
472
    public function getShebang(): ?string
473
    {
474
        return $this->shebang;
475
    }
476
477
    public function getSigningAlgorithm(): int
478
    {
479
        return $this->signingAlgorithm;
480
    }
481
482
    public function getStubBanner(): ?string
483
    {
484
        return $this->stubBanner;
485
    }
486
487
    public function getStubBannerPath(): ?string
488
    {
489
        return $this->stubBannerPath;
490
    }
491
492
    public function getStubBannerFromFile()
493
    {
494
        return $this->stubBannerFromFile;
495
    }
496
497
    public function getStubPath(): ?string
498
    {
499
        return $this->stubPath;
500
    }
501
502
    public function isExtractable(): bool
503
    {
504
        return $this->isExtractable;
505
    }
506
507
    public function isInterceptFileFuncs(): bool
508
    {
509
        return $this->isInterceptFileFuncs;
510
    }
511
512
    public function isStubGenerated(): bool
513
    {
514
        return $this->isStubGenerated;
515
    }
516
517
    public function isWebPhar(): bool
518
    {
519
        return $this->isWebPhar;
520
    }
521
522
    private static function retrieveBasePath(string $file, stdClass $raw): string
523
    {
524
        if (isset($raw->{'base-path'})) {
525
            if (false === is_dir($raw->{'base-path'})) {
526
                throw new InvalidArgumentException(
527
                    sprintf(
528
                        'The base path "%s" is not a directory or does not exist.',
529
                        $raw->{'base-path'}
530
                    )
531
                );
532
            }
533
534
            return realpath($raw->{'base-path'});
535
        }
536
537
        return realpath(dirname($file));
538
    }
539
540
    /**
541
     * @return SplFileInfo[]
542
     */
543
    private static function retrieveBinaryDirectories(stdClass $raw, string $basePath): array
544
    {
545
        if (isset($raw->{'directories-bin'})) {
546
            $directories = (array) $raw->{'directories-bin'};
547
548
            array_walk(
549
                $directories,
550
                function (&$directory) use ($basePath): void {
551
                    $directory = $basePath
552
                        .DIRECTORY_SEPARATOR
553
                        .canonicalize($directory);
554
                }
555
            );
556
557
            return $directories;
558
        }
559
560
        return [];
561
    }
562
563
    /**
564
     * @param SplFileInfo[] $binaryDirectories
565
     * @param Closure       $blacklistFilter
566
     *
567
     * @return null|iterable|SplFileInfo[] the iterator
568
     */
569
    private static function retrieveBinaryDirectoriesIterator(array $binaryDirectories, Closure $blacklistFilter): ?iterable
570
    {
571
        if ([] !== $binaryDirectories) {
572
            return Finder::create()
0 ignored issues
show
Bug Best Practice introduced by
The expression return Symfony\Component...>in($binaryDirectories) returns the type Symfony\Component\Finder\Finder which is incompatible with the type-hinted return null|iterable.
Loading history...
573
                ->files()
574
                ->filter($blacklistFilter)
575
                ->ignoreVCS(true)
576
                ->in($binaryDirectories);
577
        }
578
579
        return null;
580
    }
581
582
    /**
583
     * @return SplFileInfo[] the list of paths
584
     */
585
    private static function retrieveBinaryFiles(stdClass $raw, string $basePath): array
586
    {
587
        if (isset($raw->{'files-bin'})) {
588
            $files = [];
589
590
            foreach ((array) $raw->{'files-bin'} as $file) {
591
                $files[] = new SplFileInfo(
592
                    $basePath.DIRECTORY_SEPARATOR.canonicalize($file)
593
                );
594
            }
595
596
            return $files;
597
        }
598
599
        return [];
600
    }
601
602
    /**
603
     * @return null|iterable|SplFileInfo[] the iterator
604
     */
605
    private static function retrieveBinaryFilesIterator(array $binaryFiles): ?iterable
606
    {
607
        if ([] !== $binaryFiles) {
608
            return new ArrayIterator($binaryFiles);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new ArrayIterator($binaryFiles) returns the type ArrayIterator which is incompatible with the type-hinted return null|iterable.
Loading history...
609
        }
610
611
        return null;
612
    }
613
614
    /**
615
     * @return string[]
616
     */
617
    private static function retrieveBlacklist(stdClass $raw): array
618
    {
619
        if (isset($raw->blacklist)) {
620
            $blacklist = (array) $raw->blacklist;
621
622
            array_walk(
623
                $blacklist,
624
                function (&$file): void {
625
                    $file = canonicalize($file);
626
                }
627
            );
628
629
            return $blacklist;
630
        }
631
632
        return [];
633
    }
634
635
    /**
636
     * @param string   $basePath
637
     * @param string[] $blacklist
638
     *
639
     * @return Closure
640
     */
641
    private static function retrieveBlacklistFilter(string $basePath, array $blacklist): Closure
642
    {
643
        $base = sprintf(
644
            '/^%s/',
645
            preg_quote($basePath.DIRECTORY_SEPARATOR, '/')
646
        );
647
648
        return function (SplFileInfo $file) use ($base, $blacklist): ?bool {
649
            $path = canonicalize(
650
                preg_replace($base, '', $file->getPathname())
651
            );
652
653
            if (in_array($path, $blacklist, true)) {
654
                return false;
655
            }
656
657
            return null;
658
        };
659
    }
660
661
    /**
662
     * @param stdClass $raw
663
     * @param string   $basePath
664
     * @param Closure  $blacklistFilter
665
     *
666
     * @return iterable[]|SplFileInfo[][]
667
     */
668
    private static function retrieveBinaryIterators(stdClass $raw, string $basePath, Closure $blacklistFilter): array
669
    {
670
        if (isset($raw->{'finder-bin'})) {
671
            return self::processFinders($raw->{'finder-bin'}, $basePath, $blacklistFilter);
672
        }
673
674
        return [];
675
    }
676
677
    /**
678
     * @param stdClass $raw
679
     * @param string   $basePath
680
     *
681
     * @return string[]
682
     */
683
    private static function retrieveDirectories(stdClass $raw, string $basePath): array
684
    {
685
        if (isset($raw->directories)) {
686
            $directories = (array) $raw->directories;
687
688
            array_walk(
689
                $directories,
690
                function (&$directory) use ($basePath): void {
691
                    $directory = $basePath
692
                        .DIRECTORY_SEPARATOR
693
                        .rtrim(canonicalize($directory), DIRECTORY_SEPARATOR);
694
                }
695
            );
696
697
            return $directories;
698
        }
699
700
        return [];
701
    }
702
703
    /**
704
     * @param string[] $directories
705
     * @param Closure  $blacklistFilter
706
     *
707
     * @return null|iterable|SplFileInfo[]
708
     */
709
    private static function retrieveDirectoriesIterator(array $directories, Closure $blacklistFilter): ?iterable
710
    {
711
        if ([] !== $directories) {
712
            return Finder::create()
0 ignored issues
show
Bug Best Practice introduced by
The expression return Symfony\Component...true)->in($directories) returns the type Symfony\Component\Finder\Finder which is incompatible with the type-hinted return null|iterable.
Loading history...
713
                ->files()
714
                ->filter($blacklistFilter)
715
                ->ignoreVCS(true)
716
                ->in($directories)
717
            ;
718
        }
719
720
        return null;
721
    }
722
723
    /**
724
     * @return SplFileInfo[]
725
     */
726
    private static function retrieveFiles(stdClass $raw, string $basePath): array
727
    {
728
        if (false === isset($raw->files)) {
729
            return [];
730
        }
731
732
        $files = [];
733
734
        foreach ((array) $raw->files as $file) {
735
            $file = new SplFileInfo(
736
                $path = $basePath.DIRECTORY_SEPARATOR.canonicalize($file)
737
            );
738
739
            if (false === $file->isFile()) {
740
                throw new InvalidArgumentException(
741
                    sprintf(
742
                        'The file "%s" does not exist or is not a file.',
743
                        $path
744
                    )
745
                );
746
            }
747
748
            $files[] = $file;
749
        }
750
751
        return $files;
752
    }
753
754
    /**
755
     * @param SplFileInfo[] $files
756
     *
757
     * @return null|iterable|SplFileInfo[]
758
     */
759
    private static function retrieveFilesIterator(array $files): ?iterable
760
    {
761
        if ([] !== $files) {
762
            return new ArrayIterator($files);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new ArrayIterator($files) returns the type ArrayIterator which is incompatible with the type-hinted return null|iterable.
Loading history...
763
        }
764
765
        return null;
766
    }
767
768
    /**
769
     * @param stdClass $raw
770
     * @param string   $basePath
771
     * @param Closure  $blacklistFilter
772
     *
773
     * @return iterable[]|SplFileInfo[][]
774
     */
775
    private static function retrieveFilesIterators(stdClass $raw, string $basePath, Closure $blacklistFilter): array
776
    {
777
        if (isset($raw->finder)) {
778
            return self::processFinders($raw->finder, $basePath, $blacklistFilter);
779
        }
780
781
        return [];
782
    }
783
784
    /**
785
     * @param array   $findersConfig   the configuration
786
     * @param string  $basePath
787
     * @param Closure $blacklistFilter
788
     *
789
     * @return Finder[]
790
     */
791
    private static function processFinders(array $findersConfig, string $basePath, Closure $blacklistFilter): array
792
    {
793
        $processFinderConfig = function ($methods) use ($basePath, $blacklistFilter): Finder {
794
            $finder = Finder::create()
795
                ->files()
796
                ->filter($blacklistFilter)
797
                ->ignoreVCS(true)
798
            ;
799
800
            if (isset($methods->in)) {
801
                $methods->in = (array) $methods->in;
802
803
                array_walk(
804
                    $methods->in,
805
                    function (&$directory) use ($basePath): void {
806
                        $directory = canonicalize(
807
                            $basePath.DIRECTORY_SEPARATOR.$directory
808
                        );
809
                    }
810
                );
811
            }
812
813
            foreach ($methods as $method => $arguments) {
814
                if (false === method_exists($finder, $method)) {
815
                    throw new InvalidArgumentException(
816
                        sprintf(
817
                            'The method "Finder::%s" does not exist.',
818
                            $method
819
                        )
820
                    );
821
                }
822
823
                $arguments = (array) $arguments;
824
825
                foreach ($arguments as $argument) {
826
                    $finder->$method($argument);
827
                }
828
            }
829
830
            return $finder;
831
        };
832
833
        return array_map($processFinderConfig, $findersConfig);
834
    }
835
836
    private static function retrieveBootstrapFile(stdClass $raw, string $basePath): ?string
837
    {
838
        if (false === isset($raw->bootstrap)) {
839
            return null;
840
        }
841
842
        $file = $raw->bootstrap;
843
844
        if (false === is_absolute($file)) {
845
            $file = canonicalize(
846
                $basePath.DIRECTORY_SEPARATOR.$file
847
            );
848
        }
849
850
        if (false === file_exists($file)) {
851
            throw new InvalidArgumentException(
852
                sprintf(
853
                    'The bootstrap path "%s" is not a file or does not exist.',
854
                    $file
855
                )
856
            );
857
        }
858
859
        return $file;
860
    }
861
862
    /**
863
     * @return Compactor[]
864
     */
865
    private static function retrieveCompactors(stdClass $raw): array
866
    {
867
        if (false === isset($raw->compactors)) {
868
            return [];
869
        }
870
871
        $compactors = [];
872
873
        foreach ((array) $raw->compactors as $class) {
874
            if (false === class_exists($class)) {
875
                throw new InvalidArgumentException(
876
                    sprintf(
877
                        'The compactor class "%s" does not exist.',
878
                        $class
879
                    )
880
                );
881
            }
882
883
            if (Php::class === $class || LegacyPhp::class === $class) {
884
                $compactor = self::createPhpCompactor($raw);
885
            } else {
886
                $compactor = new $class();
887
            }
888
889
            if (false === ($compactor instanceof Compactor)) {
890
                throw new InvalidArgumentException(
891
                    sprintf(
892
                        'The class "%s" is not a compactor class.',
893
                        $class
894
                    )
895
                );
896
            }
897
898
            $compactors[] = $compactor;
899
        }
900
901
        return $compactors;
902
    }
903
904
    private static function retrieveCompressionAlgorithm(stdClass $raw): ?int
905
    {
906
        if (false === isset($raw->compression)) {
907
            return null;
908
        }
909
910
        if (false === is_string($raw->compression)) {
911
            Assertion::integer(
912
                $raw->compression,
913
                'Expected compression to be an algorithm name, found %s instead.'
914
            );
915
916
            return $raw->compression;
1 ignored issue
show
Bug Best Practice introduced by
The expression return $raw->compression returns the type string which is incompatible with the type-hinted return null|integer.
Loading history...
917
        }
918
919
        $knownAlgorithmNames = array_keys(get_phar_compression_algorithms());
920
921
        Assertion::inArray(
922
            $raw->compression,
923
            $knownAlgorithmNames,
924
            sprintf(
925
                'Invalid compression algorithm "%%s", use one of "%s" instead.',
926
                implode('", "', $knownAlgorithmNames)
927
            )
928
        );
929
930
        $value = get_phar_compression_algorithms()[$raw->compression];
931
932
        // Phar::NONE is not valid for compressFiles()
933
        if (Phar::NONE === $value) {
934
            return null;
935
        }
936
937
        return $value;
938
    }
939
940
    private static function retrieveFileMode(stdClass $raw): ?int
941
    {
942
        if (isset($raw->chmod)) {
943
            return intval($raw->chmod, 8);
944
        }
945
946
        return null;
947
    }
948
949
    private static function retrieveMainScriptPath(stdClass $raw): ?string
950
    {
951
        if (isset($raw->main)) {
952
            return canonicalize($raw->main);
953
        }
954
955
        return null;
956
    }
957
958
    private static function retrieveMainScriptContents(?string $mainScriptPath, string $basePath): ?string
959
    {
960
        if (null === $mainScriptPath) {
961
            return null;
962
        }
963
        $mainScriptPath = $basePath.DIRECTORY_SEPARATOR.$mainScriptPath;
964
965
        if (false === ($contents = @file_get_contents($mainScriptPath))) {
0 ignored issues
show
introduced by
The condition false === $contents = @f...ntents($mainScriptPath) can never be true.
Loading history...
966
            $errors = error_get_last();
967
968
            if (null === $errors) {
969
                $errors = ['message' => 'Failed to get contents of "'.$mainScriptPath.'""'];
970
            }
971
972
            throw new InvalidArgumentException($errors['message']);
973
        }
974
975
        return preg_replace('/^#!.*\s*/', '', $contents);
976
    }
977
978
    /**
979
     * @return string[]
980
     */
981
    private static function retrieveMap(stdClass $raw): array
982
    {
983
        if (false === isset($raw->map)) {
984
            return [];
985
        }
986
987
        $map = [];
988
989
        foreach ((array) $raw->map as $item) {
990
            $processed = [];
991
992
            foreach ($item as $match => $replace) {
993
                $processed[canonicalize($match)] = canonicalize($replace);
994
            }
995
996
            if (isset($processed['_empty_'])) {
997
                $processed[''] = $processed['_empty_'];
998
999
                unset($processed['_empty_']);
1000
            }
1001
1002
            $map[] = $processed;
1003
        }
1004
1005
        return $map;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $map returns an array which contains values of type array|string[] which are incompatible with the documented value type string.
Loading history...
1006
    }
1007
1008
    private static function retrieveMapper(array $map): callable
1009
    {
1010
        return new MapFile($map);
1011
    }
1012
1013
    /**
1014
     * @return mixed
1015
     */
1016
    private static function retrieveMetadata(stdClass $raw)
1017
    {
1018
        if (isset($raw->metadata)) {
1019
            if (is_object($raw->metadata)) {
1020
                return (array) $raw->metadata;
1021
            }
1022
1023
            return $raw->metadata;
1024
        }
1025
1026
        return null;
1027
    }
1028
1029
    private static function retrieveMimetypeMapping(stdClass $raw): array
1030
    {
1031
        if (isset($raw->mimetypes)) {
1032
            return (array) $raw->mimetypes;
1033
        }
1034
1035
        return [];
1036
    }
1037
1038
    private static function retrieveMungVariables(stdClass $raw): array
1039
    {
1040
        if (isset($raw->mung)) {
1041
            return (array) $raw->mung;
1042
        }
1043
1044
        return [];
1045
    }
1046
1047
    private static function retrieveNotFoundScriptPath(stdClass $raw): ?string
1048
    {
1049
        if (isset($raw->{'not-found'})) {
1050
            return $raw->{'not-found'};
1051
        }
1052
1053
        return null;
1054
    }
1055
1056
    private static function retrieveOutputPath(stdClass $raw, string $file): string
1057
    {
1058
        $base = getcwd().DIRECTORY_SEPARATOR;
1059
1060
        if (isset($raw->output)) {
1061
            $path = $raw->output;
1062
1063
            if (false === is_absolute($path)) {
1064
                $path = canonicalize($base.$path);
1065
            }
1066
        } else {
1067
            $path = $base.self::DEFAULT_ALIAS;
1068
        }
1069
1070
        if (false !== strpos($path, '@'.'git-version@')) {
1071
            $gitVersion = self::retrieveGitVersion($file);
1072
1073
            $path = str_replace('@'.'git-version@', $gitVersion, $path);
1074
        }
1075
1076
        return $path;
1077
    }
1078
1079
    private static function retrievePrivateKeyPassphrase(stdClass $raw): ?string
1080
    {
1081
        if (isset($raw->{'key-pass'})
1082
            && is_string($raw->{'key-pass'})
1083
        ) {
1084
            return $raw->{'key-pass'};
1085
        }
1086
1087
        return null;
1088
    }
1089
1090
    private static function retrievePrivateKeyPath(stdClass $raw): ?string
1091
    {
1092
        if (isset($raw->key)) {
1093
            return $raw->key;
1094
        }
1095
1096
        return null;
1097
    }
1098
1099
    private static function retrieveReplacements(stdClass $raw): array
1100
    {
1101
        if (isset($raw->replacements)) {
1102
            return (array) $raw->replacements;
1103
        }
1104
1105
        return [];
1106
    }
1107
1108
    private static function retrieveProcessedReplacements(
1109
        array $replacements,
1110
        stdClass $raw,
1111
        string $file
1112
    ): array {
1113
        if (null !== ($git = self::retrieveGitHashPlaceholder($raw))) {
1114
            $replacements[$git] = self::retrieveGitHash($file);
1115
        }
1116
1117
        if (null !== ($git = self::retrieveGitShortHashPlaceholder($raw))) {
1118
            $replacements[$git] = self::retrieveGitHash($file, true);
1119
        }
1120
1121
        if (null !== ($git = self::retrieveGitTagPlaceholder($raw))) {
1122
            $replacements[$git] = self::retrieveGitTag($file);
1123
        }
1124
1125
        if (null !== ($git = self::retrieveGitVersionPlaceholder($raw))) {
1126
            $replacements[$git] = self::retrieveGitVersion($file);
1127
        }
1128
1129
        if (null !== ($date = self::retrieveDatetimeNowPlaceHolder($raw))) {
1130
            $replacements[$date] = self::retrieveDatetimeNow(
1131
                self::retrieveDatetimeFormat($raw)
1132
            );
1133
        }
1134
1135
        $sigil = self::retrieveReplacementSigil($raw);
1136
1137
        foreach ($replacements as $key => $value) {
1138
            unset($replacements[$key]);
1139
            $replacements["$sigil$key$sigil"] = $value;
1140
        }
1141
1142
        return $replacements;
1143
    }
1144
1145
    private static function retrieveGitHashPlaceholder(stdClass $raw): ?string
1146
    {
1147
        if (isset($raw->{'git-commit'})) {
1148
            return $raw->{'git-commit'};
1149
        }
1150
1151
        return null;
1152
    }
1153
1154
    /**
1155
     * @param string $file
1156
     * @param bool   $short Use the short version
1157
     *
1158
     * @return string the commit hash
1159
     */
1160
    private static function retrieveGitHash(string $file, bool $short = false): string
1161
    {
1162
        return self::runGitCommand(
1163
            sprintf(
1164
                'git log --pretty="%s" -n1 HEAD',
1165
                $short ? '%h' : '%H'
1166
            ),
1167
            $file
1168
        );
1169
    }
1170
1171
    private static function retrieveGitShortHashPlaceholder(stdClass $raw): ?string
1172
    {
1173
        if (isset($raw->{'git-commit-short'})) {
1174
            return $raw->{'git-commit-short'};
1175
        }
1176
1177
        return null;
1178
    }
1179
1180
    private static function retrieveGitTagPlaceholder(stdClass $raw): ?string
1181
    {
1182
        if (isset($raw->{'git-tag'})) {
1183
            return $raw->{'git-tag'};
1184
        }
1185
1186
        return null;
1187
    }
1188
1189
    private static function retrieveGitTag(string $file): ?string
1190
    {
1191
        return self::runGitCommand('git describe --tags HEAD', $file);
1192
    }
1193
1194
    private static function retrieveGitVersionPlaceholder(stdClass $raw): ?string
1195
    {
1196
        if (isset($raw->{'git-version'})) {
1197
            return $raw->{'git-version'};
1198
        }
1199
1200
        return null;
1201
    }
1202
1203
    private static function retrieveGitVersion(string $file): ?string
1204
    {
1205
        try {
1206
            return self::retrieveGitTag($file);
1207
        } catch (RuntimeException $exception) {
1208
            try {
1209
                return self::retrieveGitHash($file, true);
1210
            } catch (RuntimeException $exception) {
1211
                throw new RuntimeException(
1212
                    sprintf(
1213
                        'The tag or commit hash could not be retrieved from "%s": %s',
1214
                        dirname($file),
1215
                        $exception->getMessage()
1216
                    ),
1217
                    0,
1218
                    $exception
1219
                );
1220
            }
1221
        }
1222
    }
1223
1224
    private static function retrieveDatetimeNowPlaceHolder(stdClass $raw): ?string
1225
    {
1226
        if (isset($raw->{'datetime'})) {
1227
            return $raw->{'datetime'};
1228
        }
1229
1230
        return null;
1231
    }
1232
1233
    private static function retrieveDatetimeNow(string $format)
1234
    {
1235
        $now = new DateTimeImmutable('now');
1236
1237
        $datetime = $now->format($format);
1238
1239
        if (!$datetime) {
1240
            throw new InvalidArgumentException(
1241
                sprintf(
1242
                    '""%s" is not a valid PHP date format',
1243
                    $format
1244
                )
1245
            );
1246
        }
1247
1248
        return $datetime;
1249
    }
1250
1251
    private static function retrieveDatetimeFormat(stdClass $raw): string
1252
    {
1253
        if (isset($raw->{'datetime_format'})) {
1254
            return $raw->{'datetime_format'};
1255
        }
1256
1257
        return self::DEFAULT_DATETIME_FORMAT;
1258
    }
1259
1260
    private static function retrieveReplacementSigil(stdClass $raw)
1261
    {
1262
        if (isset($raw->{'replacement-sigil'})) {
1263
            return $raw->{'replacement-sigil'};
1264
        }
1265
1266
        return self::DEFAULT_REPLACEMENT_SIGIL;
1267
    }
1268
1269
    private static function retrieveShebang(stdClass $raw): ?string
1270
    {
1271
        if (false === isset($raw->shebang)) {
1272
            return null;
1273
        }
1274
1275
        if (('' === $raw->shebang) || (false === $raw->shebang)) {
1276
            return '';
1277
        }
1278
1279
        $shebang = trim($raw->shebang);
1280
1281
        if ('#!' !== substr($shebang, 0, 2)) {
1282
            throw new InvalidArgumentException(
1283
                sprintf(
1284
                    'The shebang line must start with "#!": %s',
1285
                    $shebang
1286
                )
1287
            );
1288
        }
1289
1290
        return $shebang;
1291
    }
1292
1293
    private static function retrieveSigningAlgorithm(stdClass $raw): int
1294
    {
1295
        if (false === isset($raw->algorithm)) {
1296
            return Phar::SHA1;
1297
        }
1298
1299
        if (is_string($raw->algorithm)) {
1300
            if (false === defined('Phar::'.$raw->algorithm)) {
1301
                throw new InvalidArgumentException(
1302
                    sprintf(
1303
                        'The signing algorithm "%s" is not supported.',
1304
                        $raw->algorithm
1305
                    )
1306
                );
1307
            }
1308
1309
            return constant('Phar::'.$raw->algorithm);
1310
        }
1311
1312
        return $raw->algorithm;
1313
    }
1314
1315
    private static function retrieveStubBanner(stdClass $raw): ?string
1316
    {
1317
        if (isset($raw->{'banner'})) {
1318
            return $raw->{'banner'};
1319
        }
1320
1321
        return null;
1322
    }
1323
1324
    private static function retrieveStubBannerPath(stdClass $raw): ?string
1325
    {
1326
        if (isset($raw->{'banner-file'})) {
1327
            return canonicalize($raw->{'banner-file'});
1328
        }
1329
1330
        return null;
1331
    }
1332
1333
    private static function retrieveStubBannerFromFile(string $basePath, ?string $stubBannerPath): ?string
1334
    {
1335
        if (null == $stubBannerPath) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $stubBannerPath of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1336
            return null;
1337
        }
1338
1339
        $stubBannerPath = $basePath.DIRECTORY_SEPARATOR.$stubBannerPath;
1340
1341
        if (false === ($contents = @file_get_contents($stubBannerPath))) {
0 ignored issues
show
introduced by
The condition false === $contents = @f...ntents($stubBannerPath) can never be true.
Loading history...
1342
            $errors = error_get_last();
1343
1344
            if (null === $errors) {
1345
                $errors = ['message' => 'failed to get contents of "'.$stubBannerPath.'""'];
1346
            }
1347
1348
            throw new InvalidArgumentException($errors['message']);
1349
        }
1350
1351
        return $contents;
1352
    }
1353
1354
    private static function retrieveStubPath(stdClass $raw): ?string
1355
    {
1356
        if (isset($raw->stub) && is_string($raw->stub)) {
1357
            return $raw->stub;
1358
        }
1359
1360
        return null;
1361
    }
1362
1363
    private static function retrieveIsExtractable(stdClass $raw): bool
1364
    {
1365
        if (isset($raw->extract)) {
1366
            return $raw->extract;
1367
        }
1368
1369
        return false;
1370
    }
1371
1372
    private static function retrieveIsInterceptFileFuncs(stdClass $raw): bool
1373
    {
1374
        if (isset($raw->intercept)) {
1375
            return $raw->intercept;
1376
        }
1377
1378
        return false;
1379
    }
1380
1381
    private static function retrieveIsPrivateKeyPrompt(stdClass $raw): bool
1382
    {
1383
        if (isset($raw->{'key-pass'})
1384
            && (true === $raw->{'key-pass'})) {
1385
            return true;
1386
        }
1387
1388
        return false;
1389
    }
1390
1391
    private static function retrieveIsStubGenerated(stdClass $raw): bool
1392
    {
1393
        if (isset($raw->stub) && (true === $raw->stub)) {
1394
            return true;
1395
        }
1396
1397
        return false;
1398
    }
1399
1400
    private static function retrieveIsWebPhar(stdClass $raw): bool
1401
    {
1402
        if (isset($raw->web)) {
1403
            return $raw->web;
1404
        }
1405
1406
        return false;
1407
    }
1408
1409
    /**
1410
     * Runs a Git command on the repository.
1411
     *
1412
     * @param string $command the command
1413
     *
1414
     * @return string the trimmed output from the command
1415
     */
1416
    private static function runGitCommand(string $command, string $file): string
1417
    {
1418
        $path = dirname($file);
1419
1420
        $process = new Process($command, $path);
1421
1422
        if (0 === $process->run()) {
1423
            return trim($process->getOutput());
1424
        }
1425
1426
        throw new RuntimeException(
1427
            sprintf(
1428
                'The tag or commit hash could not be retrieved from "%s": %s',
1429
                $path,
1430
                $process->getErrorOutput()
1431
            )
1432
        );
1433
    }
1434
1435
    private static function createPhpCompactor(stdClass $raw): Compactor
1436
    {
1437
        $tokenizer = new Tokenizer();
1438
1439
        if (false === empty($raw->annotations) && isset($raw->annotations->ignore)) {
1440
            $tokenizer->ignore(
1441
                (array) $raw->annotations->ignore
1442
            );
1443
        }
1444
1445
        return new Php($tokenizer);
1446
    }
1447
}
1448