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