Skip to content

PrestaShopCorp/lightweight-container

Repository files navigation

lightweight-container

Source Code Latest Version Software License Build Status

Context

A module should not bundle and duplicate a heavy dependency from the framework used by the PrestaShop Core, we propose to replace module-lib-service-container with a simple service container to provide the following benefits :

  • avoid collisions (between modules and with the core);
  • avoid deprecation for a wider PHP and PrestaShop versions support;
  • no more cache construction phase, providers are written directly using PHP;
  • php configuration files instead of yaml;
  • lighter-weighted module zip (~400 Ko).

The goal is not to replace the symfony container but to enhance stability and compatibility for the ps_accounts module in single version mode.
Service container should be provided by the Core, and we use it when available (in the ps_accounts case we will start to use symfony controllers for PrestaShop v9 compatibility).

Installation

Configure GitHub repository

"repositories": [
    {
        "type": "vcs",
        "url": "[email protected]:PrestaShopCorp/lightweight-container.git"
    }
],

Install package

  composer require prestashopcorp/lightweight-container:dev-main

Scope dependency

It's highly recommended to scope the library to avoid collision with other modules using it with a different version.

Create a configuration file

// config.php

<?php

return [
    'my_module.a_parameter' => 'my value example',
    'my_module.log_level' => 'ERROR',
];

Instantiate service container in your module

// src/My_module.php

/**
 * @return \PrestaShopCorp\LightweightContainer\ServiceContainer\ServiceContainer
 *
 * @throws Exception
 */
public function getServiceContainer()
{
    if (null === $this->container) {
        $this->container = \PrestaShop\Module\MyModule\ServiceContainer\MyModuleServiceContainer::createInstance(
            __DIR__ . '/config.php'
        );
    }

    return $this->container;
}

/**
 * @param string $serviceName
 *
 * @return mixed
 */
public function getService($serviceName)
{
    return $this->getServiceContainer()->getService($serviceName);
}

Write a service provider

// src/ServiceContainer/Provider

namespace PrestaShop\Module\MyModule\ServiceContainer\Provider;

use PrestaShop\Module\PsAccounts\Service\MyService;
use PrestaShopCorp\LightweightContainer\ServiceContainer\Contract\IServiceProvider;
use PrestaShopCorp\LightweightContainer\ServiceContainer\ServiceContainer;

class SomethingProvider implements IServiceProvider
{
    /**
     * @param ServiceContainer $container
     *
     * @return void
     */
    public function provide(ServiceContainer $container)
    {
        $container->registerProvider(MyService::class, static function () use ($container) {
            return new ServicesBillingClient(
                $container->getParameter('my_module.a_parameter'),
                $container->get(AaService::class),
                $container->get(BbService::class)
            );
        });
    }
}

Register a service provider

You populate directly the provides array of the ServiceContainer class :

// src/ServiceContainer/MyModuleServiceContainer.php

namespace PrestaShop\Module\MyModule\ServiceContainer;

use PrestaShopCorp\LightweightContainer\ServiceContainer\ServiceContainer;

class MyModuleServiceContainer extends ServiceContainer
{
    /**
     * @var string[]
     */
    protected $provides = [
        Provider\SomethingProvider::class,
        // ...
    ];

    /**
     * @return Logger
     */
    public function getLogger()
    {
        if (null === $this->logger) {
            $this->logger = LoggerFactory::create(
                $this->getParameter(
                    'ps_accounts.log_level',
                    LoggerFactory::ERROR
                )
            );
        }

        return $this->logger;
    }

Share services with the PrestaShop Core container

For services that should stay accessible through the Core service container with the legacy MyModule::get inherited method you can create a SharedProvider :

// src/ServiceContainer/StaticProvider.php

namespace PrestaShop\Module\MyModule\ServiceContainer;

class SharedProvider
{
    /**
     * @param string $serviceName
     *
     * @return mixed
     */
    public static function provide($serviceName)
    {
        /** @var \My_module $module */
        $module = \Module::getInstanceByName('my_module');

        return $module->getService($serviceName);
    }
}

Then just declare your shared services the normal way with the PrestaShop yaml files using the SharedProvider :

# config/admin/services.yml

services:
  ##########################
  # Shared Services :
  # Those services might be accessed directly from the core container
  # by some modules.
  # Doing so we maintain compatibility & ensure the same instance is provided.

  PrestaShop\Module\MyModule\Service\MyModuleService:
    class: PrestaShop\Module\MyModule\Service\MyModuleService
    public: true
    factory: ['PrestaShop\Module\MyModule\ServiceContainer\SharedProvider', 'provide']
    arguments:
      - 'PrestaShop\Module\MyModule\Service\MyModuleService'

Caveats

If you previously were using another library to retrieve your module container services, beware of the exceptions thrown if you catched them directly somewhere in your code.

Improvements (next steps)

  • Unbind early injected logger (make it a normal service with early injection);
  • Add an optional metadata parameter to the registerProvider method containing className (in case the key doesn’t match a real class name), singleton (decide whether its a singleton, true by default), etc …

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages