注意 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;
}
?>
一个小陷阱需要注意
如果你关闭了 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 不在那里。似乎只有在某个地方使用了超级全局变量 $_SERVER,php 才会加载它。你可以这样做
<?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>';
?>
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 数组
(
[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"。
?>
将此插入感兴趣的位置;将不需要的一维变量的键添加到 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)
?>
我偶尔会将此用作一种技巧,将参数转换为数组,在需要简洁性时使用(处理遗留代码等)。
但是,在对象上下文中,它还会引入 $this
。
确保将其取消设置。如果将其映射到属性或数组,则设置键 this
不会出错。
我建议使用一个包装函数,从结果中去除 $this
。
这是一个函数,用于生成用于显示或电子邮件的调试报告
使用 get_defined_vars
。非常适合获取详细的快照,而无需
依赖于用户输入。
<?php
function generateDebugReport($method,$defined_vars,$email="undefined"){
// 用于创建显示或发送电子邮件的调试报告。
// 用法:generateDebugReport(method,get_defined_vars(),email[可选]);
// method 为 "browser" 或 "email"。
// 创建一个忽略列表,用于 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="创建调试报告于 $timestamp\n";
// 获取最后的 SQL 错误,其中 $link 是 mysql_connect 的资源标识符
// 根据您的数据库或抽象设置注释或修改。
global $link;
$sql_error=mysql_error($link);
if($sql_error){
$message.="\nMysql 错误信息:\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 数组 (键=值):\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.="\n变量 ".$key." = ".$val."\n";
}
}
if($method=="browser"){
echo nl2br($message);
}
elseif($method=="email"){
if($email=="undefined"){
$email=$_SERVER["SERVER_ADMIN"];
}
$mresult=mail($email,"来自 ".$_ENV["HOSTNAME"]." 的调试报告",$message);
if($mresult==1){
echo "调试报告已成功发送。\n";
}
else{
echo "发送调试报告失败。\n";
}
}
}
?>
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 对象转换为 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']);
?>
输出结果为
<客户 id="176C4">
<姓名>
<名>John</名>
<姓>Smith</姓>
</姓名>
<数组>
<水果>香蕉</水果>
<蔬菜>胡萝卜</蔬菜>
</数组>
</客户>
对于所有不知道如何使用此函数的人。复制这段代码并在您的计算机上运行。
注意:您需要了解什么是超级全局变量……
<?php
$A = 5 ;
$B = 10 ;
$C = 15 ;
$D = 20 ;
$F = get_defined_vars($A);
var_dump($F); //不要使用echo,否则会报错
?>
结果:将显示所有超级全局变量的状态以及您定义的
变量及其值
这是一个非常简单的调试函数。它远非完美,但我发现它非常方便。它在新的一行输出变量值和变量名。问题是,如果变量共享相同的值,它将回显任何变量及其名称。在调试时没什么大不了的,并且在回显变量时节省了编写HTML和变量名的麻烦。(ev=echo variable)。在函数内使用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条目。