Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. InstalledRepository.php

function InstalledRepository::getDependents

Returns a list of links causing the requested needle packages to be installed, as an associative array with the dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship as values. If recursive lookup was requested a third value is returned containing an identically formed array up to the root package. That third value will be false in case a circular recursion was detected.

@phpstan-return array<array{0: PackageInterface, 1: Link, 2: array<mixed>|false}>

Parameters

string|string[] $needle The package name(s) to inspect.:

ConstraintInterface|null $constraint Optional constraint to filter by.:

bool $invert Whether to invert matches to discover reasons for the package *NOT* to be installed.:

bool $recurse Whether to recursively expand the requirement tree up to the root package.:

string[] $packagesFound Used internally when recurring:

Return value

array[] An associative array of arrays as described above.

File

vendor/composer/composer/src/Composer/Repository/InstalledRepository.php, line 89

Class

InstalledRepository
Installed repository is a composite of all installed repo types.

Namespace

Composer\Repository

Code

public function getDependents($needle, ?ConstraintInterface $constraint = null, bool $invert = false, bool $recurse = true, ?array $packagesFound = null) : array {
    $needles = array_map('strtolower', (array) $needle);
    $results = [];
    // initialize the array with the needles before any recursion occurs
    if (null === $packagesFound) {
        $packagesFound = $needles;
    }
    // locate root package for use below
    $rootPackage = null;
    foreach ($this->getPackages() as $package) {
        if ($package instanceof RootPackageInterface) {
            $rootPackage = $package;
            break;
        }
    }
    // Loop over all currently installed packages.
    foreach ($this->getPackages() as $package) {
        $links = $package->getRequires();
        // each loop needs its own "tree" as we want to show the complete dependent set of every needle
        // without warning all the time about finding circular deps
        $packagesInTree = $packagesFound;
        // Replacements are considered valid reasons for a package to be installed during forward resolution
        if (!$invert) {
            $links += $package->getReplaces();
            // On forward search, check if any replaced package was required and add the replaced
            // packages to the list of needles. Contrary to the cross-reference link check below,
            // replaced packages are the target of links.
            foreach ($package->getReplaces() as $link) {
                foreach ($needles as $needle) {
                    if ($link->getSource() === $needle) {
                        if ($constraint === null || $link->getConstraint()
                            ->matches($constraint) === true) {
                            // already displayed this node's dependencies, cutting short
                            if (in_array($link->getTarget(), $packagesInTree)) {
                                $results[] = [
                                    $package,
                                    $link,
                                    false,
                                ];
                                continue;
                            }
                            $packagesInTree[] = $link->getTarget();
                            $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree) : [];
                            $results[] = [
                                $package,
                                $link,
                                $dependents,
                            ];
                            $needles[] = $link->getTarget();
                        }
                    }
                }
            }
            unset($needle);
        }
        // Require-dev is only relevant for the root package
        if ($package instanceof RootPackageInterface) {
            $links += $package->getDevRequires();
        }
        // Cross-reference all discovered links to the needles
        foreach ($links as $link) {
            foreach ($needles as $needle) {
                if ($link->getTarget() === $needle) {
                    if ($constraint === null || $link->getConstraint()
                        ->matches($constraint) === !$invert) {
                        // already displayed this node's dependencies, cutting short
                        if (in_array($link->getSource(), $packagesInTree)) {
                            $results[] = [
                                $package,
                                $link,
                                false,
                            ];
                            continue;
                        }
                        $packagesInTree[] = $link->getSource();
                        $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : [];
                        $results[] = [
                            $package,
                            $link,
                            $dependents,
                        ];
                    }
                }
            }
        }
        // When inverting, we need to check for conflicts of the needles against installed packages
        if ($invert && in_array($package->getName(), $needles, true)) {
            foreach ($package->getConflicts() as $link) {
                foreach ($this->findPackages($link->getTarget()) as $pkg) {
                    $version = new Constraint('=', $pkg->getVersion());
                    if ($link->getConstraint()
                        ->matches($version) === $invert) {
                        $results[] = [
                            $package,
                            $link,
                            false,
                        ];
                    }
                }
            }
        }
        // List conflicts against X as they may explain why the current version was selected, or explain why it is rejected if the conflict matched when inverting
        foreach ($package->getConflicts() as $link) {
            if (in_array($link->getTarget(), $needles, true)) {
                foreach ($this->findPackages($link->getTarget()) as $pkg) {
                    $version = new Constraint('=', $pkg->getVersion());
                    if ($link->getConstraint()
                        ->matches($version) === $invert) {
                        $results[] = [
                            $package,
                            $link,
                            false,
                        ];
                    }
                }
            }
        }
        // When inverting, we need to check for conflicts of the needles' requirements against installed packages
        if ($invert && $constraint && in_array($package->getName(), $needles, true) && $constraint->matches(new Constraint('=', $package->getVersion()))) {
            foreach ($package->getRequires() as $link) {
                if (PlatformRepository::isPlatformPackage($link->getTarget())) {
                    if ($this->findPackage($link->getTarget(), $link->getConstraint())) {
                        continue;
                    }
                    $platformPkg = $this->findPackage($link->getTarget(), '*');
                    $description = $platformPkg ? 'but ' . $platformPkg->getPrettyVersion() . ' is installed' : 'but it is missing';
                    $results[] = [
                        $package,
                        new Link($package->getName(), $link->getTarget(), new MatchAllConstraint(), Link::TYPE_REQUIRE, $link->getPrettyConstraint() . ' ' . $description),
                        false,
                    ];
                    continue;
                }
                foreach ($this->getPackages() as $pkg) {
                    if (!in_array($link->getTarget(), $pkg->getNames())) {
                        continue;
                    }
                    $version = new Constraint('=', $pkg->getVersion());
                    if ($link->getTarget() !== $pkg->getName()) {
                        foreach (array_merge($pkg->getReplaces(), $pkg->getProvides()) as $prov) {
                            if ($link->getTarget() === $prov->getTarget()) {
                                $version = $prov->getConstraint();
                                break;
                            }
                        }
                    }
                    if (!$link->getConstraint()
                        ->matches($version)) {
                        // if we have a root package (we should but can not guarantee..) we show
                        // the root requires as well to perhaps allow to find an issue there
                        if ($rootPackage) {
                            foreach (array_merge($rootPackage->getRequires(), $rootPackage->getDevRequires()) as $rootReq) {
                                if (in_array($rootReq->getTarget(), $pkg->getNames()) && !$rootReq->getConstraint()
                                    ->matches($link->getConstraint())) {
                                    $results[] = [
                                        $package,
                                        $link,
                                        false,
                                    ];
                                    $results[] = [
                                        $rootPackage,
                                        $rootReq,
                                        false,
                                    ];
                                    continue 3;
                                }
                            }
                            $results[] = [
                                $package,
                                $link,
                                false,
                            ];
                            $results[] = [
                                $rootPackage,
                                new Link($rootPackage->getName(), $link->getTarget(), new MatchAllConstraint(), Link::TYPE_DOES_NOT_REQUIRE, 'but ' . $pkg->getPrettyVersion() . ' is installed'),
                                false,
                            ];
                        }
                        else {
                            // no root so let's just print whatever we found
                            $results[] = [
                                $package,
                                $link,
                                false,
                            ];
                        }
                    }
                    continue 2;
                }
            }
        }
    }
    ksort($results);
    return $results;
}
RSS feed
Powered by Drupal