<?php
//您可以使用更多個錢字號
$Bar = "a";
$Foo = "Bar";
$World = "Foo";
$Hello = "World";
$a = "Hello";
$a; //回傳 Hello
$$a; //回傳 World
$$$a; //回傳 Foo
$$$$a; //回傳 Bar
$$$$$a; //回傳 a
$$$$$$a; //回傳 Hello
$$$$$$$a; //回傳 World
//... 依此類推 ...//
?>
有時,能夠使用變數的變數名稱會很方便。也就是說,一個可以動態設定和使用的變數名稱。一般的變數是用以下語句設定的:
<?php
$a = 'hello';
?>
變數的變數會取得一個變數的值,並將其視為另一個變數的名稱。在上面的例子中,*hello* 可以透過使用兩個錢字號來作為變數的名稱。例如:
<?php
$$a = 'world';
?>
此時,已經定義了兩個變數並儲存在 PHP 符號樹中:內容為 "hello" 的 $a 和內容為 "world" 的 $hello。因此,以下語句
<?php
echo "$a {$$a}";
?>
會產生與以下語句完全相同的輸出
<?php
echo "$a $hello";
?>
也就是說,它們都會產生:hello world。
為了將可變變數與陣列一起使用,必須解決一個歧義問題。也就是說,如果剖析器看到 $$a[1],那麼它需要知道 $a[1] 是否應該被用作變數,或者是否需要 $$a 作為變數,然後從該變數取得 [1]
索引。解決這種歧義的語法是:第一種情況使用 ${$a[1]},第二種情況使用 ${$a}[1]。
類別屬性也可以使用變數屬性名稱來存取。變數屬性名稱將在進行呼叫的範圍內解析。例如,如果有一個表達式,例如 $foo->$bar,則會在局部範圍內檢查 $bar,並將其值用作 $foo 屬性的名稱。即使 $bar 是陣列存取,也是如此。
也可以使用大括號來清楚地界定屬性名稱。當存取包含陣列的屬性中的值、屬性名稱由多個部分組成,或者屬性名稱包含其他無效的字元(例如,來自 json_decode() 或 SimpleXML)時,它們最有用。
範例 #1 變數屬性範例
<?php
class foo {
var $bar = 'I am bar.';
var $arr = array('I am A.', 'I am B.', 'I am C.');
var $r = 'I am r.';
}
$foo = new foo();
$bar = 'bar';
$baz = array('foo', 'bar', 'baz', 'quux');
echo $foo->$bar . "\n";
echo $foo->{$baz[1]} . "\n";
$start = 'b';
$end = 'ar';
echo $foo->{$start . $end} . "\n";
$arr = 'arr';
echo $foo->{$arr[1]} . "\n";
?>
以上範例會輸出:
請注意,在函式或類別方法中,變數變數不能與 PHP 的超全域陣列一起使用。變數 $this
也是一個不能被動態引用的特殊變數。
<?php
//您可以使用更多個錢字號
$Bar = "a";
$Foo = "Bar";
$World = "Foo";
$Hello = "World";
$a = "Hello";
$a; //回傳 Hello
$$a; //回傳 World
$$$a; //回傳 Foo
$$$$a; //回傳 Bar
$$$$$a; //回傳 a
$$$$$$a; //回傳 Hello
$$$$$$$a; //回傳 World
//... 依此類推 ...//
?>
此外,可以使用關聯式陣列來確保在函式(或類別/未測試)內可使用的變數名稱。
這種方式,可變變數的功能對於驗證變數;定義、輸出和僅在接收關聯式陣列作為參數的函式內管理變數很有用。
一個關聯式陣列
array('index'=>'value','index'=>'value');
index = 要在函式內使用的變數的參考
value = 要在函式內使用的變數的名稱
<?php
$vars = ['id'=>'user_id','email'=>'user_email'];
validateVarsFunction($vars);
function validateVarsFunction($vars){
//$vars['id']=34; <- 無法運作
// 定義允許的變數
$user_id=21;
$user_email='email@mail.com';
echo $vars['id']; // 顯示變數名稱:user_id
echo ${$vars['id']}; // 顯示 21
echo 'Email: '.${$vars['email']}; // 顯示 email@mail.com
// 在函式內宣告變數之前,我們不知道變數的名稱
}
?>
特別值得一提的是,如果變數名稱遵循某種「範本」,則可以像這樣參考它們
<?php
// 給定以下變數...
$nameTypes = array("first", "last", "company");
$name_first = "John";
$name_last = "Doe";
$name_company = "PHP.net";
// 那麼這個迴圈...
foreach($nameTypes as $type)
print ${"name_$type"} . "\n";
// ...等同於以下 print 陳述式。
print "$name_first\n$name_last\n$name_company\n";
?>
這點從其他人留下的註釋可以看出,但沒有明確說明。
可變變數名稱的功能很方便,但應盡可能避免使用。現代 IDE 軟體無法正確解析此類變數,一般的搜尋/取代功能也會失效。這有點像魔術 :) 這真的會讓程式碼重構變得困難。想像一下,您想將變數 $username 重命名為 $userName,並嘗試透過搜尋 "$userName" 來找出程式碼中所有 $username 的出現位置。您可能會很容易忽略
$a = 'username';
echo $$a;
如果您想在可變變數名稱的一部分(而非整個名稱)中使用變數值,可以參考以下做法
<?php
$price_for_monday = 10;
$price_for_tuesday = 20;
$price_for_wednesday = 30;
$today = 'tuesday';
$price_for_today = ${ 'price_for_' . $today};
echo $price_for_today; // 將會回傳 20
?>
我發現一件有趣的事情:您可以連接變數並使用空格。連接常數和函式呼叫也是可行的。
<?php
define('ONE', 1);
function one() {
return 1;
}
$one = 1;
${"foo$one"} = 'foo';
echo $foo1; // 顯示 foo
${'foo' . ONE} = 'bar';
echo $foo1; // 顯示 bar
${'foo' . one()} = 'baz';
echo $foo1; // 顯示 baz
?>
這種語法不適用於函式
<?php
$foo = 'info';
{"php$foo"}(); // 解析錯誤
// 你必須這樣做:
$func = "php$foo";
$func();
?>
注意:不要省略大括號內字串的引號,PHP 無法妥善處理這種情況。
雖然在日常 PHP 程式設計中並不相關,但在可變變數的錢字號之間插入空白和註釋似乎是可行的。所有三種註釋樣式都有效。這些資訊在編寫解析器、分詞器或其他操作 PHP 語法的工具時會變得相關。
<?php
$foo = 'bar';
$
/*
我完全合法,可以編譯,不會出現任何通知或錯誤,可以作為可變變數使用。
*/
$foo = 'magic';
echo $bar; // 輸出 magic。
?>
使用 PHP 版本 5.6.19 測試的行為
PHP 實際上至少從 5.2 版開始就支援使用變數類別名稱來呼叫類別的新實例。
<?php
class Foo {
public function hello() {
echo 'Hello world!';
}
}
$my_foo = 'Foo';
$a = new $my_foo();
$a->hello(); // 顯示 'Hello world!'
?>
此外,您可以使用變數類別名稱來存取靜態方法和屬性,但這僅限於 PHP 5.3 以後的版本。
<?php
class Foo {
public static function hello() {
echo 'Hello world!';
}
}
$my_foo = 'Foo';
$my_foo::hello(); // 顯示 'Hello world!'
?>
使用變數(字串值為類別名稱)來實例化類別的陷阱
假設您想透過變數(字串值為類別名稱)來實例化一個類別。
<?php
class Foo
{
public function __construct()
{
echo "I'm a real class!" . PHP_EOL;
}
}
$class = 'Foo';
$instance = new $class;
?>
以上程式碼可以正常運作,**除非**您在一個(已定義的)命名空間內。在命名空間內,您必須提供完整的命名空間識別符號,如下所示。即使實例化發生在相同的命名空間內,也必須如此。一般情況下(不透過變數),實例化類別不需要命名空間。這似乎建立了一個模式:如果您正在使用命名空間,並且您在字串中有一個類別名稱,則您必須提供該類別的命名空間,PHP 引擎才能正確解析(其他情況:`class_exists()`、`interface_exists()` 等)。
<?php
namespace MyNamespace;
class Foo
{
public function __construct()
{
echo "I'm a real class!" . PHP_EOL;
}
}
$class = 'MyNamespace\Foo';
$instance = new $class;
?>
以下是用動態方式參考超全域變數時可能遇到的情況。它是否有效似乎取決於目前的範圍。
<?php
$_POST['asdf'] = 'something';
function test() {
// NULL -- 與最初預期的不同
$string = '_POST';
var_dump(${$string});
// 如預期般運作
var_dump(${'_POST'});
// 如預期般運作
global ${$string};
var_dump(${$string});
}
// 如預期般運作
$string = '_POST';
var_dump(${$string});
test();
?>