Skip to content
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

registration error in PHP for classes that use other classes #328

Open
faassen opened this issue Oct 17, 2024 · 3 comments
Open

registration error in PHP for classes that use other classes #328

faassen opened this issue Oct 17, 2024 · 3 comments

Comments

@faassen
Copy link
Contributor

faassen commented Oct 17, 2024

Yesterday I got this error when I started up my PHP test script:

PHP Fatal error:  Xee\SequenceIterator must be registered before Xee\Sequence in Unknown on line 0

XeeSequenceIterator is being returned by a function on XeeSequence (to support the Aggregate interface, by implementing get_iterator, so I figured that caused this problem and I rearranged my code to define XeeSequenceIterator earlier.

But today I added some unrelated code and the problem happened again. When I moved the new, unrelated code to the bottom of the Rust module, the problem went away, but this makes me feel uneasy! So this issue is to report the behavior and I will dig into the possible causes a bit next.

It feels as if registration order is more or less random, and this is why sometimes the problem occurs and sometimes it doesn't.

I've spun this off from #327 as I think the macro errors mentioned there aren't necessarily connected to this error, even though it also seems to be a registration order related issue.

@faassen
Copy link
Contributor Author

faassen commented Oct 17, 2024

The error in question appears to be caused by lookup_class_ex in zend_inheritance.c, as that's the only place in the C code where this message seems to be generated. It's interesting that this function has a register_unresolved flag, and only if that is set can we get this error. It appears that even though this function is called "lookup" it actually does do registration.

lookup_class_ext is used by lookup_class in the same file, but that sets registered_unresolved to false so cannot be the problem.

The only other place where it's used is in register_unresolved_classes, where the flag is set to true.

This is used in zend_perform_covariant_type_check.

@faassen
Copy link
Contributor Author

faassen commented Oct 17, 2024

I haven't traced the PHP source further, but it's plausible to conclude that zend_perform_covariant_check is invoked multiple times during registration, and that it fails if the classes are registered in the wrong order.

A potential way to solve this is to sort the classes in topological order: if we know a class references another one in its method signatures, we register that one before we register the other class. A problem is that we don't know this until all methods are registered with the ClassBuilder. We'd need to collect class builders, and then sort them topologically, before we attempt to do the final build (causing the registration).

@faassen
Copy link
Contributor Author

faassen commented Oct 17, 2024

Another possible way to fix this is to see when these checks happen and whether they can't be deferred until all classes have been registered with PHP.

Looking from the registration end, ext-php-rs calls zend_register_internal_class_ex. This has registration flags set to 0. There seems to be a version dependency that calls zend_register_internal_class_with_flags directly.

Unfortunately I can't find my way through this code so twee what triggers zend_perform_covariant_type_check.

I've also looked through the constants defind in zend_compile.h to see whether there's an obvious way to turn this off, but I haven't found it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant