Skip to content

Context

Shesh Ghimire edited this page Jan 5, 2025 · 5 revisions

Context provides an expressive way to define any concrete's dependencies.

If you haven't already, refer to this page to learn how to bootstrap and use container.

NOTE: Although Context is a elegant solution to define dependencies, in most cases a simple registration might be sufficient. It is recommended to use this feature only when some not-so-simple logic is required to resolve the concrete's dependency.

Primitives

Defining primitive type's value based on Parameter Name:

class CurrentUser {
    public function __construct(private string $id) {}

    public function getId(): string {
        return $this->id;
    }
}

$app->when(CurrentUser::class)
    ->needs('$id') // Requires "$" to imply it is a parameter name (and must not be omitted).
    ->give(static fn() => 5);

$app->get(CurrentUser::class)->getId() === 5; // true.

Classes & Interfaces

Defining classes or interfaces based on Parameter TypeHint (doesn't support intersection/union types):

Usage

interface PaymentProcessor {
    public function setAmount(string $amount): self;
    public function process(): void;
}

class Stripe implements PaymentProcessor {
    private string $amount;

    public function setAmount(string $amount): self {
        $this->amount = $amount;

        return $this;
    }

    public function process(): void {
        // ...Processes using $this->amount.
    }
}

class PaymentService {
    public function __construct(private PaymentProcessor $paymentProcessor) {}

    public function process(): void {
        $this->paymentProcessor->process();
    }
}

$app->when(PaymentService::class)
    ->needs(PaymentProcessor::class)
    ->give(Stripe::class);

// When some initialization is needed before resolving the dependency.
$app->when(PaymentService::class)
    ->needs(PaymentProcessor::class)
    ->give(
        static fn(Container $c): Stripe => $c->get(Stripe::class)->setAmount(100)
    );

// When concrete is aliased, alias can be used when defining context.
$app->setAlias(PaymentService::class, 'processPayment');

$app->when('processPayment')
    ->needs(PaymentProcessor::class)
    ->give(Stripe::class);

Other Use Cases

Case 1

When more than one concrete requires same dependency.

use WeakMap;
use ArrayObject;
use ArrayAccess;

class FirstClass {
    public function __construct(public ArrayAccess $stack = new WeakMap()) {}
}

class SecondClass {
    public function __construct(public ArrayAccess $stack = new WeakMap()) {}
}

$app->when([FirstClass::class, SecondClass::class])
    ->needs(ArrayAccess::class)
    ->give(ArrayObject::class);

$app->get(FirstClass::class)->stack instanceof ArrayObject; // true.
$app->get(SecondClass::class)->stack instanceof ArrayObject; // true.

Case 2

When method call context needs to be defined.

use ArrayObject;
use ArrayAccess;

function currentUserDetails(): ArrayAccess {
    $fromDatabase = (object) // ...Typecast current user's fetched data from database.

    return new ArrayObject([
        'first_name' => $fromDatabase->firstName,
        'last_name'  => $fromDatabase->lastName,
    ]);
}

class Controller {
    public function renderCurrentUser(ArrayAccess $details): void {
        echo "First Name: {$details['first_name']}";
        echo "Last Name: {$details['last_name']}";
    }
}

$app->when(Controller::class . '::renderCurrentUser')
    ->needs(ArrayAccess::class)
    ->give(currentUserDetails(...));

$app->call(Controller::class, methodName: 'renderCurrentUser'); // Renders first name & last name.

Case 3

If an instantiated controller's method call context needs to be defined. This will limit the context's scope to the instantiated object's method call only. Otherwise, context of non-instantiated method call will be used (if it is defined).

Continuing from Case 2:

$controller = new Controller();

$app->when($controller->renderCurrentUser(...))
    ->needs(ArrayAccess::class)
    ->give(currentUserDetails(...));

$app->call($controller->renderCurrentUser(...)); // Renders first name & last name.
Clone this wiki locally