On Tue, Jun 17, 2025 at 4:26 PM Daniel Scherzer <daniel.e.scher...@gmail.com> wrote:
> Hi internals, > > I'd like to start the discussion for a new RFC about adding a > `#[\DelayedTargetValidation]` attribute. > > * RFC: https://wiki.php.net/rfc/delayedtargetvalidation_attribute > * Implementation: https://github.com/php/php-src/pull/18817 > > --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