Skip to content

GH-22354: support interfaces added by attribute validators in zend_compile_implements()#22355

Open
DanielEScherzer wants to merge 2 commits into
php:PHP-8.4from
DanielEScherzer:attribute-validator-interfaces
Open

GH-22354: support interfaces added by attribute validators in zend_compile_implements()#22355
DanielEScherzer wants to merge 2 commits into
php:PHP-8.4from
DanielEScherzer:attribute-validator-interfaces

Conversation

@DanielEScherzer

Copy link
Copy Markdown
Member

No description provided.

Comment thread ext/zend_test/test.c Outdated
@DanielEScherzer DanielEScherzer force-pushed the attribute-validator-interfaces branch from a911024 to 094b72f Compare June 17, 2026 21:41

@TimWolla TimWolla left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic-wise this seems correct. But I feel this is a “master-only” bugfix, since the situation for this to appear seems to be quite rare.

Comment thread NEWS Outdated
Comment thread ext/zend_test/test.c Outdated
Comment thread ext/zend_test/test.c Outdated
Comment thread Zend/zend_compile.c
// there are errors about trying to implement an interface that was already
// implemented.
if (EXPECTED(ce->num_interfaces == 0)) {
interface_names = emalloc(sizeof(zend_class_name) * list->children);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also be safe_emalloc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is unchanged from earlier and I'd prefer not to mess with it - I just moved it into a conditional block

Comment thread Zend/zend_compile.c Outdated
Comment thread Zend/zend_compile.c
Comment thread Zend/zend_compile.c Outdated
Comment thread Zend/zend_compile.c
efree(skipped_names);

const uint32_t initial_count = ce->num_interfaces;
interface_names = safe_erealloc(ce->interface_names, (initial_count + to_add_count), sizeof(*interface_names), 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are repeating initial_count + to_add_count a lot. I suggest a local variable.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is precisely the only place that that expression occurs? Below I use added_count which also changes each time through the loop so shouldn't be saved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. In that case using pointer arithmetic might reduce the amount of copy and paste required.

@DanielEScherzer DanielEScherzer force-pushed the attribute-validator-interfaces branch from 094b72f to 910c09b Compare June 21, 2026 02:03

@DanielEScherzer DanielEScherzer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic-wise this seems correct. But I feel this is a “master-only” bugfix, since the situation for this to appear seems to be quite rare.

I encountered this in an actual extension, https://github.com/DanielEScherzer/CustomCast - I'm not sure why I didn't notice or report it earlier, but it does indeed affect me

Comment thread ext/zend_test/test.c Outdated
Comment thread ext/zend_test/test.c Outdated
Comment thread Zend/zend_compile.c
// there are errors about trying to implement an interface that was already
// implemented.
if (EXPECTED(ce->num_interfaces == 0)) {
interface_names = emalloc(sizeof(zend_class_name) * list->children);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is unchanged from earlier and I'd prefer not to mess with it - I just moved it into a conditional block

Comment thread Zend/zend_compile.c Outdated
Comment thread Zend/zend_compile.c
Comment thread Zend/zend_compile.c
efree(skipped_names);

const uint32_t initial_count = ce->num_interfaces;
interface_names = safe_erealloc(ce->interface_names, (initial_count + to_add_count), sizeof(*interface_names), 0);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is precisely the only place that that expression occurs? Below I use added_count which also changes each time through the loop so shouldn't be saved

Comment thread Zend/zend_compile.c Outdated
Comment thread NEWS Outdated
@DanielEScherzer DanielEScherzer force-pushed the attribute-validator-interfaces branch from 910c09b to a13a680 Compare June 21, 2026 03:10
@TimWolla TimWolla requested a review from iluuu1994 June 21, 2026 10:10

@TimWolla TimWolla left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also requested another review from someone more experienced with engine code to make a target-branch call.

Comment thread Zend/zend_compile.c Outdated
Comment thread Zend/zend_compile.c Outdated
@iluuu1994

Copy link
Copy Markdown
Member

I agree with Tim that this should target master, this is borderline unintended. I also don't love this approach, because if mutation of classes is allowed, adding an interface is just one thing the attribute could do. It might be better to add a new attribute hook that triggers after the class is fully compiled, after which the attribute can do it's thing, and in this case handle the duplicate interface logic itself.

In `zend_compile_implements()`, when there were no interfaces already added,
keep the existing code of just adding the newly declared interfaces. But, when
some interfaces are already present, don't overwrite them. Instead, add to the
existing list.

In case the developer tries to add an interface that was already added by an
attribute, skip the manual interface addition to avoid errors about trying to
apply the same interface multiple times. But, only skip the *first* manual
interface addition of the same interface, if there are multiple such additions
then there really was an attempt to apply the same interface multiple times.

Fixes php#22354
@DanielEScherzer DanielEScherzer force-pushed the attribute-validator-interfaces branch from a13a680 to 3d1840c Compare June 21, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zend_compile_implements() assumes that the class entry has no interfaces already

3 participants