Heim > php教程 > PHP开发 > Zufällige Gedanken zu PHP debug_backtrace

Zufällige Gedanken zu PHP debug_backtrace

黄舟
Freigeben: 2016-12-28 13:33:22
Original
1390 Leute haben es durchsucht

Kurze Beschreibung

Wie Sie vielleicht alle wissen, gibt es in PHP eine Funktion namens debug_backtrace, die die Aufrufinformationen der Funktion zurückverfolgen kann und als Debugging-Tool bezeichnet werden kann.

Okay, schauen wir uns das an.

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
                (
                )

        )

)
*/
Nach dem Login kopieren

Übrigens gibt es eine ähnliche Funktion: debug_print_backtrace. Der Unterschied besteht darin, dass die Backtrace-Informationen direkt gedruckt werden.

Schauen Sie sich debug_backtrace an. Dem Namen nach zu urteilen, ist sein Zweck sehr klar. Es wird von Entwicklern zum Debuggen verwendet. Bis mir eines Tages auffiel, dass der zurückgegebene Dateiparameter „File“ die aufrufende Skriptquelle der Funktion oder Methode darstellt (in welcher Skriptdatei sie verwendet wird). Plötzlich dachte ich: Wenn das aktuelle Skript die aufrufende Quelle kennt, kann es dann einige interessante Funktionen basierend auf der Quelle implementieren, z. B. Dateiberechtigungsverwaltung, dynamisches Laden usw.

Praktischer Kampf

Implementieren einer magischen Funktion

Ermitteln Sie den Namen der aktuellen Funktion oder Methode

Obwohl bereits __FUNCTION__ enthalten ist PHP und __METHOD__ magische Konstanten, aber ich möchte trotzdem vorstellen, wie man debug_backtrace verwendet, um den aktuellen Funktions- oder Methodennamen abzurufen.

Der Code lautet wie folgt:

//函数外部输出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
Nach dem Login kopieren

Es sieht gut aus, gut.

Relative Pfaddateien laden

Wenn Sie relative Pfaddateien in das Projekt laden möchten, müssen Sie native Methoden wie include oder require verwenden Aber jetzt kann ich mit debug_backtrace eine benutzerdefinierte Funktion verwenden, um relative Pfaddateien zu laden.

Erstellen Sie ein neues Projekt mit der folgenden Verzeichnisstruktur:

Zufällige Gedanken zu PHP debug_backtrace

Ich möchte eine benutzerdefinierte Funktion in index.php aufrufen und relative Pfade zum Laden des Pakets/ verwenden. package.php und verwenden Sie dieselbe Methode, um _inc_func.php in package.php zu laden

Die Codes der drei Dateien lauten wie folgt (achten Sie auf den Code von index.php und package.php, die den Import aufrufen). Funktion):

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 );
}

?>
Nach dem Login kopieren

package.php:

<?php

echo &#39;package&#39;;

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

?>
Nach dem Login kopieren

_inc_func.php:

<?phpecho &#39;_inc_func&#39;;?>
Nach dem Login kopieren

Index ausführen .php:

//输出:
//package
//_inc_func
Nach dem Login kopieren

Wie Sie sehen, ist es mir gelungen.

Denkweise: Ich denke, diese Methode ist sehr leistungsfähig. Zusätzlich zu relativen Pfaden können auf dieser Idee abstrakte Funktionen wie relative Pakete und relative Module abgeleitet werden, was die Rolle der Modularisierung für einige Projekte verbessern kann.

Dateiaufrufberechtigungen verwalten

Ich habe mich auf eine Regel geeinigt: Dateinamen mit einem Unterstrich davor können nur von Dateien in aufgerufen werden aktuelles Verzeichnis, d. h. solche Dateien sind für das aktuelle Verzeichnis „privat“ und Dateien in anderen Verzeichnissen dürfen sie nicht laden.

Der Zweck davon ist ganz klar: die Codekopplung zu reduzieren. In Projekten werden manche Dateien oft nur in bestimmten Skripten verwendet. Was jedoch häufig vorkommt, ist, dass einige Programmierer feststellen, dass diese Skripte über Funktionen oder Klassen verfügen, die sie verwenden müssen, und diese daher direkt laden, um ihre eigenen Zwecke zu erreichen. Dieser Ansatz ist sehr schlecht. Der ursprüngliche Zweck des Schreibens dieser Skripte besteht nur darin, die Implementierung bestimmter Schnittstellen zu unterstützen, und andere Vielseitigkeiten werden nicht berücksichtigt. Falls die Schnittstelle intern umgestaltet werden muss, müssen diese spezifischen Skriptdateien ebenfalls geändert werden. Nach der Änderung werden jedoch einige Skripte, die scheinbar nichts mit der Schnittstelle zu tun haben, plötzlich nicht mehr ausgeführt. Bei der Prüfung stellte sich heraus, dass die Verweise auf das Dokument verwirrend waren.

Die Norm dient nur der Aufsicht. Sie schließt nicht aus, dass manche Menschen aus eigenen egoistischen Wünschen gegen diese Norm verstoßen oder sie unbeabsichtigt verletzen. Der beste Weg besteht darin, es in den Code zu implementieren und das Programm diese Situation automatisch erkennen zu lassen.

Zufällige Gedanken zu 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;
}

?>
Nach dem Login kopieren

_inc_func.php:

<?php

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

?>
Nach dem Login kopieren

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; );
}

?>
Nach dem Login kopieren

运行index.php:

//输出:
//我是一个包。
Nach dem Login kopieren

整个项目使用了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脚本
Nach dem Login kopieren

那么,这时可以使用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;
}
Nach dem Login kopieren

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

//输出:
//致命错误:/package/_inc_func.php文件属于私有文件,/index.php不能载入它
Nach dem Login kopieren

而载入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
                )

        )

)
*/
Nach dem Login kopieren

注意输出的第一个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
) 
*/
Nach dem Login kopieren

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

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

总结

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

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

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

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


Verwandte Etiketten:
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Aktuelle Ausgaben
Warum gibt debug_backtrace() leer zurück?
Aus 1970-01-01 08:00:00
0
0
0
PHP-Datenerfassung?
Aus 1970-01-01 08:00:00
0
0
0
PHP-Erweiterung intl
Aus 1970-01-01 08:00:00
0
0
0
Wie man PHP gut lernt
Aus 1970-01-01 08:00:00
0
0
0
Beliebte Empfehlungen
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage