Home  >  Article  >  Backend Development  >  Detailed explanation of PHP autoload mechanism_PHP tutorial

Detailed explanation of PHP autoload mechanism_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 17:46:39666browse

Detailed explanation of PHP autoload mechanism

(1) Overview of autoload mechanism

When developing systems using PHP's OO model, it is usually customary to store the implementation of each class in a separate file. This makes it easy to reuse classes 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:

/* 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. The Person class can then be used directly to instantiate an object.

However, as the scale of the project continues to expand, using this method will bring about 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 omissions or inclusions. Import 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:

/* 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 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 name with the actual disk file 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. . 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 bytecode sequence 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 compiled the autoload.php file into opcode, and then used these OPCODEs to study what PHP did in the process:

/* autoload.php The compiled OPCODE list is made using the OPDUMP tool developed by the author
* The generated results can be downloaded from the website http://www.phpinternals.com/.
*/
1: 2: // require_once ("Person.php");
3:
4: function __autoload ($classname) {
0 NOP                                                           0 RECV                1
5: 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
6: require_once ($classname. “.class.php”);
5 CONCAT               !0, ‘.class.php’ =>RES[~2]                                                  6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE
7: }
7 JMP                                ->8
8: }
8 RETURN null
9:
10: $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
11:
12: var_dump ($p);
7 SEND_VAR    !0
8 DO_FCALL ‘var_dump’ [extval:1]
13: ?>

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:

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 final call, 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 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 are clear, when PHP instantiates an object (actually when implementing an interface, using class constants or static variables in a class, calling static methods in a class), it will first look for the class (or interface) exists, if not, 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 finally came out. 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 is 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 any parameters, you can point autoload_func to spl_autoload.

From 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 will maintain 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. 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 in 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 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.

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/478586.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...
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