Skip to content

Commit

Permalink
qa: re-implement ServiceManager properties (and mark deprecated) to…
Browse files Browse the repository at this point in the history
… `AbstractPluginManager` which allowed implementations to configure themselves

This is mostly provided as a BC layer to keep the overall maintenance to migrate to the next major version low.

Signed-off-by: Maximilian Bösing <[email protected]>
  • Loading branch information
boesing committed Mar 9, 2023
1 parent e5749a9 commit 5fa0087
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 64 deletions.
12 changes: 12 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@
<referencedClass name="Psr\Container\ContainerExceptionInterface"/>
</errorLevel>
</InvalidThrow>
<DeprecatedProperty>
<errorLevel type="suppress">
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$initializers"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$delegators"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$shared"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$lazyServices"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$services"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$aliases"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$abstractFactories"/>
<referencedProperty name="Laminas\ServiceManager\AbstractPluginManager::$factories"/>
</errorLevel>
</DeprecatedProperty>
</issueHandlers>

<issueHandlers>
Expand Down
131 changes: 109 additions & 22 deletions src/AbstractPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@
namespace Laminas\ServiceManager;

use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException;
use Laminas\ServiceManager\Exception\CyclicAliasException;
use Laminas\ServiceManager\Exception\InvalidServiceException;
use Laminas\ServiceManager\Factory\AbstractFactoryInterface;
use Laminas\ServiceManager\Factory\DelegatorFactoryInterface;
use Laminas\ServiceManager\Initializer\InitializerInterface;
use Laminas\Stdlib\ArrayUtils;
use Psr\Container\ContainerInterface;

use function class_exists;
use function gettype;
use function is_object;
use function sprintf;

/**
* Abstract plugin manager.
*
* Abstract PluginManagerInterface implementation providing:
*
* - creation context support. The constructor accepts the parent container
* instance, which is then used when creating instances.
* - plugin validation. Implementations may define the `$instanceOf` property
* to indicate what class types constitute valid plugins, omitting the
* requirement to define the `validate()` method.
* Abstract PluginManagerInterface implementation providing creation context support.
* The constructor accepts the parent container instance, which is then used when creating instances.
*
* @template InstanceType
* @template-implements PluginManagerInterface<InstanceType>
* @psalm-import-type ServiceManagerConfigurationType from ConfigInterface
* @psalm-import-type FactoryCallableType from ConfigInterface
* @psalm-import-type DelegatorCallableType from ConfigInterface
* @psalm-import-type InitializerCallableType from ConfigInterface
* @psalm-import-type AbstractFactoriesConfigurationType from ConfigInterface
* @psalm-import-type DelegatorsConfigurationType from ConfigInterface
* @psalm-import-type FactoriesConfigurationType from ConfigInterface
* @psalm-import-type InitializersConfigurationType from ConfigInterface
* @psalm-import-type LazyServicesConfigurationType from ConfigInterface
*/
abstract class AbstractPluginManager implements PluginManagerInterface
{
Expand All @@ -43,6 +43,79 @@ abstract class AbstractPluginManager implements PluginManagerInterface

protected bool $sharedByDefault = true;

/**
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var AbstractFactoryInterface[]
*/
protected array $abstractFactories = [];

/**
* A list of aliases
*
* Should map one alias to a service name, or another alias (aliases are recursively resolved)
*
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var string[]
*/
protected array $aliases = [];

/**
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var DelegatorsConfigurationType
*/
protected array $delegators = [];

/**
* A list of factories (either as string name or callable)
*
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var FactoriesConfigurationType
*/
protected array $factories = [];

/**
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var InitializersConfigurationType
*/
protected array $initializers = [];

/**
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var LazyServicesConfigurationType
*/
protected array $lazyServices = [];

/**
* A list of already loaded services (this act as a local cache)
*
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var array<string,mixed>
*/
protected array $services = [];

/**
* Enable/disable shared instances by service name.
*
* Example configuration:
*
* 'shared' => [
* MyService::class => true, // will be shared, even if "sharedByDefault" is false
* MyOtherService::class => false // won't be shared, even if "sharedByDefault" is true
* ]
*
* @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead.
*
* @var array<string,bool>
*/
protected array $shared = [];

private ServiceManager $plugins;

/**
Expand All @@ -56,12 +129,28 @@ public function __construct(
'shared_by_default' => $this->sharedByDefault,
], $creationContext);

// TODO: support old, internal, servicemanager properties in constructor to register services from properties
/** @var ServiceManagerConfigurationType $config */
$config = ArrayUtils::merge([
'factories' => $this->factories,
'abstract_factories' => $this->abstractFactories,
'aliases' => $this->aliases,
'services' => $this->services,
'lazy_services' => $this->lazyServices,
'shared' => $this->shared,
'delegators' => $this->delegators,
'initializers' => $this->initializers,
], $config);

$this->configure($config);
}

/**
* {@inheritDoc}
* @param ServiceManagerConfigurationType $config
* @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a
* service instanceexists for a given service.
* @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the
* plugin manager.
* @throws CyclicAliasException If the configuration contains aliases targeting themselves.
*/
public function configure(array $config): static
{
Expand All @@ -79,7 +168,7 @@ public function configure(array $config): static
}

/**
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param string|class-string<InstanceType> $name
* @param InstanceType $service
Expand Down Expand Up @@ -110,7 +199,6 @@ public function get($id): mixed
/** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */
$instance = $this->plugins->get($id);
$this->validate($instance);
/** @psalm-suppress MixedReturnStatement Yes indeed, plugin managers can return mixed. */
return $instance;
}

Expand All @@ -131,14 +219,13 @@ public function build(string $name, ?array $options = null): mixed
$plugin = $this->plugins->build($name, $options);
$this->validate($plugin);

/** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */
return $plugin;
}

/**
* Add an alias.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @throws ContainerModificationsNotAllowedException If $alias already
* exists as a service and overrides are disallowed.
Expand All @@ -151,7 +238,7 @@ public function setAlias(string $alias, string $target): void
/**
* Add an invokable class mapping.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param null|string $class Class to which to map; if omitted, $name is
* assumed.
Expand All @@ -166,7 +253,7 @@ public function setInvokableClass(string $name, string|null $class = null): void
/**
* Specify a factory for a given service name.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param class-string<Factory\FactoryInterface>|FactoryCallableType|Factory\FactoryInterface $factory
* @throws ContainerModificationsNotAllowedException If $name already
Expand All @@ -180,7 +267,7 @@ public function setFactory(string $name, string|callable|Factory\FactoryInterfac
/**
* Create a lazy service mapping to a class.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param null|string $class Class to which to map; if not provided, $name
* will be used for the mapping.
Expand All @@ -193,7 +280,7 @@ public function mapLazyService(string $name, string|null $class = null): void
/**
* Add an abstract factory for resolving services.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param string|AbstractFactoryInterface $factory Abstract factory
* instance or class name.
Expand All @@ -207,7 +294,7 @@ public function addAbstractFactory(string|AbstractFactoryInterface $factory): vo
/**
* Add a delegator for a given service.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param string $name Service name
* @param string|callable|DelegatorFactoryInterface $factory Delegator
Expand All @@ -222,7 +309,7 @@ public function addDelegator(string $name, string|callable|DelegatorFactoryInter
/**
* Add an initializer.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @psalm-param class-string<InitializerInterface>|InitializerCallableType|InitializerInterface $initializer
*/
Expand All @@ -234,7 +321,7 @@ public function addInitializer(string|callable|InitializerInterface $initializer
/**
* Add a service sharing rule.
*
* @deprecated Please use {@see PluginManagerInterface::configure()} instead.
* @deprecated Please use {@see AbstractPluginManager::configure()} instead.
*
* @param bool $flag Whether or not the service should be shared.
* @throws ContainerModificationsNotAllowedException If $name already
Expand Down
3 changes: 3 additions & 0 deletions test/AbstractFactory/TestAsset/ValidatorPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@

class ValidatorPluginManager extends AbstractPluginManager
{
public function validate(mixed $instance): void
{
}
}
9 changes: 5 additions & 4 deletions test/TestAsset/InvokableObjectPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,31 @@
namespace LaminasTest\ServiceManager\TestAsset;

use Laminas\ServiceManager\AbstractPluginManager;
use Laminas\ServiceManager\AbstractSingleInstancePluginManager;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Psr\Container\ContainerInterface;

/**
* @template-extends AbstractPluginManager<InvokableObject>
*/
final class InvokableObjectPluginManager extends AbstractPluginManager
final class InvokableObjectPluginManager extends AbstractSingleInstancePluginManager
{
/** @var array<string,string> */
protected $aliases = [
protected array $aliases = [
'foo' => InvokableObject::class,

// v2 normalized FQCNs
'laminastestservicemanagertestassetinvokableobject' => InvokableObject::class,
];

/** @var array<string,string> */
protected $factories = [
protected array $factories = [
InvokableObject::class => InvokableFactory::class,
// Legacy (v2) due to alias resolution
'laminastestservicemanagertestassetinvokableobject' => InvokableFactory::class,
];

protected string|null $instanceOf = InvokableObject::class;
protected string $instanceOf = InvokableObject::class;

protected bool $sharedByDefault = false;

Expand Down
3 changes: 3 additions & 0 deletions test/TestAsset/LenientPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@
*/
final class LenientPluginManager extends AbstractPluginManager
{
public function validate(mixed $instance): void
{
}
}
6 changes: 3 additions & 3 deletions test/TestAsset/NonAutoInvokablePluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace LaminasTest\ServiceManager\TestAsset;

use Laminas\ServiceManager\AbstractPluginManager;
use Laminas\ServiceManager\AbstractSingleInstancePluginManager;

final class NonAutoInvokablePluginManager extends AbstractPluginManager
final class NonAutoInvokablePluginManager extends AbstractSingleInstancePluginManager
{
protected bool $autoAddInvokableClass = false;

protected string|null $instanceOf = InvokableObject::class;
protected string $instanceOf = InvokableObject::class;
}
6 changes: 3 additions & 3 deletions test/TestAsset/SimplePluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace LaminasTest\ServiceManager\TestAsset;

use Laminas\ServiceManager\AbstractPluginManager;
use Laminas\ServiceManager\AbstractSingleInstancePluginManager;

final class SimplePluginManager extends AbstractPluginManager
final class SimplePluginManager extends AbstractSingleInstancePluginManager
{
protected string|null $instanceOf = InvokableObject::class;
protected string $instanceOf = InvokableObject::class;
}
32 changes: 0 additions & 32 deletions test/TestAsset/V2ValidationPluginManager.php

This file was deleted.

0 comments on commit 5fa0087

Please sign in to comment.