Home  >  Article  >  Backend Development  >  Implementation analysis of PHP's autoload mechanism_PHP tutorial

Implementation analysis of PHP's autoload mechanism_PHP tutorial

WBOY
WBOYOriginal
2016-07-21 15:16:11680browse

1. Overview of the autoload mechanism

When developing a system using PHP's OO mode, it is usually customary to store the implementation of each class in a separate file, which will be very easy This enables the reuse of classes and is 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.
The following 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 example, the no-autoload.php file requires the Person class, which is included using require_once, and then the Person class can be used directly to instantiate an object.
However, 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 will be needed, which may cause Missing or including 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 which 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, and the second thing is to determine the disk path where the class file is located (in our case it is the simplest In the case where the classes are in the same folder as the PHP program file that calls them), the third thing is to load the classes into the system from the disk file. 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 in the __autoload() function, match the class name with the actual disk file to achieve 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 their class names are different from the actual disk files. The mapping rules are different. 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

We know that the execution of PHP files is divided into two independent processes. The first step is to compile the PHP file into A sequence of bytecodes commonly called OPCODE (actually compiled into a byte array called zend_op_array). The second step is to execute these OPCODEs by a virtual machine. All behaviors of PHP 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 use these OPCODEs to study what PHP does in the process:
Copy the code The code is as follows:

/* autoload.php The compiled OPCODE list is generated using the OPDUMP tool
* developed by the author. You can go to the website http ://www.phpinternals.com/ Download the software.
*/
// 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 source code of PHP (I am using PHP 5.3alpha2 version), you can find the following call sequence:
Copy code Code As follows:

ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ...) (zend_vm_def.h line 1864)
=> zend_fetch_class (zend_execute_API.c line 1434)
=>zend_lookup_class_ex ( zend_execute_API.c line 964)
=> zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c line 1040)

Before the last step of the call, let’s take a look at the key parameters of the call. :
Copy code The code is as follows:

/* 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 PHP’s own library functions. 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, 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.
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. Multiple extensions can be specified in $file_extensions to protect the expansion. Just use semicolons to separate the names; 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 using 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 the implementation of spl_autoload_call. 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. Some people even suggest that for the sake of efficiency Don't use autoload. 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. The autoload mechanism is not inherently inefficient. Only abuse of autoload and poorly designed autoload functions will lead to a decrease in efficiency.

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/325988.htmlTechArticle1. Overview of the autoload mechanism When developing systems using PHP’s OO model, it is usually customary to add the The implementation is stored in a separate file, which makes it easy to implement the class...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn