ConfigurationResolver::getFinder()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <[email protected]>
9
 *     Dariusz Rumiński <[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 PhpCsFixer\Console;
16
17
use PhpCsFixer\Cache\CacheManagerInterface;
18
use PhpCsFixer\Cache\Directory;
19
use PhpCsFixer\Cache\DirectoryInterface;
20
use PhpCsFixer\Cache\FileCacheManager;
21
use PhpCsFixer\Cache\FileHandler;
22
use PhpCsFixer\Cache\NullCacheManager;
23
use PhpCsFixer\Cache\Signature;
24
use PhpCsFixer\ConfigInterface;
25
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
26
use PhpCsFixer\Console\Report\FixReport\ReporterFactory;
27
use PhpCsFixer\Console\Report\FixReport\ReporterInterface;
28
use PhpCsFixer\Differ\DifferInterface;
29
use PhpCsFixer\Differ\NullDiffer;
30
use PhpCsFixer\Differ\UnifiedDiffer;
31
use PhpCsFixer\Finder;
32
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
33
use PhpCsFixer\Fixer\FixerInterface;
34
use PhpCsFixer\FixerFactory;
35
use PhpCsFixer\Linter\Linter;
36
use PhpCsFixer\Linter\LinterInterface;
37
use PhpCsFixer\RuleSet\RuleSet;
38
use PhpCsFixer\RuleSet\RuleSetInterface;
39
use PhpCsFixer\StdinFileInfo;
40
use PhpCsFixer\ToolInfoInterface;
41
use PhpCsFixer\Utils;
42
use PhpCsFixer\WhitespacesFixerConfig;
43
use PhpCsFixer\WordMatcher;
44
use Symfony\Component\Console\Output\OutputInterface;
45
use Symfony\Component\Filesystem\Filesystem;
46
use Symfony\Component\Finder\Finder as SymfonyFinder;
47
48
/**
49
 * The resolver that resolves configuration to use by command line options and config.
50
 *
51
 * @author Fabien Potencier <[email protected]>
52
 * @author Katsuhiro Ogawa <[email protected]>
53
 * @author Dariusz Rumiński <[email protected]>
54
 *
55
 * @internal
56
 */
57
final class ConfigurationResolver
58
{
59
    public const PATH_MODE_OVERRIDE = 'override';
60
    public const PATH_MODE_INTERSECTION = 'intersection';
61
62
    /**
63
     * @var null|bool
64
     */
65
    private $allowRisky;
66
67
    /**
68
     * @var null|ConfigInterface
69
     */
70
    private $config;
71
72
    /**
73
     * @var null|string
74
     */
75
    private $configFile;
76
77
    /**
78
     * @var string
79
     */
80
    private $cwd;
81
82
    /**
83
     * @var ConfigInterface
84
     */
85
    private $defaultConfig;
86
87
    /**
88
     * @var null|ReporterInterface
89
     */
90
    private $reporter;
91
92
    /**
93
     * @var null|bool
94
     */
95
    private $isStdIn;
96
97
    /**
98
     * @var null|bool
99
     */
100
    private $isDryRun;
101
102
    /**
103
     * @var null|FixerInterface[]
104
     */
105
    private $fixers;
106
107
    /**
108
     * @var null|bool
109
     */
110
    private $configFinderIsOverridden;
111
112
    /**
113
     * @var ToolInfoInterface
114
     */
115
    private $toolInfo;
116
117
    /**
118
     * @var array
119
     */
120
    private $options = [
121
        'allow-risky' => null,
122
        'cache-file' => null,
123
        'config' => null,
124
        'diff' => null,
125
        'dry-run' => null,
126
        'format' => null,
127
        'path' => [],
128
        'path-mode' => self::PATH_MODE_OVERRIDE,
129
        'rules' => null,
130
        'show-progress' => null,
131
        'stop-on-violation' => null,
132
        'using-cache' => null,
133
        'verbosity' => null,
134
    ];
135
136
    private $cacheFile;
137
138
    /**
139
     * @var null|CacheManagerInterface
140
     */
141
    private $cacheManager;
142
143
    /**
144
     * @var null|DifferInterface
145
     */
146
    private $differ;
147
148
    /**
149
     * @var null|Directory
150
     */
151
    private $directory;
152
153
    /**
154
     * @var null|iterable
155
     */
156
    private $finder;
157
158
    private $format;
159
160
    /**
161
     * @var null|Linter
162
     */
163
    private $linter;
164
165
    private $path;
166
167
    /**
168
     * @var null|string
169
     */
170
    private $progress;
171
172
    /**
173
     * @var null|RuleSet
174
     */
175
    private $ruleSet;
176
177
    /**
178
     * @var null|bool
179
     */
180
    private $usingCache;
181
182
    /**
183
     * @var FixerFactory
184
     */
185
    private $fixerFactory;
186
187
    public function __construct(
188
        ConfigInterface $config,
189
        array $options,
190
        string $cwd,
191
        ToolInfoInterface $toolInfo
192
    ) {
193
        $this->cwd = $cwd;
194
        $this->defaultConfig = $config;
195
        $this->toolInfo = $toolInfo;
196
197
        foreach ($options as $name => $value) {
198
            $this->setOption($name, $value);
199
        }
200
    }
201
202
    public function getCacheFile(): ?string
203
    {
204
        if (!$this->getUsingCache()) {
205
            return null;
206
        }
207
208
        if (null === $this->cacheFile) {
209
            if (null === $this->options['cache-file']) {
210
                $this->cacheFile = $this->getConfig()->getCacheFile();
211
            } else {
212
                $this->cacheFile = $this->options['cache-file'];
213
            }
214
        }
215
216
        return $this->cacheFile;
217
    }
218
219
    public function getCacheManager(): CacheManagerInterface
220
    {
221
        if (null === $this->cacheManager) {
222
            $cacheFile = $this->getCacheFile();
223
224
            if (null === $cacheFile) {
225
                $this->cacheManager = new NullCacheManager();
226
            } else {
227
                $this->cacheManager = new FileCacheManager(
228
                    new FileHandler($cacheFile),
229
                    new Signature(
230
                        PHP_VERSION,
231
                        $this->toolInfo->getVersion(),
232
                        $this->getConfig()->getIndent(),
233
                        $this->getConfig()->getLineEnding(),
234
                        $this->getRules()
235
                    ),
236
                    $this->isDryRun(),
237
                    $this->getDirectory()
238
                );
239
            }
240
        }
241
242
        return $this->cacheManager;
243
    }
244
245
    public function getConfig(): ConfigInterface
246
    {
247
        if (null === $this->config) {
248
            foreach ($this->computeConfigFiles() as $configFile) {
249
                if (!file_exists($configFile)) {
250
                    continue;
251
                }
252
253
                $configFileBasename = basename($configFile);
254
                $deprecatedConfigs = [
255
                    '.php_cs' => '.php-cs-fixer.php',
256
                    '.php_cs.dist' => '.php-cs-fixer.dist.php',
257
                ];
258
259
                if (isset($deprecatedConfigs[$configFileBasename])) {
260
                    throw new InvalidConfigurationException("Configuration file `{$configFileBasename}` is outdated, rename to `{$deprecatedConfigs[$configFileBasename]}`.");
261
                }
262
263
                $this->config = self::separatedContextLessInclude($configFile);
264
                $this->configFile = $configFile;
265
266
                break;
267
            }
268
269
            if (null === $this->config) {
270
                $this->config = $this->defaultConfig;
271
            }
272
        }
273
274
        return $this->config;
275
    }
276
277
    public function getConfigFile(): ?string
278
    {
279
        if (null === $this->configFile) {
280
            $this->getConfig();
281
        }
282
283
        return $this->configFile;
284
    }
285
286
    public function getDiffer(): DifferInterface
287
    {
288
        if (null === $this->differ) {
289
            if ($this->options['diff']) {
290
                $this->differ = new UnifiedDiffer();
291
            } else {
292
                $this->differ = new NullDiffer();
293
            }
294
        }
295
296
        return $this->differ;
297
    }
298
299
    public function getDirectory(): DirectoryInterface
300
    {
301
        if (null === $this->directory) {
302
            $path = $this->getCacheFile();
303
            if (null === $path) {
304
                $absolutePath = $this->cwd;
305
            } else {
306
                $filesystem = new Filesystem();
307
308
                $absolutePath = $filesystem->isAbsolutePath($path)
309
                    ? $path
310
                    : $this->cwd.\DIRECTORY_SEPARATOR.$path;
311
            }
312
313
            $this->directory = new Directory(\dirname($absolutePath));
314
        }
315
316
        return $this->directory;
317
    }
318
319
    /**
320
     * @return FixerInterface[] An array of FixerInterface
321
     */
322
    public function getFixers(): array
323
    {
324
        if (null === $this->fixers) {
325
            $this->fixers = $this->createFixerFactory()
326
                ->useRuleSet($this->getRuleSet())
327
                ->setWhitespacesConfig(new WhitespacesFixerConfig($this->config->getIndent(), $this->config->getLineEnding()))
0 ignored issues
show
Bug introduced by
The method getIndent() does not exist on null. ( Ignorable by Annotation )

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

327
                ->setWhitespacesConfig(new WhitespacesFixerConfig($this->config->/** @scrutinizer ignore-call */ getIndent(), $this->config->getLineEnding()))

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
328
                ->getFixers()
329
            ;
330
331
            if (false === $this->getRiskyAllowed()) {
332
                $riskyFixers = array_map(
333
                    static function (FixerInterface $fixer) {
334
                        return $fixer->getName();
335
                    },
336
                    array_filter(
337
                        $this->fixers,
338
                        static function (FixerInterface $fixer) {
339
                            return $fixer->isRisky();
340
                        }
341
                    )
342
                );
343
344
                if (\count($riskyFixers)) {
345
                    throw new InvalidConfigurationException(sprintf('The rules contain risky fixers ("%s"), but they are not allowed to run. Perhaps you forget to use --allow-risky=yes option?', implode('", "', $riskyFixers)));
346
                }
347
            }
348
        }
349
350
        return $this->fixers;
351
    }
352
353
    public function getLinter(): LinterInterface
354
    {
355
        if (null === $this->linter) {
356
            $this->linter = new Linter($this->getConfig()->getPhpExecutable());
357
        }
358
359
        return $this->linter;
360
    }
361
362
    /**
363
     * Returns path.
364
     *
365
     * @return string[]
366
     */
367
    public function getPath(): array
368
    {
369
        if (null === $this->path) {
370
            $filesystem = new Filesystem();
371
            $cwd = $this->cwd;
372
373
            if (1 === \count($this->options['path']) && '-' === $this->options['path'][0]) {
374
                $this->path = $this->options['path'];
375
            } else {
376
                $this->path = array_map(
377
                    static function (string $rawPath) use ($cwd, $filesystem) {
378
                        $path = trim($rawPath);
379
380
                        if ('' === $path) {
381
                            throw new InvalidConfigurationException("Invalid path: \"{$rawPath}\".");
382
                        }
383
384
                        $absolutePath = $filesystem->isAbsolutePath($path)
385
                            ? $path
386
                            : $cwd.\DIRECTORY_SEPARATOR.$path;
387
388
                        if (!file_exists($absolutePath)) {
389
                            throw new InvalidConfigurationException(sprintf(
390
                                'The path "%s" is not readable.',
391
                                $path
392
                            ));
393
                        }
394
395
                        return $absolutePath;
396
                    },
397
                    $this->options['path']
398
                );
399
            }
400
        }
401
402
        return $this->path;
403
    }
404
405
    /**
406
     * @throws InvalidConfigurationException
407
     */
408
    public function getProgress(): string
409
    {
410
        if (null === $this->progress) {
411
            if (OutputInterface::VERBOSITY_VERBOSE <= $this->options['verbosity'] && 'txt' === $this->getFormat()) {
412
                $progressType = $this->options['show-progress'];
413
                $progressTypes = ['none', 'dots'];
414
415
                if (null === $progressType) {
416
                    $progressType = $this->getConfig()->getHideProgress() ? 'none' : 'dots';
417
                } elseif (!\in_array($progressType, $progressTypes, true)) {
418
                    throw new InvalidConfigurationException(sprintf(
419
                        'The progress type "%s" is not defined, supported are "%s".',
420
                        $progressType,
421
                        implode('", "', $progressTypes)
422
                    ));
423
                }
424
425
                $this->progress = $progressType;
426
            } else {
427
                $this->progress = 'none';
428
            }
429
        }
430
431
        return $this->progress;
432
    }
433
434
    public function getReporter(): ReporterInterface
435
    {
436
        if (null === $this->reporter) {
437
            $reporterFactory = new ReporterFactory();
438
            $reporterFactory->registerBuiltInReporters();
439
440
            $format = $this->getFormat();
441
442
            try {
443
                $this->reporter = $reporterFactory->getReporter($format);
444
            } catch (\UnexpectedValueException $e) {
445
                $formats = $reporterFactory->getFormats();
446
                sort($formats);
447
448
                throw new InvalidConfigurationException(sprintf('The format "%s" is not defined, supported are "%s".', $format, implode('", "', $formats)));
449
            }
450
        }
451
452
        return $this->reporter;
453
    }
454
455
    public function getRiskyAllowed(): bool
456
    {
457
        if (null === $this->allowRisky) {
458
            if (null === $this->options['allow-risky']) {
459
                $this->allowRisky = $this->getConfig()->getRiskyAllowed();
460
            } else {
461
                $this->allowRisky = $this->resolveOptionBooleanValue('allow-risky');
462
            }
463
        }
464
465
        return $this->allowRisky;
466
    }
467
468
    /**
469
     * Returns rules.
470
     */
471
    public function getRules(): array
472
    {
473
        return $this->getRuleSet()->getRules();
474
    }
475
476
    public function getUsingCache(): bool
477
    {
478
        if (null === $this->usingCache) {
479
            if (null === $this->options['using-cache']) {
480
                $this->usingCache = $this->getConfig()->getUsingCache();
481
            } else {
482
                $this->usingCache = $this->resolveOptionBooleanValue('using-cache');
483
            }
484
        }
485
486
        $this->usingCache = $this->usingCache && ($this->toolInfo->isInstalledAsPhar() || $this->toolInfo->isInstalledByComposer());
487
488
        return $this->usingCache;
489
    }
490
491
    public function getFinder(): iterable
492
    {
493
        if (null === $this->finder) {
494
            $this->finder = $this->resolveFinder();
495
        }
496
497
        return $this->finder;
498
    }
499
500
    /**
501
     * Returns dry-run flag.
502
     */
503
    public function isDryRun(): bool
504
    {
505
        if (null === $this->isDryRun) {
506
            if ($this->isStdIn()) {
507
                // Can't write to STDIN
508
                $this->isDryRun = true;
509
            } else {
510
                $this->isDryRun = $this->options['dry-run'];
511
            }
512
        }
513
514
        return $this->isDryRun;
515
    }
516
517
    public function shouldStopOnViolation(): bool
518
    {
519
        return $this->options['stop-on-violation'];
520
    }
521
522
    public function configFinderIsOverridden(): bool
523
    {
524
        if (null === $this->configFinderIsOverridden) {
525
            $this->resolveFinder();
526
        }
527
528
        return $this->configFinderIsOverridden;
529
    }
530
531
    /**
532
     * Compute file candidates for config file.
533
     *
534
     * @return string[]
535
     */
536
    private function computeConfigFiles(): array
537
    {
538
        $configFile = $this->options['config'];
539
540
        if (null !== $configFile) {
541
            if (false === file_exists($configFile) || false === is_readable($configFile)) {
542
                throw new InvalidConfigurationException(sprintf('Cannot read config file "%s".', $configFile));
543
            }
544
545
            return [$configFile];
546
        }
547
548
        $path = $this->getPath();
549
550
        if ($this->isStdIn() || 0 === \count($path)) {
551
            $configDir = $this->cwd;
552
        } elseif (1 < \count($path)) {
553
            throw new InvalidConfigurationException('For multiple paths config parameter is required.');
554
        } elseif (!is_file($path[0])) {
555
            $configDir = $path[0];
556
        } else {
557
            $dirName = pathinfo($path[0], PATHINFO_DIRNAME);
558
            $configDir = $dirName ?: $path[0];
559
        }
560
561
        $candidates = [
562
            $configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php',
563
            $configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php',
564
            $configDir.\DIRECTORY_SEPARATOR.'.php_cs', // old v2 config, present here only to throw nice error message later
565
            $configDir.\DIRECTORY_SEPARATOR.'.php_cs.dist', // old v2 config, present here only to throw nice error message later
566
        ];
567
568
        if ($configDir !== $this->cwd) {
569
            $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php';
570
            $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php';
571
            $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs'; // old v2 config, present here only to throw nice error message later
572
            $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs.dist'; // old v2 config, present here only to throw nice error message later
573
        }
574
575
        return $candidates;
576
    }
577
578
    private function createFixerFactory(): FixerFactory
579
    {
580
        if (null === $this->fixerFactory) {
581
            $fixerFactory = new FixerFactory();
582
            $fixerFactory->registerBuiltInFixers();
583
            $fixerFactory->registerCustomFixers($this->getConfig()->getCustomFixers());
584
585
            $this->fixerFactory = $fixerFactory;
586
        }
587
588
        return $this->fixerFactory;
589
    }
590
591
    private function getFormat(): string
592
    {
593
        if (null === $this->format) {
594
            $this->format = null === $this->options['format']
595
                ? $this->getConfig()->getFormat()
596
                : $this->options['format'];
597
        }
598
599
        return $this->format;
600
    }
601
602
    private function getRuleSet(): RuleSetInterface
603
    {
604
        if (null === $this->ruleSet) {
605
            $rules = $this->parseRules();
606
            $this->validateRules($rules);
607
608
            $this->ruleSet = new RuleSet($rules);
609
        }
610
611
        return $this->ruleSet;
612
    }
613
614
    private function isStdIn(): bool
615
    {
616
        if (null === $this->isStdIn) {
617
            $this->isStdIn = 1 === \count($this->options['path']) && '-' === $this->options['path'][0];
618
        }
619
620
        return $this->isStdIn;
621
    }
622
623
    private function iterableToTraversable(iterable $iterable): \Traversable
624
    {
625
        return \is_array($iterable) ? new \ArrayIterator($iterable) : $iterable;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_array($iterabl...($iterable) : $iterable could return the type iterable which is incompatible with the type-hinted return Traversable. Consider adding an additional type-check to rule them out.
Loading history...
626
    }
627
628
    /**
629
     * Compute rules.
630
     */
631
    private function parseRules(): array
632
    {
633
        if (null === $this->options['rules']) {
634
            return $this->getConfig()->getRules();
635
        }
636
637
        $rules = trim($this->options['rules']);
638
        if ('' === $rules) {
639
            throw new InvalidConfigurationException('Empty rules value is not allowed.');
640
        }
641
642
        if ('{' === $rules[0]) {
643
            $rules = json_decode($rules, true);
644
            if (JSON_ERROR_NONE !== json_last_error()) {
645
                throw new InvalidConfigurationException(sprintf('Invalid JSON rules input: "%s".', json_last_error_msg()));
646
            }
647
648
            return $rules;
649
        }
650
651
        $rules = [];
652
653
        foreach (explode(',', $this->options['rules']) as $rule) {
654
            $rule = trim($rule);
655
            if ('' === $rule) {
656
                throw new InvalidConfigurationException('Empty rule name is not allowed.');
657
            }
658
659
            if ('-' === $rule[0]) {
660
                $rules[substr($rule, 1)] = false;
661
            } else {
662
                $rules[$rule] = true;
663
            }
664
        }
665
666
        return $rules;
667
    }
668
669
    /**
670
     * @throws InvalidConfigurationException
671
     */
672
    private function validateRules(array $rules): void
673
    {
674
        /**
675
         * Create a ruleset that contains all configured rules, even when they originally have been disabled.
676
         *
677
         * @see RuleSet::resolveSet()
678
         */
679
        $ruleSet = [];
680
        foreach ($rules as $key => $value) {
681
            if (\is_int($key)) {
682
                throw new InvalidConfigurationException(sprintf('Missing value for "%s" rule/set.', $value));
683
            }
684
685
            $ruleSet[$key] = true;
686
        }
687
        $ruleSet = new RuleSet($ruleSet);
688
689
        /** @var string[] $configuredFixers */
690
        $configuredFixers = array_keys($ruleSet->getRules());
691
692
        $fixers = $this->createFixerFactory()->getFixers();
693
694
        /** @var string[] $availableFixers */
695
        $availableFixers = array_map(static function (FixerInterface $fixer) {
696
            return $fixer->getName();
697
        }, $fixers);
698
699
        $unknownFixers = array_diff(
700
            $configuredFixers,
701
            $availableFixers
702
        );
703
704
        if (\count($unknownFixers)) {
705
            $matcher = new WordMatcher($availableFixers);
706
707
            $message = 'The rules contain unknown fixers: ';
708
            foreach ($unknownFixers as $unknownFixer) {
709
                $alternative = $matcher->match($unknownFixer);
710
                $message .= sprintf(
711
                    '"%s"%s, ',
712
                    $unknownFixer,
713
                    null === $alternative ? '' : ' (did you mean "'.$alternative.'"?)'
714
                );
715
            }
716
717
            throw new InvalidConfigurationException(substr($message, 0, -2).'.');
718
        }
719
720
        foreach ($fixers as $fixer) {
721
            $fixerName = $fixer->getName();
722
            if (isset($rules[$fixerName]) && $fixer instanceof DeprecatedFixerInterface) {
723
                $successors = $fixer->getSuccessorsNames();
724
                $messageEnd = [] === $successors
725
                    ? sprintf(' and will be removed in version %d.0.', Application::getMajorVersion() + 1)
726
                    : sprintf('. Use %s instead.', str_replace('`', '"', Utils::naturalLanguageJoinWithBackticks($successors)));
727
728
                Utils::triggerDeprecation(new \RuntimeException("Rule \"{$fixerName}\" is deprecated{$messageEnd}"));
729
            }
730
        }
731
    }
732
733
    /**
734
     * Apply path on config instance.
735
     */
736
    private function resolveFinder(): iterable
737
    {
738
        $this->configFinderIsOverridden = false;
739
740
        if ($this->isStdIn()) {
741
            return new \ArrayIterator([new StdinFileInfo()]);
742
        }
743
744
        $modes = [self::PATH_MODE_OVERRIDE, self::PATH_MODE_INTERSECTION];
745
746
        if (!\in_array(
747
            $this->options['path-mode'],
748
            $modes,
749
            true
750
        )) {
751
            throw new InvalidConfigurationException(sprintf(
752
                'The path-mode "%s" is not defined, supported are "%s".',
753
                $this->options['path-mode'],
754
                implode('", "', $modes)
755
            ));
756
        }
757
758
        $isIntersectionPathMode = self::PATH_MODE_INTERSECTION === $this->options['path-mode'];
759
760
        $paths = array_filter(array_map(
761
            static function (string $path) {
762
                return realpath($path);
763
            },
764
            $this->getPath()
765
        ));
766
767
        if (!\count($paths)) {
768
            if ($isIntersectionPathMode) {
769
                return new \ArrayIterator([]);
770
            }
771
772
            return $this->iterableToTraversable($this->getConfig()->getFinder());
773
        }
774
775
        $pathsByType = [
776
            'file' => [],
777
            'dir' => [],
778
        ];
779
780
        foreach ($paths as $path) {
781
            if (is_file($path)) {
782
                $pathsByType['file'][] = $path;
783
            } else {
784
                $pathsByType['dir'][] = $path.\DIRECTORY_SEPARATOR;
785
            }
786
        }
787
788
        $nestedFinder = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $nestedFinder is dead and can be removed.
Loading history...
789
        $currentFinder = $this->iterableToTraversable($this->getConfig()->getFinder());
790
791
        try {
792
            $nestedFinder = $currentFinder instanceof \IteratorAggregate ? $currentFinder->getIterator() : $currentFinder;
793
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
794
        }
795
796
        if ($isIntersectionPathMode) {
797
            if (null === $nestedFinder) {
0 ignored issues
show
introduced by
The condition null === $nestedFinder is always false.
Loading history...
798
                throw new InvalidConfigurationException(
799
                    'Cannot create intersection with not-fully defined Finder in configuration file.'
800
                );
801
            }
802
803
            return new \CallbackFilterIterator(
804
                new \IteratorIterator($nestedFinder),
805
                static function (\SplFileInfo $current) use ($pathsByType) {
806
                    $currentRealPath = $current->getRealPath();
807
808
                    if (\in_array($currentRealPath, $pathsByType['file'], true)) {
809
                        return true;
810
                    }
811
812
                    foreach ($pathsByType['dir'] as $path) {
813
                        if (0 === strpos($currentRealPath, $path)) {
814
                            return true;
815
                        }
816
                    }
817
818
                    return false;
819
                }
820
            );
821
        }
822
823
        if (null !== $this->getConfigFile() && null !== $nestedFinder) {
824
            $this->configFinderIsOverridden = true;
825
        }
826
827
        if ($currentFinder instanceof SymfonyFinder && null === $nestedFinder) {
0 ignored issues
show
introduced by
$currentFinder is never a sub-type of Symfony\Component\Finder\Finder.
Loading history...
828
            // finder from configuration Symfony finder and it is not fully defined, we may fulfill it
829
            return $currentFinder->in($pathsByType['dir'])->append($pathsByType['file']);
830
        }
831
832
        return Finder::create()->in($pathsByType['dir'])->append($pathsByType['file']);
833
    }
834
835
    /**
836
     * Set option that will be resolved.
837
     *
838
     * @param mixed $value
839
     */
840
    private function setOption(string $name, $value): void
841
    {
842
        if (!\array_key_exists($name, $this->options)) {
843
            throw new InvalidConfigurationException(sprintf('Unknown option name: "%s".', $name));
844
        }
845
846
        $this->options[$name] = $value;
847
    }
848
849
    private function resolveOptionBooleanValue(string $optionName): bool
850
    {
851
        $value = $this->options[$optionName];
852
853
        if (!\is_string($value)) {
854
            throw new InvalidConfigurationException(sprintf('Expected boolean or string value for option "%s".', $optionName));
855
        }
856
857
        if ('yes' === $value) {
858
            return true;
859
        }
860
861
        if ('no' === $value) {
862
            return false;
863
        }
864
865
        throw new InvalidConfigurationException(sprintf('Expected "yes" or "no" for option "%s", got "%s".', $optionName, $value));
866
    }
867
868
    private static function separatedContextLessInclude(string $path): ConfigInterface
869
    {
870
        $config = include $path;
871
872
        // verify that the config has an instance of Config
873
        if (!$config instanceof ConfigInterface) {
874
            throw new InvalidConfigurationException(sprintf('The config file: "%s" does not return a "PhpCsFixer\ConfigInterface" instance. Got: "%s".', $path, \is_object($config) ? \get_class($config) : \gettype($config)));
875
        }
876
877
        return $config;
878
    }
879
}
880