一、基礎(chǔ)
PHP 中的變量用一個(gè)美元符號(hào)后面跟變量名來(lái)表示。變量名是區(qū)分大小寫(xiě)的。變量名與 PHP 中其它的標(biāo)簽一樣遵循相同的規(guī)則。一個(gè)有效的變量名由字母或者下劃線開(kāi)頭,后面跟上任意數(shù)量的字母,數(shù)字,或者下劃線。 按照正常的正則表達(dá)式,它將被表述為:’^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$’。
注意:
- 在此所說(shuō)的字母是 a-z,A-Z,以及 ASCII 字符從 128 到 255(0x80-0xff)。
- $this 是一個(gè)特殊的變量,它不能被賦值。 PHP 7.1.0 之前,間接賦值(例如通過(guò)使用可變變量)是可能的。
<?php $var = 'Bob'; $Var = 'Joe'; echo "$var, $Var"; // 輸出 "Bob, Joe" $4site = 'not yet'; // 非法變量名;以數(shù)字開(kāi)頭 $_4site = 'not yet'; // 合法變量名;以下劃線開(kāi)頭 $i站點(diǎn)is = 'mansikka'; // 合法變量名;可以用中文 ?>
變量默認(rèn)總是傳值賦值。那也就是說(shuō),當(dāng)將一個(gè)表達(dá)式的值賦予一個(gè)變量時(shí),整個(gè)原始表達(dá)式的值被賦值到目標(biāo)變量。例如,當(dāng)一個(gè)變量的值賦予另外一個(gè)變量時(shí),改變其中一個(gè)變量的值,將不會(huì)影響到另外一個(gè)變量。
PHP 也提供了另外一種方式給變量賦值:引用賦值。表示新的變量簡(jiǎn)單的引用(換言之,“成為其別名” 或者 “指向”)了原始變量,改動(dòng)新的變量將影響到原始變量,反之亦然。使用引用賦值,簡(jiǎn)單地將一個(gè) & 符號(hào)加到將要賦值的變量前(源變量)。例如,下列代碼片斷將輸出“My name is Bob”兩次:
<?php $foo = 'Bob'; // 將 'Bob' 賦給 $foo $bar = &$foo; // 通過(guò) $bar 引用 $foo $bar = "My name is $bar"; // 修改 $bar 變量 echo $bar; echo $foo; // $foo 的值也被修改 ?>
只有有名字的變量才可以引用賦值。
<?php $foo = 25; $bar = &$foo; // 合法的賦值 $bar = &(24 * 7); // 非法; 引用沒(méi)有名字的表達(dá)式 function test() { return 25; } $bar = &test(); // 非法 ?>
雖然在 PHP 中并不需要初始化變量,但對(duì)變量進(jìn)行初始化是個(gè)好習(xí)慣。未初始化的變量具有其類(lèi)型的默認(rèn)值 – 布爾類(lèi)型的變量默認(rèn)值是 false,整形和浮點(diǎn)型變量默認(rèn)值是零,字符串型變量(例如用于 echo 中)默認(rèn)值是空字符串以及數(shù)組變量的默認(rèn)值是空數(shù)組。
示例 :未初始化變量的默認(rèn)值
<?php // 未設(shè)置和未引用(不使用上下文)的變量;輸出 NULL var_dump($unset_var); // Boolean 用法;輸出 'false' (See ternary operators for more on this syntax) echo $unset_bool ? "true\n" : "false\n"; // String 用法;輸出 'string(3) "abc"' $unset_str .= 'abc'; var_dump($unset_str); // Integer 用法;輸出 'int(25)' $unset_int += 25; // 0 + 25 => 25 var_dump($unset_int); // Float 用法;輸出 'float(1.25)' $unset_float += 1.25; var_dump($unset_float); // Array 用法;輸出 array(1) { [3]=> string(3) "def" } $unset_arr[3] = "def"; // array() + array(3 => "def") => array(3 => "def") var_dump($unset_arr); // Object 用法:創(chuàng)建新 stdClass 對(duì)象 (see http://www.php.net/manual/en/reserved.classes.php) // Outputs: object(stdClass)#1 (1) { ["foo"]=> string(3) "bar" } $unset_obj->foo = 'bar'; var_dump($unset_obj); ?>
依賴(lài)未初始化變量的默認(rèn)值在某些情況下會(huì)有問(wèn)題,例如把一個(gè)文件包含到另一個(gè)之中時(shí)碰上相同的變量名。 使用未初始化的變量會(huì)發(fā)出 E_WARNING (在 PHP 8.0.0 之前是 E_NOTICE) 錯(cuò)誤,但是在向一個(gè)未初始化的數(shù)組附加單元時(shí)不會(huì)。isset() 語(yǔ)言結(jié)構(gòu)可以用來(lái)檢測(cè)一個(gè)變量是否已被初始化。
二、預(yù)定義變量
PHP 提供了大量的預(yù)定義變量,由于許多變量依賴(lài)于運(yùn)行的服務(wù)器的版本和設(shè)置,及其它因素,所以并沒(méi)有詳細(xì)說(shuō)明。一些預(yù)定義變量在 PHP 以命令行形式運(yùn)行時(shí)并不生效。
PHP 提供了一套附加的預(yù)定數(shù)組,這些數(shù)組變量包含了來(lái)自 web 服務(wù)器(如果可用),運(yùn)行環(huán)境和用戶輸入的數(shù)據(jù)。這些數(shù)組非常特別,它們?cè)谌址秶鷥?nèi)自動(dòng)生效,例如,在任何范圍內(nèi)自動(dòng)生效。因此通常被稱(chēng)為自動(dòng)全局變量(autoglobals)或者超全局變量(superglobals)。(PHP 中沒(méi)有用戶自定義超全局變量的機(jī)制。)
注意:
- 超級(jí)全局變量不能被用作函數(shù)或類(lèi)方法中的可變變量。
- 如果某些 variables_order 中的變量沒(méi)有設(shè)定,它們的對(duì)應(yīng)的 PHP 預(yù)定義數(shù)組也是空的。
三、變量范圍
變量的范圍即它定義的上下文背景(也就是它的生效范圍)。大部分的 PHP 變量只有一個(gè)單獨(dú)的范圍。這個(gè)單獨(dú)的范圍跨度同樣包含了 include 和 require 引入的文件。例如:
<?php $a = 1; include 'b.inc'; ?>
這里變量 $a 將會(huì)在包含文件 b.inc 中生效。但是,在用戶自定義函數(shù)中,一個(gè)局部函數(shù)范圍將被引入。任何用于函數(shù)內(nèi)部的變量按缺省情況將被限制在局部函數(shù)范圍內(nèi)。例如:
<?php $a = 1; /* global scope */ function Test() { echo $a; /* reference to local scope variable */ } Test(); ?>
這個(gè)腳本會(huì)生成未定義變量 E_WARNING(PHP 8.0.0 之前是 E_NOTICE)診斷提示。然而,如果 display_errors INI 設(shè)置為隱藏這類(lèi)診斷提示,然后在任何情況下都不會(huì)有輸出,這是因?yàn)?echo 語(yǔ)句引用了一個(gè)局部版本的變量 $a,而且在這個(gè)范圍內(nèi),它并沒(méi)有被賦值。你可能注意到 PHP 的全局變量和 C 語(yǔ)言有一點(diǎn)點(diǎn)不同,在 C 語(yǔ)言中,全局變量在函數(shù)中自動(dòng)生效,除非被局部變量覆蓋。這可能引起一些問(wèn)題,有些人可能不小心就改變了一個(gè)全局變量。PHP 中全局變量在函數(shù)中使用時(shí)必須聲明為 global。
1、global關(guān)鍵字
首先,一個(gè)使用 global 的例子:
示例 :使用global
<?php $a = 1; $b = 2; function Sum() { global $a, $b; $b = $a + $b; } Sum(); echo $b; ?>
以上腳本的輸出將是“3”。在函數(shù)中聲明了全局變量 $a 和 $b 之后,對(duì)任一變量的所有引用都會(huì)指向其全局版本。對(duì)于一個(gè)函數(shù)能夠聲明的全局變量的最大個(gè)數(shù),PHP 沒(méi)有限制。
在全局范圍內(nèi)訪問(wèn)變量的第二個(gè)辦法,是用特殊的 PHP 自定義 $GLOBALS 數(shù)組。前面的例子可以寫(xiě)成:
示例:使用$GLOBALS替代global
<?php $a = 1; $b = 2; function Sum() { $GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b']; } Sum(); echo $b; ?>
$GLOBALS 是一個(gè)關(guān)聯(lián)數(shù)組,每一個(gè)變量為一個(gè)元素,鍵名對(duì)應(yīng)變量名,值對(duì)應(yīng)變量的內(nèi)容。$GLOBALS 之所以在全局范圍內(nèi)存在,是因?yàn)?$GLOBALS 是一個(gè)超全局變量。以下范例顯示了超全局變量的用處:
示例 :演示超全局變量和作用域的例子
<?php function test_superglobal() { echo $_POST['name']; } ?>
2、使用靜態(tài)變量
變量范圍的另一個(gè)重要特性是靜態(tài)變量(static variable)。靜態(tài)變量?jī)H在局部函數(shù)域中存在,但當(dāng)程序執(zhí)行離開(kāi)此作用域時(shí),其值并不丟失。看看下面的例子:
示例:需要靜態(tài)變量
<?php function Test() { $a = 0; echo $a; $a++; } ?>
本函數(shù)沒(méi)什么用處,因?yàn)槊看握{(diào)用時(shí)都會(huì)將 $a 的值設(shè)為 0 并輸出 0。將變量加一的 $a++ 沒(méi)有作用,因?yàn)橐坏┩顺霰竞瘮?shù)則變量 $a 就不存在了。要寫(xiě)一個(gè)不會(huì)丟失本次計(jì)數(shù)值的計(jì)數(shù)函數(shù),要將變量 $a 定義為靜態(tài)的:
示例:使用靜態(tài)變量
<?php function test() { static $a = 0; echo $a; $a++; } ?>
現(xiàn)在,變量 $a 僅在第一次調(diào)用 test() 函數(shù)時(shí)被初始化,之后每次調(diào)用 test() 函數(shù)都會(huì)輸出 $a 的值并加一。
靜態(tài)變量也提供了一種處理遞歸函數(shù)的方法。遞歸函數(shù)是一種調(diào)用自己的函數(shù)。寫(xiě)遞歸函數(shù)時(shí)要小心,因?yàn)榭赡軙?huì)無(wú)窮遞歸下去。必須確保有充分的方法來(lái)中止遞歸。以下這個(gè)簡(jiǎn)單的函數(shù)遞歸計(jì)數(shù)到 10,使用靜態(tài)變量 $count 來(lái)判斷何時(shí)停止:
示例:靜態(tài)變量與遞歸函數(shù)
<?php function test() { static $count = 0; $count++; echo $count; if ($count < 10) { test(); } $count--; } ?>
常量表達(dá)式的結(jié)果可以賦值給靜態(tài)變量,但是動(dòng)態(tài)表達(dá)式(比如函數(shù)調(diào)用)會(huì)導(dǎo)致解析錯(cuò)誤。
示例:聲明靜態(tài)變量
<?php function foo(){ static $int = 0; // 正確 static $int = 1+2; // 正確 static $int = sqrt(121); // 錯(cuò)誤(因?yàn)樗呛瘮?shù)) $int++; echo $int; } ?>
從 PHP 8.1.0 開(kāi)始,當(dāng)繼承(不是覆蓋)使用有靜態(tài)變量的方法時(shí),繼承的方法將會(huì)跟父級(jí)方法共享靜態(tài)變量。這意味著方法中的靜態(tài)變量現(xiàn)在跟靜態(tài)屬性有相同的行為。
示例:在繼承方法中使用靜態(tài)變量
<?php class Foo { public static function counter() { static $counter = 0; $counter++; return $counter; } } class Bar extends Foo {} var_dump(Foo::counter()); // int(1) var_dump(Foo::counter()); // int(2) var_dump(Bar::counter()); // int(3),PHP 8.1.0 之前 int(1) var_dump(Bar::counter()); // int(4),PHP 8.1.0 之前 int(2) ?>
注意:靜態(tài)聲明在編譯時(shí)解析。
3、全局和靜態(tài)變量引用
對(duì)于變量的 static 和 global 定義是以引用的方式實(shí)現(xiàn)的。例如,在一個(gè)函數(shù)域內(nèi)部用 global 語(yǔ)句導(dǎo)入的一個(gè)真正的全局變量實(shí)際上是建立了一個(gè)到全局變量的引用。這有可能導(dǎo)致預(yù)料之外的行為,如以下例子所演示的:
<?php function test_global_ref() { global $obj; $new = new stdClass; $obj = &$new; } function test_global_noref() { global $obj; $new = new stdClass; $obj = $new; } test_global_ref(); var_dump($obj); test_global_noref(); var_dump($obj); ?>
以上示例會(huì)輸出:
NULL object(stdClass)#1 (0) { }
類(lèi)似的行為也適用于 static 語(yǔ)句。引用并不是靜態(tài)地存儲(chǔ)的:
<?php function &get_instance_ref() { static $obj; echo 'Static object: '; var_dump($obj); if (!isset($obj)) { $new = new stdClass; // 將一個(gè)引用賦值給靜態(tài)變量 $obj = &$new; } if (!isset($obj->property)) { $obj->property = 1; } else { $obj->property++; } return $obj; } function &get_instance_noref() { static $obj; echo 'Static object: '; var_dump($obj); if (!isset($obj)) { $new = new stdClass; // 將一個(gè)對(duì)象賦值給靜態(tài)變量 $obj = $new; } if (!isset($obj->property)) { $obj->property = 1; } else { $obj->property++; } return $obj; } $obj1 = get_instance_ref(); $still_obj1 = get_instance_ref(); echo "\n"; $obj2 = get_instance_noref(); $still_obj2 = get_instance_noref(); ?>
以上示例會(huì)輸出:
Static object: NULL Static object: NULL Static object: NULL Static object: object(stdClass)#3 (1) { ["property"]=> int(1) }
上例演示了當(dāng)把一個(gè)引用賦值給一個(gè)靜態(tài)變量時(shí),第二次調(diào)用 &get_instance_ref() 函數(shù)時(shí)其值并沒(méi)有被記住。
四、可變變量
有時(shí)候使用可變變量名是很方便的。就是說(shuō),一個(gè)變量的變量名可以動(dòng)態(tài)的設(shè)置和使用。一個(gè)普通的變量通過(guò)聲明來(lái)設(shè)置,例如:
<?php $a = 'hello'; ?>
一個(gè)可變變量獲取了一個(gè)普通變量的值作為這個(gè)可變變量的變量名。在上面的例子中 hello 使用了兩個(gè)美元符號(hào)($)以后,就可以作為一個(gè)可變變量的變量了。例如:
<?php $$a = 'world'; ?>
這時(shí),兩個(gè)變量都被定義了:$a 的內(nèi)容是“hello”并且 $hello 的內(nèi)容是“world”。因此,以下語(yǔ)句:
<?php echo "$a {$$a}"; ?>
與以下語(yǔ)句輸出完全相同的結(jié)果:
<?php echo "$a $hello"; ?>
它們都會(huì)輸出:hello world。
要將可變變量用于數(shù)組,當(dāng)我們寫(xiě)下 $$a[1] 時(shí),解析器需要確定我們是想使用 $a[1] 作為一個(gè)變量,還是想使用 $$a 作為一個(gè)變量并取出該變量中索引為 [1] 的值。為了解決這個(gè)問(wèn)題,我們可以使用 {a[1]} 表示第一種情況,即將 $a[1] 視為一個(gè)變量;而使用 {a}[1] 則表示第二種情況,即將 $$a 視為一個(gè)變量,并獲取該變量中索引為 [1] 的值。
類(lèi)的屬性也可以通過(guò)可變屬性名來(lái)訪問(wèn)??勺儗傩悦麑⒃谠撜{(diào)用所處的范圍內(nèi)被解析。例如,對(duì)于 $foo->$bar 表達(dá)式,則會(huì)在本地范圍來(lái)解析 $bar 并且其值將被用于 $foo 的屬性名。對(duì)于 $bar 是數(shù)組單元時(shí)也是一樣。
也可使用花括號(hào)來(lái)給屬性名清晰定界。最有用是在屬性位于數(shù)組中,或者屬性名包含有多個(gè)部分或者屬性名包含有非法字符時(shí)(例如來(lái)自 json_decode() 或 SimpleXML)。
示例 :可變屬性示例
<?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"; ?>
以上示例會(huì)輸出:
I am bar. I am bar. I am bar. I am r.
注意,在 PHP 的函數(shù)和類(lèi)的方法中,超全局變量不能用作可變變量。$this 變量也是一個(gè)特殊變量,不能被動(dòng)態(tài)引用。
五、PHP 之外的變量
1、HTML 表單
當(dāng)一個(gè)表單(GET 和 POST)提交給 PHP 腳本時(shí),表單中的信息會(huì)自動(dòng)在腳本中可用。有幾個(gè)方法訪問(wèn)此信息,例如:
示例:一個(gè)簡(jiǎn)單的 HTML 表單
<form action="foo.php" method="POST"> Name: <input type="text" name="username"><br /> Email: <input type="text" name="email"><br /> <input type="submit" name="submit" value="Submit me!" /> </form>
只有兩種方法可以訪問(wèn) HTML 表單中的數(shù)據(jù)。 以下列出了當(dāng)前有效的方法:
示例:從一個(gè)簡(jiǎn)單的 POST HTML 表單訪問(wèn)數(shù)據(jù)
<?php echo $_POST['username']; echo $_REQUEST['username']; ?>
使用 GET 表單也類(lèi)似,只不過(guò)要用適當(dāng)?shù)?GET 預(yù)定義變量。GET 也適用于 QUERY_STRING(URL 中在“?”之后的信息)。因此,舉例說(shuō),http://www.example.com/test.php?id=3 包含有可用 $_GET[‘id’] 來(lái)訪問(wèn)的 GET 數(shù)據(jù)。
注意:
變量名中的點(diǎn)和空格被轉(zhuǎn)換成下劃線。例如 <input name=”a.b” /> 變成了 $_REQUEST[“a_b”]。
PHP 也懂得表單變量上下文中的數(shù)組。例如可以將相關(guān)的變量編成組,或者用此特性從多選輸入框中取得值。例如,將一個(gè)表單 POST 給自己并在提交時(shí)顯示數(shù)據(jù):
示例 :更復(fù)雜的表單變量
<?php if (isset($_POST['action']) && $_POST['action'] == 'submitted') { echo '<pre>'; print_r($_POST); echo '<a href="'. $_SERVER['PHP_SELF'] .'" rel="external nofollow" >Please try again</a>'; echo '</pre>'; } else { ?> <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post"> Name: <input type="text" name="personal[name]"><br /> Email: <input type="text" name="personal[email]"><br /> Beer: <br> <select multiple name="beer[]"> <option value="warthog">Warthog</option> <option value="guinness">Guinness</option> <option value="stuttgarter">Stuttgarter Schwabenbr</option> </select><br /> <input type="hidden" name="action" value="submitted" /> <input type="submit" name="submit" value="submit me!" /> </form> <?php } ?>
注意:如果一個(gè)外部變量名以有效的數(shù)組語(yǔ)法開(kāi)始,后續(xù)的字符將會(huì)被靜默忽略。例如,<input name=”foo[bar]baz”> 會(huì)被解析為 $_REQUEST[‘foo’][‘bar’]。
2、IMAGE SUBMIT變量名
當(dāng)提交表單時(shí),可以用一幅圖像代替標(biāo)準(zhǔn)的提交按鈕,用類(lèi)似這樣的標(biāo)記:
<input type="image" src="image.gif" name="sub" />
當(dāng)用戶點(diǎn)擊到圖像中的某處時(shí),相應(yīng)的表單會(huì)被傳送到服務(wù)器,并加上兩個(gè)變量 sub_x 和 sub_y。它們包含了用戶點(diǎn)擊圖像的坐標(biāo)。有經(jīng)驗(yàn)的用戶可能會(huì)注意到被瀏覽器發(fā)送的實(shí)際變量名包含的是一個(gè)點(diǎn)而不是下劃線(即 sub.x 和 sub.y),但 PHP 自動(dòng)將點(diǎn)轉(zhuǎn)換成了下劃線。
3、HTTP Cookies
PHP 透明地支持 RFC 6265定義中的 HTTP cookies。Cookies 是一種在遠(yuǎn)端瀏覽器端存儲(chǔ)數(shù)據(jù)并能追蹤或識(shí)別再次訪問(wèn)的用戶的機(jī)制。可以用 setcookie() 函數(shù)設(shè)定 cookies。Cookies 是 HTTP 信息頭中的一部分,因此 SetCookie 函數(shù)必須在向?yàn)g覽器發(fā)送任何輸出之前調(diào)用。對(duì)于 header() 函數(shù)也有同樣的限制。Cookie 數(shù)據(jù)會(huì)在相應(yīng)的 cookie 數(shù)據(jù)數(shù)組中可用,例如 $_COOKIE 和 $_REQUEST。
注意: 從 PHP 版本 7.2.34、7.3.23 和 7.4.11 開(kāi)始,出于安全考慮,傳入的 cookie 名稱(chēng)不再進(jìn)行 URL 解碼。
如果要將多個(gè)值賦給一個(gè) cookie 變量,必須將其賦成數(shù)組。例如:
<?php setcookie("MyCookie[foo]", 'Testing 1', time()+3600); setcookie("MyCookie[bar]", 'Testing 2', time()+3600); ?>
這將會(huì)建立兩個(gè)單獨(dú)的 cookie,盡管 MyCookie 在腳本中是一個(gè)單一的數(shù)組。如果想在僅僅一個(gè) cookie 中設(shè)定多個(gè)值,考慮先在值上使用 serialize() 或 explode()。
注意在瀏覽器中一個(gè) cookie 會(huì)替換掉上一個(gè)同名的 cookie,除非路徑或者域不同。因此對(duì)于購(gòu)物車(chē)程序可以保留一個(gè)計(jì)數(shù)器并一起傳遞,例如:
示例 : 一個(gè) setcookie() 的示例
<?php if (isset($_COOKIE['count'])) { $count = $_COOKIE['count'] + 1; } else { $count = 1; } setcookie('count', $count, time()+3600); setcookie("Cart[$count]", $item, time()+3600); ?>
4、變量名中的點(diǎn)
通常,PHP 不會(huì)改變傳遞給腳本中的變量名。然而應(yīng)該注意到點(diǎn)(句號(hào))不是 PHP 變量名中的合法字符。至于原因,看看:
<?php $varname.ext; /* 非法變量名 */ ?>
這時(shí),解析器看到是一個(gè)名為 $varname 的變量,后面跟著一個(gè)字符串連接運(yùn)算符,后面跟著一個(gè)裸字符串(即沒(méi)有加引號(hào)的字符串,且不匹配任何已知的健名或保留字)’ext’。很明顯這不是想要的結(jié)果。出于此原因,要注意 PHP 將會(huì)自動(dòng)將變量名中的點(diǎn)替換成下劃線。
5、確定變量類(lèi)型
因?yàn)?PHP 會(huì)判斷變量類(lèi)型并在需要時(shí)進(jìn)行轉(zhuǎn)換(通常情況下),因此在某一時(shí)刻給定的變量是何種類(lèi)型并不明顯。PHP 包括幾個(gè)函數(shù)可以判斷變量的類(lèi)型,例如:gettype(),is_array(),is_float(),is_int(),is_object() 和 is_string()。
由于 HTTP 是一種文本協(xié)議,大部分甚至所有以超全局?jǐn)?shù)組(如 $_POST 和 _GET)形式傳遞的內(nèi)容都將保留為字符串。PHP 不會(huì)嘗試將值轉(zhuǎn)換為特定類(lèi)型。在下面的示例中, GET)形式傳遞的內(nèi)容都將保留為字符串。PHP不會(huì)嘗試將值轉(zhuǎn)換為特定類(lèi)型。在下面的示例中,_GET[“var1”] 將包含字符串 “null”,而 $_GET[“var2”] 將包含字符串 “123”。
/index.php?var1=null&var2=123
6、更新日志