PackageFinder::iterateDependencies()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5.2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 5
cp 0.8
rs 9.6111
cc 5
nc 6
nop 2
crap 5.2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Composer\Config\Package;
6
7
use Composer\Package\PackageInterface;
8
use Yiisoft\Composer\Config\Package;
9
10
final class PackageFinder
11
{
12
    /**
13
     * Plain list of all project dependencies (including nested) as provided by composer.
14
     * The list is unordered (chaotic, can be different after every update).
15
     */
16
    private array $plainList = [];
17
18
    /**
19
     * Ordered list of package in form: package => depth.
20
     * For order description see {@see findPackages()}.
21
     */
22
    private array $orderedList = [];
23
24
    private PackageInterface $rootPackage;
25
26
    /**
27
     * @var PackageInterface[]
28
     */
29
    private array $packages;
30
31
    private string $vendorDir;
32
33 2
    public function __construct(string $vendorDir, PackageInterface $rootPackage, array $packages)
34
    {
35 2
        $this->rootPackage = $rootPackage;
36 2
        $this->packages = $packages;
37 2
        $this->vendorDir = $vendorDir;
38 2
    }
39
40
    /**
41
     * Returns ordered list of packages:
42
43
     * - Packages listed earlier in the composer.json will get earlier in the list.
44
     * - Children are listed before parents.
45
     *
46
     * @return Package[]
47
     */
48 2
    public function findPackages(): array
49
    {
50 2
        $root = new Package($this->rootPackage, $this->vendorDir);
51 2
        $this->plainList[$root->getPrettyName()] = $root;
52 2
        foreach ($this->packages as $package) {
53
            $this->plainList[$package->getPrettyName()] = new Package($package, $this->vendorDir);
54
        }
55 2
        $this->orderedList = [];
56 2
        $this->iteratePackage($root, true);
57
58 2
        $result = [];
59 2
        foreach (array_keys($this->orderedList) as $name) {
60
            /** @psalm-var array-key $name */
61 2
            $result[] = $this->plainList[$name];
62
        }
63
64 2
        return $result;
65
    }
66
67
    /**
68
     * Iterates through package dependencies.
69
     *
70
     * @param Package $package to iterate.
71
     * @param bool $includingDev process development dependencies, defaults to not process.
72
     */
73 2
    private function iteratePackage(Package $package, bool $includingDev = false): void
74
    {
75 2
        $name = $package->getPrettyName();
76
77
        // prevent infinite loop in case of circular dependencies
78 2
        static $processed = [];
79 2
        if (array_key_exists($name, $processed)) {
80
            return;
81
        }
82
83 2
        $processed[$name] = 1;
84
85
        // package depth in dependency hierarchy
86 2
        static $depth = 0;
87 2
        ++$depth;
88
89 2
        $this->iterateDependencies($package);
90 2
        if ($includingDev) {
91 2
            $this->iterateDependencies($package, true);
92
        }
93 2
        if (!array_key_exists($name, $this->orderedList)) {
94 2
            $this->orderedList[$name] = $depth;
95
        }
96
97 2
        --$depth;
98 2
    }
99
100
    /**
101
     * Iterates dependencies of the given package.
102
     *
103
     * @param Package $package
104
     * @param bool $dev type of dependencies to iterate: true - dev, default - general.
105
     */
106 2
    private function iterateDependencies(Package $package, bool $dev = false): void
107
    {
108 2
        $dependencies = $dev ? $package->getDevRequires() : $package->getRequires();
109 2
        foreach (array_keys($dependencies) as $target) {
110 2
            if (array_key_exists($target, $this->plainList) && empty($this->orderedList[$target])) {
111
                $this->iteratePackage($this->plainList[$target]);
112
            }
113
        }
114 2
    }
115
}
116