Zend Service Manager

What is the Service Manager?

The Zend (Laminas) Service Manager is a Dependency Injection (DI) container.

It is responsible for:

  • Creating objects (services)
  • Injecting required dependencies automatically
  • Managing and reusing shared instances

In short:

It's a centralized object factory that manages class dependencies and lifecycle.


Why Use Service Manager?

Using the Service Manager provides several important benefits:

Benefit

Description

Centralized control

Manages object creation and configuration in one place

Loose coupling

Avoids hardcoded dependencies like new ClassName()

Testable code

Easily mock or replace services during testing

Reusability

Reuse shared services (e.g., DB connection, Logger)

Better scalability

Easy to manage complex apps with many dependencies


Basic Example with Explanation

Step-by-Step Scenario:

You have two classes: Logger and UserService.

UserService needs a Logger to log user actions.

Class Definitions:

module\Application\src\Service\Logger.php

class Logger {
    public function log($message) {
        echo "Log: $message";
    }
}


module\Application\src\Service\UserService.php

class UserService {
  protected $logger;

  public function __construct(Logger $logger) {
    $this->logger = $logger;
  }

  public function register($user) {
    $this->logger->log("Registering user: $user");
  }
}

Register in Module.php (module\Application\src\Module.php)

use Application\Service\Logger;
use Application\Service\UserService;

class Module
{
    public function getConfig(): array
    {
        /** @var array $config */
        $config = include __DIR__ . '/../config/module.config.php';
        return $config;
    }
    public function getServiceConfig()
    {
        return [
            'factories' => [
                Logger::class => function($container) {
                    return new Logger();
                },
                UserService::class => function($container) {
                    $logger = $container->get(Logger::class);
                    return new UserService($logger);
                },
            ],
        ];
    }
}

Use in Controller:

use Application\Service\UserService;

public function homeAction()
{ 
    $userService = $this->getEvent()
                        ->getApplication()
                        ->getServiceManager()
                        ->get(UserService::class);
    $userService->register('John Doe');
    return new ViewModel();
}

 Explanation:

  • UserService depends on Logger, but we don't create it manually.
  • The Service Manager builds and injects the dependency automatically.
  • You get clean, decoupled, testable code.

What is a Factory?

A Factory in Zend/Laminas is a special class or function used by the Service Manager to create and return an instance of a service.

It is especially useful when:

  • A class requires dependencies in its constructor
  • You need to customize how a service is built

A factory gives you full control over how a service (or controller) is constructed.


Create a Factory Class (module/Application/src/Service/Factory/UserServiceFactory.php)

namespace Application\Factory;

use Psr\Container\ContainerInterface;
use Application\Service\UserService;
use Application\Service\Logger;

class UserServiceFactory
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $logger = $container->get(Logger::class);
        return new UserService($logger);
    }
}

Register in Module.php (module\Application\src\Module.php)


use Application\Service\Logger;
use Application\Service\UserService;
use Application\Factory\UserServiceFactory;

class Module
{
    public function getConfig(): array
    {
        /** @var array $config */
        $config = include __DIR__ . '/../config/module.config.php';
        return $config;
    }
    public function getServiceConfig()
    {
        return [
            'factories' => [
                Logger::class => function($container) {
                    return new Logger();
                },
                UserService::class => UserServiceFactory::class,
            ],
        ];
    }
}

Use in Controller

use Application\Service\UserService;

public function indexAction()
{
  $userService = $this->getEvent()
            ->getApplication()
            ->getServiceManager()
            ->get(Application\Service\UserService::class);

  $userService->register('Alpha User');
  return new ViewModel();
}

 Explanation:

  • UserServiceFactory creates UserService and injects Logger
  • Service Manager calls the factory whenever UserService is requested
  • All wiring is hidden from the controller

What is Shared vs Non-Shared?

In Zend/Laminas Service Manager, "shared" means the same instance of a service is reused every time it’s requested.

"non-shared" means a new instance is created each time the service is fetched.


Why Use Shared or Non-Shared?

Use Case

Shared (Default)

Non-Shared

Fast access

Same object reused

Slow if heavy to build

Global state

Keeps data in memory

Not suitable for state

Stateless

Avoid if using per-user data

Good for temporary or disposable objects


Basic Example with Explanation

Create a Service Class

module\Application\src\Service\CounterService.php

class Counter {
  private $count = 0;
  public function increment() {
    return ++$this->count;
  }
}


Register in Module.php (module\Application\src\Module.php)


use Application\Service\CounterService;

class Module
{
    public function getConfig(): array
    {
        /** @var array $config */
        $config = include __DIR__ . '/../config/module.config.php';
        return $config;
    }
    public function getServiceConfig()
    {
        return [
            'factories' => [
                CounterService::class => function($container) {
                    return new CounterService();
                }
            ],
            'shared' => [
                CounterService::class => false, // This makes it non-shared
            ],
        ];
    }
}


Use in Controller

use Application\Service\CounterService;

public function homeAction()
{ 
    $counter1 = $this->getEvent()
                    ->getApplication()
                    ->getServiceManager()->get(CounterService::class);
    echo $counter1->increment(); // 1

    $counter2 = $this->getEvent()
                    ->getApplication()
                    ->getServiceManager()->get(CounterService::class);
    echo $counter2->increment(); // 1 again (non-shared)
    return new ViewModel();
}

Explanation:

  • Non-shared returns new Counter instance every time.
  • If shared = true (default), second call returns 2.

What are Aliases?

An Alias is an alternative name for a service. It allows you to refer to services with custom, simpler names.


Why Use Aliases?

Reason

Benefit

Simplicity

Use short names instead of long class names

Cleaner Code

Easier to refactor or rename later

Flexible

Change underlying class without changing usage everywhere


Basic Example with Explanation

In Module.php (module\Application\src\Module.php)


use Application\Service\CounterService;

class Module
{
    public function getConfig(): array
    {
        /** @var array $config */
        $config = include __DIR__ . '/../config/module.config.php';
        return $config;
    }
    public function getServiceConfig()
    {
        return [
            'factories' => [
                CounterService::class => function($container) {
                    return new CounterService();
                },
            ],
            'aliases' => [
                'Counter' => CounterService::class,
            ],
        ];
    }
}


Use in Controller

public function homeAction()
{ 
    $counter1 = $this->getEvent()
                    ->getApplication()
                    ->getServiceManager()->get('Counter');
    echo $counter1->increment(); // 1

    $counter2 = $this->getEvent()
                    ->getApplication()
                    ->getServiceManager()->get('Counter');
    echo $counter2->increment(); // 1 again (non-shared)
    return new ViewModel();
}

Explanation:

  • Counter is just an alias.
  • Internally, it still uses the CounterService class factory.

What is Controller + Service Injection?

Zend/Laminas uses factories for controllers, just like services.

This allows injecting services into controllers — instead of creating them manually inside the controller.


Why Inject Services into Controllers?

Benefit

Description

Testable Code

Easy to mock services in unit tests

Single Responsibility

Controller focuses only on request handling

Loose Coupling

Swappable services, easier maintenance


Basic Example with Explanation

Controller Factory (module\Application\src\Factory\MainControllerFactory.php)

class MainControllerFactory
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $counterService = $container->get(CounterService::class);
        return new MainController($counterService);
    }
}

Register in Module.php (module\Application\src\Module.php)

use Application\Service\CounterService;

class Module
{
    public function getConfig(): array
    {
        /** @var array $config */
        $config = include __DIR__ . '/../config/module.config.php';
        return $config;
    }
    public function getServiceConfig()
    {
        return [
            'factories' => [
                CounterService::class => function($container) {
                    return new CounterService();
                }
            ]
        ];
    }
}

Route Config (module.config.php) (module/Application/config/module.config.php)

use Application\Controller\MainController;
use Application\Factory\MainControllerFactory;

'controllers' => [
    'factories' => [
        MainController::class => MainControllerFactory::class,
    ],
],

Controller (module\Application\src\Controller\MainController.php)

use Application\Service\CounterService;

class MainController extends AbstractActionController
{
    private $counterService;
    public function __construct(CounterService $counterService)
    {
        $this->counterService = $counterService;
    }
    public function homeAction()
    { 
        echo $this->counterService->increment();
        echo $this->counterService->increment(); 
        return new ViewModel();
    }
}

Real-World Usage

The Service Manager is ideal for managing:

Use Case

Example Class

Database

Laminas\Db\Adapter\Adapter

Email

MailService, PHPMailer, etc.

Auth

Laminas\Authentication\AuthenticationService

APIs

PaymentGatewayService, SMSService

Custom Logic

InvoiceGenerator, NotificationManager, etc.

Whereisstuff is simple learing platform for beginer to advance level to improve there skills in technologies.we will provide all material free of cost.you can write a code in runkit workspace and we provide some extrac features also, you agree to have read and accepted our terms of use, cookie and privacy policy.
© Copyright 2024 www.whereisstuff.com. All rights reserved. Developed by whereisstuff Tech.