I made a small dependency injection container, inspired from Symfony covered by unit testing which works perfectly under php 7 but when switching to php8 get an error regarding the testIfServiceWithInjectionfound test which tells:
1) Tests\Container\ContainerTest::testIfServiceWithInjectionIsFound
App\Container\NotFoundException:
C:\wamp\www\lockpassword\app\Container\Container.php:108
C:\wamp\www\lockpassword\app\Container\Definition.php:101
C:\wamp\www\lockpassword\app\Container\Definition.php:98
C:\wamp\www\lockpassword\app\Container\Container.php:126
C:\wamp\www\lockpassword\tests\Container\ContainerTest.php:57
think it is related to the update of ReflectionParameter :: getClass () because there is only that I change from then php7, give you the compet code, I've been stuck on it for a week now:
update:
The problem concerns certainly the ReflectionParameter in method make and register but don't know how to do it, can you show me?
class ContainerTest extends TestCase
{
public function testIfSharedServiceIsFound()
{
$container = new Container();
$response1 = $container->get(ResponseContext::class);
$this->assertInstanceOf(ResponseContext::class, $response1);
$response2 = $container->get(ResponseContext::class);
$this->assertEquals(spl_object_id($response1), spl_object_id($response2));
}
public function testIfNoSharedServiceIsFound()
{
$container = new Container();
$container->getDefinition(ResponseContext::class)->setShared(false);
$response1 = $container->get(ResponseContext::class);
$this->assertInstanceOf(ResponseContext::class, $response1);
$response2 = $container->get(ResponseContext::class);
$this->assertNotEquals(spl_object_id($response1), spl_object_id($response2));
}
public function testIfServiceWithInjectionIsFound()
{
$container = new Container();
$bar = $container->get(Bar::class);
$this->assertInstanceOf(Bar::class, $bar);
$this->assertInstanceOf(Foo::class, $bar->foo);
}
public function testIfServiceWithParametersIsFound()
{
$container = new Container();
$container
->addParameter("path", "path")
->addParameter("action", "action")
;
$route = $container->get(Route::class);
$this->assertInstanceOf(Route::class, $route);
}
public function testIfServiceAliasIsFound()
{
$container = new Container();
$container->addAlias(RequestContextInterface::class, RequestContext::class);
$request = $container->get(RequestContextInterface::class);
$this->assertInstanceOf(RequestContextInterface::class, $request);
$this->assertInstanceOf(RequestContext::class, $request);
}
public function testIfServiceParameterIsFound()
{
$container = new Container();
$container->addParameter("key", "value");
$this->assertEquals("value", $container->getParameter("key"));
}
public function testIfParameterNotFound()
{
$container = new Container();
$this->expectException(NotFoundExceptionInterface::class);
$container->getParameter("fail");
}
public function testIfClassNotFound()
{
$container = new Container();
$this->expectException(NotFoundExceptionInterface::class);
$container->get(Fail::class);
}
}
class Container implements ContainerInterface
{
/**
* @var array
*/
private array $parameters = [];
/**
* @var Definition[]
*/
private array $definitions = [];
/**
* @var array
*/
private array $instances = [];
/**
* @var array
*/
private array $aliases = [];
/**
* Retrieves the requested reflection class and registers it.
*
* @param $id The service identifier.
* @return $this This registered service.
*/
public function register(string $id): self
{
$reflectionClass = new ReflectionClass($id);
if ($reflectionClass->isInterface()) {
$this->register($this->aliases[$id]);
$this->definitions[$id] = &$this->definitions[$this->aliases[$id]];
return $this;
}
$dependencies = [];
if (null !== $reflectionClass->getConstructor()) {
$dependencies = array_map(
fn (ReflectionParameter $parameter) => $this->getDefinition($parameter->getType()->getName()),
array_filter(
$reflectionClass->getConstructor()->getParameters(),
fn (ReflectionParameter $parameter) => $parameter->getType() && !$parameter->getType()->isBuiltin()
? new ReflectionClass($parameter->getType()->getName()) : null
)
);
}
$aliases = array_filter($this->aliases, fn (string $alias) => $id === $alias);
$this->definitions[$id] = new Definition($id, true, $aliases, $dependencies);
var_dump($this);
return $this;
}
/**
* Definition instance to gets for this service.
*
* @param $id The id of Definition instance.
* @return Definition Returns the definition value.
*/
public function getDefinition($id): Definition
{
if (!isset($this->definitions[$id])) {
$this->register($id);
}
return $this->definitions[$id];
}
/**
* Adds an parameter to pass to the service constructor method.
*
* @param $id The parameter name.
* @param $value The parameter value.
* @return $this Returns this parameter.
*/
public function addParameter($id, $value): self
{
$this->parameters[$id] = $value;
return $this;
}
/**
* Gets an parameter to pass to the service constructor method.
*
* @param $id The parameter name.
* @return array|bool|float|int|string|null Returns the parameter value.
* @throws NotFoundException When the parameter does not exist.
*/
public function getParameter($id)
{
if (!isset($this->parameters[$id])) {
throw new NotFoundException();
}
return $this->parameters[$id];
}
/**
* Gets a service.
*
* @param string $id The service identifier.
* @return mixed|object Returns the associated service.
* @throws NotFoundException When the service does not exist.
*/
public function get($id)
{
if (!$this->has($id)) {
if (!class_exists($id) && !interface_exists($id)) {
throw new NotFoundException();
}
$instance = $this->getDefinition($id)->make($this);
if (!$this->getDefinition($id)->isShared()) {
return $instance;
}
$this->instances[$id] = $instance;
}
return $this->instances[$id];
}
/**
* Determine if the given service is defined.
*
* @param string $id The service identifier.
* @return bool Returns true if the service is defined, false otherwise.
*/
public function has($id): bool
{
return isset($this->instances[$id]);
}
/**
* Adds a specific alias for this service.
*
* @param string $id The service identifier.
* @param string $class The associated class name.
* @return $this Returns this alias.
*/
public function addAlias(string $id, string $class): self
{
$this->aliases[$id] = $class;
return $this;
}
}
class Definition
{
/**
* @var string
*/
private string $id;
/**
* @var bool
*/
private bool $shared = true;
/**
* @var array
*/
private array $aliases = [];
/**
* @var Definition[]
*/
private array $dependencies = [];
/**
* @var ReflectionClass
*/
private ReflectionClass $class;
/**
* Definition constructor.
*
* @param string $id The service identifier.
* @param bool $shared the shared service.
* @param array $aliases this alias
* @param array $dependencies
*/
public function __construct(string $id, bool $shared = true, array $aliases = [], array $dependencies = [])
{
$this->id = $id;
$this->shared = $shared;
$this->aliases = $aliases;
$this->dependencies = $dependencies;
$this->class = new ReflectionClass($id);
}
/**
* Sets if the service must be shared or not.
*
* @param bool $shared True if the service is shared. False else.
* @return self Returns the shared service.
*/
public function setShared(bool $shared): self
{
$this->shared = $shared;
return $this;
}
/**
* Whether this service is shared.
*
* @return bool Returns true if the container is shared. False else.
*/
public function isShared(): bool
{
return $this->shared;
}
/**
* Creates a service.
*
* @param ContainerInterface $container Interface ContainerInterface allows to use it's methods
* @return object Returns a new instance for the current service.
*/
public function make(ContainerInterface $container): object
{
$constructor = $this->class->getConstructor();
if (null === $constructor) {
return $this->class->newInstance();
}
$parameters = $constructor->getParameters();
return $this->class->newInstanceArgs(
array_map(function (ReflectionParameter $param) use ($container) {
if ($param->getType() && !$param->getType()->isBuiltin()
? new ReflectionClass($param->getType()->getName()) : null === null) {
return $container->getParameter($param->getName());
}
return $container->get($param->getType()->getName());
}, $parameters)
);
}
}
interface ContainerInterface extends PsrContainerInterface
{
/**
* @param string $id
* @return $this
*/
public function register(string $id): self;
/**
* @param $id
* @return Definition
*/
public function getDefinition($id): Definition;
/**
* @param $id
* @param $value
* @return $this
*/
public function addParameter($id, $value): self;
/**
* @param $id
* @return mixed
*/
public function getParameter($id);
/**
* @param string $id
* @param string $class
* @return $this
*/
public function addAlias(string $id, string $class): self;
}
Aucun commentaire:
Enregistrer un commentaire