PackagesOrderer   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 71
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 2

Importance

Changes 0
Metric Value
wmc 8
lcom 0
cbo 2
dl 0
loc 71
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A reorderPackages() 0 15 2
B walkPackagesList() 0 32 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace TheCodingMachine\Discovery;
6
7
use Composer\Package\Link;
8
use Composer\Package\PackageInterface;
9
10
/**
11
 * This class is dedicated to reordering packages.
12
 *
13
 * @author David Negrier
14
 */
15
class PackagesOrderer
16
{
17
    /**
18
     * Method: go through the tree, loading child first.
19
     * Each time we go through a package, lets ensure the package is not already part of the packages to install.
20
     * If so, ignore.
21
     *
22
     * @param PackageInterface[] $unorderedPackagesList
23
     *
24
     * @return array|PackageInterface[]
25
     */
26
    public static function reorderPackages(array $unorderedPackagesList) : array
27
    {
28
        // The very first step is to reorder the packages alphabetically.
29
        // This is to ensure the same order every time, even between packages that are unrelated.
30
        usort($unorderedPackagesList, function (PackageInterface $packageA, PackageInterface $packageB) {
31
            return strcmp($packageA->getName(), $packageB->getName());
32
        });
33
34
        $orderedPackagesList = array();
35
        foreach ($unorderedPackagesList as $package) {
36
            $orderedPackagesList = self::walkPackagesList($package, $orderedPackagesList, $unorderedPackagesList);
37
        }
38
39
        return $orderedPackagesList;
40
    }
41
42
    /**
43
     * Function used to sort packages by dependencies (packages depending from no other package in front of others)
44
     * Invariant hypothesis for this function: $orderedPackagesList is already ordered and the package we add
45
     * has all its dependencies already accounted for. If not, we add the dependencies first.
46
     *
47
     * @param PackageInterface   $package
48
     * @param PackageInterface[] $orderedPackagesList The list of sorted packages
49
     * @param PackageInterface[] $availablePackages   The list of all packages not yet sorted
50
     *
51
     * @return PackageInterface[]
52
     */
53
    private static function walkPackagesList(PackageInterface $package, array $orderedPackagesList, array &$availablePackages) : array
54
    {
55
        // First, let's check that the package we want to add is not already in our list.
56
        foreach ($orderedPackagesList as $includedPackage) {
57
            if ($includedPackage->equals($package)) {
58
                return $orderedPackagesList;
59
            }
60
        }
61
62
        // We need to make sure there is no loop (if a package A requires a package B that requires the package A)...
63
        // We do that by removing the package from the list of all available packages.
64
        $key = array_search($package, $availablePackages);
65
        unset($availablePackages[$key]);
66
67
        // Now, let's see if there are dependencies.
68
        foreach ($package->getRequires() as $require) {
69
            /* @var $require Link */
70
            foreach ($availablePackages as $iterPackage) {
71
                if ($iterPackage->getName() === $require->getTarget()) {
72
                    $orderedPackagesList = self::walkPackagesList($iterPackage, $orderedPackagesList, $availablePackages);
73
                    break;
74
                }
75
            }
76
        }
77
78
        // FIXME: manage dev-requires and "provides"
79
80
        // Finally, let's add the package once all dependencies have been added.
81
        $orderedPackagesList[] = $package;
82
83
        return $orderedPackagesList;
84
    }
85
}
86