Skip to content

Commit

Permalink
Merge 38260ea into 84018a2
Browse files Browse the repository at this point in the history
  • Loading branch information
feryardiant committed Jun 2, 2021
2 parents 84018a2 + 38260ea commit d9248ac
Show file tree
Hide file tree
Showing 23 changed files with 306 additions and 817 deletions.
48 changes: 4 additions & 44 deletions .github/wiki/Home.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
[![Version](https://img.shields.io/packagist/v/projek-xyz/container?style=flat-square)](https://packagist.org/packages/projek-xyz/container)
[![Lisence](https://img.shields.io/packagist/l/projek-xyz/container?style=flat-square)](https://github.com/projek-xyz/slim-plates/blob/master/LICENSE.md)
[![Actions Status](https://img.shields.io/github/workflow/status/projek-xyz/container/Tests/master?style=flat-square)](https://github.com/projek-xyz/container/actions)
[![Coverage Status](https://img.shields.io/coveralls/github/projek-xyz/container/master?style=flat-square)](https://coveralls.io/github/projek-xyz/container)
[![Coverage Status](https://img.shields.io/codeclimate/coverage/projek-xyz/container?style=flat-square)](https://codeclimate.com/github/projek-xyz/container)
[![Maintainability](https://img.shields.io/codeclimate/coverage-letter/projek-xyz/container?label=maintainability&style=flat-square)](https://codeclimate.com/github/projek-xyz/container/maintainability)

This tiny library aims to provide dead simple PSR-11 implementation with flexible service registration.

## Requirements

- PHP 7.2+ and tested up-to PHP 8.0
- PHP 7.2+ and tested up-to PHP 8.1

## Installation

Expand All @@ -21,10 +14,9 @@ $ composer require projek-xyz/container --prefer-dist

## API

- [`Container::set()`](Registering-an-instance) to Registering an instance
- [`Container::unset()`](Remove-an-instance) to Remove an instance
- [`Container::make()`](Create-an-instance) to Create an instance
- [`Container::extend()`](Extending-an-instance) to Extending an instance
- [`Container::set()`](registering-an-instance) to Registering an instance
- [`Container::make()`](create-an-instance) to Create an instance
- [`Container::extend()`](extending-an-instance) to Extending an instance

## Basic Usage

Expand Down Expand Up @@ -88,35 +80,3 @@ $container->get('db'); // etc
### PSR-11 Compliant

Means it has `get($id)` and `has($id)` method as required by [PSR-11 Standard](https://www.php-fig.org/psr/psr-11/)

## Extra Flexibilities

In-case you'd like to accessing a service instance using array, yes you can by registering `ArrayContainer` as a service

```php
use Projek\Container\ArrayContainer;

$container->set(ArrayContainer::class, ArrayContainer::class);

$container->set('myService', function (ArrayContainer $container) {
return new MyService(
$container['db'],
$container[Psr\Log\LoggerInterface::class]
);
});
```

Same thing when you want access it as a property:

```php
use Projek\Container\PropertyContainer;

$container->set(PropertyContainer::class, PropertyContainer::class);

$container->set('myService', function (PropertyContainer $container) {
return new MyService(
$container->db,
$container->{Psr\Log\LoggerInterface::class} // Not convenient indeed, but yes you could 😅
);
});
```
3 changes: 2 additions & 1 deletion .github/wiki/Registering-an-instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ $container->set('myService', SomeFactoryClass::class);

### 3. Use existing entry (as an alias)

you can use name of the registered service as the `$concrete` parameter.
You can use name of the registered service as the `$entry` parameter.

```php
// Based on example above
$container->set(CertainInterface::class, function () {
Expand Down
35 changes: 0 additions & 35 deletions .github/wiki/Remove-an-instance.md

This file was deleted.

10 changes: 5 additions & 5 deletions .github/wiki/_Footer.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Version](https://img.shields.io/packagist/v/projek-xyz/container?style=flat-square)](https://packagist.org/packages/projek-xyz/container)
[![Lisence](https://img.shields.io/packagist/l/projek-xyz/container?style=flat-square)](https://github.com/projek-xyz/slim-plates/blob/master/LICENSE.md)
[![Actions Status](https://img.shields.io/github/workflow/status/projek-xyz/container/Tests/master?style=flat-square)](https://github.com/projek-xyz/container/actions)
[![Coverage Status](https://img.shields.io/coveralls/github/projek-xyz/container/master?style=flat-square)](https://coveralls.io/github/projek-xyz/container)
[![Coverage Status](https://img.shields.io/codeclimate/coverage/projek-xyz/container?style=flat-square)](https://codeclimate.com/github/projek-xyz/container)
[![Maintainability](https://img.shields.io/codeclimate/coverage-letter/projek-xyz/container?label=maintainability&style=flat-square)](https://codeclimate.com/github/projek-xyz/container/maintainability)
[![Version](https://img.shields.io/packagist/v/projek-xyz/container?style=flat-square)](https://packagist.org/packages/projek-xyz/container)<br/>
This library is open-sourced software licensed under [![Lisence](https://img.shields.io/packagist/l/projek-xyz/container?style=flat-square)](https://github.com/projek-xyz/slim-plates/blob/master/LICENSE)
[![Coverage Status](https://img.shields.io/coveralls/github/projek-xyz/container/master?style=flat-square&logo=coveralls)](https://coveralls.io/github/projek-xyz/container)
[![Coverage Status](https://img.shields.io/codeclimate/coverage/projek-xyz/container?style=flat-square&logo=code-climate)](https://codeclimate.com/github/projek-xyz/container)
[![Maintainability](https://img.shields.io/codeclimate/coverage-letter/projek-xyz/container?style=flat-square)](https://codeclimate.com/github/projek-xyz/container/maintainability)
9 changes: 4 additions & 5 deletions .github/wiki/_Sidebar.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
## Contents

- [Home](Home)
- [Home](home)
- [Changelog](https://github.com/projek-xyz/container/blob/master/CHANGELOG.md)

## API

- [Registering an instance](Registering-an-instance)
- [Remove an instance](Remove-an-instance)
- [Create an instance](Create-an-instance)
- [Extending an instance](Extending-an-instance)
- [Registering an instance](registering-an-instance)
- [Create an instance](create-an-instance)
- [Extending an instance](extending-an-instance)
146 changes: 61 additions & 85 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
namespace Projek;

use Closure;
use Projek\Container\{Exception, Resolver};
use Psr\Container\ContainerInterface;

/**
* PSR-11 Container impementation class.
*
* @package Projek\Container
*/
class Container implements ContainerInterface
{
/**
Expand All @@ -27,7 +31,7 @@ class Container implements ContainerInterface
/**
* Service container resolver.
*
* @var Resolver
* @var Container\Resolver
*/
private $resolver;

Expand All @@ -38,11 +42,10 @@ class Container implements ContainerInterface
*/
public function __construct(array $entries = [])
{
$this->resolver = new Resolver($this);
$this->resolver = new Container\Resolver($this);
$this->entries = [
self::class => $this,
ContainerInterface::class => $this,
Container\ContainerInterface::class => $this,
];

foreach ($entries as $id => $instance) {
Expand All @@ -55,16 +58,22 @@ public function __construct(array $entries = [])
*/
public function __clone()
{
$this->resolver = new Resolver($this);
$this->resolver = new Container\Resolver($this);
}

/**
* {@inheritDoc}
* Retrieve the registered **entry** by $id.
*
* @see ContainerInterface::get()
* @param string $id The **entry** identifier.
* @return mixed Entry
* @throws Container\NotFoundException
* @throws Container\Exception
*/
public function get(string $id)
{
if (! $this->has($id)) {
throw new Exception\NotFoundException($id);
throw new Container\NotFoundException($id);
}

if (isset($this->handledEntries[$id])) {
Expand All @@ -81,27 +90,33 @@ public function get(string $id)
}

/**
* {@inheritDoc}
* Determine whether the **entry** is registered.
*
* {@inheritdoc}
* @see ContainerInterface::has()
* @param string $id The **entry** identifier.
* @return bool
*/
public function has(string $id): bool
{
return \array_key_exists($id, $this->entries);
}

/**
* Add new instance.
* Registering an **entry** to the container stack.
*
* @param string $id
* @param mixed $entry
* @link https://github.com/projek-xyz/container/wiki/registering-an-instance
* @param string $id The **entry** identifier.
* @param callable $factory
* @return static
*/
public function set(string $id, $entry)
public function set(string $id, $factory): self
{
if ($this->has($id)) {
return $this;
}

$this->entries[$id] = $this->resolver->resolve($entry);
$this->entries[$id] = $this->resolver->resolve($factory);

if (isset($this->handledEntries[$id])) {
unset($this->handledEntries[$id]);
Expand All @@ -110,23 +125,6 @@ public function set(string $id, $entry)
return $this;
}

/**
* Unset instance.
*
* @param string ...$id
* @return void
*/
public function unset(string ...$id): void
{
foreach ($id as $entry) {
unset($this->entries[$entry]);

if (isset($this->handledEntries[$entry])) {
unset($this->handledEntries[$entry]);
}
}
}

/**
* Resolve an instance without adding it to the stack.
*
Expand All @@ -150,90 +148,68 @@ public function unset(string ...$id): void
* })
* ```
*
* @link https://github.com/projek-xyz/container/pull/12
* @param string|callable $entry String of class name or callable
* @param null|array|\Closure ...$args
* @link https://github.com/projek-xyz/container/wiki/create-an-instance
* @param string|callable $instance String of class name or callable
* @param array|\Closure ...$args
* @param null|\Closure ...$callback
* @return mixed
* @throws Container\InvalidArgumentException
* @throws Container\Exception
*/
public function make($entry, ...$args)
public function make($instance, $args = [], ?\Closure $callback = null)
{
$entry = $this->resolver->resolve($entry);
if (null === $callback && $args instanceof \Closure) {
$callback = $args;
$args = [];
}

if (! is_array($args)) {
throw new Container\InvalidArgumentException(\sprintf(
'Argument #2 must be an %s, %s given',
(null === $callback ? 'array or instance of closure' : 'array'),
\gettype($args)
));
}

[$args, $condition] = ($count = \count($args = \array_filter($args)))
? $this->assertParams($count, $args)
: [[], null];
$instance = $this->resolver->resolve($instance, $args);

if ($condition instanceof \Closure) {
$entry = $condition($entry) ?: $entry;
if ($callback) {
$instance = $callback($instance) ?: $instance;
}

return $this->resolver->handle($entry, $args);
return $this->resolver->handle($instance, $args);
}

/**
* Extending an entry.
*
* @link https://github.com/projek-xyz/container/wiki/extending-an-instance
* @param string $id Identifier of existing entry.
* @param Closure $callable Callback to extend the functionality of the entry.
* @param \Closure $callable Callback to extend the functionality of the entry.
* @return object Returns the object instance.
* @throws Exception\NotFoundException If $id is not found.
* @throws Exception If trying to extends a callable.
* @throws Container\NotFoundException If $id is not found.
* @throws Container\Exception If trying to extends a callable.
*/
public function extend(string $id, \Closure $callable): object
{
$entry = $this->get($id);

// We tread any callable like a factory which could returns different instance
// when it invoked. So we should only extend object instance.
if (! \is_object($entry) || method_exists($entry, '__invoke')) {
throw new Exception(
sprintf('Could not extending a non-object or callable entry of "%s"', $id)
if (! \is_object($entry) || \method_exists($entry, '__invoke')) {
throw new Container\Exception(
\sprintf('Cannot extending a non-object or a callable entry of "%s"', $id)
);
}

$extended = $this->make($callable, [$entry]);

if (! is_a($extended, $class = get_class($entry))) {
throw new Exception(
sprintf('Argument #2 callback must be returns of type "%s"', $class)
if (! \is_a($extended, $class = \get_class($entry))) {
throw new Container\Exception(
\sprintf('Argument #2 callback must be returns of type "%s"', $class)
);
}

$this->unset($id);

return $this->entries[$id] = $extended;
}

/**
* Assert $argumens and $condition by $params
*
* @param int $count
* @param array $params
* @return array List of [$argumens, $condition]
*/
private function assertParams(int $count, array $params = []): array
{
if (2 === $count) {
if (! \is_array($params[0])) {
throw new Exception\InvalidArgumentException(2, ['array'], $params[0]);
} elseif (! ($params[1] instanceof \Closure) && null !== $params[1]) {
throw new Exception\InvalidArgumentException(3, ['Closure'], $params[1]);
}

return $params;
}

if (1 === $count) {
if (! \is_array($params[0]) && ! ($params[0] instanceof \Closure)) {
throw new Exception\InvalidArgumentException(2, ['array', 'Closure'], $params[0]);
}

return [
\is_array($params[0]) ? $params[0] : [],
$params[0] instanceof \Closure ? $params[0] : null
];
}

throw new Exception\RangeException(3, $count + 1);
}
}
1 change: 1 addition & 0 deletions src/Container/AbstractContainerAware.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* This class is internal uses, please implements `ContainerAware` and use
* `HasContainer` trait yourself instead.
*
* @package Projek\Container
* @internal
*/
abstract class AbstractContainerAware implements ContainerAware
Expand Down

0 comments on commit d9248ac

Please sign in to comment.