In a quite large project, the loading of all services takes time, how to load only needed services

I have round about 350 factories in the cached app config and it instantiates about 195 services on every start of the application which consumes time (about 0.2s) (may be more, but i track only my own services that way).
Is there a way to prevent the servicemanager from instantiate every service on start? Like only when it is used?
lazy_services does not the trick, the services are not doing any initialisations which consumes time.

My config is not dynamic, its all in php files.

Any hints or ideas how to improve the load time / preventing the service manager from instantiate every service?

Hello and welcome to our forums! :smiley:

Here is an extract from the documentation:

For performance reasons, factories objects are not created until requested.

Hmm i know that. It came from ZF1 > ZF2 > Laminas

It seems it instantiates every controller and there are the services needed in the constructor. In ZF3/Laminas its mandatory (or at least strongly recommended) to have used services declared in the constructor.

Is there an other way, the service manager does not instantiate every controller?

The same here: controllers are not created until requested.

The controller manager is a specialized dependency injection container called plugin manager which is a service manager. So they all have the same behaviour.

Why do you think that every controller is instantiated?

Do you use the options for caching the configuration and the module class mapping?

Yes i do, thats my config:

'module_listener_options' => array(
        // This should be an array of paths in which modules reside.
        // If a string key is provided, the listener will consider that a module
        // namespace, the value of that key the specific path to that module's
        // Module class.
        'module_paths'             => array(
            './module',
            './vendor',
        ),
        // An array of paths from which to glob configuration files after
        // modules are loaded. These effectively override configuration
        // provided by modules themselves. Paths may use GLOB_BRACE notation.
        'config_glob_paths'        => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
        // Whether or not to enable a configuration cache.
        // If enabled, the merged configuration will be cached and used in
        // subsequent requests.
        'config_cache_enabled'     => true,
        // The key used to create the configuration cache file name.
        'config_cache_key'         => 'application.config.cache',
        // Whether or not to enable a module class map cache.
        // If enabled, creates a module class map cache which will be used
        // by in future requests, to reduce the autoloading process.
        'module_map_cache_enabled' => true,
        // The key used to create the class map cache file name.
        'module_map_cache_key'     => 'application.module.cache',
        // The path in which to cache merged configuration.
        'cache_dir'                => 'data/cache/',
        // Whether or not to enable modules dependency checking.
        // Enabled by default, prevents usage of modules that depend on other modules
        // that weren't loaded.
        // 'check_dependencies' => true,
    ),

The cached module-config-cache.application.config.cache.php file is about 56k lines long

The controller are not the problem, i checked on that and they are instantiated only when they are used. Still i got like 195 Services created.

I’ve created as a test a new Service which has no dependencies, has no listeners, no nothing who could need an instance. Just configures in factories and the result is:
Its not instantiated… as expected

Just looked after event listener and there it is: 127 Eventlisteners… and the used services will instantiate the other services which results in total of about 195 services instantiated.

So my follow up question is:
Is there a way to improve the event listeners?
I mean register an event listener needs a instantiated service, right?

Have you checked what kind of listeners they are? The number indicates many user-defined listeners.

They are all „Listener aggregates“

In my case i pass the instantiated service an attach the events with that service.

When i do not want to instantiate the needed service, i could bind the events to the listener class and load the service only when the event has triggered.

Which are user-defined / custom aggregates of listeners.

In the documentation of laminas-eventmanager there you will find an entire section of lazy listeners. You should look at LazyListenerAggregate.

But you should also check that not every listener is instantiated in the init or onBootstrap method of a module class. Logic not specific to MVC should not be in these methods.

all these listeners came from early stages from the project and have been kept to be added the same way. I see, that i have to move them to lazy listeners and this is exactly the behaviour i’m looking for.

old style attach an event listener with an instance of the service:

$this->listeners[] = $events->attach(
    'XyModule\Controller\ControllerClass',
    'event.update',
    array(
        $this->service, // instance of a service
        'onUpdateSingleField'
    ),
    1000
);

Adapted to lazy listeners:

$this->listeners[] = $events->attach(
    'XyModule\Controller\ControllerClass',
    'event.update',
    new LazyListener(
        [
            'listener' => $this->service, // only the service class reference
            'method'   => 'onTestEvent',
        ],
        $this->container
    ),
    1000
);

All listeners are attached in the onBootstrap method of the module. Where should they go, when not there? The documentation says thats the right place to attach the module specific listeners.