Passed
Pull Request — master (#3)
by Dmitriy
13:39
created

PackageFinder   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 13
eloc 32
c 4
b 0
f 1
dl 0
loc 92
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A iteratePackage() 0 25 4
A iterateDependencies() 0 6 5
A findPackages() 0 16 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Composer\Config\Package;
6
7
use Composer\Composer;
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 Composer $composer;
25
26
    public function __construct(Composer $composer)
27
    {
28
        $this->composer = $composer;
29
    }
30
31
    /**
32
     * Returns ordered list of packages:
33
34
     * - Packages listed earlier in the composer.json will get earlier in the list.
35
     * - Children are listed before parents.
36
     *
37
     * @return Package[]
38
     */
39
    public function findPackages(): array
40
    {
41
        $root = new Package($this->composer->getPackage(), $this->composer);
42
        $this->plainList[$root->getPrettyName()] = $root;
43
        foreach ($this->composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages() as $package) {
44
            $this->plainList[$package->getPrettyName()] = new Package($package, $this->composer);
45
        }
46
        $this->orderedList = [];
47
        $this->iteratePackage($root, true);
48
49
        $result = [];
50
        foreach (array_keys($this->orderedList) as $name) {
51
            $result[] = $this->plainList[$name];
52
        }
53
54
        return $result;
55
    }
56
57
    /**
58
     * Iterates through package dependencies.
59
     *
60
     * @param Package $package to iterate.
61
     * @param bool $includingDev process development dependencies, defaults to not process.
62
     */
63
    private function iteratePackage(Package $package, bool $includingDev = false): void
64
    {
65
        $name = $package->getPrettyName();
66
67
        // prevent infinite loop in case of circular dependencies
68
        static $processed = [];
69
        if (isset($processed[$name])) {
70
            return;
71
        }
72
73
        $processed[$name] = 1;
74
75
        // package depth in dependency hierarchy
76
        static $depth = 0;
77
        ++$depth;
78
79
        $this->iterateDependencies($package);
80
        if ($includingDev) {
81
            $this->iterateDependencies($package, true);
82
        }
83
        if (!isset($this->orderedList[$name])) {
84
            $this->orderedList[$name] = $depth;
85
        }
86
87
        --$depth;
88
    }
89
90
    /**
91
     * Iterates dependencies of the given package.
92
     *
93
     * @param Package $package
94
     * @param bool $dev type of dependencies to iterate: true - dev, default - general.
95
     */
96
    private function iterateDependencies(Package $package, bool $dev = false): void
97
    {
98
        $dependencies = $dev ? $package->getDevRequires() : $package->getRequires();
99
        foreach (array_keys($dependencies) as $target) {
100
            if (isset($this->plainList[$target]) && empty($this->orderedList[$target])) {
101
                $this->iteratePackage($this->plainList[$target]);
102
            }
103
        }
104
    }
105
}
106