Passed
Push — master ( 4165df...b83769 )
by Théo
01:49
created

Configuration::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 74
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 74
rs 9.0335
c 0
b 0
f 0
cc 1
eloc 36
nc 1
nop 36

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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