-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix bug #81156 unset() on ArrayObject inside foreach skips next index. #7553
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?
Conversation
…->is_delete * because zend_hash_iterators_update() was used in many place.
I write a new function zend_hash_set_is_delete() to set is_delete = 1. |
@nikic |
I don't like this approach. The core problem here is that the HT iterator mechanism (which exists specifically to update iterators on unsets and similar changes) is designed for use with foreach on arrays, which performs the sequence:
That is, when the loop body executes, the iterator already points to the next element, and the update mechanism is written with that in mind. If the element to which the iterator points is deleted, then we move to the next one, rather than the previous one (which would make sense if the iterator pointer to the current element). Now, for
In this case the iterator points to the current element, which is why the update mechanism fails. Addressing this using an additional "is deleted" flag is not correct. I see generally two approaches on how to fix it:
I'm not sure what issues either of those options would encounter. |
https://bugs.php.net/bug.php?id=81156
The function _zend_hash_iterators_update() and zend_hash_move_forward_ex() will modify the iterator position when unseting value circularly.
For example, _zend_hash_iterators_update() change iterator position 0 to 1 and zend_hash_move_forward_ex() change 1 to 2 when unset $ao[0]. So the program will unset $ao[2] in the next cycle.
I made the following changes to solve such problem.
First, recording iterator position changing by using has_updated_iterator field.
Secondly, It means that the iterator position has changed by function _zend_hash_iterators_update() and the current position is valid if the result of function sql_check_update_iterator() is SUCCESS.
The function spl_array_it_move_forward can will execute spl_array_next_ex if the result of function sql_check_update_iterator() is FAILURE.
To be honest, this pr can not solve the following bug. The results are always weird.
In my branch, the result is {"0":true,"2":true}.
In the main version of php , the result is {"0":true,"2":true,"3":false}.
Can we accept this change? 😃
Welcome to comment the pr if something wrong with my pr and it will help me to perfect it. Thanks.