Giter VIP home page Giter VIP logo

definitions's Introduction

Yii Definitions


Latest Stable Version Total Downloads Build status Code Coverage Mutation testing badge static analysis type-coverage

The package provides syntax constructs describing a way to create and configure a service or an object. It is used by yiisoft/di and yiisoft/factory but could be used in other 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

  • PHP 8.0 or higher.

Installation

The package could be installed with composer:

composer require yiisoft/definitions

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.

ArrayDefinition

Array definition allows describing a service or an object declaratively:

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.

For multiple method call postfix key with unique string, for example:

[
    'class' => Collector::class,
    'add()' => ['Alex'],
    'add()2' => ['Mike'],
]

CallableDefinition

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

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:

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:

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:

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

Optional reference returns null when there's no corresponding definition in container:

[
    MyService::class => [
        '__construct()' => [
            // If container doesn't have definition for `EventDispatcherInterface` reference returns `null`
            // when resolving dependencies
            Reference::optional(EventDispatcherInterface::class), 
        ],
    ],
]

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

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

In order to pass an array of IDs as references to a property or an argument, Yiisoft\Definitions\ReferencesArray or Yiisoft\Definitions\DynamicReferencesArray could be used:

//params.php
return [
   'yiisoft/data-response' => [
       'contentFormatters' => [
           'text/html' => HtmlDataResponseFormatter::class,
           'application/xml' => XmlDataResponseFormatter::class,
           'application/json' => JsonDataResponseFormatter::class,
       ],
   ],
];

//web.php

ContentNegotiator::class => [
    '__construct()' => [
        'contentFormatters' => ReferencesArray::from($params['yiisoft/data-response']['contentFormatters']),
    ],
],

Definition storage

Definition storage could be used to hold and obtain definitions and check if a certain definition could be instantiated. Usually it is used by an implementation using the definitions:

use Yiisoft\Definitions\DefinitionStorage;

$storage = new DefinitionStorage([
    MyInterface::class => MyClass::class,
]);
$storage->setDelegateContainer($fallbackContainer);

if (!$storage->has(MyInterface::class)) {
    $buildStack = $storage->getBuildStack();
    // ...
}

In the above $buildStack will contain a stack with definition IDs in the order the latest dependency obtained would be built.

By default, if a class is checked in has() and it is not explicitly defined, the storage tries to autoload it first before failing. The storage may also work in a strict mode when everything in it should be defined explicitly:

use Yiisoft\Definitions\DefinitionStorage;

$storage = new DefinitionStorage([], true);
var_dump($storage->has(EngineMarkOne::class));

has() will return false even if EngineMarkOne exists.

Documentation

Support

If you need help or have a question, the Yii Forum is a good place for that. You may also check out other Yii Community Resources.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack

License

The Yii Access is free software. It is released under the terms of the BSD License. Please see LICENSE for more information.

Maintained by Yii Software.

definitions's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

definitions's Issues

Remove `var_export` usage

I'd remove var_export usage because it's insecure to disclose array's values into the world/logs.
Better to use keys or variable types.

Is it correct return of DefinitionExtractor::fromClass()?

return $dependencies;

At the beginnig of the function u r checking defined dependencies for specified class (it's ok):

if (isset(self::$dependencies[$class])) {
        return self::$dependencies[$class];
}

but at the end of function u r return all dependencies.

self::$dependencies[$class] = $dependencies;

return $dependencies;

I have not watched use of this method, but looks like bug.

[Bug] All exceptions are caught for optional dependencies

What steps will reproduce the problem?

Having the sample code:

class A
{
    public function __construct()
    {
        throw new RuntimeException('Something happened on initialization');
    }
}

class B
{
    public function __construct(private ?A $a = null)
    {
    }
}

$container->get(B::class);

What is the expected result?

The exception is thrown

What do you get instead?

Instance of B with a wrong dependency, and no exception is thrown. It' a hell to debug this situation.

Additional info

This code is responsible for the specified behavior. I'm pretty sure container definitions must not have a deal with application-level exceptions. In my opinion in the case when a dependency has a definition, and an exception is thrown on its resolving, this exception must not be catched by container/definition resolver, and doesn't matter if this dependency is optional or not.

Incorrect resolving of a spreaded parameter

What steps will reproduce the problem?

  1. Define a spreaded parameter in constructor like this: __construct(...$test)
  2. Define its value in config: ['__construct()' => ['test' => [1, 2]]]

What is the expected result?

$test in constructor contains [1, 2] value

What do you get instead?

$test in constructor contains [0 => [1, 2]] value

Additional info

Q A
Version dev-master
PHP version 8.1
Operating system Alpine

Optimizing constructor and argument initialization

I propose to add an alternative syntax for a simplified notation of the constructor and its arguments.
For example:

use \Yiisoft\Definitions\ArrayDefinition;

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

So it can be written like this:

use \Yiisoft\Definitions\ArrayDefinition;

$definition = ArrayDefinition::fromConfig([
    MyServiceInterface::class,
    [42], 
    'propertyName' => 'value',
    'setName()' => ['Alex'],
]);

Here the unnamed first (0) parameter (string) is the name of the class.
The second unnamed (1) parameter (array, if present) is the constructor arguments.

Raise an error if unnamed parameters are present along with "__construct()" or "class".

Also, the '$' notation here may be redundant and looks like a developer error visually (lack of double quotes).

This improvement will simplify development and reduce the amount of code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.