# RFC: Function Autoloading

- Status: Withdrawn
- Authors: Anthony Ferrara ircmaxell@php.net
- Date: 2015-09-04
- Version: 0.3
- Original: https://wiki.php.net/rfc/function_autoloading
- Page: https://externals.io/rfc/function_autoloading
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Discussions on the mailing list

- [\[DRAFT\] \[RFC\] Function autoloading](https://externals.io/message/68693.md) — started on 2013-08-30, 71 messages
- [Idea: Function autoloading using dummy namespaces](https://externals.io/message/94533.md) — started on 2016-07-17, 11 messages
- [Function auto-loading](https://externals.io/message/94895.md) — started on 2016-08-07, 47 messages
- [RFC: Interest in function autoloading?](https://externals.io/message/109082.md) — started on 2020-03-17, 2 messages

---

## Introduction

PHP presently offers the ability to import class-like structures (classes, interfaces and traits) via a callback (or series of them) that can be registered. This lets a developer “catch” the fact that a class isn't present, and gives them a chance to load it. This is used to great effect in the PHP community.

Presently, other types of symbols tables are not autoloadable. This RFC proposes a new unified autoloading mechanism to unify autoloading efforts across all four symbol tables (class, function, stream wrapper and constant).

## Proposal

This RFC proposes to add a suite of functions and constants in the *php* namespace to achieve total control over autoloading.

### Constants

This proposal registers the following constants:

- *php\\AUTOLOAD\_CLASS =&gt; 1* - Represents Class autoloading
- *php\\AUTOLOAD\_FUNCTION =&gt; 2* - Represents Function autoloading
- *php\\AUTOLOAD\_CONSTANT =&gt; 4* - Represents Constant autoloading
- *php\\AUTOLOAD\_STREAM = 8* - Represents Stream autoloading

### Userland Functions

This proposal registers / modifies the following functions:

#### bool php\\autoload\_register(callable $callback, int $type, bool $prepend = false)

This function behaves similar to the current *spl\_autoload\_register* function. You can pass in a bitmask of types to register for this autoloader.

#### bool php\\autoload\_unregister(callable $callback, int $type = 0)

This function behaves similar to the current *spl\_autoload\_unregister* function, and unregisters a callback that was previously registered. Note that if you registered the same callback for multiple types, this will unregister all of them unless the *$type* argument is specified.

#### array php\\autoload\_list(int $type)

This function will return a list of all registered autoloaders for a specific type.

#### function\_exists()

A new optional boolean argument is added to *function\_exists()* to match the behavior of *class\_exists()* when it comes to autoloading functions.

#### defined()

A new optional boolean argument is added to *defined()* to match the behavior of *class\_exists()* when it comes to autoloading constants.

### Internal Typedefs

This proposal adds the following internal structures:

#### zend\_autoload\_func

This structure stores the autoloader function.

```c
typedef struct zend_autoload_func {
    zend_fcall_info fci;
    zend_fcall_info_cache fcc;
    zval *callable;
    int pass_type;
} _zend_autoload_func;
```

### Internal Functions

This proposal adds the following ZEND\_API functions/macros:

#### void\* zend\_autoload\_call(zend\_string \*name, zend\_string \*lname, int type)

This function will call an autoloader of the specified type. It requires two versions of the name, one which is the called case, and one which is the normalized lookup case. In the case of case-sensitive constants, they should be identical. In the case of insensitive constants, functions and classes, *lname* should be a lowercase version of the name.

#### int zend\_autoload\_register(zend\_autoload\_func\* func, int type, int flags)

This will register an autoload function with the specified flags (prepend is currently the only supported flag).

#### int zend\_autoload\_unregister(zend\_autoload\_func\* func, int type)

This will unregister an autoloader function that's passed.

#### int zend\_lookup\_function(const char \*name, int name\_length, zend\_function &lt;nowiki&gt;\*\*&lt;/nowiki&gt;fbc)

Lookup a function by name, using autoloading

#### int zend\_lookup\_function\_ex(const char \*name, int name\_length, const zend\_literal \*key, int use\_autoload, zend\_function &lt;nowiki&gt;\*\*&lt;/nowiki&gt;fbc)

Lookup a function by name, optionally calling the autoloader.

#### ZEND\_LOOKUP\_FUNCTION\_BY\_NAME(name, fbc)

This macro will find a function in the symbol table, or attempt to autoload it if not defined. The name must be a zend\_string.

#### ZEND\_LOOKUP\_FUNCTION\_BY\_KEY(name, key, fbc)

This macro will find a function in the symbol table, or attempt to autoload it if not defined. This separates the called function in *name* from the looked up function in *key*. Both name and key must be zend\_string.

#### ZEND\_LOOKUP\_FUNCTION\_BY\_NS\_KEY(name, key, fbc)

This macro will find a function in the symbol table, or attempt to autoload it if not defined. This separates the called function in *name* from the looked up function in *key*. Both name and key must be zend\_string.

### Behavior

Registering autoloaders with the new API will allow callbacks to be fired on class, function and/or constant missing errors.

#### Single Type Behavior

By passing a single constant to the register function, the callback will only be called for types that match (the *$type* parameter is still set, but will never vary).

[single\_type.php](https://wiki.php.net/_export/code/rfc/function_autoloading?codeblock=1)

```file
<?php
php\autoload_register(function($name, $type) {
    var_dump($name, $type);
    eval("function $name(){}");
    // We don't need a switch, since we only register for functions.
}, php\AUTOLOAD_FUNCTION);
foo(); // string(3) "foo" int(2)
new foo(); // FATAL_ERROR as no autoloader is registered
?>
```

#### Multiple Type Behavior

By passing a bitwise-or'd constant to the register function, the callback will only be called for types that match).

[multiple\_type.php](https://wiki.php.net/_export/code/rfc/function_autoloading?codeblock=2)

```file
<?php
php\autoload_register(function($name, $type) {
    var_dump($name, $type);
    switch ($type) {
       case php\AUTOLOAD_FUNCTION:
           eval("function $name(){}");
           break;
       case php\AUTOLOAD_CONSTANT:
           define($name, $name);
           break;
    }
}, php\AUTOLOAD_FUNCTION | php\AUTOLOAD_CONSTANT);
foo(); // string(3) "foo" int(2)
FOO; // string(3) "FOO" int(4)
new foo(); // FATAL_ERROR as no autoloader is registered
?>
```

#### Registering The Same Callback Multiple Times For Different Types

[multiple\_registration.php](https://wiki.php.net/_export/code/rfc/function_autoloading?codeblock=3)

```file
<?php
$callback = function($name, $type) {
    var_dump($name);
    if ($name === 'foo') {
        eval("function $name(){}");
    } else {
        define($name, $name);
    }
};
php\autoload_register($callback, php\AUTOLOAD_FUNCTION);
php\autoload_register($callback, php\AUTOLOAD_CONSTANT);
foo(); // string(3) "foo" int(2)
FOO; // string(3) "FOO", "FOO"
?>
```

### Userland Backwards Compatibility

#### SPL

This RFC proposes to strip the current *spl\_autoload\_register* functionality, and make *spl\_autoload\_\** simple proxies for registering core autoloaders. They will function exactly as they do now, but under the hood they will be using the new interface.

This means that calls to *spl\_autoload\_functions()* will include any autoloader (which indicates support for *php\\AUTOLOAD\_CLASS*) registered through *php\\autoload\_register()*. However, all autoloaders registered via *spl\_autoload\_register* will set the *pass\_type* flag to 0, meaning that only a single argument will be passed to the callback. This is for compatiblity.

#### \_\_autoload()

The legacy *\_\_autoload()* function still works (only for classes) if no autoloader has been registered. If any autoloader is registered (class, function or constant), the legacy system will disable itself (this is how it works currently).

### C API Backwards Compatibility

#### SPL

The autoload related SPL globals have been removed, due to the implementation being centralized.

#### Zend

A pair of new functions have been added:

- *ZEND\_API int zend\_lookup\_function(const char \*name, int name\_length, zend\_function \*\*fbc TSRMLS\_DC)*
- *ZEND\_API int zend\_lookup\_function\_ex(const char \*name, int name\_length, const zend\_literal \*key, int use\_autoload, zend\_function \*\*fbc TSRMLS\_DC)*

These will do a normal lookup for a function, and then fall back to an autoloader.

A pair of new “helper macros” have also been added:

- *ZEND\_LOOKUP\_FUNCTION\_BY\_NAME(name, name\_length, fbc)*

- *ZEND\_LOOKUP\_FUNCTION\_BY\_LITERAL(name, name\_length, literal, fbc)*

These two will do a legacy style hash-table lookup before triggering the autoloading function call (to *zend\_lookup\_function()*). The reason for this is performance.

Opcodes which lookup functions, are using this new macro. This way, there should be no performance regression at all (thanks to short-circuit operators) for defined functions.

## General questions &amp; answers

### Why Rewrite A Complete Autoloader?

Initially, I implemented this as *spl\_function\_autoload\_register*. Quickly, it became clear that there was a lot of duplication, and the original system was a bit stringy.

This implementation greatly simplifies the (internal) handling of autoloading in general.

### Why not support multiple "types" for a single autoloader?

Existing autoloaders may support a second parameter. If we attempted to provide the type as a second argument to an existing autoloader, it could clash. This happens with the existing *spl\_autoload()* autoloader.

### What Filename Conventions Does This Support?

None, and all. This proposal presently implements no type of file loading handler.

The only thing that is implemented is the ability to register a callback to attempt to load a function or constant (or class) when it is missing. How the callback maps structures to files is outside of the scope of this RFC.

### Doesn't This Complicate The Engine?

Nope! The reason is that the current autoloading mechanism for classes is extremely fragile as is.

For example, the implementation hinges on a global variable which sets the php-level callback to call on autoload. This requires setting up a *zend\_fcall\_info* struct, and a *zend\_fcall\_info\_cache* struct, as well as dispatching a function internally to autoload. [The Current Implementation](https://github.com/php/php-src/blob/9e17094cf4dde60432569246a9a59e48783530bb/Zend/zend_execute_API.c#L1066).

The implementation of *spl\_autoload\_call* and *spl\_autoload\_register* are also extremely complicated. [The current SPL implementation](https://github.com/php/php-src/blob/9e17094cf4dde60432569246a9a59e48783530bb/ext/spl/php_spl.c#L466).

This refactor cleans both of these pieces up significantly.

## Backward Incompatible Changes

### Userland

There should be no user-land BC changes.

### PECL

#### EG(autoload\_func)

PECL extensions which rely on the *EG(autoload\_func)* global variable will break (due to refactor).

A quick scan of LXR shows that only the [optimizer](http://lxr.php.net/xref/PECL/optimizer/optimize.c#4660) extension would change.

#### autoload\_func\_info

PECL extensions which reply on the SPL type *autoload\_func\_info* will break (due to refactor).

A quick scan of LXR shows that no extensions use this.

#### SPL\_G(autoload\_functions)

PECL extensions which rely on the SPL globals will break (due to refactor).

A quick scan of LXR shows that no extensions use this.

## Proposed PHP Version(s)

PHP 7.1.x

## SAPIs Impacted

None.

## Impact to Existing Extensions

See Backward Incompatible Changes

## php.ini Defaults

None.

## Open Issues

None yet.

## Discussion Points

### Autoloading Constants

#### Summary

When refactoring the autoloader to support functions, adding support for constants isn't significantly difficult, yet can have an advantage.

#### Stance

This RFC takes the stance that it is worth while autoloading constants for consistency.

### Deprecation of \_\_autoload()

#### Summary

The legacy *function \_\_autoload()* can be deprecated.

#### Stance

This is out of scope for this RFC.

### Deprecation of spl\_autoload\_register()

#### Summary

Since there is a new implementation which supports class autoloading, *spl\_autoload\_register()* is redundant. Therefore, it can be deprecated.

#### Stance

This RFC takes the stance that deprecation should not happen right away, if at all. It is therefore out of scope for this RFC.

## Patches and Tests

A patch will be created shortly

## References

- Importing namespaces: <http://php.net/manual/en/language.namespaces.importing.php>
- SPL Autoloading: <http://php.net/manual/en/language.oop5.autoload.php>

## Rejected Features

\- None.

## Vote

## Changelog

- 2013-08-29 0.1 Initial Creation
- 2013-08-30 0.2 Add performance section and basic benchmarks
- 2015-09-04 0.3 Re-proposed
