-
Notifications
You must be signed in to change notification settings - Fork 7.9k
[RFC] Add #[\DelayedTargetValidation]
attribute
#18817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[RFC] Add #[\DelayedTargetValidation]
attribute
#18817
Conversation
037775b
to
26ad0f2
Compare
1978ce1
to
82f556a
Compare
#[\DelayedTargetValidation]
attribute#[\DelayedTargetValidation]
attribute
Currently on vacation, so can't review, but I trust that you are capable of correctly moving error messages 😄 |
82f556a
to
abbef91
Compare
abbef91
to
6b11fd2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some first thoughts. I don't currently have the mental capacity to perform an in-depth review (diff is larger than anticipated) and didn't look at the entire PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After sleeping on this, my preferred approach would be:
- Stop using
zend_error_noreturn()
invalidator
in favor ofzend_throw_error()
for target validation, instruct from UPGRADING.INTERNALS to do the same for extensions. - Use
EG(exception)
fromzend_compile_attributes()
to understand whether the validator succeeded. - Without
#[DelayedTargetValidation]
, promote to a fatal error withzend_exception_uncaught_error()
. - With
#[DelayedTargetValidation]
, record whether the validator has succeeded and store it inzend_attribute
.EG(exception)
would have to be removed/freed. - When it failed, repeat the validation on
ReflectionAttribute::newInstance()
. Skipping for successful validators prevents writing to locked shm. Validators should be idempotent, i.e. we should be able to rely on the fact that calling it again will also throw.
The benefit is that we don't need to handle this for every validator, and we don't need the ZEND_ATTRIBUTE_NO_TARGET_VALIDATION
/ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION
flags. Then, this feature should work mostly automatically.
WDYT?
@@ -8422,6 +8446,10 @@ static zend_op_array *zend_compile_func_decl_ex( | |||
|
|||
CG(active_op_array) = op_array; | |||
|
|||
zend_oparray_context_begin(&orig_oparray_context, op_array); | |||
CG(context).active_property_info_name = property_info_name; | |||
CG(context).active_property_hook_kind = hook_kind; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with this.
zend_throw_error() will just use zend_error_noreturn() if I'm reading correctly, Lines 1831 to 1836 in 222f751
when validators are run,
No validators should be writing anything at runtime, e.g. for allow dynamic properties the validator just |
RIght, at least when
Yes, but every validator must abort to avoid this, which is a potential foot gun. What I'm suggesting is a way for |
Even if it is not empty, the |
Yes, you can change this value as well. But there are alternatives. E.g. |
I switched it so that validators return error messages instead of emitting them, which allowed removing the ZEND_ATTRIBUTE_NO_TARGET_VALIDATION flag. For some reason I kept getting segfaults if I tried to add a field to the zend_attribute struct to hold the error message, even if I didn't actually touch the field, I'll try to debug |
Valgrind or sanitizers might help. Adding |
Pretty sure the issue was that I was putting the new field after the |
8959bec
to
254926f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes thus far. It seems simpler to me, hopefully you agree.
|
||
/* Report the delayed validator error for internal attributes */ | ||
if (delayed_target_validation && ce->type == ZEND_INTERNAL_CLASS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think we need to load this at all. When validation_error
is set, we can just throw the error.
* - the attribute is a user attribute, and neither target nor repetition | ||
* have been validated. | ||
*/ | ||
uint32_t flags = zend_attribute_attribute_get_flags(marker, ce); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of these changes, could we not just attach target & repetition validation to validation_error
in zend_compile.c? Then errors would all be handled the same way for internal attributes.
"delayedtargetvalidation", | ||
strlen("delayedtargetvalidation"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
"delayedtargetvalidation", | |
strlen("delayedtargetvalidation"), | |
ZEND_STRL("delayedtargetvalidation"), |
{ | ||
ZEND_ASSERT(CG(in_compilation)); | ||
const zend_string *prop_info_name = CG(context).active_property_info_name; | ||
if (prop_info_name == NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: No hard rules on this, but most similar code puts the happy path at the bottom. This way, more conditions can be added without nesting.
https://wiki.php.net/rfc/delayedtargetvalidation_attribute