Usually when PHP5 uses a class, if it finds that the class is not loaded, it will automatically run the __autoload() function. In this function, we can load the class we need to use.
In our simple example, we directly add the class name with the extension ".class.php" to form the class file name, and then use require_once to load it.
From this example, we can see that autoload has to do at least three things,
The first thing is to determine the class file name based on the class name;
The second thing is to determine the disk path where the class files are located (in our case the simplest case, the classes are in the same folder as the PHP program files that call them);
The third thing is to load the class from the disk file into the system. The third step is the simplest, just use include/require.
To realize the functions of the first and second steps, the mapping method between the class name and the disk file must be agreed upon during development. Only in this way can we find its corresponding disk file based on the class name.
Therefore, when there are a large number of class files to be included, we only need to determine the corresponding rules, and then match the class names with the actual disk files in the __autoload() function to achieve the lazy loading effect.
From here we can also see that the most important thing in the implementation of the __autoload() function is the implementation of the mapping rules between the class name and the actual disk file.
But now comes the problem. If you need to use many other class libraries in the implementation of a system, these class libraries may be written by different developers, and the mapping rules between their class names and actual disk files are different. same. At this time, if you want to implement automatic loading of class library files, you must implement all mapping rules in the __autoload() function. In this case, the __autoload() function may be very complicated or even impossible to implement. In the end, the __autoload() function may become very bloated. Even if it can be implemented, it will have a great negative impact on future maintenance and system efficiency. In this case, isn't there a simpler and clearer solution? The answer is of course: NO!
Before looking at further solutions, let’s first take a look at how the autoload mechanism in PHP is implemented.
2. Implementation of PHP’s autoload mechanism
When PHP instantiates an object (actually when implementing an interface, using class constants or static variables in a class, or calling static methods in a class), it will first check whether the class (or interface) exists in the system , if it does not exist, try to use the autoload mechanism to load the class.
The main execution process of the autoload mechanism is:
(1) Check whether the executor global variable function pointer autoload_func is NULL.
(2) If autoload_func==NULL, check whether the __autoload() function is defined in the system. If not, report an error and exit.
(3) If the __autoload() function is defined, execute __autoload() to try to load the class and return the loading result.
(4) If autoload_func is not NULL, directly execute the function pointed to by the autoload_func pointer to load the class. Note that it does not check whether the __autoload() function is defined at this time.
PHP provides two methods to implement the automatic loading mechanism:
One way we have mentioned before is to use the user-defined __autoload() function, which is usually implemented in the PHP source program;
The other is to design a function and point the autoload_func pointer to it. This is usually implemented in a PHP extension using C language.
If both the __autoload() function and autoload_func are implemented (point autoload_func to a certain PHP function), then only the autoload_func function will be executed.
3. Implementation of SPL autoload mechanism
SPL is the abbreviation of Standard PHP Library.
It is an extension library introduced in PHP5. Its main functions include the implementation of the autoload mechanism and various Iterator interfaces or classes.
The SPL autoload mechanism is implemented by pointing the function pointer autoload_func to a self-implemented function with autoloading function.
SPL has two different functions spl_autoload and spl_autoload_call. Different automatic loading mechanisms are implemented by pointing autoload_func to these two different function addresses.
spl_autoload is the default automatic loading function implemented by SPL, and its function is relatively simple.
It can receive two parameters. The first parameter is $class_name, which represents the class name. The second parameter, $file_extensions, is optional and represents the extension of the class file. Multiple extensions can be specified in $file_extensions, and the extension names can be separated by semicolons; if not specified, it will use the default extension .inc or .php.
spl_autoload first changes $class_name to lowercase, then searches for $class_name.inc or $class_name.php files in all include paths (if the $file_extensions parameter is not specified), and if found, loads the class file.
You can manually use spl_autoload("Person", ".class.php") to load the Person class. In fact, it is similar to require/include, except that it can specify multiple extensions.
How to make spl_autoload work automatically, that is, point autoload_func to spl_autoload? The answer is to use the spl_autoload_register function. By calling spl_autoload_register() for the first time in a PHP script without any parameters, you can point autoload_func to spl_autoload.
Through the above description, we know that the function of spl_autoload is relatively simple, and it is implemented in the SPL extension, and we cannot expand its functions. What if you want to implement your own more flexible automatic loading mechanism? At this time, the spl_autoload_call function makes its debut.
Let’s first take a look at the wonderful features of spl_autoload_call implementation. Inside the SPL module, there is a global variable autoload_functions, which is essentially a HashTable, but we can simply think of it as a linked list. Each element in the linked list is a function pointer, pointing to a function that has the function of automatically loading classes. function. The implementation of spl_autoload_call itself is very simple. It simply executes each function in the linked list in order. After each function is executed, it is judged whether the required class has been loaded. If the loading is successful, it returns directly and does not continue to execute the linked list. other functions. If the class has not been loaded after all functions in this linked list have been executed, spl_autoload_call will exit directly without reporting an error to the user. Therefore, using the autoload mechanism does not guarantee that the class will be automatically loaded correctly. The key still depends on how your autoloading function is implemented.
So who maintains the automatic loading function list autoload_functions?
It is the spl_autoload_register function mentioned earlier. It can register the user-defined autoloading function into this linked list and point the autoload_func function pointer to the spl_autoload_call function. We can also delete registered functions from the autoload_functions linked list through the spl_autoload_unregister function.
As mentioned in the previous section, when the autoload_func pointer is non-null, the __autoload() function will not be automatically executed. Now autoload_func has pointed to spl_autoload_call. What should we do if we still want the __autoload() function to work? Of course, still use the spl_autoload_register(__autoload) call to register it in the autoload_functions linked list.
Now back to the last question in the first section, we have a solution: implement their own autoloading functions according to the different naming mechanisms of each class library, and then use spl_autoload_register to register them into the SPL autoloading function queue respectively. . This way we don't have to maintain a very complicated __autoload function.
4. Autoload efficiency issues and countermeasures
When using the autoload mechanism, many people’s first reaction is that using autoload will reduce system efficiency. Some people even suggest not to use autoload for the sake of efficiency.
After we understand the principle of autoload implementation, we know that the autoload mechanism itself is not the reason that affects system efficiency. It may even improve system efficiency because it will not load unnecessary classes into the system.
So why do many people have the impression that using autoload will reduce system efficiency?
In fact, it is the user-designed autoloading function that affects the efficiency of the autoload mechanism.
If it cannot efficiently match the class name with the actual disk file (note, this refers to the actual disk file, not just the file name), the system will have to do a lot of checking whether the file exists (which needs to be checked in each include path included in the path), and determining whether the file exists requires disk I/O operations. As we all know, the efficiency of disk I/O operations is very low, so this is the culprit that reduces the efficiency of the autoload mechanism!
Therefore, when we design the system, we need to define a clear mechanism for mapping class names to actual disk files. The simpler and clearer this rule is, the more efficient the autoload mechanism will be.
Conclusion: The autoload mechanism is not naturally inefficient. Only abuse of autoload and poorly designed autoloading functions will lead to a reduction in its efficiency.