2024 PHP 日本研討會

變數的變數

有時,能夠使用變數的變數名稱會很方便。也就是說,一個可以動態設定和使用的變數名稱。一般的變數是用以下語句設定的:

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

?>

以上範例會輸出:


I am bar.
I am bar.
I am bar.
I am r.

警告

請注意,在函式或類別方法中,變數變數不能與 PHP 的超全域陣列一起使用。變數 $this 也是一個不能被動態引用的特殊變數。

新增註解

使用者貢獻的註解 10 則註解

userb at exampleb dot org
14 年前
<?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

//... 依此類推 ...//

?>
sebastopolys at gmail dot com
2 年前
此外,可以使用關聯式陣列來確保在函式(或類別/未測試)內可使用的變數名稱。

這種方式,可變變數的功能對於驗證變數;定義、輸出和僅在接收關聯式陣列作為參數的函式內管理變數很有用。
一個關聯式陣列
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

// 在函式內宣告變數之前,我們不知道變數的名稱
}
?>
匿名
19 年前
特別值得一提的是,如果變數名稱遵循某種「範本」,則可以像這樣參考它們

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

這點從其他人留下的註釋可以看出,但沒有明確說明。
marcin dot dzdza at gmail dot com
5 年前
可變變數名稱的功能很方便,但應盡可能避免使用。現代 IDE 軟體無法正確解析此類變數,一般的搜尋/取代功能也會失效。這有點像魔術 :) 這真的會讓程式碼重構變得困難。想像一下,您想將變數 $username 重命名為 $userName,並嘗試透過搜尋 "$userName" 來找出程式碼中所有 $username 的出現位置。您可能會很容易忽略
$a = 'username';
echo $$a;
jefrey.sobreira [at] gmail [dot] com
9 年前
如果您想在可變變數名稱的一部分(而非整個名稱)中使用變數值,可以參考以下做法

<?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
?>
Sinured
17 年前
我發現一件有趣的事情:您可以連接變數並使用空格。連接常數和函式呼叫也是可行的。

<?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 無法妥善處理這種情況。
herebepost (ta at ta) [iwonderr] gmail dot com
8 年前
雖然在日常 PHP 程式設計中並不相關,但在可變變數的錢字號之間插入空白和註釋似乎是可行的。所有三種註釋樣式都有效。這些資訊在編寫解析器、分詞器或其他操作 PHP 語法的工具時會變得相關。

<?php

$foo
= 'bar';
$

/*
我完全合法,可以編譯,不會出現任何通知或錯誤,可以作為可變變數使用。
*/
$foo = 'magic';

echo
$bar; // 輸出 magic。

?>

使用 PHP 版本 5.6.19 測試的行為
mason
14 年前
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!'
?>
nils dot rocine at gmail dot com
12 年前
使用變數(字串值為類別名稱)來實例化類別的陷阱

假設您想透過變數(字串值為類別名稱)來實例化一個類別。

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

?>
Nathan Hammond
16 年前
以下是用動態方式參考超全域變數時可能遇到的情況。它是否有效似乎取決於目前的範圍。

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

?>
To Top