Repository: symfony/symfony
File: src/Symfony/Component/Security/Core/Role/RoleHierarchy.php (PHP)

Code before:
     */
    public function __construct(array $hierarchy)
    {
        $this->hierarchy = $hierarchy;

        $this->buildRoleMap();
    }

    public function getReachableRoleNames(array $roles): array
    {
        $reachableRoles = $roles;

        foreach ($roles as $role) {
            if (!isset($this->map[$role])) {
                continue;
            }

            foreach ($this->map[$role] as $r) {
                $reachableRoles[] = $r;
            }
        }

        return array_values(array_unique($reachableRoles));
    }

    public function getEncompassingRoleNames(array $roles): array
    {
        $encompassingRoles = $roles;

        foreach ($roles as $role) {
            foreach ($this->map as $parent => $children) {
                if (\in_array($role, $children, true)) {
                    $encompassingRoles[] = $parent;
                }
            }
        }

        return array_values(array_unique($encompassingRoles));
    }

    protected function buildRoleMap(): void
    {
        $this->map = [];
        foreach ($this->hierarchy as $main => $roles) {
            $this->map[$main] = $roles;
            $visited = [];
            $additionalRoles = $roles;
            while ($role = array_shift($additionalRoles)) {
                if (!isset($this->hierarchy[$role])) {
                    continue;
                }

Review comment (performance):
The issue with this design is that each call to the method results in iterating over two nested foreach loops, which can be inefficient (especially when dealing with a large role hierarchy).

⬇️Suggestion (not tested)
```php
private ?array $reverseMap = null;

public function getEncompassingRoleNames(array $roles): array
{
    if (null === $this->reverseMap) {
        $this->buildReverseMap();
    }

    $encompassingRoles = $roles;

    foreach ($roles as $role) {
        if (isset($this->reverseMap[$role])) {
            $encompassingRoles = [...$encompassingRoles, ...$this->reverseMap[$role]];
        }
    }

    return array_values(array_unique($encompassingRoles));
}

private function buildReverseMap(): void
{
    $this->reverseMap = [];

    foreach ($this->map as $parent => $children) {
        foreach ($children as $child) {
            $this->reverseMap[$child][] = $parent;
        }
    }
}
```

Code after:
        $this->buildRoleMap();
    }

    public function getReachableRoleNames(array $roles): array
    {
        $reachableRoles = [];

        foreach ($roles as $role) {
            $reachableRoles[$role] = $role;

            if (!isset($this->map[$role])) {
                continue;
            }

            foreach ($this->map[$role] as $r) {
                $reachableRoles[$r] = $r;
            }
        }

        return $reachableRoles;
    }

    /**
     * @param string[] $roles
     *
     * @return array<string, string>
     */
    public function getParentRoleNames(array $roles): array
    {
        $parentRoles = [];

        foreach ($roles as $role) {
            $parentRoles[$role] = $role;

            foreach ($this->map as $parent => $children) {
                if (isset($children[$role])) {
                    $parentRoles[$parent] = $parent;
                }
            }
        }

        return $parentRoles;
    }

    protected function buildRoleMap(): void
    {
        $this->map = [];
        foreach ($this->hierarchy as $main => $roles) {
            $map = [];
            $visited = [];
            $additionalRoles = $roles;