[PHP-DEV] [RFC] [Discussion] #[\DelayedTargetValidation] attribute

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

On Wed, Jun 18, 2025, at 01:26, Daniel Scherzer wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

Interesting. I’d also argue for the inverse more than this, though. I’d like for my attributes to be validated during compilation instead of delayed to runtime – which, it isn’t actually. Runtime validation ONLY happens when calling ->newInstance() on ReflectionAttribute, and never before then. So, only when an attribute is actually read during reflection is it validated. Further, if you never actually instantiate it … it is never actually validated (i.e., just looking for the presence of an attribute, not the details).

https://3v4l.org/UqQKi

— Rob

Von meinem iPhone gesendet

Am 18.06.2025 um 11:26 schrieb Rob Landers rob@bottled.codes:

On Wed, Jun 18, 2025, at 01:26, Daniel Scherzer wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

Interesting. I’d also argue for the inverse more than this, though. I’d like for my attributes to be validated during compilation instead of delayed to runtime – which, it isn’t actually. Runtime validation ONLY happens when calling ->newInstance() on ReflectionAttribute, and never before then. So, only when an attribute is actually read during reflection is it validated. Further, if you never actually instantiate it … it is never actually validated (i.e., just looking for the presence of an attribute, not the details).

https://3v4l.org/UqQKi

As Daniel’s rfc mentions you need to differentiate between compiler and userland attributes.

The compiler attributes are validated at compile time not dueing newInstance.

— Rob

On 18 June 2025 13:09:26 CEST, kontakt@beberlei.de wrote:

Von meinem iPhone gesendet

Am 18.06.2025 um 11:26 schrieb Rob Landers <rob@bottled.codes>:

On Wed, Jun 18, 2025, at 01:26, Daniel Scherzer wrote:
Hi internals,

I'd like to start the discussion for a new RFC about adding a `#[\DelayedTargetValidation]` attribute.

* RFC: PHP: rfc:delayedtargetvalidation_attribute
* Implementation: Add `#[\DelayedTargetValidation]` attribute by DanielEScherzer · Pull Request #18817 · php/php-src · GitHub

--Daniel

Interesting. I’d also argue for the inverse more than this, though. I’d like for my attributes to be validated during compilation instead of delayed to runtime -- which, it isn’t actually. Runtime validation ONLY happens when calling ->newInstance() on ReflectionAttribute, and never before then. So, only when an attribute is actually read during reflection is it validated. Further, if you never actually instantiate it ... it is never actually validated (i.e., just looking for the presence of an attribute, not the details).

Online PHP editor | output for UqQKi

As Daniel’s rfc mentions you need to differentiate between compiler and userland attributes.

The compiler attributes are validated at compile time not dueing newInstance.

— Rob

Just as an information:

We are at the moment in the proces of setting up a registry for userland attributes at the PHP-FIG.

For more information regarding that check out <https://github.com/php-fig/fig-standards/pull/1331&gt;

Cheers

Andreas

--
Andreas Heigl

On Wed, Jun 18, 2025, at 13:09, kontakt@beberlei.de wrote:

Von meinem iPhone gesendet

Am 18.06.2025 um 11:26 schrieb Rob Landers rob@bottled.codes:

On Wed, Jun 18, 2025, at 01:26, Daniel Scherzer wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

Interesting. I’d also argue for the inverse more than this, though. I’d like for my attributes to be validated during compilation instead of delayed to runtime – which, it isn’t actually. Runtime validation ONLY happens when calling ->newInstance() on ReflectionAttribute, and never before then. So, only when an attribute is actually read during reflection is it validated. Further, if you never actually instantiate it … it is never actually validated (i.e., just looking for the presence of an attribute, not the details).

https://3v4l.org/UqQKi

As Daniel’s rfc mentions you need to differentiate between compiler and userland attributes.

The compiler attributes are validated at compile time not dueing newInstance.

— Rob

Yes, but when would it check it during runtime? It doesn’t say in the RFC. From what I can tell, it effectively disables the attribute, unless someone happens to enumerate and instantiate all the attributes (which is unlikely).

— Rob

Hi Daniel,

While I’m in favor of the RFC, I’d more like to see the default behavior of internal and userland attributes adjusted to work the same, with both having delayed validation enabled by default. Treating core attributes differently doesn’t make sense to me.

So, if that’s someone you’re willing to work on, I’d very much prefer that.
But if there are downsides to this approach or if this is too complicated for other reasons, this attribute is a net positive in my book to make forward compatibility possible.

One note on the example code:

class Base {
class Child extends Parent { \

Parent isn’t a valid class name, and I think you wanted to extend from Base here? I’d prefer the example to compile so that they can be used for documentation later on where possible.

Kind Regards,
Volker

···

Volker Dusch
Head of Engineering

Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprint

Sitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127

On Wed, Jun 18, 2025 at 5:22 AM Volker Dusch <volker@tideways-gmbh.com> wrote:

On Wed, Jun 18, 2025 at 1:28 AM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

Hi Daniel,

One note on the example code:

class Base {
class Child extends Parent { \

Parent isn’t a valid class name, and I think you wanted to extend from Base here? I’d prefer the example to compile so that they can be used for documentation later on where possible.

Kind Regards,
Volker

Fixed, thanks.

Hi Volker

On Wed, Jun 18, 2025 at 2:23 PM Volker Dusch <volker@tideways-gmbh.com> wrote:

On Wed, Jun 18, 2025 at 1:28 AM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

I'd like to start the discussion for a new RFC about adding a `#[\DelayedTargetValidation]` attribute.

* RFC: PHP: rfc:delayedtargetvalidation_attribute
* Implementation: Add `#[\DelayedTargetValidation]` attribute by DanielEScherzer · Pull Request #18817 · php/php-src · GitHub

While I'm in favor of the RFC, I'd more like to see the default behavior of internal and userland attributes adjusted to work the same, with both having delayed validation enabled by default. Treating core attributes differently doesn't make sense to me.

So, if that's someone you're willing to work on, I'd very much prefer that.

I wouldn't support that, because internal attributes with effects are
not usually instantiated.

class C {
    #[\SensitiveParameter] // This doesn't actually do anything, only
works on parameters
    public $prop;
}

I would get no indication that this attribute doesn't behave as I
expect. We have at least a few attributes with confusable targets
(SensitiveParameter, Override, Deprecated, NoDiscard). User attributes
can't have effects without at least reading them through reflection,
although granted they don't necessarily need to be instantiated, and
so might also not trigger the error.

Ilija

Le mer. 18 juin 2025 à 23:49, Ilija Tovilo <tovilo.ilija@gmail.com> a écrit :

Hi Volker

On Wed, Jun 18, 2025 at 2:23 PM Volker Dusch <volker@tideways-gmbh.com> wrote:

On Wed, Jun 18, 2025 at 1:28 AM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

While I’m in favor of the RFC, I’d more like to see the default behavior of internal and userland attributes adjusted to work the same, with both having delayed validation enabled by default. Treating core attributes differently doesn’t make sense to me.

So, if that’s someone you’re willing to work on, I’d very much prefer that.

I 100% agree with what Volker posted here.

I wouldn’t support that, because internal attributes with effects are
not usually instantiated.

class C {
#[\SensitiveParameter] // This doesn’t actually do anything, only
works on parameters
public $prop;
}

I would get no indication that this attribute doesn’t behave as I
expect. We have at least a few attributes with confusable targets
(SensitiveParameter, Override, Deprecated, NoDiscard). User attributes
can’t have effects without at least reading them through reflection,
although granted they don’t necessarily need to be instantiated, and
so might also not trigger the error.

These considerations work exactly the same for userland attributes: if I use #[Whatever] on a location that is not allowed by the declaration of the attribute, nothing will ever tell me at runtime, because nobody will ever try to read that attribute on that unexpected location.

Yet, this “silent” behavior is by design, because that’s the core of the “attributes are declarative metadata” promise. If you make them be enforced by the engine, then this promise falls apart and attributes become way less useful.

The correct solution to the validation problem is to use a static analyzer. There, one can easily spot that some attributes are not correctly placed, and one can then also ignore the report of the SA tool, because eg #[Deprecated] is used on a class on purpose for that library (e.g. for the forward compat use case).

The exception to the declarative nature of attributes that is currently allowed for engine-provided ones is detrimental to me: it allows turning them into “syntax hacks” basically (=engine-enforced-thus-not-declarative rules).

To paraphrase Volker, I’m in favor of the RFC, but only if we cannot agree on the need for internal and userland attributes to have the same conceptual roots (truly declarative ones).

Cheers,
Nicolas

On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

It seems a common point of discussion is the difference in behavior between internal and userland attributes, so I wanted to clarify a few things:

  • Userland attributes are always metadata, in part because of the backwards and forward compatibility that those attributes provide - the attribute does not need to exist in order to be used, it just needs to exist (and target the location) when you call ReflectionAttribute::newInstance() to instantiate
  • Internal attributes are a mix between metadata and a way to plug into the engine. There were already existing ways to plug into the engine (e.g. using magic methods or implementing the Countable or ArrayAccess interfaces) but attributes provided a way to plug into the engine when you don’t just want to add a function override, but rather something else (like indicating a parameter should be redacted in backtraces with #[\SensitiveParameter]). It would probably be impossible to do that securely with a userland attribute and userland error handler that manually redacted things…
  • Attributes were designed to have good compatibility - the syntax chosen for PHP 8.0 was, in prior versions of PHP, used for comments - so any code with attributes could also work in PHP 7.4, just without the metadata. Similarly, by not validating userland attributes until they are accessed with reflection, you can add an attribute that does not exist yet (e.g. if using different versions of a library) with minimal complications.
  • Internal attributes are validated at compile time - because they can be. Internal attributes tell the engine to do something, and it makes sense (at least to me) that if the engine cannot do that thing, there should be an error without needing to wait for ReflectionAttribute::newInstance() to be called.

But, the validation of internal attributes at compile time means that they lose the compatibility features of userland attributes, and that is what this RFC is trying to address. To be clear, I think the difference in behavior between userland and internal attributes is a) fundamentally based on the difference in capabilities (pure metadata vs engine behavior) and b) something that should not be changed without significant further investigation.

This new #[\DelayedTargetValidation] is meant to simplify things, and to partially unify the behavior of the errors - if you are getting any errors from internal attributes and want to suppress them at compile time, just add the new attribute everywhere.

-Daniel

Hi Daniel, internals,

On Wed, Jun 18, 2025, 02:29 Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

How about completely disabling errors caused by a bad target at compile time?
And just ignore the attribute, that can be validated at runtime if needed.
IDEs and static code analysis would show the problem and that might be enough.


Alex

On Sun, Jun 22, 2025 at 10:24 PM Alexandru Pătrănescu <drealecs@gmail.com> wrote:

Hi Daniel, internals,

On Wed, Jun 18, 2025, 02:29 Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

How about completely disabling errors caused by a bad target at compile time?
And just ignore the attribute, that can be validated at runtime if needed.
IDEs and static code analysis would show the problem and that might be enough.


Alex

If you mean to always disable those errors, that is exactly what I responded to above - https://news-web.php.net/php.internals/127734. Basically, internal attributes are not pure metadata but also trigger engine behavior, while userland attributes are metadata (and the attributes don’t even need to exist to be used!). Thus, there is a benefit to compile time errors, since otherwise you don’t know that the engine behavior isn’t being triggered.

This attribute completely disables errors caused by a bad target at compile time whenever the new #[\DelayedTargetValidation] is applied. This gives the best of both worlds

  • you get errors if you misuse an internal attribute
  • you can suppress those errors intentionally if you have a reason (e.g. forward compatibility), and just delay the validation until runtime

Since I proposed this RFC, a proposal to support #[\Override] on properties has been discussed (https://news-web.php.net/php.internals/127831), highlighting the benefits of this #[\DelayedTargetValidation] attribute. E.g. if we add it in 8.5, then when a later version adds #[\Override], a library can apply #[\Override] to properties without requiring a newer version of PHP.

-Daniel

On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

This discussion has been ongoing for more than 2 weeks, and from what I can see there have only been 2 concerns/suggestions raised:

  • Going in the opposite direction and validating userland attributes at compile time too - this is counter to the design of attributes, which are meant to be easy to apply and can even be used when the specified attribute does not exist, with errors only raised when attributes are parsed.
  • Avoid the need for the #[\DelayedTargetValidation] attribute by changing the default behavior - I (and others) have responded about why this would be problematic, and I have updated the RFC to include changing the default behavior as a rejected feature with an explanation of the issues.

If there is no further feedback, I intend to start a vote in a few days.

-Daniel

On Sun, Jun 22, 2025, at 22:00, Daniel Scherzer wrote:

On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

It seems a common point of discussion is the difference in behavior between internal and userland attributes, so I wanted to clarify a few things:

  • Userland attributes are always metadata, in part because of the backwards and forward compatibility that those attributes provide - the attribute does not need to exist in order to be used, it just needs to exist (and target the location) when you call ReflectionAttribute::newInstance() to instantiate
  • Internal attributes are a mix between metadata and a way to plug into the engine. There were already existing ways to plug into the engine (e.g. using magic methods or implementing the Countable or ArrayAccess interfaces) but attributes provided a way to plug into the engine when you don’t just want to add a function override, but rather something else (like indicating a parameter should be redacted in backtraces with #[\SensitiveParameter]). It would probably be impossible to do that securely with a userland attribute and userland error handler that manually redacted things…
  • Attributes were designed to have good compatibility - the syntax chosen for PHP 8.0 was, in prior versions of PHP, used for comments - so any code with attributes could also work in PHP 7.4, just without the metadata. Similarly, by not validating userland attributes until they are accessed with reflection, you can add an attribute that does not exist yet (e.g. if using different versions of a library) with minimal complications.
  • Internal attributes are validated at compile time - because they can be. Internal attributes tell the engine to do something, and it makes sense (at least to me) that if the engine cannot do that thing, there should be an error without needing to wait for ReflectionAttribute::newInstance() to be called.

But, the validation of internal attributes at compile time means that they lose the compatibility features of userland attributes, and that is what this RFC is trying to address. To be clear, I think the difference in behavior between userland and internal attributes is a) fundamentally based on the difference in capabilities (pure metadata vs engine behavior) and b) something that should not be changed without significant further investigation.

I don’t think this is quite right. Because non-existent attributes can be used, if you use attributes in your library, you already know to only ever, and I mean only ever, instantiate your own attributes. Attempting to instantiate someone else’s attributes can result in crashes. This means any delayed attributes will most likely never be validated by anything.

This new #[\DelayedTargetValidation] is meant to simplify things, and to partially unify the behavior of the errors - if you are getting any errors from internal attributes and want to suppress them at compile time, just add the new attribute everywhere.

-Daniel

I don’t think this is “forward compatibility”, this is completely disabling the attribute.

— Rob

On Fri, Jul 4, 2025 at 4:50 PM Rob Landers rob@bottled.codes wrote:

On Sun, Jun 22, 2025, at 22:00, Daniel Scherzer wrote:

On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

It seems a common point of discussion is the difference in behavior between internal and userland attributes, so I wanted to clarify a few things:

  • Userland attributes are always metadata, in part because of the backwards and forward compatibility that those attributes provide - the attribute does not need to exist in order to be used, it just needs to exist (and target the location) when you call ReflectionAttribute::newInstance() to instantiate
  • Internal attributes are a mix between metadata and a way to plug into the engine. There were already existing ways to plug into the engine (e.g. using magic methods or implementing the Countable or ArrayAccess interfaces) but attributes provided a way to plug into the engine when you don’t just want to add a function override, but rather something else (like indicating a parameter should be redacted in backtraces with #[\SensitiveParameter]). It would probably be impossible to do that securely with a userland attribute and userland error handler that manually redacted things…
  • Attributes were designed to have good compatibility - the syntax chosen for PHP 8.0 was, in prior versions of PHP, used for comments - so any code with attributes could also work in PHP 7.4, just without the metadata. Similarly, by not validating userland attributes until they are accessed with reflection, you can add an attribute that does not exist yet (e.g. if using different versions of a library) with minimal complications.
  • Internal attributes are validated at compile time - because they can be. Internal attributes tell the engine to do something, and it makes sense (at least to me) that if the engine cannot do that thing, there should be an error without needing to wait for ReflectionAttribute::newInstance() to be called.

But, the validation of internal attributes at compile time means that they lose the compatibility features of userland attributes, and that is what this RFC is trying to address. To be clear, I think the difference in behavior between userland and internal attributes is a) fundamentally based on the difference in capabilities (pure metadata vs engine behavior) and b) something that should not be changed without significant further investigation.

I don’t think this is quite right. Because non-existent attributes can be used, if you use attributes in your library, you already know to only ever, and I mean only ever, instantiate your own attributes. Attempting to instantiate someone else’s attributes can result in crashes. This means any delayed attributes will most likely never be validated by anything.

Symfony instantiates arbitrary attributes, https://github.com/symfony/symfony/pull/40307 - and while delayed attributes may not end up being validated, that is a feature, not a bug - the goal is that the validation not be performed at compile time, and so it was either delay to runtime, or not do the validation at all.

This new #[\DelayedTargetValidation] is meant to simplify things, and to partially unify the behavior of the errors - if you are getting any errors from internal attributes and want to suppress them at compile time, just add the new attribute everywhere.

-Daniel

I don’t think this is “forward compatibility”, this is completely disabling the attribute.

— Rob

So I guess my use of “forward compatibility” was a bit confusing. This allows a library to be backwards compatible (using PHP 8.6 attribute targets but running on PHP 8.5 without errors) by making the language forward compatible (allowing PHP 8.5 to process 8.6 attribute targets without errors, by delaying those errors). The attribute is not disabled at all though.

  • If the attribute is used in a place where it would do anything (i.e. there wouldn’t be an error about being a bad target) then that attribute is used as normal, and #[\DelayedTargetValidation] does not affect it
  • If the attribute is used in a place where it would error, then it already cannot do anything, and #[\DelayedTargetValidation] just delays the error - the attribute is already going to be inactive anyway

-Daniel

On Sat, Jul 5, 2025, at 21:53, Daniel Scherzer wrote:

On Fri, Jul 4, 2025 at 4:50 PM Rob Landers rob@bottled.codes wrote:

On Sun, Jun 22, 2025, at 22:00, Daniel Scherzer wrote:

On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scherzer@gmail.com> wrote:

Hi internals,

I’d like to start the discussion for a new RFC about adding a #[\DelayedTargetValidation] attribute.

–Daniel

It seems a common point of discussion is the difference in behavior between internal and userland attributes, so I wanted to clarify a few things:

  • Userland attributes are always metadata, in part because of the backwards and forward compatibility that those attributes provide - the attribute does not need to exist in order to be used, it just needs to exist (and target the location) when you call ReflectionAttribute::newInstance() to instantiate
  • Internal attributes are a mix between metadata and a way to plug into the engine. There were already existing ways to plug into the engine (e.g. using magic methods or implementing the Countable or ArrayAccess interfaces) but attributes provided a way to plug into the engine when you don’t just want to add a function override, but rather something else (like indicating a parameter should be redacted in backtraces with #[\SensitiveParameter]). It would probably be impossible to do that securely with a userland attribute and userland error handler that manually redacted things…
  • Attributes were designed to have good compatibility - the syntax chosen for PHP 8.0 was, in prior versions of PHP, used for comments - so any code with attributes could also work in PHP 7.4, just without the metadata. Similarly, by not validating userland attributes until they are accessed with reflection, you can add an attribute that does not exist yet (e.g. if using different versions of a library) with minimal complications.
  • Internal attributes are validated at compile time - because they can be. Internal attributes tell the engine to do something, and it makes sense (at least to me) that if the engine cannot do that thing, there should be an error without needing to wait for ReflectionAttribute::newInstance() to be called.

But, the validation of internal attributes at compile time means that they lose the compatibility features of userland attributes, and that is what this RFC is trying to address. To be clear, I think the difference in behavior between userland and internal attributes is a) fundamentally based on the difference in capabilities (pure metadata vs engine behavior) and b) something that should not be changed without significant further investigation.

I don’t think this is quite right. Because non-existent attributes can be used, if you use attributes in your library, you already know to only ever, and I mean only ever, instantiate your own attributes. Attempting to instantiate someone else’s attributes can result in crashes. This means any delayed attributes will most likely never be validated by anything.

Symfony instantiates arbitrary attributes, https://github.com/symfony/symfony/pull/40307 - and while delayed attributes may not end up being validated, that is a feature, not a bug - the goal is that the validation not be performed at compile time, and so it was either delay to runtime, or not do the validation at all.

This new #[\DelayedTargetValidation] is meant to simplify things, and to partially unify the behavior of the errors - if you are getting any errors from internal attributes and want to suppress them at compile time, just add the new attribute everywhere.

-Daniel

I don’t think this is “forward compatibility”, this is completely disabling the attribute.

— Rob

So I guess my use of “forward compatibility” was a bit confusing. This allows a library to be backwards compatible (using PHP 8.6 attribute targets but running on PHP 8.5 without errors) by making the language forward compatible (allowing PHP 8.5 to process 8.6 attribute targets without errors, by delaying those errors). The attribute is not disabled at all though.

  • If the attribute is used in a place where it would do anything (i.e. there wouldn’t be an error about being a bad target) then that attribute is used as normal, and #[\DelayedTargetValidation] does not affect it
  • If the attribute is used in a place where it would error, then it already cannot do anything, and #[\DelayedTargetValidation] just delays the error - the attribute is already going to be inactive anyway

-Daniel

Hmmm… I still don’t get it. If you scan your codebase for these attributes (maybe using something like https://github.com/olvlvl/composer-attribute-collector) and then instantiate them … then you are right back where you started. You’ll end up getting the same validation errors you’d normally get. But I don’t know why you’d do such a thing. You can use \NoDiscard today, but if you try to instantiate it – it will crash without a polyfill. There are attributes used only by IDEs (like \Pure) and attempting to instantiate them will crash. This is why you don’t instantiate attributes you don’t own.

By adding this, you force all framework to try instantiating these built-in attributes; and scanning for them. This increases the bug surface, boot times, and maintenance cost … to basically end up right where we started.

I just don’t see the point of this. I think the problem is real, I just don’t think this is the right solution.

Instead, it would probably be simpler to backport a change to earlier versions of PHP that simply allows the future target without validation; or to do a two phase rollout of a target change (minor + 1: add the target without validation, then minor + 2, enable validation); or choose a different name.

Another alternative would be to simply write code for the minimum version of PHP you support. For example, if you want to use the pipe operator, you either need to support 8.5 as a minimum, or not use it.

— Rob

Hi

On 7/5/25 00:49, Daniel Scherzer wrote:

If there is no further feedback, I intend to start a vote in a few days.

Looking at your #[\Deprecated] for traits RFC ([RFC] [Discussion] #[\Deprecated] for traits - Externals):

How will #[\DelayedTargetValidation] interact with the `validator` of `zend_internal_attribute`? Would applying #[\DelayedTargetValidation] + #[\Deprecated] on a regular class error or not?

Best regards
Tim Düsterhus

On Sun, Jul 6, 2025 at 5:48 AM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

On 7/5/25 00:49, Daniel Scherzer wrote:

If there is no further feedback, I intend to start a vote in a few days.

Looking at your #[\Deprecated] for traits RFC
(https://externals.io/message/127912):

How will #[\DelayedTargetValidation] interact with the validator of
zend_internal_attribute? Would applying #[\DelayedTargetValidation] +
#[\Deprecated] on a regular class error or not?

Best regards
Tim Düsterhus

For validators, an extra flag is passed to the validator to indicate if target validation should be disabled, in which case the errors about targeting the wrong type of thing (e.g. a non-trait in the case of #[\Deprecated]) are suppressed until runtime as well. Thanks for pointing this out - I actually hadn’t done this properly, fixed it now - take a look at the “errors_from_validator.phpt” and “validator_success.phpt” test cases in the PR.

The reasoning here is exactly the same - validators are used to verify that an attribute is being used on things that are appropriate, just like targets. While the only current (non-zend_test) validator is for dynamic properties, and it is less likely that there will be a future where the validator accepts more things, we shouldn’t assume that things will never change, e.g. I wouldn’t object to allowing #[\AllowDynamicProperties] for read-only classes. But this will also set the stage for the #[\Deprecated] validator - that validator will enforce that a zend_class_entry is for a trait, but if validation is delayed then it will allow using #[\Deprecated] on interfaces or classes and the error being delaying until runtime.

-Daniel