Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laminas Servicemanager Laravel Package

laminas/laminas-servicemanager

Powerful dependency injection and service container for PHP. Manage factories, abstract factories, delegators, aliases, and shared services, with PSR-11 interoperability and robust configuration for complex applications.

View on GitHub
Deep Wiki
Context7

Migration Guide

The Service Manager was first introduced for Laminas 2.0.0. Its API remained the same throughout that version.

Version 3 is the first new major release of the Service Manager, and contains a number of backwards compatibility breaks. These were introduced to provide better performance and stability.

Case Sensitivity and Normalization

v2 normalized service names as follows:

  • It stripped non alphanumeric characters.
  • It lowercased the resulting string.

This was done to help prevent typographical errors from creating configuration errors. However, it also presented a large performance hit, and led to some unexpected behaviors.

In v3, service names are case sensitive, and are not normalized in any way.

As such, you must refer to services using the same case in which they were registered.

Configuration

A number of changes have been made to configuration of service and plugin managers:

  • Minor changes in configuration arrays may impact your usage.
  • ConfigInterface implementations and consumers will need updating.

Configuration arrays

Configuration for v2 consisted of the following:

[
    'services' => [
        // service name => instance pairs
    ],
    'aliases' => [
        // alias => service name pairs
    ],
    'invokables' => [
        // service name => class name pairs
    ],
    'factories' => [
        // service name => factory pairs
    ],
    'abstract_factories' => [
        // abstract factories
    ],
    'initializers' => [
        // initializers
    ],
    'delegators' => [
        // service name => [ delegator factories ]
    ],
    'shared' => [
        // service name => boolean
    ],
    'share_by_default' => boolean,
]

In v3, the configuration remains the same, with the following additions:

[
    'lazy_services' => [
        // The class_map is required if using lazy services:
        'class_map' => [
            // service name => class name pairs
        ],
        // The following are optional:
        'proxies_namespace'  => 'Alternate namespace to use for generated proxy classes',
        'proxies_target_dir' => 'path in which to write generated proxy classes',
        'write_proxy_files'  => true, // boolean; false by default
    ],
]

The main change is the addition of integrated lazy service configuration is now integrated.

ConfigInterface

The principal change to the ConfigInterface is the addition of the toArray() method. This method is intended to return a configuration array in the format listed above, for passing to either the constructor or the configure() method of the ServiceManager..

Config class

Laminas\ServiceManager\Config has been updated to follow the changes to the ConfigInterface and ServiceManager. This essentially means that it removes the various getter methods, and adds the toArray() method.

Invokables

Invokables no longer exist, at least, not identically to how they existed in Laminas.

Internally, ServiceManager now does the following for invokables entries:

  • If the name and value match, it creates a factories entry mapping the service name to Laminas\ServiceManager\Factory\InvokableFactory.
  • If the name and value do not match, it creates an aliases entry mapping the service name to the class name, and a factories entry mapping the class name to Laminas\ServiceManager\Factory\InvokableFactory.

This means that you can use your existing invokables configuration from version 2 in version 3. However, we recommend starting to update your configuration to remove invokables entries in favor of factories (and aliases, if needed).

Invokables and plugin managers

If you are creating a plugin manager and in-lining invokables into the class definition, you will need to make some changes.

$invokableClasses will need to become $factories entries, and you will potentially need to add $aliases entries.

As an example, consider the following, from laminas-math v2.x:

class AdapterPluginManager extends AbstractPluginManager
{
    protected $invokableClasses = [
        'bcmath' => Adapter\Bcmath::class,
        'gmp'    => Adapter\Gmp::class,
    ];
}

Because we no longer define an $invokableClasses property, for v3.x, this now becomes:

use Laminas\ServiceManager\Factory\InvokableFactory;

class AdapterPluginManager extends AbstractPluginManager
{
    protected $aliases = [
        'bcmath' => Adapter\Bcmath::class,
        'gmp'    => Adapter\Gmp::class,
    ];

    protected $factories = [
        Adapter\BcMath::class => InvokableFactory::class,
        Adapter\Gmp::class    => InvokableFactory::class,
    ];
}

Lazy Services

In v2, if you wanted to create a lazy service, you needed to take the following steps:

  • Ensure you have a config service, with a lazy_services key that contained the configuration necessary for the LazyServiceFactory.
  • Assign the LazyServiceFactoryFactory as a factory for the LazyServiceFactory
  • Assign the LazyServiceFactory as a delegator factory for your service.

As an example:

use Laminas\ServiceManager\Proxy\LazyServiceFactoryFactory;

$config = [
    'lazy_services' => [
        'class_map' => [
            'MyClass' => 'MyClass',
        ],
        'proxies_namespace'  => 'TestAssetProxy',
        'proxies_target_dir' => 'data/proxies/',
        'write_proxy_files'  => true,
    ],
];

return [
    'services' => [
        'config' => $config,
    ],
    'invokables' => [
        'MyClass' => 'MyClass',
    ],
    'factories' => [
        'LazyServiceFactory' => LazyServiceFactoryFactory::class,
    ],
    'delegators' => [
        'MyClass' => [
            'LazyServiceFactory',
        ],
    ],
];

This was done in part because lazy services were introduced later in the v2 cycle, and not fully integrated in order to retain the API.

In order to reduce the number of dependencies and steps necessary to configure lazy services, the following changes were made for v3:

  • Lazy service configuration can now be passed directly to the service manager; it is no longer dependent on a config service.
  • The ServiceManager itself is now responsible for creating the LazyServiceFactory delegator factory, based on the configuration present.

The above example becomes the following in v3:

use Laminas\ServiceManager\Factory\InvokableFactory;
use Laminas\ServiceManager\Proxy\LazyServiceFactory;

return [
    'factories' => [
        'MyClass' => InvokableFactory::class,
    ],
    'delegators' => [
        'MyClass' => [
            LazyServiceFactory::class,
        ],
    ],
    'lazy_services' => [
        'class_map' => [
            'MyClass' => 'MyClass',
        ],
        'proxies_namespace'  => 'TestAssetProxy',
        'proxies_target_dir' => 'data/proxies/',
        'write_proxy_files'  => true,
    ],
];

Additionally, assuming you have configured lazy services initially with the proxy namespace, target directory, etc., you can map lazy services using the new method mapLazyService($name, $class):

$container->mapLazyService('MyClass', 'MyClass');
// or, more simply:
$container->mapLazyService('MyClass');

ServiceLocatorInterface Changes

The ServiceLocatorInterface now extends the container-interop interface ContainerInterface, which defines the same get() and has() methods as were previously defined.

Additionally, it adds a new method:

public function build($name, array $options = null)

This method is defined to always return a new instance of the requested service, and to allow using the provided $options when creating the instance.

ServiceManager API Changes

Laminas\ServiceManager\ServiceManager remains the primary interface with which developers will interact. It has the following changes in v3:

  • It adds a new method, configure(), which allows configuring all instance generation capabilities (aliases, factories, abstract factories, etc.) at once.
  • Peering capabilities were removed.
  • Exceptions are always thrown when service instance creation fails or produces an error; you can no longer disable this.
  • Configuration no longer requires a Laminas\ServiceManager\Config instance. Config can be used, but is not needed.
  • It adds a new method, build(), for creating discrete service instances.

Methods Removed

The following methods are removed in v3:

  • setShareByDefault()/shareByDefault(); this can be passed during instantiation or via configure().
  • setThrowExceptionInCreate()/getThrowExceptionInCreate(); exceptions are always thrown when errors are encountered during service instance creation.
  • setRetrieveFromPeeringManagerFirst()/retrieveFromPeeringManagerFirst(); peering is no longer supported.

Constructor

The constructor now accepts an array of service configuration, not a Laminas\ServiceManager\Config instance.

Use build() for discrete instances

The new method build() acts as a factory method for configured services, and will always return a new instance, never a shared one.

Additionally, it provides factory capabilities; you may pass an additional, optional argument, $options, which should be an array of additional options a factory may use to create a new instance. This is primarily of interest when creating plugin managers (more on plugin managers below), which may pass that information on in order to create discrete plugin instances with specific state.

As examples:

use Laminas\Validator\Between;

$between = $container->build(Between::class, [
    'min'        => 5,
    'max'        => 10,
    'inclusive' => true,
]);

$alsoBetween = $container->build(Between::class, [
    'min'       => 0,
    'max'       => 100,
    'inclusive' => false,
]);

The above two validators would be different instances, with their own configuration.

Factories

Internally, the ServiceManager now only uses the new factory interfaces defined in the Laminas\ServiceManager\Factory namespace. These replace the interfaces defined in version 2, and define completely new signatures.

For migration purposes, all original interfaces were retained, and now inherit from the new interfaces. This provides a migration path; you can add the methods defined in the new interfaces to your existing factories targeting v2, and safely upgrade. (Typically, you will then have the version 2 methods proxy to those defined in version 3.)

Interfaces and relations to version 2

Version 2 Interface Version 3 Interface
Laminas\ServiceManager\AbstractFactoryInterface Laminas\ServiceManager\Factory\AbstractFactoryInterface
Laminas\ServiceManager\DelegatorFactoryInterface Laminas\ServiceManager\Factory\DelegatorFactoryInterface
Laminas\ServiceManager\FactoryInterface Laminas\ServiceManager\Factory\FactoryInterface

The version 2 interfaces now extend those in version 3, but are marked deprecated. You can continue to use them, but will be required to update your code to use the new interfaces in the future.

AbstractFactoryInterface

The previous signature of the AbstractFactoryInterface was:

interface AbstractFactoryInterface
{
    /**
     * Determine if we can create a service with name
     *
     * [@param](https://github.com/param) ServiceLocatorInterface $serviceLocator
     * [@param](https://github.com/param) $name
     * [@param](https://github.com/param) $requestedName
     * [@return](https://github.com/return) bool
     */
    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName);

    /**
     * Create service with name
     *
     * [@param](https://github.com/param) ServiceLocatorInterface $serviceLocator
     * [@param](https://github.com/param) $name
     * [@param](https://github.com/param) $requestedName
     * [@return](https://github.com/return) mixed
     */
    public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName);
}

The new signature is:

interface AbstractFactoryInterface extends FactoryInterface
{
    /**
     * Does the factory have a way to create an instance for the service?
     *
     * [@param](https://github.com/param)  ContainerInterface $container
     * [@param](https://github.com/param)  string $requestedName
     * [@return](https://github.com/return) bool
     */
    public function canCreate(ContainerInterface $container, $requestedName);
}

Note that it now extends the FactoryInterface (detailed below), and thus the factory logic has the same signature.

In v2, the abstract factory defined the method canCreateServiceWithName(); in v3, this is renamed to canCreate(), and the method also now receives only two arguments, the container and the requested service name.

To prepare your version 2 implementation to work upon upgrade to version 3:

  • Add the methods canCreate() and __invoke() as defined in version 3.
  • Modify your existing canCreateServiceWithName() method to proxy to canCreate()
  • Modify your existing createServiceWithName() method to proxy to __invoke()

As an example, given the following implementation from version 2:

use Laminas\ServiceManager\AbstractFactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class LenientAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $services, $name, $requestedName)
    {
        return class_exists($requestedName);
    }

    public function createServiceWithName(ServiceLocatorInterface $services, $name, $requestedName)
    {
        return new $requestedName();
    }
}

To update this for version 3 compatibility, you will add the methods canCreate() and __invoke(), move the code from the existing methods into them, and update the existing methods to proxy to the new methods:

use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\AbstractFactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class LenientAbstractFactory implements AbstractFactoryInterface
{
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        return class_exists($requestedName);
    }

    public function canCreateServiceWithName(ServiceLocatorInterface $services, $name, $requestedName)
    {
        return $this->canCreate($services, $requestedName);
    }

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new $requestedName();
    }

    public function createServiceWithName(ServiceLocatorInterface $services, $name, $requestedName)
    {
        return $this($services, $requestedName);
    }
}

After you have upgraded to version 3, you can take the following steps to remove the migration artifacts:

  • Update your class to implement the new interface.
  • Remove the canCreateServiceWithName() and createServiceWithName() methods from your implementation.

From our example above, we would update the class to read as follows:

use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\AbstractFactoryInterface; // <-- note the change!

class LenientAbstractFactory implements AbstractFactoryInterface
{
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        return class_exists($requestedName);
    }

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new $requestedName();
    }
}

DelegatorFactoryInterface

The previous signature of the DelegatorFactoryInterface was:

interface DelegatorFactoryInterface
{
    /**
     * A factory that creates delegates of a given service
     *
     * [@param](https://github.com/param) ServiceLocatorInterface $serviceLocator the service locator which requested the service
     * [@param](https://github.com/param) string                  $name           the normalized service name
     * [@param](https://github.com/param) string                  $requestedName  the requested service name
     * [@param](https://github.com/param) callable                $callback       the callback that is responsible for creating the service
     *
     * [@return](https://github.com/return) mixed
     */
    public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback);
}

The new signature is:

interface DelegatorFactoryInterface
{
    /**
     * A factory that creates delegates of a given service
     *
     * [@param](https://github.com/param)  ContainerInterface $container
     * [@param](https://github.com/param)  string             $name
     * [@param](https://github.com/param)  callable           $callback
     * [@param](https://github.com/param)  null|array         $options
     * [@return](https://github.com/return) object
     */
    public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null);
}

Note that the $name and $requestedName arguments are now merged into a single $name argument, and that the factory now allows passing additional options to use (typically as passed via build()).

To prepare your existing delegator factories for version 3, take the following steps:

  • Implement the __invoke() method in your existing factory, copying the code from your existing createDelegatorWithName() method into it.
  • Modify the createDelegatorWithName() method to proxy to the new method.

Consider the following delegator factory that works for version 2:

use Laminas\ServiceManager\DelegatorFactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class ObserverAttachmentDelegator implements DelegatorFactoryInterface
{
    public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
    {
        $subject = $callback();
        $subject->attach($serviceLocator->get(Observer::class);
        return $subject;
    }
}

To prepare this for version 3, we'd implement the __invoke() signature from version 3, and modify createDelegatorWithName() to proxy to it:

use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\DelegatorFactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class ObserverAttachmentDelegator implements DelegatorFactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, callable $callback, array $options = null)
    {
        $subject = $callback();
        $subject->attach($container->get(Observer::class);
        return $subject;
    }

    public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
    {
        return $this($serviceLocator, $requestedName, $callback);
    }
}

After you have upgraded to version 3, you can take the following steps to remove the migration artifacts:

  • Update your class to implement the new interface.
  • Remove the createDelegatorWithName() method from your implementation.

From our example above, we would update the class to read as follows:

use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; // <-- note the change!

class ObserverAttachmentDelegator implements DelegatorFactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, callable $callback, array $options = null)
    {
        $subject = $callback();
        $subject->attach($container->get(Observer::class);
        return $subject;
    }
}

FactoryInterface

The previous signature of the FactoryInterface was:

interface FactoryInterface
{
    /**
     * Create service
     *
     * [@param](https://github.com/param) ServiceLocatorInterface $serviceLocator
     * [@return](https://github.com/return) mixed
     */
    public function createService(ServiceLocatorInterface $serviceLocator);
}

The new signature is:

interface FactoryInterface
{
    /**
     * Create an object
     *
     * [@param](https://github.com/param)  ContainerInterface $container
     * [@param](https://github.com/param)  string             $requestedName
     * [@param](https://github.com/param)  null|array         $options
     * [@return](https://github.com/return) object
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null);
}

Note that the factory now accepts an additional required argument, $requestedName; v2 already passed this argument, but it was not specified in the interface itself. Additionally, a third optional argument, $options, allows you to provide $options to the ServiceManager::build() method; factories can then take these into account when creating an instance.

Because factories now can expect to receive the service name, they may be re-used for multiple services, largely replacing abstract factories in version 3.

To prepare your existing factories for version 3, take the following steps:

  • Implement the __invoke() method in your existing factory, copying the code from your existing createService() method into it.
  • Modify the createService() method to proxy to the new method.

Consider the following factory that works for version 2:

use Laminas\ServiceManager\FactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class FooFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $services)
    {
        return new Foo($services->get(Bar::class));
    }
}

To prepare this for version 3, we'd implement the __invoke() signature from version 3, and modify createService() to proxy to it:

use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\FactoryInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;

class FooFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new Foo($container->get(Bar::class));
    }

    public function createService(ServiceLocatorInter...
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport