English 中文(简体)
3. 要求在不将变数泄入范围的情况下,使用任意的购买力平价文档
原标题:Require an arbitrary PHP file without leaking variables into scope

Is it possible in PHP to require an arbitrary file without leaking any variables from the current scope into the required file s variable namespace or polluting the global variable scope?

I m想要用PHP文档进行轻重排减,并且为了纯洁而想的是,如果能够装载一个模板,而没有其范围的任何变数,而是打算的变数。

我已经进行了一项考验,我希望找到解决办法。 可要求<代码>RequiredFile.php,并交回Success,不出现泄漏变量。

RequiredFile.php:

<?php

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

?>

最近的I ve gotten正在使用封闭装置,但它仍然使用返回代码<>Failed,泄漏变量:_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() );

任何想法?

最佳回答

参看:

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

我早就能够提出使用<代码>eval的解决方案,将变量固定下来,从而防止其泄漏。

虽然使用<代码>eval肯定不是完美的解决办法,但它的确为必要的档案创造了“适当清洁”的范围,而PHP似乎无法从本土做些什么。

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

EDIT:

这一技术上的 solution甚至是一种完美的解决办法,因为它在<代码>>>>><>>>>>上和 cop_array变量上形成了一种“影子”,从而使得这些变量无法自然地进入范围。

EDIT:

我可以抵制试图书写一个无影子的解决办法。 除非直接通过,否则所执行守则不得使用<代码>$<>ts、来自以往范围的全球或当地变量。

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

在进行了一些研究之后,我在这里做了些什么。 唯一的(明确)解决办法是使用成员职能和案例/类别变量。

你们需要:

  • Reference everything using $this and not function arguments.
  • Unset all globals, superglobals and restore them afterwards.
  • Use a possible race condition of some sorts. i.e.: In my example below, render() will set instance variables that _render() will use afterwards. In a multi-threaded system, this creates a race condition: thread A may call render() at the same time as thread B and the data will be inexact for one of them. Fortunately, for now, PHP isn t multi-threaded.
  • Use a temporary file to include, containing a closure, to avoid the use of eval.

1. 模板一分为以下几类:

class template {

    // Store the template data
    protected $_data = array();

    // Store the template filename
    protected $_file, $_tmpfile;

    // Store the backed up $GLOBALS and superglobals
    protected $_backup;

    // Render a template $file with some $data
    public function render($file, $data) {
        $this->_file = $file;
        $this->_data = $data;
        $this->_render();
    }

    // Restore the unset superglobals
    protected function _restore() {
        // Unset all variables to make sure the template don t inject anything
        foreach ($GLOBALS as $var => $value) {
             // Unset $GLOBALS and you re screwed
             if ($var ===  GLOBALS ) continue;

             unset($GLOBALS[$var]);
        }

        // Restore all variables
        foreach ($this->_backup as $var => $value) {
             // Set back all global variables
             $GLOBALS[$var] = $value;
        }
    }

    // Backup the global variables and superglobals
    protected function _backup() {
        foreach ($GLOBALS as $var => $value) {
            // Unset $GLOBALS and you re screwed
            if ($var ===  GLOBALS ) continue;

            $this->_backup[$var] = $value;
            unset($GLOBALS[$var]);
        }
    }

    // Render the template
    protected function _render() {
        $this->_backup();

        $this->_tmpfile = tempnam(sys_get_temp_dir(), __CLASS__);
        $code =  <?php $render = function() { .
                                   extract( .var_export($this->_data, true). ); .
                                   require " .$this->_file. "; .
                                 }; $render(); 
        file_put_contents($this->_tmpfile, $code);
        include $this->_tmpfile;

        $this->_restore();
    }
}

这里是试验案例:

// Setting some global/superglobals
$_GET[ get ] =  get is still set ;
$hello =  hello is still set ;

$t = new template;
$t->render( template.php , array( foo => bar ,  this => hello world ));

// Checking if those globals/superglobals are still set
var_dump($_GET[ get ], $hello);

// Those shouldn t be set anymore
var_dump($_SERVER[ bar ], $GLOBALS[ stack ]); // undefined indices 

模板文件:

<?php 

var_dump($GLOBALS);             // prints an empty list

$_SERVER[ bar ] =  baz ;        // will be unset later
$GLOBALS[ stack ] =  overflow ; // will be unset later

var_dump(get_defined_vars());   // foo, this

?>

简言之,这一解决办法:

  • Hides all globals and superglobals. The variables themselves ($_GET, $_POST, etc.) can still be modified, but they will revert back to what they were previously.
  • Does not shadow variables. (Almost) everything can be used, including $this. (Except for $GLOBALS, see below).
  • Does not bring anything into scope that wasn t passed.
  • Does not lose any data nor trigger destructors, because the refcount never reaches zero for any variable.
  • Does not use eval or anything like that.

这里,我得出以下结果:

array(1) {
  ["GLOBALS"]=>
  *RECURSION*
}
array(2) {
  ["this"]=>
  string(11) "hello world"
  ["foo"]=>
  string(3) "bar"
}

string(10) "get is still set"
string(12) "hello is still set"
Notice: Undefined index: bar in /var/www/temp/test.php on line 75

Call Stack:
    0.0003     658056   1. {main}() /var/www/temp/test.php:0

Notice: Undefined index: stack in /var/www/temp/test.php on line 75

Call Stack:
    0.0003     658056   1. {main}() /var/www/temp/test.php:0

NULL
NULL

如果您在发出呼吁之前就放弃了“GLOBALS。

The only possible issue is that someone still can execute something like:

unset($GLOBALS);

......,你重写。 也没有办法。

If you need a very simple templating engine, your approach with a function is good enough. Tell me, what are the real disadvantages of exposing that $_file variable?

If you need to do real work, grab Twig and stop worrying. Any proper templating engine compiles your templates into pure PHP anyway, so you don t lose speed. You also gain significant advantages - simpler syntax, enforced htmlspecialchars and other.

You could always hide your $_file in a superglobal:
$_SERVER[ MY_COMPLEX_NAME ] = $_file;
unset($_file);
include($_SERVER[ MY_COMPLEX_NAME ]);
unset($_SERVER[ MY_COMPLEX_NAME ]);





相关问题
Brute-force/DoS prevention in PHP [closed]

I am trying to write a script to prevent brute-force login attempts in a website I m building. The logic goes something like this: User sends login information. Check if username and password is ...

please can anyone check this while loop and if condition

<?php $con=mysql_connect("localhost","mts","mts"); if(!con) { die( unable to connect . mysql_error()); } mysql_select_db("mts",$con); /* date_default_timezone_set ("Asia/Calcutta"); $date = ...

定值美元

如何确认来自正确来源的数字。

Generating a drop down list of timezones with PHP

Most sites need some way to show the dates on the site in the users preferred timezone. Below are two lists that I found and then one method using the built in PHP DateTime class in PHP 5. I need ...

Text as watermarking in PHP

I want to create text as a watermark for an image. the water mark should have the following properties front: Impact color: white opacity: 31% Font style: regular, bold Bevel and Emboss size: 30 ...

How does php cast boolean variables?

How does php cast boolean variables? I was trying to save a boolean value to an array: $result["Users"]["is_login"] = true; but when I use debug the is_login value is blank. and when I do ...

热门标签