Skip to content
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 {
publicfunction__construct(privatestring$id) {}
publicfunctiongetId(): string {
return$this->id;
}
}
$app->when(CurrentUser::class)
->needs('$id') // Requires "$" to imply it is a parameter name (and must not be omitted).
->give(staticfn() => 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 {
publicfunctionsetAmount(string$amount): self;
publicfunctionprocess(): void;
}
class Stripe implements PaymentProcessor {
privatestring$amount;
publicfunctionsetAmount(string$amount): self {
$this->amount = $amount;
return$this;
}
publicfunctionprocess(): void {
// ...Processes using $this->amount.
}
}
class PaymentService {
publicfunction__construct(privatePaymentProcessor$paymentProcessor) {}
publicfunctionprocess(): 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(
staticfn(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.

useWeakMap;
useArrayObject;
useArrayAccess;
class FirstClass {
publicfunction__construct(publicArrayAccess$stack = newWeakMap()) {}
}
class SecondClass {
publicfunction__construct(publicArrayAccess$stack = newWeakMap()) {}
}
$app->when([FirstClass::class, SecondClass::class])
->needs(ArrayAccess::class)
->give(ArrayObject::class);
$app->get(FirstClass::class)->stackinstanceof ArrayObject; // true.$app->get(SecondClass::class)->stackinstanceof ArrayObject; // true.

Case 2

When method call context needs to be defined.

useArrayObject;
useArrayAccess;
functioncurrentUserDetails(): ArrayAccess {
$fromDatabase = (object) // ...Typecast current user's fetched data from database.
return newArrayObject([
'first_name' => $fromDatabase->firstName,
'last_name' => $fromDatabase->lastName,
]);
}
class Controller {
publicfunctionrenderCurrentUser(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 = newController();
$app->when($controller->renderCurrentUser(...))
->needs(ArrayAccess::class)
->give(currentUserDetails(...));
$app->call($controller->renderCurrentUser(...)); // Renders first name & last name.

Clone this wiki locally