function AutowireTest::testCoreServiceAliases

Same name in other branches
  1. 10 core/tests/Drupal/KernelTests/Core/DependencyInjection/AutowireTest.php \Drupal\KernelTests\Core\DependencyInjection\AutowireTest::testCoreServiceAliases()

Tests that core services have aliases correctly defined where possible.

File

core/tests/Drupal/KernelTests/Core/DependencyInjection/AutowireTest.php, line 53

Class

AutowireTest
Tests auto-wiring services.

Namespace

Drupal\KernelTests\Core\DependencyInjection

Code

public function testCoreServiceAliases() : void {
    $services = [];
    $aliases = [];
    $filenames = array_map(fn($module) => "core/modules/{$module[0]}/{$module[0]}.services.yml", $this->coreModuleListDataProvider());
    $filenames[] = 'core/core.services.yml';
    foreach (array_filter($filenames, 'file_exists') as $filename) {
        foreach (Yaml::decode(file_get_contents($filename))['services'] as $id => $service) {
            if (is_string($service)) {
                $aliases[$id] = substr($service, 1);
            }
            elseif (isset($service['class']) && class_exists($service['class'])) {
                // Ignore services named by their own class.
                if ($id === $service['class']) {
                    continue;
                }
                // Ignore certain tagged services.
                if (isset($service['tags'])) {
                    foreach ($service['tags'] as $tag) {
                        if (in_array($tag['name'], [
                            'access_check',
                            'cache.context',
                            'context_provider',
                            'event_subscriber',
                        ])) {
                            continue 2;
                        }
                    }
                }
                $services[$id] = $service['class'];
            }
        }
    }
    $interfaces = [];
    foreach (get_declared_classes() as $class) {
        // Ignore proxy classes for autowiring purposes.
        if (str_contains($class, '\\ProxyClass\\')) {
            continue;
        }
        foreach (class_implements($class) as $interface) {
            $interfaces[$interface][] = $class;
        }
    }
    $expected = [];
    foreach ($services as $id => $class) {
        // Skip services that share a class.
        if (count(array_keys($services, $class)) > 1) {
            continue;
        }
        // Skip IDs that are interfaces already.
        if (interface_exists($id)) {
            continue;
        }
        // Expect standalone classes to be aliased.
        $implements = class_implements($class);
        if (!$implements) {
            $expected[$class] = $id;
        }
        elseif (count($implements) === 1 && TrustedCallbackInterface::class === reset($implements)) {
            // Classes implementing only TrustedCallbackInterface should be aliased.
            $expected[$class] = $id;
        }
        // Expect classes that are the only implementation of their interface to
        // be aliased.
        foreach ($implements as $interface) {
            if (count($interfaces[$interface]) === 1) {
                $expected[$interface] = $id;
            }
        }
    }
    $missing = array_diff($expected, $aliases);
    $formatted = Yaml::encode(array_map(fn($alias) => sprintf('@%s', $alias), $missing));
    $this->assertSame($expected, array_intersect($expected, $aliases), sprintf('The following core services do not have map the class name to an alias. Add the following to core.services.yml in the appropriate place: %s%s%s', \PHP_EOL, \PHP_EOL, $formatted));
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.