Argument Resolvers

Wire provides two different autowiring argument resolvers, one for callables like functions, closures, etc and another for constructors. These resolvers are used internally by the creator.

Resolve callable arguments

To create an associative array of callable arguments, create a CallableResolver instance using the Wire factory and pass the callable to the resolve method. You can then pass the arguments to the callable via the ... operator, for example.

Functions

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

function readValue(Value $value): string
{
    return $value->str;
}

$resolver = Wire::callableResolver();
$args = $resolver->resolve('readValue');

assert(readValue(...$args) === 'value property');

Closures

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

$closure =  function (Value $value): string {
    return $value->str;
};
$resolver = Wire::callableResolver();
$args = $resolver->resolve($closure);

assert($closure(...$args) === 'value property');

Instance methods

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

class Model
{
    public function readValue(Value $value): string
    {
        return $value->str;
    }
}

$model = new Model();
$resolver = Wire::callableResolver();
$args = $resolver->resolve([$model, 'readValue']);

assert($model->readValue(...$args) === 'value property');

Static methods

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

class Model
{
    public static function readValue(Value $value): string
    {
        return $value->str;
    }
}

$resolver = Wire::callableResolver();
$args = $resolver->resolve([Model::class, 'readValue']);

assert(Model::readValue(...$args) === 'value property');

Resolve constructor arguments

You simply pass the fully qualified class name to the resolve method of the ConstructorResolver instance created by the Wire factory:

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

class Model
{
    public function __construct(public Value $value)
    {
    }
}

$resolver = Wire::constructorResolver();
$args = $resolver->resolve(Model::class);

assert((new Model(...$args))->value->str === 'value property');

Factory methods

If you have a class with a factory method you can use the callable resolver as shown in the static methods example.

Assist resolvers with arguments that are already available

If you have some of the necessary arguments already at hand, you can pass them converted to an associative array to the resolve method's predefinedArgs and/or predefinedTypes parameters. The array's keys must match the names or types of the parameters of the callable to be resolved.
For more information, especially the difference between predefinedArgs and predefinedTypes, see Creator's section about the same topic, which works in a similar way.

An example using the callable resolver:

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

class Model
{
    public Value $value;
    public string $arg;
    public string $type;

    public static function create(string $arg, string $type, Value $value)
    {
        $model = new self();
        $model->value = $value;
        $model->arg = $arg;
        $model->type = $type;

        return $model;
    }
}

$resolver = Wire::callableResolver();
$args = $resolver->resolve(
    [Model::class, 'create'],
    predefinedArgs: ['arg' => 'predefined argument'],
    predefinedTypes: ['string' => 'predefined type'],
);
$model = Model::create(...$args);

assert($model->value->str === 'value property');
assert($model->arg === 'predefined argument');
assert($model->type === 'predefined type');

The constructor resolver works the same way:

use Conia\Wire\Wire;

class Value
{
    public string $str = 'value property';
}

class Model
{
    public function __construct(
        public string $arg,
        public string $type,
        public Value $value
    ) {
    }
}

$resolver = Wire::constructorResolver();
$args = $resolver->resolve(
    Model::class,
    predefinedArgs: ['arg' => 'predefined argument'],
    predefinedTypes: ['string' => 'predefined type']
);
$model = new Model(...$args);

assert($model->value->str === 'value property');
assert($model->arg === 'predefined argument');
assert($model->type === 'predefined type');

Using a PSR-11 container

Like the creator, resolvers can be initialized with a container to help with unresolvable parameter arguments. (See also: PSR-11 Containers)

use Conia\Wire\Tests\Fixtures\Container;
use Conia\Wire\Wire;

class Value
{
    public function __construct(protected string $str)
    {
    }

    public function get(): string
    {
        return $this->str;
    }
}

class Model
{
    public function __construct(protected Value $value)
    {
    }

    public function get(): string
    {
        return $this->value->get();
    }
}

$container = new Container();
$container->add(Value::class, new Value('Model value'));

$resolver = Wire::constructorResolver($container);
$args = $resolver->resolve(Model::class);

assert((new Model(...$args))->get() === 'Model value');

An example using the callable resolver:

use Conia\Wire\Tests\Fixtures\Container;
use Conia\Wire\Wire;

class Value
{
    public function __construct(protected string $str)
    {
    }

    public function get(): string
    {
        return $this->str;
    }
}

function readValue(Value $value): string
{
    return $value->get();
}

$container = new Container();
$container->add(Value::class, new Value('Model value'));

$resolver = Wire::callableResolver($container);
$args = $resolver->resolve('readValue');

assert(readValue(...$args) === 'Model value');

Creating the resolvers without the Wire factory

Internally the Wire factory initializes the resolvers with the creator like shown here:

use Conia\Wire\CallableResolver;
use Conia\Wire\ConstructorResolver;
use Conia\Wire\Creator;
// A PSR-11 container implementation like
// https://conia.dev/registry or https://php-di.org
use Conia\Wire\Tests\Fixtures\Container;

$container = new Container();
$creator = new Creator($container);
$callableresolver = new CallableResolver($creator);
$constructorResolver = new ConstructorResolver($creator);

// Or without container
$creator = new Creator();
$callableresolver = new CallableResolver($creator);
$constructorResolver = new ConstructorResolver($creator);