Completed
Pull Request — master (#34)
by Théo
02:20
created

Configuration::isPrivateKeyPrompt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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