-
Notifications
You must be signed in to change notification settings - Fork 0
Context
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.
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.
Defining classes or interfaces based on Parameter TypeHint (doesn't support intersection/union types):
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);
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.
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.
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.