PHP debug_backtrace, phpdebugbacktrace
The sample code test environment in this article is APMServ (PHP5.2.6) in Windows)
Brief Introduction
As you may know, there is a function in php calledDebug_backtraceIt can trace the call information of a function and is a debugging tool.
Okay. Let's review it.
One (); function one () {two ();} function two () {three ();} function three () {print_r (debug_backtrace ());} /* output: Array ([0] => Array ([file] => D: \ apmserv \ www \ htdocs \ test \ debug \ index. php [line] => 10 [function] => three [args] => Array () [1] => Array ([file] => D: \ apmserv \ www \ htdocs \ test \ debug \ index. php [line] => 6 [function] => two [args] => Array () [2] => Array ([file] => D: \ apmserv \ www \ htdocs \ test \ debug \ index. php [line] => 3 [function] => one [args] => Array ()))*/
By the way, similar functions are provided:Debug_print_backtraceThe difference is that it directly prints backtracing information.
Come backDebug_backtraceFrom the perspective of the name, the purpose is clear and developers can use it for debugging. Until one day I noticed that it returnedFileParameter,FileIndicates the script source for calling a function or method (in which script file is used ). Suddenly I thought, if the current script knows the call source, can it implement some interesting functions, such as file permission management and dynamic loading, based on different sources.
Practice
Implement magic functions to obtain the name of the current function or Method
Although PHP already has_ FUNCTION __And_ METHOD __Magic constant, but I 'd like to introduce itDebug_backtraceObtain the name of the current function or method.
The Code is as follows:
// Echo getFuncName (); printFuncName (); Object: printMethodName (); // after calling the above two functions, output getFuncName externally, check whether there are issues such as 'cache' echo getFuncName (); function printFuncName () {echo getFuncName ();} class Object {static function printMethodName () {echo getFuncName () ;}}/*** get the name of the current function or method * The Name Of The function is getFuncName, I cannot think of a good name ** @ return string name */function getFuncName () {$ debug_back Trace = debug_backtrace (); // if the function name is the following, the script is loaded, getFuncName is called outside the function // in this case, null $ ignore = array ('include ', 'include _ once', 'require ', 'require _ once') should be returned '); // The first backtrace is the current function getFuncName, And the last (second) backtrace is the function that calls getFuncName. $ handle_func = $ debug_backtrace [1]; if (isset ($ handle_func ['function']) &! In_array ($ handle_func ['function'], $ ignore) {return $ handle_func ['function'];} return null;} // output: // null // printFuncName // printMethodName // null
It looks okay.
Load relative path File
If you want to load files with relative paths in the project, you must useIncludeOrRequireAnd other native methods, but now we haveDebug_backtraceYou can use a custom function to load relative path files.
Create a project with the following directory structure:
I wantIndex. phpCall the custom function and use the relative path to loadPackage/package. phpAndPackage. phpUsing the same method for loading_ Inc_func.php
The code for the three files is as follows (note:Index. phpAndPackage. phpCallImportFunction Code ):
Index. php:
<? Phpimport ('. /package. php ');/*** load the file in the current project ** @ param string $ path relative file path */function import ($ path) {// obtain the backstrace list $ debug_backtrace = debug_backtrace (); // The first backstrace is the source script for calling import $ source = $ debug_backtrace [0]; // obtain the directory path of the call source, which is combined with the file path. You can calculate the complete path $ source_dir = dirname ($ source ['file']); require realpath ($ source_dir. '/'. $ path) ;}?>
Package. php:
<?phpecho 'package';import( './_inc_func.php' );?>
_ Inc_func.php:
<?phpecho '_inc_func';?>
RunIndex. php:
// Output: // package // _ inc_func
As you can see, I have succeeded.
Thinking: I think this method is very powerful. In addition to relative paths, we can draw out abstract features such as relative packages and relative modules based on this idea, which can enhance modularization for some projects.
Manage File call Permissions
I agree on a Convention: files with underscores before a file name can only be called by files in the current directory, that is, these files belong to the current directory 'private', and files in other directories cannot be loaded into them.
The purpose is clear: to reduce code coupling. In projects, some files are often used only in specific scripts. But it often happens that some programmers find that these scripts have functions or classes they need, so they can directly load them for their own purposes. This is not a good practice. Originally, these scripts were written to assist some interfaces, and they did not take into account other versatility. In case that the interface needs to be restructured internally, you also need to modify these specific script files. However, after the changes, some scripts that seem unrelated to this interface suddenly fail to run. Once checked, it is found that the file reference is complex.
A regulation is only a supervisory function. It does not rule out violations or unintentional violations of this Regulation for selfish desires. The best way is to implement the Code and let the program automatically detect this situation.
Create a project with the following directory structure.
For this project,_ Inc_func.phpBelongPackagePrivate file of the Directory, onlyPackage. phpIt can be loaded, andIndex. phpYou do not have this permission.
PackageA directory is a package,Package. phpProvides the interface for this package._ Inc_func.phpYesPackage. phpSome functions that need to be used.Index. phpThe interface file of this package will be used, that isPackage. php
Their code is as follows:
Index. php:
<? Phpheader ("Content-type: text/html; charset = UTF-8"); // define the project root directory define ('app _ path ', dirname (_ FILE _); import (APP_PATH. '/package. php '); // Package_printInfo () of the output package (); /*** load the file in the current project ** @ param string $ path file path */function import ($ path) {// check the validity of the path $ real_path = realpath ($ path); $ in_app = (stripos ($ real_path, APP_PATH) = 0 ); if (empty ($ real_path) |! $ In_app) {throw new Exception ('file path does not exist or is not allowed ');} include $ real_path ;}?>
_ Inc_func.php:
<?phpfunction _Package_PrintStr( $string ) { echo $string;}?>
Package. php:
<? Phpdefine ('package _ path', dirname (_ FILE _); // introduce the private FILE import (PACKAGE_PATH. '/_ inc_func.php'); function Package_printInfo () {_ Package_PrintStr ('I am a package. ') ;}?>
Run index. php:
// Output: // I am a package.
The entire project is usedImportThe function loads the file and the code looks normal. But I canIndex. phpLoadPackage/_ inc_func.phpFile and call its method.
Index. phpChangeImport (APP_PATH. '/package. php ');And run:
Import (APP_PATH. '/package/_ inc_func.php'); _ Package_PrintStr ('I loaded/package/_ inc_func.php script'); // output: // I loaded the/package/_ inc_func.php script
Then, you can useDebug_backtraceCheck Loading_ Inc_func.phpWhere is the file path? I changed it.Index. phpInImportThe complete code is as follows:
/*** Load the file in the current project ** @ param string $ path file path */function import ($ path) {// check the validity of the path $ real_path = realpath ($ path); $ in_app = (stripos ($ real_path, APP_PATH) = 0 ); if (empty ($ real_path) |! $ In_app) {throw new Exception ('file path does not exist or is not allowed ');} $ path_info = pathinfo ($ real_path ); // determine whether the object is private $ is_private = (substr ($ path_info ['basename'], 0, 1) = '_'); if ($ is_private) {// obtain the backstrace list $ debug_backtrace = debug_backtrace (); // The first backstrace is the source script for calling import $ source = $ debug_backtrace [0]; // obtain the call source Path, use it to compare with the target path $ source_dir = dirname ($ source ['file']); $ target_dir = $ path_info ['dirn Ame']; // if ($ source_dir! ==$ Target_dir) {$ relative_source_file = str_replace (APP_PATH, '', $ source ['file']); $ relative_target_file = str_replace (APP_PATH,'', $ real_path ); $ error = $ relative_target_file. 'File is a private file ,'. $ relative_source_file. 'cannot load it. '; Throw new Exception ($ error) ;}include $ real_path ;}
Then runIndex. php, Will generate a fatal error:
// Output: // fatal error: the/package/_ inc_func.php file is a private file, And/index. php cannot load it.
While LoadingPackage. phpNo problem. We will not demonstrate it here.
As you can see, my original thought succeeded. In this casePackage. phpIn factIndex. phpCan still be called_ Inc_func.phpFunction (Package. phpLoad it ). Except for anonymous functions, other functions are globally visible, including classes. However, this can more or less alert programmers. The key is to look at the programmers themselves. Even the best norms and constraints cannot resist bad programmers. They will always be 'smart' than you '.
'Bug 'of debug_backtrace'
If you useCall_user_funcOrCall_user_func_arrayCall other functions.Debug_backtrace.
Example:
Call_user_func ('import'); function import () {print_r (debug_backtrace ();}/* output: array ([0] => Array ([function] => import [args] => Array () [1] => Array ([file] => F: \ www \ test \ index. php [line] => 3 [function] => call_user_func [args] => Array ([0] => import )))*/
Note that the first output isBacktrace, Its call Source PathFileNo. As a result, problems may occur in my previous examples. Of course, you may have noticed the secondBacktraceIf the first one does not exist, go back. However, the practice is not feasible. I have encountered this situation before, and there will also be problems, but now the code at that time cannot be retrieved. If you find out, please tell me the problem. For the moment, it is best not to use this method. A better solution is to use PHP reflection API.
Use reflection
Using the reflection API, you can know the detailed information about the function, including the file and number of rows it declares.
Call_user_func ('import'); function import () {$ debug_backtrace = debug_backtrace (); $ backtrace = $ debug_backtrace [0]; if (! Isset ($ backtrace ['file']) {// use the reflection API to obtain the file and number of rows declared by the function. $ reflection_function = new ReflectionFunction ($ backtrace ['function']); $ backtrace ['file'] = $ reflection_function-> getFileName (); $ backtrace ['line'] = $ reflection_function-> getStartLine ();} print_r ($ backtrace );} /* output: Array ([function] => import [args] => Array () [file] => F: \ www \ test \ index. php [line] => 5 )*/
You can see that the reflection interface is usedReflectionMethodMethod,FileAgain.
The reflection interface of the class method isReflectionMethodTo obtain the declaration method.GetFileName.
Summary
In a project, I usually do not directly useIncludeOrRequireLoad the script. I like to encapsulate them into a function and call this function when loading the script. In this way, you can make some judgments in the function, such as whether to introduce this file or add some calling rules to facilitate maintenance.
Fortunately, with this habit, I can immediatelyDebug_backtraceSome ideas are applied to the entire project.
In generalDebug_backtraceWith great flexibility, You can implement some interesting functions with just a little use. At the same time, I found that it is not very well controlled, because every call to any method or function may change its value. If you want to use it for some logic processing (for example, some ideas I mentioned in this article), you need a system with good standards, at least in file loading.