Skip to content

Commit

Permalink
Fix #9: Add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
samdark committed Sep 24, 2021
1 parent f14ecac commit 577f771
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 24 deletions.
144 changes: 143 additions & 1 deletion README.md
Expand Up @@ -15,7 +15,14 @@
[![static analysis](https://github.com/yiisoft/definitions/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/definitions/actions?query=workflow%3A%22static+analysis%22)
[![type-coverage](https://shepherd.dev/github/yiisoft/definitions/coverage.svg)](https://shepherd.dev/github/yiisoft/definitions)

The package ...
The package provides syntax constructs describing a way to create and configure a service or an object. It is used by [yiisoft/di](https://github.com/yiisoft/di) and [yiisoft/factory](https://github.com/yiisoft/factory)
but could be used in other [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible packages as well.

The following are provided:

- Definitions describing services or objects to create. This includes syntax, its validation and resolving it to objects.
- References and dynamic references to point to other definitions. These include additional utility to refer to multiple
definitions at once.

## Requirements

Expand All @@ -31,6 +38,141 @@ composer require yiisoft/definitions --prefer-dist

## General usage

### Definitions

Definition is describing a way to create and configure a service, an object
or return any other value. It must implement `Yiisoft\Definitions\Contract\DefinitionInterface`
that has a single method `resolve(ContainerInterface $container)`. References are
typically stored in the container or a factory and are resolved into object
at the moment of obtaining a service instance or creating an object.

#### `ClassDefinition`

Class definition points to a class or interface name. Union type could be used as well.

```php
use \Yiisoft\Definitions\ClassDefinition;

$definition = new ClassDefinition(MyServiceInterface::class, false);
$object = $definition->resolve($container);
```

The second argument above is an "optional" flag. Set it to true if null should be returned instead of throwing
an exception when resolving the definition.

#### `ArrayDefinition`

Array definition allows describing a service or an object declaratively:

```php
use \Yiisoft\Definitions\ArrayDefinition;

$definition = ArrayDefinition::fromConfig([
'class' => MyServiceInterface::class,
'__construct()' => [42],
'$propertyName' => 'value',
'setName()' => ['Alex'],
]);
$object = $definition->resolve($container);
```

In the above:

- `class` contains the name of the class to be instantiated.
- `__construct()` holds an array of constructor arguments.
- The rest of the config are property values (prefixed with `$`)
and method calls, postfixed with `()`. They are set/called
in the order they appear in the array.

#### `CallableDefinition`

Callable definition builds an object by executing a callable injecting
dependencies based on types used in its signature:

```php
use \Yiisoft\Definitions\CallableDefinition;

$definition = new CallableDefinition(
fn (SomeFactory $factory) => $factory->create('args')
);
$object = $definition->resolve($container);

// or

$definition = new CallableDefinition(
fn () => MyFactory::create('args')
);
$object = $definition->resolve($container);

// or

$definition = new CallableDefinition(
[MyFactory::class, 'create']
);
$object = $definition->resolve($container);
```

In the above we use a closure, a static call and a static
method passed as array-callable. In each case we determine
and pass dependencies based on the types of arguments in
the callable signature.

#### `ParameterDefinition`

Parameter definition resolves an object based on information from `ReflectionParameter` instance:

```php
use \Yiisoft\Definitions\ParameterDefinition;

$definition = new ParameterDefinition($reflectionParameter);
$object = $definition->resolve($container);
```

It is mostly used internally when working with callables.

#### `ValueDefinition`

Value definition resolves value passed as is:

```php
use \Yiisoft\Definitions\ValueDefinition;

$definition = new ValueDefinition(42, 'int');
$value = $definition->resolve($container); // 42
```

### References

References point to other definitions so when defining a definition you can use other definitions as its
dependencies:

```php
[
InterfaceA::class => ConcreteA::class,
'alternativeForA' => ConcreteB::class,
MyService::class => [
'__construct()' => [
Reference::to('alternativeForA'),
],
],
]
```

The `DynamicReference` defines a dependency to a service not defined in the container:

```php
[
MyService::class => [
'__construct()' => [
DynamicReference::to([
'class' => SomeClass::class,
'$someProp' => 15
])
]
]
]
```

## Testing

### Unit testing
Expand Down
2 changes: 1 addition & 1 deletion composer.json
@@ -1,7 +1,7 @@
{
"name": "yiisoft/definitions",
"type": "library",
"description": "_____",
"description": "The package provides definition syntax",
"keywords": [
"definitions"
],
Expand Down
2 changes: 1 addition & 1 deletion src/ArrayDefinition.php
Expand Up @@ -14,7 +14,7 @@
use function count;

/**
* Builds object by array config
* Builds an object by array config.
*
* @psalm-type MethodOrPropertyItem = array{0:string,1:string,2:mixed}
* @psalm-type ArrayDefinitionConfig = array{class:class-string,'__construct()'?:array}&array<string, mixed>
Expand Down
8 changes: 7 additions & 1 deletion src/CallableDefinition.php
Expand Up @@ -17,6 +17,10 @@
use function is_array;
use function is_object;

/**
* Builds an object by executing a callable injecting
* dependencies based on types used in its signature.
*/
final class CallableDefinition implements DefinitionInterface
{
/**
Expand All @@ -26,7 +30,9 @@ final class CallableDefinition implements DefinitionInterface
private $callable;

/**
* @param array|callable $callable
* @param array|callable $callable Callable to be used for building
* an object. Dependencies are determined and passed based
* on the types of arguments in the callable signature.
*
* @psalm-param callable|array{0:class-string,1:string} $callable
*/
Expand Down
27 changes: 17 additions & 10 deletions src/ClassDefinition.php
Expand Up @@ -14,33 +14,34 @@
use function is_object;

/**
* Reference points to a class name in the container
* Points to a class or interface name.
* Union type could be used as well.
*/
final class ClassDefinition implements DefinitionInterface
{
private string $class;
private bool $optional;

/**
* Constructor.
*
* @param string $class the class name
* @param bool $optional if null should be returned instead of throwing an exception
* @param string $class A class or interface name. Union type could be used as well.
* @param bool $optional If null should be returned instead of throwing an exception.
*/
public function __construct(string $class, bool $optional)
{
$this->class = $class;
$this->optional = $optional;
}

/**
* Get type of the object to be created.
*
* @return string An interface or a class name.
*/
public function getType(): string
{
return $this->class;
}

/**
* @throws InvalidConfigException
*/
public function resolve(ContainerInterface $container)
{
if ($this->isUnionType()) {
Expand All @@ -67,9 +68,13 @@ public function resolve(ContainerInterface $container)
}

/**
* Resolve union type string provided as a class name.
*
* @throws InvalidConfigException If an object of incorrect type was created.
* @throws Throwable
*
* @return mixed
* @return mixed|null Ready to use object or null if definition can
* not be resolved and is marked as optional.
*/
private function resolveUnionType(ContainerInterface $container)
{
Expand Down Expand Up @@ -104,7 +109,9 @@ private function isUnionType(): bool
}

/**
* @param mixed $value
* Get type of the value provided.
*
* @param mixed $value Value to get type for.
*/
private function getValueType($value): string
{
Expand Down
17 changes: 10 additions & 7 deletions src/Contract/DefinitionInterface.php
Expand Up @@ -11,19 +11,22 @@
use Yiisoft\Definitions\Exception\NotInstantiableException;

/**
* Interface DefinitionInterface
* Definition is describing a way to create and configure a service or an object.
*/
interface DefinitionInterface
{
/**
* @param DependencyResolverInterface $dependencyResolver
* Resolve this definition.
*
* @throws CircularReferenceException
* @throws NotFoundException
* @throws NotInstantiableException
* @throws InvalidConfigException
* @throws InvalidConfigException If an object of incorrect type was created.
* @throws CircularReferenceException If there is a circular reference detected
* when resolving the definition.
* @throws NotFoundException If container does not know how to resolve
* the definition.
* @throws NotInstantiableException If an object can not be instantiated.
*
* @return mixed|object
* @return mixed|null Ready to use object or null if definition can
* not be resolved and is marked as optional.
*/
public function resolve(ContainerInterface $container);
}
8 changes: 7 additions & 1 deletion src/Contract/ReferenceInterface.php
Expand Up @@ -6,10 +6,16 @@

use Yiisoft\Definitions\Exception\InvalidConfigException;

/**
* Reference points to another named defintion. Usually it is another service in the container
* or another object defintion in the factory.
*/
interface ReferenceInterface extends DefinitionInterface
{
/**
* @param mixed $id
* Create an instance of the reference pointing to ID specified.
*
* @param mixed $id ID of the service or object to point to.
*
* @throws InvalidConfigException
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Exception/NotFoundException.php
Expand Up @@ -14,6 +14,10 @@ final class NotFoundException extends Exception implements NotFoundExceptionInte
{
private string $id;

/**
* @param string $id ID of the definition or name of the class that was not found.
* @param array $buildStack Stack of IDs of services requested defintion or class that was not found.
*/
public function __construct(string $id, array $buildStack = [])
{
$this->id = $id;
Expand Down
3 changes: 3 additions & 0 deletions src/Exception/NotInstantiableClassException.php
Expand Up @@ -6,6 +6,9 @@

use Exception;

/**
* NotInstantiableClassException is thrown when a class can not be instantiated for whatever reason.
*/
final class NotInstantiableClassException extends NotInstantiableException
{
public function __construct(string $class, string $message = null, int $code = 0, Exception $previous = null)
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/NotInstantiableException.php
Expand Up @@ -9,7 +9,7 @@

/**
* NotInstantiableException represents an exception caused by incorrect dependency injection container
* configuration or usage.
* or factory configuration or usage.
*/
class NotInstantiableException extends Exception implements ContainerExceptionInterface
{
Expand Down
3 changes: 3 additions & 0 deletions src/ParameterDefinition.php
Expand Up @@ -11,6 +11,9 @@
use Yiisoft\Definitions\Contract\DefinitionInterface;
use Yiisoft\Definitions\Exception\NotInstantiableException;

/**
* Parameter definition resolves an object based on information from `ReflectionParameter` instance.
*/
final class ParameterDefinition implements DefinitionInterface
{
private ReflectionParameter $parameter;
Expand Down
11 changes: 10 additions & 1 deletion src/ValueDefinition.php
Expand Up @@ -7,6 +7,9 @@
use Psr\Container\ContainerInterface;
use Yiisoft\Definitions\Contract\DefinitionInterface;

/**
* Value definition resolves value passed as is.
*/
final class ValueDefinition implements DefinitionInterface
{
/**
Expand All @@ -17,14 +20,20 @@ final class ValueDefinition implements DefinitionInterface
private ?string $type;

/**
* @param mixed $value
* @param mixed $value Value to be returned on resolving.
* @param ?string $type Value type.
*/
public function __construct($value, string $type = null)
{
$this->value = $value;
$this->type = $type;
}

/**
* Get type of the value.
*
* @return string|null Value type.
*/
public function getType(): ?string
{
return $this->type;
Expand Down

0 comments on commit 577f771

Please sign in to comment.