Skip to content

Commit

Permalink
Merge pull request #2 from young-steveo/iterating-on-container
Browse files Browse the repository at this point in the history
Iterating on container
  • Loading branch information
young-steveo committed Jun 3, 2023
2 parents 5ac8c1a + e427e49 commit 3c771bb
Show file tree
Hide file tree
Showing 29 changed files with 1,514 additions and 182 deletions.
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.yml]
indent_style = space
indent_size = 2
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
],
"minimum-stability": "stable",
"require": {
"psr/container": "^2.0"
"psr/container": "^2.0",
"psr/event-dispatcher": "^1.0"
},
"scripts": {
"post-install-cmd": [
Expand Down
66 changes: 58 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 83 additions & 21 deletions src/Cabinet/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,53 @@
*/
class Container implements \ArrayAccess, ContainerInterface
{
/**
* Resolver used to build classes.
*/
protected Resolver $resolver;

public function __construct()
/**
* @var array<class-string, Provider>
*/
protected array $providers = [];

/**
* Container uses a resolver to instantiate services.
*/
protected function __construct(Resolver $resolver = null)
{
$this->resolver = Resolver::forContainer($this);
$this->resolver = $resolver ?? Resolver::forContainer($this);
}

/**
* @var array<class-string, Provider>
* Create a new container.
*/
protected array $providers = [];
public static function create(): self
{
return new self();
}

/**
* @var array<class-string, mixed>
* Create a new container from a resolver.
*/
protected array $container = [];
public static function fromResolver(Resolver $resolver): self
{
return new self($resolver);
}

/**
* Register a service on the container.
*
* @param class-string $serviceName
* @param class-string|null $implementation
*/
public function service(string $serviceName): void
public function service(string $serviceName, string|null $implementation = null): void
{
$this->factory($serviceName, function (Container $container) use ($serviceName) {
return $container->resolver->resolve($serviceName);
if ($implementation === null) {
$implementation = $serviceName;
}
$this->factory($serviceName, function (Container $container) use ($implementation) {
return $container->resolver->resolve($implementation);
});
}

Expand All @@ -60,56 +81,97 @@ public function provider(string $serviceName, Provider $provider): void
$this->providers[$serviceName] = $provider;
}

/**
* Register a service instance on the container.
*
* @param class-string $serviceName
*/
public function instance(string $serviceName, mixed $instance): void
{
$this->factory($serviceName, fn () => $instance);
}

/**
* Register a prototype on the container.
*
* The container will create a new instance
* of the service each time it is requested.
*
* @param class-string $serviceName
*/
public function prototype(string $serviceName): void
{
$this->prototypeFactory($serviceName, function (Container $container) use ($serviceName) {
return $container->resolver->resolve($serviceName);
});
}

/**
* Register a prototype factory on the container.
*
* The container will create a new instance
* of the service each time it is requested.
*
* @param class-string $serviceName
*/
public function prototypeFactory(string $serviceName, \Closure $factory): void
{
$this->provider($serviceName, PrototypeProvider::fromFactory($factory));
}

/**
* ArrayAccess methods
*/

/**
* offsetSet
*
* @param class-string $offset
*/
public function offsetSet($offset, $value): void
{
if (!is_string($offset)) {
throw new Error\InvalidKey("Invalid key type: " . gettype($offset));
}
$this->container[$offset] = $value;
$this->providers[$offset] = SimpleProvider::fromFactory(fn() => $value);
}

/**
* offsetExists
*
* @param class-string $offset
*/
public function offsetExists($offset): bool
{
return isset($this->container[$offset]);
return isset($this->providers[$offset]);
}

/**
* offsetUnset
*
* @param class-string $offset
*/
public function offsetUnset($offset): void
{
unset($this->container[$offset]);
unset($this->providers[$offset]);
}

/**
* offsetGet
*
* @param class-string $offset
*/
public function offsetGet($offset): mixed
{
if (!is_string($offset)) {
throw new Error\InvalidKey("Invalid key type: " . gettype($offset));
}
if (isset($this->container[$offset])) {
return $this->container[$offset];
}

if (isset($this->providers[$offset])) {
$this->container[$offset] = $this->providers[$offset]($this);
} else {
throw new Error\OutOfBounds("No entry was found for this identifier: $offset");
$provider = $this->providers[$offset] ?? new NullProvider();
if ($instance = $provider($this)) {
return $instance;
}

return $this->container[$offset];
throw new Error\OutOfBounds("No entry was found for this identifier: $offset");
}

/**
Expand Down
23 changes: 23 additions & 0 deletions src/Cabinet/Event/ServiceResolved.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Arcanum\Cabinet\Event;

final class ServiceResolved extends \Arcanum\Echo\Event
{
/**
* @param mixed $service
*/
public function __construct(private mixed $service)
{
}

/**
* Get the service that was resolved.
*/
public function service(): mixed
{
return $this->service;
}
}
13 changes: 13 additions & 0 deletions src/Cabinet/EventDispatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Arcanum\Cabinet;

/**
* If Arcanum\Cabinet\EventDispatcher is registered in the container, the
* container will dispatch resolving events to it.
*/
interface EventDispatcher extends \Psr\EventDispatcher\EventDispatcherInterface
{
}
16 changes: 16 additions & 0 deletions src/Cabinet/NullProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Arcanum\Cabinet;

final class NullProvider extends Provider
{
/**
* Provide null
*/
public function __invoke(Container $container): mixed
{
return null;
}
}
Loading

0 comments on commit 3c771bb

Please sign in to comment.