請注意 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
函式 filter_eval_vars(陣列 $vars): 陣列
{
foreach ($vars as $key => $value) {
if ($key[0] === '_' && $key[1] === '_') {
unset($vars[$key]);
}
}
return $vars;
}
?>
需要注意的一個小陷阱
如果您關閉 RegisterGlobals 和相關設定,然後使用 get_defined_vars(),您可能會看到如下內容:
<?php
陣列
(
[GLOBALS] => 陣列
(
[GLOBALS] => 陣列
*遞迴*
[_POST] => 陣列()
[_GET] => 陣列()
[_COOKIE] => 陣列()
[_FILES] => 陣列()
)
[_POST] => 陣列()
[_GET] => 陣列()
[_COOKIE] => 陣列()
[_FILES] => 陣列()
)
?>
請注意,$_SERVER 並不存在。似乎 PHP 只有在某處使用到 $_SERVER 超全域變數時才會載入它。您可以這樣做:
<?php
echo '<pre>' . htmlspecialchars( print_r(get_defined_vars(), true)) . '</pre>';
echo '<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
// Set "get_defined_vars()" to 2nd argument.
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;
/*
if $show_id === 1, only shows below.
$_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;
# if $show_id === 2, $_2 turns "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"){
// Function to create a debug report to display or email.
// Usage: generateDebugReport(method,get_defined_vars(),email[optional]);
// Where method is "browser" or "email".
// Create an ignore list for keys returned by 'get_defined_vars'.
// For example, HTTP_POST_VARS, HTTP_GET_VARS and others are
// redundant (same as _POST, _GET)
// Also include vars you want ignored for security reasons - i.e. 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";
// Get the last SQL error for good measure, where $link is the resource identifier
// for mysql_connect. Comment out or modify for your database or abstraction setup.
global $link;
$sql_error=mysql_error($link);
if($sql_error){
$message.="\nMysql Messages:\n".mysql_error($link);
}
// End MySQL
// Could use a recursive function here. You get the idea ;-)
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";
}
}
}
?>
`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
函式 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>
提供給所有不知道如何使用這個函式的人。複製並在你的電腦上執行這段程式碼。
注意:你需要知道什麼是超全域變數.....
<?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 賦值為 return $this。