Passed
Pull Request — master (#8)
by Dmitriy
11:32 queued 10s
created

PackageFinder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 5
rs 10
c 1
b 0
f 1
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
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
    protected array $plainList = [];
17
18
    /**
19
     * Ordered list of package in form: package => depth
20
     * For order description @see findPackages.
21
     */
22
    protected array $orderedList = [];
23
24
    private PackageInterface $rootPackage;
25
26
    /**
27
     * @var PackageInterface[]
28
     */
29
    private array $packages;
30
31
    private string $vendorDir;
32
33
    public function __construct(string $vendorDir, PackageInterface $rootPackage, array $packages)
34
    {
35
        $this->rootPackage = $rootPackage;
36
        $this->packages = $packages;
37
        $this->vendorDir = $vendorDir;
38
    }
39
40
    /**
41
     * Returns ordered list of packages:
42
     * - listed earlier in the composer.json will get earlier in the list
43
     * - childs before parents.
44
     *
45
     * @return Package[]
46
     */
47
    public function findPackages(): array
48
    {
49
        $root = new Package($this->rootPackage, $this->vendorDir);
50
        $this->plainList[$root->getPrettyName()] = $root;
51
        foreach ($this->packages as $package) {
52
            $this->plainList[$package->getPrettyName()] = new Package($package, $this->vendorDir);
53
        }
54
        $this->orderedList = [];
55
        $this->iteratePackage($root, true);
56
57
        $res = [];
58
        foreach (array_keys($this->orderedList) as $name) {
59
            $res[] = $this->plainList[$name];
60
        }
61
62
        return $res;
63
    }
64
65
    /**
66
     * Iterates through package dependencies.
67
     *
68
     * @param Package $package to iterate
69
     * @param bool $includingDev process development dependencies, defaults to not process
70
     */
71
    protected function iteratePackage(Package $package, bool $includingDev = false): void
72
    {
73
        $name = $package->getPrettyName();
74
75
        /// prevent infinite loop in case of circular dependencies
76
        static $processed = [];
77
        if (isset($processed[$name])) {
78
            return;
79
        }
80
81
        $processed[$name] = 1;
82
83
        /// package depth in dependency hierarchy
84
        static $depth = 0;
85
        ++$depth;
86
87
        $this->iterateDependencies($package);
88
        if ($includingDev) {
89
            $this->iterateDependencies($package, true);
90
        }
91
        if (!isset($this->orderedList[$name])) {
92
            $this->orderedList[$name] = $depth;
93
        }
94
95
        --$depth;
96
    }
97
98
    /**
99
     * Iterates dependencies of the given package.
100
     *
101
     * @param Package $package
102
     * @param bool $dev which dependencies to iterate: true - dev, default - general
103
     */
104
    protected function iterateDependencies(Package $package, bool $dev = false): void
105
    {
106
        $deps = $dev ? $package->getDevRequires() : $package->getRequires();
107
        foreach (array_keys($deps) as $target) {
108
            if (isset($this->plainList[$target]) && empty($this->orderedList[$target])) {
109
                $this->iteratePackage($this->plainList[$target]);
110
            }
111
        }
112
    }
113
}
114