注意 get_defined_vars() 函数在不同上下文中的返回值
- 在全局作用域中,所有已定义变量的列表,无论是超级全局变量、命令行参数还是用户定义的变量
- 在函数作用域中,只有用户定义的变量列表(包括参数和函数体内的定义)
- 在类/对象方法作用域中,不返回类/对象属性;
此外,从 PHP 5.4 开始,即使可用,也不返回 $_ENV。
有关更多详细信息和作用域测试/结果,请参见 https://github.com/php/doc-en/issues/1317
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
get_defined_vars — 返回所有已定义变量的数组
此函数返回一个多维数组,包含在调用 get_defined_vars() 的作用域中定义的所有变量的列表,无论是环境变量、服务器变量还是用户定义的变量。
此函数没有参数。
包含所有变量的多维数组。
示例 #1 get_defined_vars() 示例
<?php
$b = array(1, 1, 2, 3, 5, 8);
$arr = get_defined_vars();
// 打印 $b
print_r($arr["b"]);
/* 打印到 PHP 解释器的路径(如果用作 CGI)
* 例如 /usr/local/bin/php */
echo $arr["_"];
// 打印所有命令行参数(如果有)
print_r($arr["argv"]);
// 打印所有服务器变量
print_r($arr["_SERVER"]);
// 打印变量数组的所有可用键
print_r(array_keys(get_defined_vars()));
?>
注意 get_defined_vars() 函数在不同上下文中的返回值
- 在全局作用域中,所有已定义变量的列表,无论是超级全局变量、命令行参数还是用户定义的变量
- 在函数作用域中,只有用户定义的变量列表(包括参数和函数体内的定义)
- 在类/对象方法作用域中,不返回类/对象属性;
此外,从 PHP 5.4 开始,即使可用,也不返回 $_ENV。
有关更多详细信息和作用域测试/结果,请参见 https://github.com/php/doc-en/issues/1317
注意:当您使用 eval() 调用代码时,PHP 会将自身变量附加到结果中,它们具有 "__" 前缀(在 PHP 7.3 中为 "__source_code" 和 "__bootstrap_file")。
修复
<?php
function filter_eval_vars(array $vars): array
{
foreach ($vars as $key => $value) {
if ($key[0] === '_' && $key[1] === '_') {
unset($vars[$key]);
}
}
return $vars;
}
?>
get_defined_vars() 非常适合一次导入多个值
到另一个作用域(例如用户定义的函数)。
以下是一个示例,用于在表格中显示一些值及其变量名。
(在调试中很有用)
您可以在脚本中的多个位置放置此用户定义的函数
以显示一些值(要在循环中更改的值)
以检查它们是否与预期的一致。
<?php
// 将 "get_defined_vars()" 设置为第二个参数。
function get_value_table($name_array, $gdv) {
$name_value_table = [];
foreach ($name_array as $name) :
if (!array_key_exists($name, $gdv)) :
$value = 'undefined';
elseif (is_bool($gdv[$name])) :
$value = $gdv[$name]? 'true' : 'false';
elseif (is_numeric($gdv[$name]) || is_string($gdv[$name])) :
$value = $gdv[$name];
elseif (is_array($gdv[$name])) :
$value = '<pre>'.print_r($gdv[$name],true).'</pre>';
else :
$value = (PHP_VERSION_ID >= 80000)? get_debug_type($gdv[$name]) : get_type($gdv[$name]);
endif;
$name_value_table[] = '<tr><td>$'.$name.'<td>'.$value;
endforeach;
return '<table border=1>'.implode("\n", $name_value_table).'</table>';
} // (f) get_value_table()
$_1 = 'a';
$_2 = 'b';
$_3 = [1,2];
$_4 = false;
$_5 = null;
$name_array = ['_1','_2','_3','_4','_5','_6'];
$show_id = 1;
if ($show_id === 1) :
echo get_value_table($name_array, get_defined_vars());
endif;
/*
如果 $show_id === 1,只会显示以下内容。
$_1 a
$_2 b
$_3 Array
(
[0] => 1
[1] => 2
)
$_4 false
$_5 null
$_6 undefined
*/
$_2 = 'c';
if ($show_id === 2) :
echo get_value_table($name_array, get_defined_vars()); // $_2 c
endif;
# 如果 $show_id === 2,$_2 变为 "c"。
?>
一个小陷阱要注意
如果你关闭了 RegisterGlobals 和相关设置,然后使用 get_defined_vars(),你可能会看到如下内容
<?php
Array
(
[GLOBALS] => Array
(
[GLOBALS] => Array
*RECURSION*
[_POST] => Array()
[_GET] => Array()
[_COOKIE] => Array()
[_FILES] => Array()
)
[_POST] => Array()
[_GET] => Array()
[_COOKIE] => Array()
[_FILES] => Array()
)
?>
注意 $_SERVER 不存在。 似乎只有在某个地方使用它时,php 才会加载超级全局变量 $_SERVER。你可以这样做
<?php
print '<pre>' . htmlspecialchars(print_r(get_defined_vars(), true)) . '</pre>';
print '<pre>' . htmlspecialchars(print_r($_SERVER, true)) . '</pre>';
?>
然后 $_SERVER 会出现在两个列表中。 我想这并不算是一个陷阱,因为无论哪种方式都不会发生什么不好的事情,但这仍然是一个有趣的发现。
由于 get_defined_vars() 只获取你在调用函数时定义的变量,所以有一种简单的方法可以获取当前作用域中定义的变量。
<?php
// PHP 脚本的最顶端
$vars = get_defined_vars();
// 现在执行你的操作
$foo = 'foo';
$bar = 'bar';
// 获取当前作用域中定义的所有变量
$vars = array_diff(get_defined_vars(),$vars);
echo '<pre>';
print_r($vars);
echo '</pre>';
?>
在感兴趣的位置插入这段代码;将不需要的第一个维度变量的键添加到 array_diff_key 的第二个参数中
<?php
echo '<pre>defined_except '; var_dump (array_diff_key (get_defined_vars(), ['GLOBALS'=>0,'_SERVER'=>0])); echo ' '.basename(__FILE__).':'.__LINE__.'</pre>'; #die;
?>
引用变量是通过引用返回的(在 PHP 5.5.11 上测试)
<?php
$a = null;
$b = &$a;
get_defined_vars()['b'] = 4;
var_dump($b); // int(4)
?>
我偶尔会用它作为一种 hack 将参数转换为数组,在需要简洁的情况下(处理遗留代码等)。
但是,在对象上下文中,它也会拉入 $this。
确保将其 unset。 如果你将它映射到属性或数组,那么设置键 this 不会出错。
我建议使用一个包装函数,它将 this 从结果中剥离。
get_defined_vars() 返回所有变量 - 局部定义的变量和全局变量(实际上只是超级全局变量)。如果你只需要局部变量 - 例如你需要从一个文件获取变量 - 比如 config.php,并且你不想让一个缺失的值被一个在其他地方定义的全局变量替换。
<?php
/**
* 过滤掉 get_defined_vars() 中的全局变量,以获取仅局部变量
* @param array $localVars 将 get_defined_vars() 传递到这里
*
* @return array 仅局部变量
*/
function removeGlobals(array $localVars) {
return array_diff_key($localVars, $GLOBALS);
}
define('CONFIG_FILE_PATH', '/path/to/config.php');
function readConfig() {
require CONFIG_FILE_PATH;
$config = removeGlobals(get_defined_vars());
return $config;
}
?>
这里的一些评论指出,这个函数不会返回引用。 但是它确实返回名称,而名称是“引用”。
我不建议这里将它转换为引用的建议。
相反...
public function x($a, $b, $c) {
foreach(array_keys(get_defined_vars()) as $key)
if($key !== 'this')
$this->y(${$key});
}
public function y(&$input) {
$input++;
}
除了 ${} 之外,你也可以使用 $$。
我在我的时代做过一些怪异的事情来编写非常通用的代码,但我从未做过像上面那样的操作。 它甚至可能不起作用(但应该起作用,因为它与 $a[$key] 没有区别)。
你也可以使用 `$$key++`,但我从未见过这种代码,除了那些糟糕透顶的代码(在没有益处的情况下使用动态变量)。
如果你要这样做,那么要仔细检查一下。
这是一个生成用于显示或电子邮件的调试报告的函数
使用 `get_defined_vars`。非常适合在没有
依赖用户输入的情况下获取详细快照。
<?php
function generateDebugReport($method,$defined_vars,$email="undefined"){
// 用于创建用于显示或电子邮件的调试报告的函数。
// 使用方法:generateDebugReport(方法,get_defined_vars(),电子邮件[可选]);
// 其中方法为 "浏览器" 或 "电子邮件"。
// 为 'get_defined_vars' 返回的键创建忽略列表。
// 例如,HTTP_POST_VARS、HTTP_GET_VARS 和其他变量是
// 多余的(与 _POST、_GET 相同)
// 还包括你出于安全原因想要忽略的变量 - 例如 PHPSESSID。
$ignorelist=array("HTTP_POST_VARS","HTTP_GET_VARS",
"HTTP_COOKIE_VARS","HTTP_SERVER_VARS",
"HTTP_ENV_VARS","HTTP_SESSION_VARS",
"_ENV","PHPSESSID","SESS_DBUSER",
"SESS_DBPASS","HTTP_COOKIE");
$timestamp=date("m/d/y h:m:s");
$message="Debug report created $timestamp\n";
// 获取最新的 SQL 错误,其中 $link 是 mysql_connect 的资源标识符
// 用于 mysql_connect。根据你的数据库或抽象设置进行注释或修改。
global $link;
$sql_error=mysql_error($link);
if($sql_error){
$message.="\nMysql Messages:\n".mysql_error($link);
}
// 结束 MySQL
// 可以在这里使用递归函数。你明白了 ;-)
foreach($defined_vars as $key=>$val){
if(is_array($val) && !in_array($key,$ignorelist) && count($val) > 0){
$message.="\n$key array (key=value):\n";
foreach($val as $subkey=>$subval){
if(!in_array($subkey,$ignorelist) && !is_array($subval)){
$message.=$subkey." = ".$subval."\n";
}
elseif(!in_array($subkey,$ignorelist) && is_array($subval)){
foreach($subval as $subsubkey=>$subsubval){
if(!in_array($subsubkey,$ignorelist)){
$message.=$subsubkey." = ".$subsubval."\n";
}
}
}
}
}
elseif(!is_array($val) && !in_array($key,$ignorelist) && $val){
$message.="\nVariable ".$key." = ".$val."\n";
}
}
if($method=="browser"){
echo nl2br($message);
}
elseif($method=="email"){
if($email=="undefined"){
$email=$_SERVER["SERVER_ADMIN"];
}
$mresult=mail($email,"Debug Report for ".$_ENV["HOSTNAME"]."",$message);
if($mresult==1){
echo "Debug Report sent successfully.\n";
}
else{
echo "Failed to send Debug Report.\n";
}
}
}
?>
对于所有不知道如何使用此函数的人,请将此代码复制到你的计算机并运行。
注意:你需要了解什么是超级全局变量......
<?php
$A = 5 ;
$B = 10 ;
$C = 15 ;
$D = 20 ;
$F = get_defined_vars($A);
var_dump($F); // 不要使用 echo。它会显示错误
?>
结果:将显示所有超级全局变量的状态以及你定义的
变量及其值
将 `get_defined_vars` 对象转换为 XML 的简单例程。
<?php
function obj2xml($v, $indent='') {
while (list($key, $val) = each($v)) {
if ($key == '__attr') continue;
// 检查 __attr
if (is_object($val->__attr)) {
while (list($key2, $val2) = each($val->__attr)) {
$attr .= " $key2=\"$val2\"";
}
}
else $attr = '';
if (is_array($val) || is_object($val)) {
print("$indent<$key$attr>\n");
obj2xml($val, $indent.' ');
print("$indent</$key>\n");
}
else print("$indent<$key$attr>$val</$key>\n");
}
}
// 例如对象
$x->name->first = "John";
$x->name->last = "Smith";
$x->arr['Fruit'] = 'Bannana';
$x->arr['Veg'] = 'Carrot';
$y->customer = $x;
$y->customer->__attr->id='176C4';
$z = get_defined_vars();
obj2xml($z['y']);
?>
将输出
<customer id="176C4">
<name>
<first>John</first>
<last>Smith</last>
</name>
<arr>
<Fruit>Bannana</Fruit>
<Veg>Carrot</Veg>
</arr>
</customer>
这是一个非常简单的调试函数。它远非完美,但我发现它非常方便。它在一行新行上输出变量值和变量名称。问题是如果变量值相同,它会回显所有变量及其名称。在调试时这没什么大不了的,并且节省了在回显变量时编写 HTML 和变量名称的麻烦。(ev=回显变量)。在函数内使用 `get_defined_vars()` 会将变量名称重命名为函数变量,因此对于调试不太有用。当然,你需要访问 `$GLOBALS` 数组。
<?
function ev($variable){
foreach($GLOBALS as $key => $value){
if($variable===$value){
echo '<p>'.$key.' - '.$value.'</p>';
}
}
}
$a=0;
ev($a);
$b=0;
ev($b);
$c=0;
ev($c);
?>
将输出
a - 0
a - 0
b - 0
a - 0
b - 0
c - 0
需要注意的是,`get_defined_vars()` 不会返回一组变量引用(正如我所希望的)。例如
<?php
// 定义一个变量
$my_var = "foo";
// 获取定义的变量列表
$defined_vars = get_defined_vars();
// 现在尝试通过返回的数组更改值
$defined_vars["my_var"] = "bar";
echo $my_var, "\n";
?>
将输出 "foo"(原始值)。如果 `get_defined_vars()` 有一个可选参数可以将它们作为引用,那就太好了,但我认为这是一个相当专业的要求。你可以自己用类似下面的代码来实现(不太方便)
<?php
$defined_vars = array();
$var_names = array_keys(get_defined_vars());
foreach ($var_names as $var_name)
{
$defined_vars[$var_name] =& $$var_name;
}
?>
我之前在这里发帖讨论过 `this` 在 `get_defined_vars` 中的问题。
事实证明,它并不总是存在,但在某些情况下它会莫名其妙地出现。
php -r '
class Test {
public function a() {var_dump(array_keys(get_defined_vars()));$a = 123;}
public function b() {var_dump(array_keys(get_defined_vars()));$this;}
}
$t = new Test();
$t->a();
$t->b();
'
array()
array('this')
这种情况在 PHP 7.2 中不会发生,但在 PHP 5.6 中会发生。
`get_defined_vars()` 返回所有变量(在当前作用域中),如果只想获取所有你的变量,而不是 PHP 的超级全局变量,该怎么办呢?
<?php
var_export(array_diff(get_defined_vars(), array(array())));
?>
例如...
<?php
$TOP_LEVEL_VAR=1;
var_export(array_diff(get_defined_vars(), array(array())));
?>
输出(register_globals 关闭)应该是...
array (
'TOP_LEVEL_VAR' => 1,
)
...它完美地消除了所有超级全局变量,而无需我指定它们!(注意,如果 register_globals 打开,输出将包括这些全局变量,然后是 TOP_LEVEL_VAR)。
这是一个函数...(这是我所能做的最好的 {我不能在 `get_user_defined_vars()` 中调用 `get_defined_vars()`,因为作用域问题})。
<?php
header('Content-type: text/plain');
$TOP_LEVEL_VAR=1;
echo 'register_globals(';
echo ini_get('register_globals');
echo ') '.phpversion()."\n";
var_export(get_user_defined_vars(get_defined_vars()));
function get_user_defined_vars($vars) {
return array_diff($vars, array(array()));
}
?>
注意,最初我有一个包含我想从 `get_defined_vars()` 数组中删除的超级全局变量的数组,然后我注意到即使是一个空的双数组,`array(array())`,也能让我得到正确的结果。奇怪。
这在 PHP 5.2.9 上测试过。
注意,这只会返回你使用过的内容。参见 http://bugs.php.net/bug.php?id=52110 。因此,不要期望它包含 `$this` 条目,除非你将 `$this` 赋值为返回 `$this`。
请注意,`function_get_vars` 只返回在调用 `function_get_vars` 函数之前的点定义的变量,它不会为你扫描整个函数,只扫描调用它之前的行。