Skip to content

Commit

Permalink
Merge pull request #230 from InvisibleSmiley/issue-228
Browse files Browse the repository at this point in the history
Make `ServiceLocatorInterface#get()` generic
  • Loading branch information
Ocramius committed Apr 3, 2024
2 parents 1998492 + c936ac7 commit ae01570
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 3 deletions.
5 changes: 5 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@
<code><![CDATA[MixedPluginManager]]></code>
</UnusedClass>
</file>
<file src="test/StaticAnalysis/ServiceLocatorInterfaceConsumer.php">
<UnusedClass>
<code><![CDATA[ServiceLocatorInterfaceConsumer]]></code>
</UnusedClass>
</file>
<file src="test/StaticAnalysis/ServiceManagerConfiguration.php">
<UnusedClass>
<code><![CDATA[ServiceManagerConfiguration]]></code>
Expand Down
4 changes: 2 additions & 2 deletions src/PluginManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function validate(mixed $instance): void;
/**
* @template TRequestedInstance extends InstanceType
* @psalm-param class-string<TRequestedInstance>|string $id Service name of plugin to retrieve.
* @psalm-return ($id is class-string ? TRequestedInstance : InstanceType)
* @psalm-return ($id is class-string<TRequestedInstance> ? TRequestedInstance : InstanceType)
* @throws Exception\ServiceNotFoundException If the manager does not have
* a service definition for the instance, and the service is not
* auto-invokable.
Expand All @@ -43,7 +43,7 @@ public function get(string $id): mixed;
*
* @template TRequestedInstance extends InstanceType
* @psalm-param string|class-string<TRequestedInstance> $name
* @psalm-return ($name is class-string ? TRequestedInstance : InstanceType)
* @psalm-return ($name is class-string<TRequestedInstance> ? TRequestedInstance : InstanceType)
* @throws Exception\ServiceNotFoundException If no factory/abstract
* factory could be found to create the instance.
* @throws Exception\ServiceNotCreatedException If factory/delegator fails
Expand Down
14 changes: 13 additions & 1 deletion src/ServiceLocatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

/**
* Interface for service locator
*/
interface ServiceLocatorInterface extends ContainerInterface
{
/**
* Build a service by its name, using optional options (such services are NEVER cached).
* Builds a service by its name, using optional options (such services are NEVER cached).
*
* @template T of object
* @param string|class-string<T> $name
Expand All @@ -27,4 +28,15 @@ interface ServiceLocatorInterface extends ContainerInterface
* @throws ContainerExceptionInterface If any other error occurs.
*/
public function build(string $name, ?array $options = null): mixed;

/**
* Finds an entry of the container by its identifier and returns it.
*
* @template T of object
* @param string|class-string<T> $id
* @psalm-return ($id is class-string<T> ? T : mixed)
* @throws ContainerExceptionInterface Error while retrieving the entry.
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
*/
public function get(string $id);
}
6 changes: 6 additions & 0 deletions src/ServiceManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public function get(string $id): mixed
// We start by checking if we have cached the requested service;
// this is the fastest method.
if (isset($this->services[$id])) {
/** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */
return $this->services[$id];
}

Expand All @@ -217,6 +218,8 @@ public function get(string $id): mixed
if ($sharedService) {
$this->services[$id] = $service;
}

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

Expand All @@ -234,6 +237,8 @@ public function get(string $id): mixed
// If the alias is configured as a shared service, we are done.
if ($sharedAlias) {
$this->services[$id] = $this->services[$resolvedName];

/** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */
return $this->services[$resolvedName];
}

Expand All @@ -247,6 +252,7 @@ public function get(string $id): mixed
$this->services[$id] = $service;
}

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

Expand Down
65 changes: 65 additions & 0 deletions test/StaticAnalysis/ServiceLocatorInterfaceConsumer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace LaminasTest\ServiceManager\StaticAnalysis;

use DateTimeImmutable;
use Laminas\ServiceManager\ServiceLocatorInterface;

use function assert;

final class ServiceLocatorInterfaceConsumer
{
public function canInferTypeFromGet(): void
{
$serviceProvider = $this->getServiceProvider();

$date = $serviceProvider->get(DateTimeImmutable::class);
echo $date->format('Y-m-d H:i:s');

$value = $serviceProvider->get('foo');
assert($value === 'bar');
}

public function canInferTypeFromBuild(): void
{
$serviceProvider = $this->getServiceProvider();

$date = $serviceProvider->build(DateTimeImmutable::class);
echo $date->format('Y-m-d H:i:s');

$value = $serviceProvider->build('foo');
assert($value === 'bar');
}

private function getServiceProvider(): ServiceLocatorInterface
{
$services = [
'foo' => 'bar',
DateTimeImmutable::class => new DateTimeImmutable(),
];
return new class ($services) implements ServiceLocatorInterface {
public function __construct(private readonly array $services)
{
}

public function has(string $id): bool
{
return isset($this->services[$id]);
}

public function build(string $name, ?array $options = null): mixed
{
/** @psalm-suppress MixedReturnStatement Yes indeed, can return mixed. */
return $this->services[$name] ?? null;
}

public function get(string $id): mixed
{
/** @psalm-suppress MixedReturnStatement Yes indeed, can return mixed. */
return $this->services[$id] ?? null;
}
};
}
}

0 comments on commit ae01570

Please sign in to comment.