Service Locator

A service locator is an object that knows how to provide all sorts of services (or components) that an application might need. Within a service locator, each component exists as only a single instance, uniquely identified by an ID. You use the ID to retrieve a component from the service locator.

In Yii, a service locator is simply an instance of yii\di\ServiceLocator or a child class.

The most commonly used service locator in Yii is the application object, which can be accessed through \Yii::$app. The services it provides are called application components, such as the request, response, and urlManager components. You may configure these components, or even replace them with your own implementations, easily through functionality provided by the service locator.

Besides the application object, each module object is also a service locator.

To use a service locator, the first step is to register components with it. A component can be registered via yii\di\ServiceLocator::set(). The following code shows different ways of registering components:

use yii\di\ServiceLocator;
use yii\caching\FileCache;

$locator = new ServiceLocator;

// register "cache" using a class name that can be used to create a component
$locator->set('cache', 'yii\caching\ApcCache');

// register "db" using a configuration array that can be used to create a component
$locator->set('db', [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=demo',
    'username' => 'root',
    'password' => '',
]);

// register "search" using an anonymous function that builds a component
$locator->set('search', function () {
    return new app\components\SolrService;
});

// register "pageCache" using a component
$locator->set('pageCache', new FileCache);

Once a component has been registered, you can access it using its ID, in one of the two following ways:

$cache = $locator->get('cache');
// or alternatively
$cache = $locator->cache;

As shown above, yii\di\ServiceLocator allows you to access a component like a property using the component ID. When you access a component for the first time, yii\di\ServiceLocator will use the component registration information to create a new instance of the component and return it. Later, if the component is accessed again, the service locator will return the same instance.

You may use yii\di\ServiceLocator::has() to check if a component ID has already been registered. If you call yii\di\ServiceLocator::get() with an invalid ID, an exception will be thrown.

Because service locators are often being created with configurations, a writable property named components is provided. This allows you to configure and register multiple components at once. The following code shows a configuration array that can be used to configure a service locator (e.g. an application) with the db, cache and search components:

return [
    // ...
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=demo',
            'username' => 'root',
            'password' => '',
        ],
        'cache' => 'yii\caching\ApcCache',
        'search' => function () {
            $solr = new app\components\SolrService('127.0.0.1');
            // ... other initializations ...
            return $solr;
        },
    ],
];

In the above, there is an alternative way to configure the search component. Instead of directly writing a PHP callback which builds a SolrService instance, you can use a static class method to return such a callback, like shown as below:

class SolrServiceBuilder
{
    public static function build($ip)
    {
        return function () use ($ip) {
            $solr = new app\components\SolrService($ip);
            // ... other initializations ...
            return $solr;
        };
    }
}

return [
    // ...
    'components' => [
        // ...
        'search' => SolrServiceBuilder::build('127.0.0.1'),
    ],
];

This alternative approach is most preferable when you are releasing a Yii component which encapsulates some non-Yii 3rd-party library. You use the static method like shown above to represent the complex logic of building the 3rd-party object, and the user of your component only needs to call the static method to configure the component.