Support uninitialized typed properties in PATCH DTO validation

Hello!

I’m currently using yiisoft/validator for validating DTOs in a REST API and noticed an issue (or perhaps a missing feature) when validating PATCH requests.

With typed PHP properties there are three possible states:

  1. Property is uninitialized (field was not present in the request)
  2. Property is initialized with null
  3. Property is initialized with a value

For PATCH requests these states have different meanings:

  • uninitialized → don’t change the field
  • null → explicitly set the field to null
  • value → update the field

For example:

I can’t use the generic skipOnEmpty: true option because it treats empty strings as empty values as well. As a result, if the client explicitly sends "", the Length rule is skipped and the empty string passes validation instead of failing the minimum length constraint. For PATCH DTOs I only want to skip validation when the field is not provided, not when it is provided with an empty value.

final class UpdateUserRequest
{
    #[Length(
        min: 3,
        max: 255,
        skipOnEmpty: new WhenMissing(),
    )]
    public string $name;
}

When the request body is:

{}

I’d expect the Length rule to be skipped because the field wasn’t provided.

However, validation fails because the uninitialized property is internally treated as null.

Using WhenNull currently works:

#[Length(
    min: 3,
    max: 255,
    skipOnEmpty: new WhenNull(),
)]
public string $name;

but this seems to work only because uninitialized properties are normalized to null before validation. It feels like an implementation detail rather than intended behavior.

I also noticed that empty conditions already receive an $isPropertyMissing parameter:

public function __invoke(mixed $value, bool $isPropertyMissing = false): bool

but it appears to be used only for missing array keys, not for uninitialized object properties.

Is there a recommended way to handle this scenario today?

Would it make sense to either:

  • treat uninitialized object properties as “missing” for WhenMissing, or
  • introduce a dedicated WhenUninitialized condition?

I think this would make PATCH DTO validation much easier while preserving the distinction between:

  • field is absent
  • field is explicitly null
  • field has a value

I’m interested to hear whether this use case has already been discussed or if there’s another recommended approach.