需要任意 PHP 文件,而不会将变量泄漏到作用域中

2022-08-30 16:11:51

在 PHP 中,是否可以在不将任何变量从当前作用域泄漏到所需文件的变量命名空间或污染全局变量作用域的情况下访问任意文件?require

我想用PHP文件做轻量级的模板,并且为了纯度的缘故,我想知道是否可以加载一个模板文件,除了预期的变量之外,它的作用域中没有任何变量。

我已经设置了一个测试,我希望解决方案通过。它应该可以要求并让它返回。RequiredFile.phpSuccess, no leaking variables.

必需文件.php:

<?php

print array() === get_defined_vars()
    ? "Success, no leaking variables."
    : "Failed, leaked variables: ".implode(", ",array_keys(get_defined_vars()));

?>

我得到的最接近的是使用闭包,但它仍然返回。Failed, leaked variables: _file

$scope = function( $_file, array $scope_variables ) {
    extract( $scope_variables ); unset( $scope_variables );
    //No way to prevent $_file from leaking since it's used in the require call
    require( $_file );
};
$scope( "RequiredFile.php", array() );

有什么想法吗?


答案 1

看看这个:

$scope = function() {
    // It's very simple :)
    extract(func_get_arg(1));
    require func_get_arg(0);
};
$scope("RequiredFile.php", []);

答案 2

我已经能够提出一个解决方案,使用将变量作为常量内联,从而防止它泄漏。eval

虽然使用绝对不是一个完美的解决方案,但它确实为所需的文件创建了一个“完全干净”的范围,PHP似乎无法在本地做到这一点。eval

$scope = function( $file, array $scope_array ) {
    extract( $scope_array ); unset( $scope_array );
    eval( "unset( \$file ); require( '".str_replace( "'", "\\'", $file )."' );" );
};
$scope( "test.php", array() );

编辑:

从技术上讲,这甚至不是一个完美的解决方案,因为它会在 和 变量上创建一个“阴影”,防止它们自然地传递到作用域中。filescope_array

编辑2:

我可以抗拒尝试编写无阴影的解决方案。执行的代码不应有权访问先前作用域中的 全局变量或局部变量,除非直接传入。$this

$scope = function( $file, array $scope_array ) {
    $clear_globals = function( Closure $closure ) {
        $old_globals = $GLOBALS;
        $GLOBALS = array();
        $closure();
        $GLOBALS = $old_globals;
    };
    $clear_globals( function() use ( $file, $scope_array ) {
        //remove the only variable that will leak from the scope
        $eval_code = "unset( \$eval_code );";

        //we must sort the var name array so that assignments happens in order
        //that forces $var = $_var before $_var = $__var;
        $scope_key_array = array_keys( $scope_array );
        rsort( $scope_key_array );

        //build variable scope reassignment
        foreach( $scope_key_array as $var_name ) {
            $var_name = str_replace( "'", "\\'", $var_name );
            $eval_code .= "\${'$var_name'} = \${'_{$var_name}'};";
            $eval_code .= "unset( \${'_{$var_name}'} );";
        }
        unset( $var_name );

        //extract scope into _* variable namespace
        extract( $scope_array, EXTR_PREFIX_ALL, "" ); unset( $scope_array );

        //add file require with inlined filename
        $eval_code .= "require( '".str_replace( "'", "\\'", $file )."' );";
        unset( $file );

        eval( $eval_code );
    } );
};
$scope( "test.php", array() );

推荐