Promoted properties are assigned before the constructor body runs, so validation in the body reads either the parameter or the property and gets the same value.
final class OrderTotal
{
private int $vatCents; // derived, not promoted
public function __construct(
private int $netCents,
private int $vatRateBasisPoints,
) {
if ($netCents < 0) {
throw new InvalidArgumentException('net cannot be negative');
}
$this->vatCents = intdiv($netCents * $vatRateBasisPoints, 10_000);
}
}
Mixing promoted and declared properties in one class is the shape most real classes need and is missing from every example in the announcement — a service with five injected dependencies and one computed field promotes five and declares one. The assignment ordering means an exception thrown in the body leaves a partially constructed object, which is invisible unless something caught the exception and kept the reference, and that is a good reason to validate the arguments rather than the properties.