介紹 PHP 變數的用法,包含命名、指派 (assign)、宣告、變數範圍、預定義變數、全域變數、可變變數 (Variable variables)、POST、GET、REQUEST 和 COOKIE。

基礎

命名規則

變數是由錢號 ($) 開始,後面接著變數名稱,變數名稱有大小寫之分。變數名稱可以是英文字母、數字、底線和十六進位制為 0x7f - 0xff 的字元所組成,但是第一個字元不能是以數字開頭。正規表示法如下:

1
[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*

一些宣告範例如下:

1
2
3
4
5
6
7
$User = 'dindin';
$user = 'lala';
echo "$User, $user"; // 大小寫不同: dindin, lala

//$3m = '300cm'; // parse error, 不能是數字開頭
$_55 = 66; // 有效的
$lälä = '偽物'; // 有效的, 但必須存檔為 UTF-8, 否則會 Parse error, 'ä' ASCII 228

指派

變數的指派 (assign) 一般都是傳值的方式複製一份,而物件型別的變數除外。PHP 中可使用 (&) 符號讓其他型別的變數也能以傳址的方式指派,且只有變數才能使用 (&) 傳址。

1
2
3
4
5
6
$fool = 'dindin';
$bar = &$fool;
$bar = "My name is {$bar}.";
echo $bar; // My name is dindin.
echo $fool; // My name is dindin.
//$bar = &(24 * 7); // Parse error, 只有變數才能用 &

宣告

PHP 中的變數並不需要特別宣告,所有未宣告的變數都視為 NULL,變數的型別也會直接依據指派的值來決定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var_dump($unset_var);        // NULL

$unset_str .= '30cm';
var_dump($unset_str); // string(4) "30cm"

$unset_int += 178;
var_dump($unset_int); // int(178)

$unset_float += 169.99;
var_dump($unset_float); // float(169.99)

$unset_arr[3] = 9527;
var_dump($unset_arr); // array(1) { [3]=> int(9527) }

$unset_obj->fool = 'dindin';
var_dump($unset_obj); // object(stdClass)#1 (1) { ["fool"]=> string(6) "dindin" }

預定義變數

PHP 有許多預先定義的變數,要注意不要使用到這些名稱作為自己的變數,例如 $_GET$_POST…等。有哪些可使用的預定義變數和詳細說明,預計在之後的文章再做介紹。在 php.ini 中,有些設定與預定義變數有相關在此稍作說明。

register_globals

在 PHP 4 之前的版本,php.ini 設定中的 register_globals 預設為 on,所以可以直接使用 $id 取得 http//localhost/index.php?id=1id 值;PHP 4.2.0 之後 register_globals 則改為預設 off,就必須使用 $_GET['id'] 的方式來取得。不只是 GET 其他預定義變數例如 $_POST$_SERVER…等,也是要用這種方式取值。基於安全性問題,並不建議開啟 register_globals,一個攻擊範例如下:

1
2
3
4
5
6
// 網址列傳遞參數  ?_SESSION[id]=1
if(isset($_SESSION['id'])) {
echo "有登入"; // register_globals on, 判斷為 true
} else {
echo "沒登入"; // register_globals off, 判斷為 false
}

register_long_arrays

在 PHP 5 之前,是使用 $HTTP_*_VARS 的方式去取值,對應於 $_*,例如 $HTTP_GET_VARS$HTTP_POST_VARS…等,分別對應於 $_GET$_POST…等;在 PHP 5 之後,則改採用 $_* 的方式,若要使用舊的 $HTTP_*_VARS 的用法,可以將 register_long_arrays 設為 on

register_long_arrays 開啟時,$HTTP_*_VARS$_* 並不是相同的物件,修改其中一個,對應的另一個並不會一起被修改到。另外,$_* 是屬於超級全域變數 (Superglobals),但 $HTTP_*_VARS 並不是。超級全域變數之後再做介紹。理論上關閉此功能的效能會比較好。

如果新的程式要跑在舊版的環境下,可以在程式開頭加入以下的程式碼來達到向下相容:

1
2
3
4
5
6
7
8
if (!isset($_SERVER)) {
$_GET = &$HTTP_GET_VARS;
$_POST = &$HTTP_POST_VARS;
$_ENV = &$HTTP_ENV_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
$_COOKIE = &$HTTP_COOKIE_VARS;
$_REQUEST = array_merge($_GET, $_POST, $_COOKIE);
}

變數範圍

基礎

程式中我們可能會在不同的位置,用到相同的變數名稱,表示不同的東西,如果變數沒有範圍就可能會互相干擾。所以在程式中,會有不同的變數範圍,當程式離開這個範圍之後,屬於此範圍的變數就會清除。PHP 中分為全域變數和區域變數,基本上依照下述的原則:

  • function 中的變數為區域變數。
  • include 中的變數範圍與呼叫 include 的所在範圍相同。
  • 其他為全域變數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$num = 100;             // 全域變數
include("inc.php"); // int(100), inc.php 中的範圍為全域

function func() {
var_dump($num); // NULL, 在 func 範圍中沒有 $num
include("inc.php"); // NULL, inc.php 中的範圍為 func 的區域
}
func();

function func2() {
var_dump($num);
$num = 200; // 區域變數, 不會影響全域的 $num
}
func2(); // NULL, func2 又為另一個區域
func2(); // NULL, 上次進入 func2 的 $num 在離開函式就清除

var_dump($num); // int(100)
include("inc.php"); // int(100)

if (true) {
$num2 = 300; // if中仍為全域變數
}
var_dump($num2); // int(300)

for ($i = 0; $i < 5; $i++) {
$num3 = 400;
}
var_dump($i); // int(5)

inc.php

1
var_dump($num);

使用全域變數

要在區域範圍中使用全域變數可以使用以下兩種方法:

global

使用 global 關鍵字來宣告要使用的全域變數。以 global 關鍵字開頭,後面接著要使用的全域變數。

1
2
3
4
5
6
7
8
9
10
11
$num = 55;
$num2 = 66;

function func() {
var_dump($num); // NULL
global $num, $num2;
var_dump($num); // int(55)
$num = $num2;
}
func();
var_dump($num); // int(66)

$GLOBALS

使用預定義變數 $GLOBALS 來取得全域變數。$GLOBALS 是關聯式陣列,使用變數名稱做為索引值即可取得變數。

1
2
3
4
5
6
7
8
9
$num = 55;
$num2 = 66;

function func() {
var_dump($GLOBALS['num']); // int(55)
$GLOBALS['num'] = $GLOBALS['num2'];
}
func();
var_dump($num); // int(66)

static

另外有一個變數型態為靜態變數,使用 static 關鍵字來宣告,靜態變數會一直存在,直到程式結束。例如用在一個函式可能會重複被叫用,而想在每次叫用時使用同一個變數,就可以使用靜態變數。靜態變數有一些特性:

  • 靜態變數的指派必須要直接給一個定值,不可以是運算、變數、函式結果和建立物件等來源。
  • 重複的宣告靜態變數時,以最後一次宣告的值為初始值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$num = 100;

function func() {
return 10;
}

function counter() {
static $count = 0;
$count++;
var_dump($count);
unset($count); // $count = NULL, 但沒有真正的被清除
static $count = 55; // 以最後宣告為初始值

// 以下皆為 parse error, 必須直接給定值
// static $count = new stdClass;
// static $count = 1 + 1;
// static $count = $GLOBALS['num'];
// static $count = func();
}

counter(); // int(56)
counter(); // int(57)

常見問題

清除全域變數

在區域範圍中要清除全域變數時,要注意使用 unset() 函式和設為 null 的結果並不相同,範例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$num = 178;
function func() {
global $num;
unset ($num);
}

function func2() {
global $num;
$num = null;
}
func();
var_dump($num); // int(178)
func2();
var_dump($num); // NULL

區域範圍傳址

在區域範圍中使用 global 關鍵字與 $GLOBALS 陣列方式去接另一個變數的位址時,會有不同的結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$num = 100;
function func() {
global $num;
$num2 = 200;
$num = &$num2;
}
func();
var_dump($num); // int(100)

function func2() {
$num2 = 200;
$GLOBALS['num'] = &$num2;
}
func2();
var_dump($num); // int(200)

造成上面兩個問題的原因相同,global 關鍵字的行為可以想成:建立一個區域變數,來源為全域變數的位址。例如,global $num; 相當於 $num = &$GLOBALS['num'];

可變變數

基礎

可變變數 (Variable variables,或變數的變數),意思是可以使用變動的內容來轉換為另一個變數的名稱使用,用法規則如下:

  • 使用錢號 ($) 加上變數、常數或函式回傳值等來轉換,可以多重使用。
  • 與字串中的變數類似,可使用大括號來明確標示出來源。
  • 不能用在類別和函式中的超級全域變數和 $this 關鍵字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$user = "fool";
$fool = "lala";
$lala = "黃色";
echo $$fool; // 黃色
echo $${$user}; // 黃色, 可以多重使用和大括號用法

function func($bar) {
var_dump($$bar); // NULL, 函式中不作用
//var_dump($_SERVER); // array(....
}
func("_SERVER");

function func2() {
return "fool";
}
echo ${func2()}; // lala

$teletubbies = array('lala' => '黃色', 'dindin' => '紫色');
$group = "teletubbies";

var_dump($$group['lala']); // NULL, 當成 ${$group['lala']}
var_dump(${$group}['lala']); // string(4) "黃色"

class Lala {
var $color = "黃色";

function func($bar) {
var_dump($$bar); // 拿掉下行註解則不為 NULL
//var_dump($this); // 若此 function 中出現過 $this 的用法, 則會作用
}
}
$la = new Lala;
$property = "color";
echo $la->color; // 黃色
echo $la->$property; // 黃色
$la->func('this'); // NULL

其他用法

利用此用法,甚至可以使用不合法的變數命名作為變數名稱:

1
2
3
4
5
$var = "7-11";
$$var = "Always open";
var_dump($$var); // string(11) "Always open"
//var_dump($7-11); // parse error
var_dump(${"7-11"}); // string(11) "Always open"

外部來源變數

透過表單、網址列傳值或 Cookie 等方式,可以從使用者端接受到一些資料。這些資料依據來源可以從不同的預定義變數中取得,包括 $_POST$_GET$_COOKIE$_REQUEST$_FILES

POST

基礎

$_POST$_FILES 是透過表單的 POST Method 來送出,例如有個 HTML 的表單如下:

1
2
3
4
5
<form action="test.php" method="post" enctype="multipart/form-data">
檔案: <input type="file" name="uploadFile"><br/>
描述: <input type="text" name="description"><br/>
<input type="submit" name="submit" value="送出">
</form>

action 為資料要送往的地方。method 為使用 POSTGET,設定為 POST,則資料從 $_POST 取得;若用 GET 則從 $_GET 取得。enctype="multipart/form-data" 表示有包含檔案。上傳檔案必須要使用 POST,上傳的檔案使用 $_FILES 取得。input 欄位的 name 則為在陣列中的 Key,例如上面的範例可得到 $_FILES['uploadFile']$_POST['description']$_POST['submit'] 等資料。

POST 和上傳的檔案允許的大小限制可在 php.ini 中設定,分別為 post_max_sizeupload_max_filesize

POST 也可送出陣列,將 input 的命名為 name[] 的格式送出。例如下面會得到一個 $_POST['id'] 的陣列:

1
2
3
4
5
<form action="test.php" method="post">
<input type="checkbox" name="id[]" value="1"> 1. Lala<br/>
<input type="checkbox" name="id[]" value="2"> 2. Dindin<br/>
<input type="submit" name="submit" value="刪除">
</form>

其他用法

POST 的資料也可以使用讀檔的方式取得,可以利用 file_get_contents() 函式,讀取虛擬的檔案路徑 php://input 來取得 POST 資料,而資料會以類似查詢字串 (參考下個章節) 的格式呈現。

1
2
3
4
5
6
7
<form action="test.php" method="post" >
<input type="hidden" name="fool" value="lala"><br/>
<input type="submit" name="submit" value="Post">
</form>
<?php
echo file_get_contents("php://input"); // fool=lala&submit=Post
?>

查詢字串

查詢字串 (Query string) 是透過網址列傳值的方式送出,或者利用 methodgetform 送出。網址列傳址的使用方式如下:

  1. 在URL的後方加上問號 (?),作為使用查詢字串的開頭。
  2. 後面則接上 key=value 的變數內容。
  3. 變數間使用 & 隔開。
    • 在 URL 的後方加上問號 (?),作為使用查詢字串的開頭。
    • 後面則接上 key=value 的變數內容。
    • 變數間使用 & 隔開。

例如:http://localhost/test.php?id=1&mode=delete,可得到 $_GET['id']=1$_GET['mode']='delete' 的資料。

URL 的長度一般限制最大為 2048 個字元,IE 為 2083 個字元。

查詢字串也可以送出陣列,一樣使用 name[] 的方式送出例如:http://localhost/test.php?id[]=1&id[]=2,可以得到一個 $_GET['id'] 的陣列。

Cookie 是存放在使用者的瀏覽器,除了 JavaScript 外,也可以由 PHP 的 setcookie() 函式去設定,然後使用 $_COOKIE 去取得。

雖然 Cookie 是透過 JavaScript 或 PHP 的函式去產生,但他與 $_GET$_POST 都是由使用者端送出,資料是可以被修改的,不適合拿來作為權限檢驗等用途。

Request

$_GET$_POST$_COOKIE 等使用者送出的請求 (Request),會自動合併成 $_REQUEST 陣列,至於哪些資料要合併,則在 php.ini 中的 request_ordervariables_order 設定,在 PHP 5.3.0 之後預設為 "GP",也就是包含 $_GET$_POST,且 $_POST 順序在 $_GET 之後,亦即當有重複變數時,由 $_POST 蓋掉 $_GET。可以設定的值如下:

  • E: $_ENV
  • G: $_GET
  • P: $_POST
  • C: $_COOKIE
  • S: $_SERVER

例如:request_order = "CGP" 表示 $_REQUEST 包含 $_COOKIE$_GET$_POST

常見問題

變數名稱轉換

在送出的變數名稱如果包含點 (.)、左中括號 ([)、空白和 ASCII 字元 128 到 159 會被轉換成底線 (_),而左中括號一旦出現後之後就不會再轉換,例如:

1
2
3
4
5
6
7
<form action="hello.php" method="post">
<input name="a b.c[d" value="dindin" type="hidden">
<input name="a b[c.d" value="lala" type="submit">
</form>
<?php
print_r($_POST); // Array ( [a_b_c_d] => dindin [a_b_c.d] => lala )
?>

選單多選

HTML 選單 (Select) 使用多選時,名稱必須要使用 name[] 的格式才能正確取得多選陣列,例如:

1
2
3
4
5
6
7
<form action="hell.php" method="post">
<select multiple="multiple" name="fools[]">
<option value="lala">Lala</option>
<option value="dindin">Dindin</option>
</select>
<input name="submit" value="送出" type="submit">
</form>

圖形送出按鈕

有時候我們會利用圖片按鈕 (input type="image") 來取代表單的送出按鈕,在送出後會得到 name_xname_y 的變數,分別表示點擊時滑鼠在圖片的座標位置。其中 value 會依據瀏覽器不同,而有不同結果,例如:在Firefox 會多送出 name=value 的資料,但 IE 則不會送出。

1
2
3
4
5
6
7
<form action="test.php" method="post" >
<input type="image" name="submit" value="something" src="submit.gif">
</form>
<?php
print_r($_POST); // IE: Array ( [submit_x] => ... [submit_y] => ... )
// FF: Array ( [submit_x] => ... [submit_y] => ... [submit] => something)
?>

延伸閱讀

上一篇 PHP 教學 - 資料型態 (Data Type) - 下
下一篇 PHP 教學 - 常數 (Constants)