SoFunction
Updated on 2025-03-10

Strongly declare: Do not use (include/require)_once

There is a long discussion about using include or include_once (the following includes require_once), and the conclusion has always been, just try to use include instead of include_once. The most common reason in the past was that include_once needed to query the loaded file list, confirm whether it exists, and then load it.

Admittedly, this reason is correct, but what I want to say today is another reason.

We know that when PHP determines whether a file is loaded, it needs to get the opened_path of the file, which means, for example:

Copy the codeThe code is as follows:

<?php
set_include_path("/tmp/:/tmp2/");
include_once("");
?>

When PHP sees include_once "", it does not know what the actual path of the file is, so it cannot judge whether it has been loaded from the loaded file list. Therefore, in the implementation of include_once, it will first try to parse the real path of the file (for ordinary files, this parsing is just like checking getcwd and file paths, so if it is a relative path, it will generally not succeed). If the parsing is successful, look for EG(include_files). If it exists, it means that it has been included, return, otherwise open the file, thereby obtaining the opened_path of the file. For example, in the above example, this file exists in "/tmp2/".

Then, after obtaining this opened_path, PHP searches for the loaded file list to see if it has been included. If it is not included, then directly compile and no longer needs to open file.

1. Try to parse the absolute path of the file. If the parsing is successful, check EG(including_files), if it exists, it returns, if it does not exist, it continues
2. Open the file and get the open path of the file (opened path)
3. Use the opened path to search for EG (included_files) to see if it exists. If it exists, it will return, and if it does not exist, it will continue
4. Compile file (compile_file)

This is not a problem in most cases, but the problem lies in when you use APC...

When using APC, APC hijacks the compiled file pointer, compile_file, and directly obtains the compilation results from cache, avoiding opening the actual file, and avoiding system calls to open the open.

However, when you use include_once in your code, PHP has tried to open the file before compile_file, and then enter the compile file hijacked by APC, which will generate an additional open operation. APC introduces include_once_override. When include_once_override is enabled, APC will hijack PHP's ZEND_INCLUDE_OR_EVAL opcode handler, determine the absolute path of the file through stat, and then if it is found that it is not loaded, rewrite the opcode to include and make a tricky solution.

However, it's a pity that, as I said, the APC's include_once_override implementation has always been bad, and there will be some undefined problems, such as:

Copy the codeThe code is as follows:

<?php
set_include_path("/tmp");
function a($arg = array()) {
    include_once("");
}
a();
a();
?>

Then, our placement is in "/tmp/", and the content is as follows:
Copy the codeThe code is as follows:

<?php
  class B {}
?>

Then when opening apc.include_once_override, you will get the following error when you continuously access:

Fatal error - include() : Cannot redeclare class b

To exclude these technical factors, I have always believed that we should use include instead of include_once, because we can plan ourselves and a file is only loaded once. We can also do this with the help of automatic loading.

Your use of include_once can only prove that you have no confidence in your code.

Therefore, I suggest that you don’t use include_once anymore