Skip to content

Add new core autoloading mechanism for classes and functions #8294

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

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

Girgias
Copy link
Member

@Girgias Girgias commented Apr 1, 2022

This PR introduces a new autoloading mechanism built into the engine which supports the autoloading of classes and functions.

RFC: https://wiki.php.net/rfc/core-autoloading

@Danack
Copy link
Contributor

Danack commented Oct 6, 2022

fyi, the test Zend/tests/autoloading/function/register_autoloader.phpt appears to be failing due to the param parsing failing as the passed in string is not already callable.

@Danack
Copy link
Contributor

Danack commented Oct 6, 2022

The test exceptions_during_autoloading004.phpt appears to be failing as the function autoloading doesn't appear to be triggered for the function in a namespace.

Girgias added a commit to Girgias/php-src that referenced this pull request Dec 2, 2022
…ot be unregistered)

There are two issues to resolve:
 1. The FCC is not refetch when trying to unregister a trampoline
 2. Comparing the function pointer of trampolines is meaningless as they are reallocated, thus we need to compare the name of the function

Found while working on phpGH-8294
Girgias added a commit that referenced this pull request Dec 2, 2022
…be unregistered)

There are two issues to resolve:
 1. The FCC is not refetch when trying to unregister a trampoline
 2. Comparing the function pointer of trampolines is meaningless as they are reallocated, thus we need to compare the name of the function

Found while working on GH-8294

Closes GH-10033
@Girgias Girgias force-pushed the zend_autoloader branch 2 times, most recently from ca7a80f to 41f9378 Compare December 15, 2022 02:04
@Girgias Girgias force-pushed the zend_autoloader branch 2 times, most recently from 2e93f3c to f88a32e Compare April 3, 2023 13:02
@Girgias Girgias force-pushed the zend_autoloader branch 3 times, most recently from 359966e to 90c39d3 Compare April 12, 2023 16:31
Comment on lines 3759 to 3766
function_name = (zval*)RT_CONSTANT(opline, opline->op2);
func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(function_name+1));
if (UNEXPECTED(func == NULL)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper);
zval *function_name = (zval*)RT_CONSTANT(opline, opline->op2);
/* Fetch lowercase name stored in the next literal slot */
fbc = zend_lookup_function_ex(Z_STR_P(function_name), Z_STR_P(function_name+1), /* use_autoload */ true);
if (UNEXPECTED(fbc == NULL)) {
if (EXPECTED(!EG(exception))) {
ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper);
}
HANDLE_EXCEPTION();
Copy link
Member

Choose a reason for hiding this comment

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

I would propose to try autoloading only after simple hash lookup.
This should completely eliminate performance degradation for existing code.
e.g.

		function_name = (zval*)RT_CONSTANT(opline, opline->op2);
		func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(function_name+1));
		if (UNEXPECTED(func == NULL)) {
                    func = zend_autoload_function(Z_STR_P(function_name), Z_STR_P(function_name+1));
		    if (UNEXPECTED(func == NULL)) {
			if (EXPECTED(!EG(exception))) {
				ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper);

Copy link
Member Author

Choose a reason for hiding this comment

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

Shouldn't this already be the case because of how I implemented zend_lookup_function_ex()?

As it will first check the HashTable, and then call the autoloader if it hasn't found an entry.

Copy link
Member

Choose a reason for hiding this comment

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

Your implementation introduces overhead of call to zend_lookup_function_ex(), few additional checks and use of zend_hash_find() instead of zend_hash_find_known_hash().

@MaxD2
Copy link

MaxD2 commented Jul 25, 2023

I don't know where to write, but I vote for consts (defines) autoloading, that will be extra-usable after const objects got allowed in 8.1.

@medabkari
Copy link

Is there any plans to implement this feature in 8.4 release?

@Girgias
Copy link
Member Author

Girgias commented Feb 8, 2024

Is there any plans to implement this feature in 8.4 release?

I need to get back to finishing the RFC to explain why certain proposed ideas don't work. But that's the objective yes.

@britkig
Copy link

britkig commented Feb 16, 2024

Just a thought, but would it be another logical way of using one, yet-agnostic, list for all autoload functions rather than two, but have a bitflag on each registered callable as to what autoload occasions that function should fire on?

Another possible approach to this is to have a third, but generic, set autoload functions which take a bitwise parameter to map one or more event types (i.e. AUTOLOAD_CLASS=1, AUTOLOAD_FUNCTION=2), and have event-specific functions, as-currently mentioned in the RFC as-of-writing, serve as stubs for one specific autoload type?

Something else to consider, is that there will certainly be cases where a coder may want the exact same segment of code to execute in more than one particular event. From a RAM-occupancy standpoint, having only one copy of a registered function in-memory, where its purpose is to fire on more than one event type, would be a little more efficient. Also, if, in a future RFC, that there is the thought to add more autoload event types, additional bitflags can be reserved for them (i.e. AUTOLOAD_CONSTANT=4), or even have one function to invoke on any autoload event (i.e. AUTOLOAD_ALL=0).

@Girgias
Copy link
Member Author

Girgias commented Feb 17, 2024

Just a thought, but would it be another logical way of using one, yet-agnostic, list for all autoload functions rather than two, but have a bitflag on each registered callable as to what autoload occasions that function should fire on?

Another possible approach to this is to have a third, but generic, set autoload functions which take a bitwise parameter to map one or more event types (i.e. AUTOLOAD_CLASS=1, AUTOLOAD_FUNCTION=2), and have event-specific functions, as-currently mentioned in the RFC as-of-writing, serve as stubs for one specific autoload type?

Something else to consider, is that there will certainly be cases where a coder may want the exact same segment of code to execute in more than one particular event. From a RAM-occupancy standpoint, having only one copy of a registered function in-memory, where its purpose is to fire on more than one event type, would be a little more efficient. Also, if, in a future RFC, that there is the thought to add more autoload event types, additional bitflags can be reserved for them (i.e. AUTOLOAD_CONSTANT=4), or even have one function to invoke on any autoload event (i.e. AUTOLOAD_ALL=0).

I don't think this is a good API at all. Which is why I did not go with this in the first place, the previous proposal for function autoloading did just that, and I think it is confusing and if one of the autoloading mechanisms needs to be amened to behave differently via a flag or what-no this would pollute the whole API surface.

Individual functions are cheap, and clear.

Moreover, I frankly think it is unlikely that a class autoloader and a function autoloader are remotely close in implementation. Classes in PHP are typically stored in a one to one correspondences with files, whereas doing that for functions is a questionable choice.

Similarly, thing for autoloading constants, or type aliases.

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.

6 participants