diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 0164d512..086fcea0 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -211,6 +211,11 @@
+
+
+
+
+
diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php
index 483dece7..ed04d137 100644
--- a/src/PluginManagerInterface.php
+++ b/src/PluginManagerInterface.php
@@ -29,7 +29,7 @@ public function validate(mixed $instance): void;
/**
* @template TRequestedInstance extends InstanceType
* @psalm-param class-string|string $id Service name of plugin to retrieve.
- * @psalm-return ($id is class-string ? TRequestedInstance : InstanceType)
+ * @psalm-return ($id is class-string ? TRequestedInstance : InstanceType)
* @throws Exception\ServiceNotFoundException If the manager does not have
* a service definition for the instance, and the service is not
* auto-invokable.
@@ -43,7 +43,7 @@ public function get(string $id): mixed;
*
* @template TRequestedInstance extends InstanceType
* @psalm-param string|class-string $name
- * @psalm-return ($name is class-string ? TRequestedInstance : InstanceType)
+ * @psalm-return ($name is class-string ? TRequestedInstance : InstanceType)
* @throws Exception\ServiceNotFoundException If no factory/abstract
* factory could be found to create the instance.
* @throws Exception\ServiceNotCreatedException If factory/delegator fails
diff --git a/src/ServiceLocatorInterface.php b/src/ServiceLocatorInterface.php
index a7ecb161..b4e91977 100644
--- a/src/ServiceLocatorInterface.php
+++ b/src/ServiceLocatorInterface.php
@@ -8,6 +8,7 @@
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
/**
* Interface for service locator
@@ -15,7 +16,7 @@
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 $name
@@ -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 $id
+ * @psalm-return ($id is class-string ? T : mixed)
+ * @throws ContainerExceptionInterface Error while retrieving the entry.
+ * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
+ */
+ public function get(string $id);
}
diff --git a/src/ServiceManager.php b/src/ServiceManager.php
index 1d0b2b63..f97480ff 100644
--- a/src/ServiceManager.php
+++ b/src/ServiceManager.php
@@ -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];
}
@@ -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;
}
@@ -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];
}
@@ -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;
}
diff --git a/test/StaticAnalysis/ServiceLocatorInterfaceConsumer.php b/test/StaticAnalysis/ServiceLocatorInterfaceConsumer.php
new file mode 100644
index 00000000..67f00b87
--- /dev/null
+++ b/test/StaticAnalysis/ServiceLocatorInterfaceConsumer.php
@@ -0,0 +1,65 @@
+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;
+ }
+ };
+ }
+}