Detailed explanation of PHP autoload mechanism
(1) Overview of autoload mechanism
When using PHP’s OO mode to develop systems, it is usually customary for everyone to The implementation of each class is stored in a separate file, which makes it easy to reuse the class and is also convenient for future maintenance. This is also one of the basic ideas of OO design. Before PHP5, if you need to use a class, you only need to include it directly using include/require. Here is a practical example:
Copy the code The code is as follows:
/* Person.class.php */
class Person {
var $name, $age;
function __construct ($name, $age)
{
$this->name = $name;
$this->age = $age;
}
}
?>
/* no_autoload.php */
require_once (" Person.class.php");
$person = new Person("Altair", 6);
var_dump ($person);
?>
In this In the example, the no-autoload.php file requires the Person class, which is included using require_once. Then the Person class can be used directly to instantiate an object.
But as the scale of the project continues to expand, using this method will bring some hidden problems: if a PHP file needs to use many other classes, then a lot of require/include statements are needed, so there are May cause omission or inclusion of unnecessary class files. If a large number of files require the use of other classes, it will be a nightmare to ensure that each file contains the correct class file.
PHP5 provides a solution to this problem, which is the autoload mechanism of classes. The autoload mechanism makes it possible for PHP programs to automatically include class files only when classes are used, instead of including all class files from the beginning. This mechanism is also called lazy loading.
The following is an example of using the autoload mechanism to load the Person class:
Copy the code The code is as follows:
/* autoload.php */
function __autoload($classname) {
require_once ($classname . “class.php”);
}
$person = new Person("Altair", 6);
var_dump ($person);
?>
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 "title="extension">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, and the second thing is to determine the disk path where the class file is located (in our example, it is 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 classes from the disk file into the system. The third step is the simplest, just use include/require to achieve the first. The first step and the second step of the function must agree on the mapping method between the class name and the disk file 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 classes. When the file is to be included, we only need to determine the corresponding rules, and then match the class name with the actual disk file in the __autoload() function to achieve the effect of lazy loading. From here we can also see the effect of __autoload. The most important thing in the implementation of the () 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. At this time, if you want to implement automatic loading of class library files, you must use the __autoload() function. If all mapping rules are implemented, the __autoload() function may be very complicated or even impossible to implement. In the end, the __autoload() function may be very bloated. Even if it can be implemented, it will hinder future maintenance and maintenance. System efficiency has a great negative impact. In this case, isn't there a simpler and clearer solution? The answer is of course: NO! Before looking at further solutions, let's take a look at PHP. How is the autoload mechanism implemented? In the process, the first step is to compile the PHP file into a bytecode sequence commonly called OPCODE (actually compiled into a byte array called zend_op_array), and the second step is to execute these OPCODE PHP files by a virtual machine. All behaviors are implemented by these OPCODEs. Therefore, in order to study the implementation mechanism of autoload in PHP, we compile the autoload.php file into opcode, and then study what PHP does in the process based on these OPCODEs:
/* autoload.php The compiled OPCODE list is made using the OPDUMP tool developed by the author
*/
Copy the code The code is as follows:
// require_once (”Person.php”);
function __autoload ($classname) {
0 NOP
0 RECV 1
if (!class_exists($classname)) {
1 SEND_VAR !0
2 DO_FCALL ‘class_exists' [extval:1]
3 BOOL_NOT $0 =>RES[~1]
4 JMPZ ~1, ->8
require_once ($classname. “.class.php”);
5 CONCAT !0, ‘.class.php' =>RES[~2]
6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE
}
7 JMP ->8
}
8 RETURN null
$p = new Person('Fred', 35);
1 FETCH_CLASS ‘Person' =>RES[:0]
2 NEW :0 =>RES[$1]
3 SEND_VAL ‘Fred'
4 SEND_VAL 35
5 DO_FCALL_BY_NAME [extval:2]
6 ASSIGN !0, $1
var_dump ($p);
7 SEND_VAR !0
8 DO_FCALL ‘var_dump' [extval:1]
?>
In line 10 of autoload.php we need to instantiate an object for the class Person. Therefore, the autoload mechanism will definitely be reflected in the compiled opcode of this line. From the OPCODE generated by the 10th line of code above, we know that when instantiating the object Person, the FETCH_CLASS instruction must first be executed. Let's start our journey of exploration from PHP's processing of the FETCH_CLASS instruction.
By checking the PHP source code (I am using PHP 5.3alpha2 version), you can find the following calling sequence:
ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, …) (zend_vm_def.h line 1864)
=> ZEND_FETCH_CLASS (Zend_execute_api.c 1434)
= & gt; zend_lookup_class_ex (zend_execute_api.c 964)
= & gt; ION (& Fcall_info, & Fcall_cache) (zend_execute_api.c 1040 line)
in the last step Before calling, let's take a look at the key parameters when calling:
/* Set the autoload_function variable value to "__autoload" */
fcall_info.function_name = &autoload_function; // Ooops, finally found "__autoload"
…
fcall_cache.function_handler = EG(autoload_func); // autoload_func !
zend_call_function is one of the most important functions in Zend Engine. Its main function is to execute user-defined functions in PHP programs. Or the library function of PHP itself. zend_call_function has two important pointer parameters fcall_info and fcall_cache, which point to two important structures respectively, one is zend_fcall_info and the other is zend_fcall_info_cache. The main workflow of zend_call_function is as follows: If the fcall_cache.function_handler pointer is NULL, try to find a function named fcall_info.function_name, and if it exists, execute it; if fcall_cache.function_handler is not NULL, directly execute the function pointed to by fcall_cache.function_handler. function.
Now we know clearly that when PHP instantiates an object (this is actually the case when implementing an interface, using class constants or static variables in a class, and calling static methods in a class), it will first be in the system Check whether the class (or interface) exists. 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, the function pointed to by the autoload_func pointer is directly executed to load the class. Note that it does not check whether the __autoload() function is defined at this time.
The truth is finally revealed. PHP provides two methods to implement the automatic loading mechanism. One, as we have mentioned before, is to use the user-defined __autoload() function, which is usually implemented in the PHP source program; the other One is to design a function and point the autoload_func pointer to it, which 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. "title="Extension">Extension, which can be found in Specify multiple extensions in $file_extensions" title="Extension">Extension names, just separate them with semicolons; if not specified, it will use the default extension" title="Extension name"> extension .inc or .php. spl_autoload first changes $class_name to lowercase, and then searches for $class_name.inc or $class_name.php files in all include paths (if the $file_extensions parameter is not specified), if If found, load 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. title="Extension">Extension.
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 function. 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 (note that there is an exception, and the specific situation is left to everyone to think about). 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. If we still want the __autoload() function to work, we should What to do? 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 to the SPL autoloading function respectively. Just put it in the queue. 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, and some even It is simply recommended not to use autoload for efficiency. After we understand the principle of autoload implementation, we know that the autoload mechanism itself is not the reason for affecting 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 precisely the user-designed autoloading function that affects the efficiency of the autoload mechanism. If it cannot efficiently match the class name to 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 file existence verification (requiring in each include path (to search in the path included in the file), 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 inherently inefficient. Only abuse of autoload and poorly designed autoload functions will lead to a reduction in its efficiency.
http://www.bkjia.com/PHPjc/327352.htmlwww.bkjia.comtruehttp: //www.bkjia.com/PHPjc/327352.htmlTechArticleDetailed explanation of PHP autoload mechanism (1) Overview of autoload mechanism When developing systems using PHP's OO mode, everyone is usually used to Store the implementation of each class in a separate file, which will be easier...