Home php教程 PHP开发 Random thoughts about PHP debug_backtrace

Random thoughts about PHP debug_backtrace

Dec 28, 2016 pm 01:33 PM

Brief description

As you may all know, there is a function in PHP called debug_backtrace, which can backtrace the calling information of the function and can be said to be a debugging tool.

Okay, let’s review.

one();

function one() {
    two();
}

function two() {
    three();
}

function three() {
    print_r( debug_backtrace() );
}

/*
输出:
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
                (
                )

        )

)
*/
Copy after login

By the way, a similar function: debug_print_backtrace, the difference is that it will directly print the backtrace information.

Come back and look at debug_backtrace. Judging from the name, its purpose is very clear. It is used by developers for debugging. Until one day I noticed the file parameter it returned. File represents the calling script source of the function or method (in which script file it is used). Suddenly I thought, if the current script knows the calling source, can it implement some interesting functions based on the source, such as file permission management, dynamic loading, etc.

Practical combat

Implement the magic function

Get the name of the current function or method

Although there is already __FUNCTION__ in PHP and __METHOD__ magic constants, but I still want to introduce how to use debug_backtrace to get the current function or method name.

The code is as follows:

//函数外部输出getFuncName的值
echo getFuncName();

printFuncName();

Object::printMethodName();

//调用了上面两个函数后,再次在外部输出getFuncName,看看是否有‘缓存’之类的问题
echo getFuncName();



function printFuncName() {
    echo getFuncName();
}

class Object {
    static function printMethodName() {
        echo getFuncName();
    }
}

/**
 * 获取当前函数或者方法的名称
 * 函数名叫getFuncName,好吧,其实method也可以当做function,实在想不出好名字
 * 
 * @return string name
 */
function getFuncName() {
    $debug_backtrace = debug_backtrace();
    //如果函数名是以下几个,表示载入了脚本,并在函数外部调用了getFuncName
    //这种情况应该返回空
    $ignore = array(
        'include',
        'include_once',
        'require',
        'require_once'
    );
    //第一个backtrace就是当前函数getFuncName,再上一个(第二个)backtrace就是调用getFuncName的函数了
    $handle_func = $debug_backtrace[1];
    if( isset( $handle_func['function'] ) && !in_array( $handle_func['function'], $ignore ) ) {
        return $handle_func['function'];
    }
    return null;
}


//输出:
//null
//printFuncName
//printMethodName
//null
Copy after login

It looks like there is no problem, it’s good.

Load relative path files

If you want to load relative path files in the project, you must use native methods such as include or require. But now with debug_backtrace, I can use a custom function to load relative path files.

Create a new project with the following directory structure:

Random thoughts about PHP debug_backtrace

I want to call a custom function in index.php and use relative paths to load package/ package.php, and use the same method to load _inc_func.php in package.php

The codes of the three files are as follows (note the code of index.php and package.php calling the import function):

index.php:

<?php

import( &#39;./package/package.php&#39; );

/**
 * 加载当前项目下的文件
 * 
 * @param string $path 相对文件路径
 */
function import( $path ) {
    //获得backstrace列表
    $debug_backtrace = debug_backtrace();
    //第一个backstrace就是调用import的来源脚本
    $source = $debug_backtrace[0];

    //得到调用源的目录路径,和文件路径结合,就可以算出完整路径
    $source_dir = dirname( $source[&#39;file&#39;] );
    require realpath( $source_dir . &#39;/&#39; . $path );
}

?>
Copy after login

package.php:

<?php

echo &#39;package&#39;;

import( &#39;./_inc_func.php&#39; );

?>
Copy after login

_inc_func.php:

<?phpecho &#39;_inc_func&#39;;?>
Copy after login

Run index.php:

//输出:
//package
//_inc_func
Copy after login

Okay See, I succeeded.

Thinking: I think this method is very powerful. In addition to relative paths, abstract features such as relative packages and relative modules can be derived based on this idea, which can enhance the role of modularization for some projects.

Manage file calling permissions

I have agreed on a standard: file names with an underscore in front of them can only be called by files in the current directory, that is to say Such files are 'private' to the current directory and files in other directories are not allowed to load them.

The purpose of this is very clear: to reduce code coupling. In projects, many times some files are only used in specific scripts. But what often happens is that some programmers find that these scripts have functions or classes they need to use, so they load it directly to achieve their own purposes. This approach is very bad. The original purpose of writing these scripts is only to assist in the implementation of certain interfaces, and they do not take other versatility into consideration. In case the interface needs to be refactored internally, these specific script files also need to be modified. However, after the modification, some scripts that seem to have nothing to do with the interface suddenly fail to run. Upon inspection, it turned out that the references to the document were confusing.

The norms are only for supervision. It does not rule out that some people violate these norms for their own selfish desires, or violate them unintentionally. The best way is to implement it into the code and let the program automatically detect this situation.

Random thoughts about PHP debug_backtrace

那么对于这个项目来说,_inc_func.php属于package目录的私有文件,只有package.php可以载入它,而index.php则没有这个权限。

package目录是一个包,package.php下提供了这个包的接口,同时_inc_func.php有package.php需要用到的一些函数。index.php将会使用这个包的接口文件,也就是package.php

它们的代码如下

index.php:

<?php

header("Content-type: text/html; charset=utf-8");

//定义项目根目录
define( &#39;APP_PATH&#39;, dirname( __FILE__ ) );

import( APP_PATH . &#39;/package/package.php&#39; );
//输出包的信息
Package_printInfo();

/**
 * 加载当前项目下的文件
 * 
 * @param string $path 文件路径
 */
function import( $path ) {
    
    //应该检查路径的合法性
    $real_path = realpath( $path );
    $in_app = ( stripos( $real_path, APP_PATH ) === 0 );
    if( empty( $real_path ) || !$in_app ) {
        throw new Exception( &#39;文件路径不存在或不被允许&#39; );
    }
    
    include $real_path;
}

?>
Copy after login

_inc_func.php:

<?php

function _Package_PrintStr( $string ) {
    echo $string;
}

?>
Copy after login

package.php:

<?php

define( &#39;PACKAGE_PATH&#39;, dirname( __FILE__ ) );

//引入私有文件
import( PACKAGE_PATH . &#39;/_inc_func.php&#39; );

function Package_printInfo() {
    _Package_PrintStr( &#39;我是一个包。&#39; );
}

?>
Copy after login

运行index.php:

//输出:
//我是一个包。
Copy after login

整个项目使用了import函数载入文件,并且代码看起来是正常的。但是我可以在index.php中载入package/_inc_func.php文件,并调用它的方法。

index.php中更改import( APP_PATH . '/package/package.php' );处的代码,并运行:

import( APP_PATH . &#39;/package/_inc_func.php&#39; );

_Package_PrintStr( &#39;我载入了/package/_inc_func.php脚本&#39; );

//输出:
//我载入了/package/_inc_func.php脚本
Copy after login

那么,这时可以使用debug_backtrace检查载入_inc_func.php文件的路径来自哪里,我改动了index.php中的import函数,完整代码如下:

/**
 * 加载当前项目下的文件
 * 
 * @param string $path 文件路径
 */
function import( $path ) {
    
    //首先应该检查路径的合法性
    $real_path = realpath( $path );
    $in_app = ( stripos( $real_path, APP_PATH ) === 0 );
    if( empty( $real_path ) || !$in_app ) {
        throw new Exception( &#39;文件路径不存在或不被允许&#39; );
    }
    
    $path_info = pathinfo( $real_path );
    //判断文件是否属于私有
    $is_private = ( substr( $path_info[&#39;basename&#39;], 0, 1 ) === &#39;_&#39; );
    if( $is_private ) {
        //获得backstrace列表
        $debug_backtrace = debug_backtrace();
        //第一个backstrace就是调用import的来源脚本
        $source = $debug_backtrace[0];
        
        //得到调用源路径,用它来和目标路径进行比较
        $source_dir = dirname( $source[&#39;file&#39;] );
        $target_dir = $path_info[&#39;dirname&#39;];
        //不在同一目录下时抛出异常
        if( $source_dir !== $target_dir ) {
            $relative_source_file = str_replace( APP_PATH, &#39;&#39;, $source[&#39;file&#39;] );
            $relative_target_file = str_replace( APP_PATH, &#39;&#39;, $real_path );
            $error = $relative_target_file . &#39;文件属于私有文件,&#39; . $relative_source_file . &#39;不能载入它。&#39;;
            throw new Exception( $error );
        }
    }
    
    include $real_path;
}
Copy after login

这时再运行index.php,将产生一个致命错误:

//输出:
//致命错误:/package/_inc_func.php文件属于私有文件,/index.php不能载入它
Copy after login

而载入package.php则没有问题,这里不进行演示。

可以看到,我当初的想法成功了。尽管这样,在载入package.php后,其实在index.php中仍然还可以调用_inc_func.php的函数(package.php载入了它)。因为除了匿名函数,其它函数是全局可见的,包括类。不过这样或多或少可以让程序员警觉起来。关键还是看程序员本身,再好的规范和约束也抵挡不住烂程序员,他们总是会比你‘聪明’。

debug_backtrace的'BUG'

如果使用call_user_func或者call_user_func_array调用其它函数,它们调用的函数里面使用debug_backtrace,将获取不到路径的信息。

例:

call_user_func(&#39;import&#39;);

function import() {
    print_r( debug_backtrace() );
}


/*
输出:
Array
(
    [0] => Array
        (
            [function] => import
            [args] => Array
                (
                )

        )

    [1] => Array
        (
            [file] => F:\www\test\test\index.php
            [line] => 3
            [function] => call_user_func
            [args] => Array
                (
                    [0] => import
                )

        )

)
*/
Copy after login

注意输出的第一个backtrace,它的调用源路径file没有了,这样一来我之前的几个例子将会产生问题。当然可能你注意到第二个backtrace,如果第一个没有就往回找。但经过实践是不可行的,之前我就碰到这种情况,同样会有问题,但是现在无法找回那时的代码了,如果你发现,请将问题告诉我。就目前来说,最好不要使用这种方法,我有一个更好的解决办法,就是使用PHP的反射API。

使用反射

使用反射API可以知道函数很详细的信息,当然包括它声明的文件和所处行数

call_user_func(&#39;import&#39;);

function import() {
    $debug_backtrace = debug_backtrace();
    $backtrace = $debug_backtrace[0];
    if( !isset( $backtrace[&#39;file&#39;] ) ) {
        //使用反射API获取函数声明的文件和行数
        $reflection_function = new ReflectionFunction( $backtrace[&#39;function&#39;] );
        $backtrace[&#39;file&#39;] = $reflection_function->getFileName();
        $backtrace[&#39;line&#39;] = $reflection_function->getStartLine();
    }
    print_r($backtrace);
}

/*
输出:
Array
(
    [function] => import
    [args] => Array
        (
        )

    [file] => F:\www\test\test\index.php
    [line] => 5
) 
*/
Copy after login

可以看到通过使用反射接口ReflectionMethod的方法,file又回来了。

类方法的反射接口是ReflectionMethod,获取声明方法同样是getFileName。

总结

在一个项目中,我通常不会直接使用include或者require载入脚本。我喜欢把它们封装到一个函数里,需要载入脚本的时候调用这个函数。这样可以在函数里做一些判断,比如说是否引入过这个文件,或者增加一些调用规则等,维护起来比较方便。

幸好有了这样的习惯,所以我可以马上把debug_backtrace的一些想法应用到整个项目中。

总体来说debug_backtrace有很好的灵活性,只要稍加利用,可以实现一些有趣的功能。但同时我发现它并不是很好控制,因为每次调用任何一个方法或函数,都有可能改变它的值。如果要使用它来做一些逻辑处理(比如说我本文提到的一些想法),需要一个拥有良好规范准则的系统,至少在加载文件方面吧。

 以上就是PHP debug_backtrace的胡思乱想的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement of this Website
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

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Have Crossplay?
1 months ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

How To Set Up Visual Studio Code (VS Code) for PHP Development How To Set Up Visual Studio Code (VS Code) for PHP Development Dec 20, 2024 am 11:31 AM

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

7 PHP Functions I Regret I Didn't Know Before 7 PHP Functions I Regret I Didn't Know Before Nov 13, 2024 am 09:42 AM

If you are an experienced PHP developer, you might have the feeling that you’ve been there and done that already.You have developed a significant number of applications, debugged millions of lines of code, and tweaked a bunch of scripts to achieve op

How do you parse and process HTML/XML in PHP? How do you parse and process HTML/XML in PHP? Feb 07, 2025 am 11:57 AM

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

PHP Program to Count Vowels in a String PHP Program to Count Vowels in a String Feb 07, 2025 pm 12:12 PM

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? Apr 03, 2025 am 12:03 AM

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.

See all articles