una tabla saneamiento otro otra llamar include_once funcion filtros filtrar ejemplo desde datos carpeta php global-variables scope

tabla - include_once php



Requerir un archivo PHP arbitrario sin filtrar variables en el alcance (4)

Después de algunas investigaciones, esto es lo que se me ocurrió. La única solución (limpia) es usar funciones miembro y variables de instancia / clase.

Necesitas:

  • Haz referencia a todo usando $this y no argumentos de función.
  • Desarmar todos los globales, superglobales y restaurarlos después.
  • Utilice una posible condición de carrera de algún tipo. es decir: en mi ejemplo a continuación, render() establecerá las variables de instancia que _render() usará después. En un sistema de múltiples subprocesos, esto crea una condición de carrera: el subproceso A puede llamar a render () al mismo tiempo que el subproceso B y los datos serán inexactos para uno de ellos. Afortunadamente, por ahora, PHP no es multihilo.
  • Use un archivo temporal para incluir, que contiene un cierre, para evitar el uso de eval .

La plantilla de plantilla que se me ocurrió:

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

Y aquí está el caso de prueba:

// 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

Y el archivo de plantilla:

<?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 ?>

En resumen, esta solución:

  • Oculta a todos los globales y superglobales. Las variables en sí mismas ($ _GET, $ _POST, etc.) todavía pueden modificarse, pero volverán a ser lo que eran anteriormente.
  • No sombrea las variables. (Casi) todo puede ser usado, incluyendo $this . (Excepto por $GLOBALS , ver más abajo).
  • No trae nada al alcance que no haya sido aprobado.
  • No pierde ningún dato ni desencadena destructores, porque el refcount nunca llega a cero para ninguna variable.
  • No utiliza eval ni nada de eso.

Aquí está el resultado que tengo para lo anterior:

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

Si vuelves $GLOBALS después del hecho, debería ser como antes de la llamada.

El único problema posible es que alguien todavía puede ejecutar algo como:

unset($GLOBALS);

... y estas jodido. Y no hay manera de evitar eso.

¿Es posible en PHP require un archivo arbitrario sin perder ninguna variable del alcance actual en el espacio de nombres de la variable del archivo requerido o contaminar el alcance de la variable global?

Tengo ganas de crear plantillas ligeras con archivos PHP y me preguntaba por motivos de pureza si era posible cargar un archivo de plantilla sin ninguna variable en su ámbito, sino las previstas.

He configurado una prueba que me gustaría que pasara una solución. Debe ser posible exigir RequiredFile.php y hacer que devuelva Success, no leaking variables. .

RequiredFile.php:

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

Lo más cercano que obtuve fue el uso de un cierre, pero aún así devuelve 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() );

¿Algunas ideas?


He podido encontrar una solución utilizando eval para incluir la variable como una constante, evitando así que se filtre.

Si bien el uso de eval definitivamente no es una solución perfecta, crea un alcance "perfectamente limpio" para el archivo requerido, algo que PHP no parece poder hacer de forma nativa.

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

EDITAR:

Esto, técnicamente, ni siquiera es una solución perfecta, ya que crea una "sombra" sobre el file y scope_array variables scope_array , evitando que se pasen al ámbito de forma natural.

EDIT2:

Podría resistirme a intentar escribir una solución sin sombras. El código ejecutado no debe tener acceso a $this , variables globales o locales de los ámbitos anteriores, a menos que se transfiera directamente.

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


Mira esto:

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


Si necesita un motor de plantillas muy simple, su enfoque con una función es lo suficientemente bueno. Dígame, ¿cuáles son las desventajas reales de exponer esa variable $_file ?

Si necesitas hacer un trabajo real, toma Twig y deja de preocuparte. Cualquier motor de plantillas adecuado compila tus plantillas en PHP puro de todos modos, para que no pierdas velocidad. También obtiene ventajas significativas: una sintaxis más simple, htmlspecialchars forzada y otros.

Siempre puedes ocultar tu $_file en una superglobal:
$_SERVER[''MY_COMPLEX_NAME''] = $_file;
unset($_file);
include($_SERVER[''MY_COMPLEX_NAME'']);
unset($_SERVER[''MY_COMPLEX_NAME'']);