Learning Objectives
- 依命名規則建立變數,說明 PHP 是動態型別:變數的型別由目前存放的值決定
- 分辨 int、float、string、bool 與 null,用 var_dump、get_debug_type 檢查型別
- 說明整數超過 PHP_INT_MAX 會變成浮點數、浮點數有精度限制(0.1 + 0.2)
- 預測自動型別轉換的結果,使用 (int)、(float)、(string)、(bool) 與 intval 強制轉換,並列出哪些值會被當成 false
- 用 define 與 const 定義常數,用 isset、empty 與 ?? 處理可能不存在的變數
程式要記住東西,就需要變數
程式處理的每一筆資料(使用者的名字、商品價格、是否已登入)都要先存起來才能使用,存放的地方就是變數(variable)。
每個值都有型別(type):20 是整數、172.5 是浮點數、'Amy' 是字串。
PHP 是動態型別的語言:變數不用事先宣告型別,存進什麼值,它就是什麼型別;同一個變數之後也可以換成另一種型別的值。
這很方便,但也代表 PHP 常常會自動轉換型別,例如把字串 '5' 當成數字 5 來計算。
這一課要弄清楚:PHP 有哪些型別、什麼時候會自動轉換、轉換的規則是什麼。這些規則是很多 PHP 程式錯誤的來源。
變數的寫法與命名規則
$score = 90; // 建立變數 $score,把 90 存進去(指派)
$score = 95; // 換成新的值
$total = $score + 5; // 讀取變數的值來計算
| 規則 | 範例 |
|---|---|
以 $ 開頭,後面接名稱 |
$name |
| 名稱的第一個字元是英文字母或底線,不能是數字 | $_count 可以,$1st 不行 |
| 之後可以是英文字母、數字、底線 | $student_2 |
| 區分大小寫 | $score 和 $Score 是不同的變數 |
不能用 $this(物件裡有特別用途,第 14 課) |
PHP 也允許中文等非英文字元當變數名稱($分數),但會讓字串插值容易出錯,也不方便和別人合作,建議一律用英文。
多個英文字組成的名稱,常見兩種寫法:$studentName(小駝峰)或 $student_name(底線),同一個專案選一種用到底。
名稱要說明內容:$price 比 $p 好,$isLoggedIn 一看就知道是是非題。
PHP 的資料型別
PHP 的值常被分成 8 種型別:
| 分類 | 型別 | 例子 | 說明 |
|---|---|---|---|
| 純量 | int(整數) |
42、-7、0 |
沒有小數點的數字 |
| 純量 | float(浮點數) |
3.14、2.0、1.5e3 |
有小數點或用科學記號寫的數字 |
| 純量 | string(字串) |
'Amy'、"你好" |
一串文字 |
| 純量 | bool(布林) |
true、false |
只有真、假兩個值 |
| 複合 | array(陣列) |
[1, 2, 3] |
一組值,第 08 課 |
| 複合 | object(物件) |
new DateTime() |
第 14 課 |
| 特殊 | null |
null |
「沒有值」;只有這一個值 |
| 特殊 | resource(資源) |
fopen() 的回傳值 |
開啟中的檔案等外部資源,第 12 課 |
這一課專注在純量型別與 null。檢查型別的三種方法:
| 寫法 | 結果 | 用途 |
|---|---|---|
var_dump($x) |
int(20)、string(3) "Amy" |
除錯時最常用:同時看到型別與值 |
get_debug_type($x) |
"int"、"float"、"null" |
PHP 8.0 新增,回傳標準的型別名稱 |
gettype($x) |
"integer"、"double"、"NULL" |
舊函式;名稱是歷史寫法(浮點數回傳 "double") |
echo 不適合用來檢查型別:echo false; 什麼都不印,echo null; 也是,你分不出差別。
五種值與 var_dump
左邊的變數區會顯示每個變數目前的值:浮點數一定有小數點,字串有引號,true、null 直接顯示。看第 8 行 var_dump 的輸出格式,以及最後 $age 從整數變成字串。
整數與浮點數
整數有範圍限制。現在的伺服器幾乎都是 64 位元,最大值是常數 PHP_INT_MAX(9223372036854775807,約 9.2 × 10¹⁸),
最小值是 PHP_INT_MIN。計算結果超過範圍時,PHP 不會報錯,而是自動變成浮點數,精度也跟著下降。
整數有幾種寫法:
| 寫法 | 意思 | 值 |
|---|---|---|
1_000_000 |
底線只是方便閱讀 | 1000000 |
0x1F |
十六進位 | 31 |
0b101 |
二進位 | 5 |
0o17 或 017 |
八進位(0o 寫法是 PHP 8.1 加入的) |
15 |
浮點數用二進位表示小數,很多十進位小數(例如 0.1)沒辦法精確表示,只能存一個非常接近的值,
所以 0.1 + 0.2 實際上是 0.30000000000000004。echo 只顯示 14 位有效數字,看起來是 0.3;var_dump 會顯示完整的值。
不要用 == 比較兩個浮點數,改成比較差距夠不夠小,或先 round() 再比較。金額計算可以改用整數(以「分」為單位)。
整數溢位與浮點數精度
第 4 行讓最大的整數再加 1:結果不是錯誤,而是變成浮點數。第 8、9 行用兩種方式輸出同一個 $sum,比較 echo 與 var_dump 的差別。
字串:單引號與雙引號
單引號 '…' |
雙引號 "…" |
|
|---|---|---|
| 變數 | 不替換:'$name' 就是 $name 這幾個字 |
替換成值:"Hi $name" |
| 跳脫字元 | 只認得 \' 與 \\ |
\n(換行)、\t(Tab)、\"、\$、\\ |
| 適合 | 固定的文字 | 需要放變數或換行的文字 |
雙引號字串裡放變數,建議一律寫成 {$name}:大括號清楚標出變數名稱到哪裡結束。
變數後面緊接中文或英文字母時,一定要加大括號,否則 PHP 會把後面的字也當成變數名稱的一部分。
另外,"${name}" 這種寫法從 PHP 8.2 起被標為棄用,不要使用。字串的各種操作在第 09 課介紹。
型別轉換
自動轉換(type juggling):運算子需要什麼型別,PHP 就把值轉成那個型別。
| 情況 | 例子 | 結果 |
|---|---|---|
| 算術運算遇到數字字串 | '5' + 3 |
8(int) |
| 字串串接遇到數字 | 'NT$' . 100 |
'NT$100'(string) |
| 開頭是數字、後面有其他字的字串 | '12abc' + 1 |
13,並出現 Warning: A non-numeric value encountered |
| 完全不是數字的字串 | 'abc' + 1 |
TypeError,程式停止(PHP 8 起) |
if 等需要真假的地方 |
if ('0') |
轉成 bool,見下一節 |
強制轉換(type casting):在值前面加上 (型別),自己決定要轉成什麼。
| 寫法 | 規則 | 例子 |
|---|---|---|
(int)、intval() |
取開頭的數字部分,沒有就是 0;浮點數直接捨去小數(不是四捨五入) | (int) '12abc' → 12、(int) 'abc' → 0、(int) 3.99 → 3 |
(float)、floatval() |
取開頭的數字部分 | (float) '3.14 公尺' → 3.14 |
(string)、strval() |
數字轉成文字;true → '1'、false 與 null → '' |
(string) 2.0 → '2' |
(bool)、boolval() |
見下一節的「假值」 | (bool) '0' → false |
強制轉換不會出現警告,所以 (int) 適合用在「確定要一個整數,不合格就當 0」的地方。
使用者從表單送來的資料一律是字串(第 11 課),計算前常常要先轉換。
自動轉換與強制轉換
前三組是 PHP 自動轉換,第 10 到 12 行是強制轉換。最後一行的 '12abc' + 1 會算出結果,但 PHP 同時印出 Warning 提醒你這個字串不完全是數字。
想一想:型別與轉換
下面的程式會印出什麼?
<?php
$x = '10';
$y = $x + 5;
var_dump($y);
$z = $x . 5;
var_dump($z);
下面的程式會印出什麼?
<?php
echo (int) '3.9kg' + (int) 2.9;
下面的程式執行的結果是什麼?
<?php
$n = 'abc' + 1;
echo $n;
哪些值會被當成 false
if、while、!、&& 這些地方需要一個真假值,PHP 會把任何值自動轉成 bool。只有下面這些值是 false,其他全部是 true:
| 值 | 說明 |
|---|---|
false |
布林的假 |
0、0.0(以及 -0.0) |
整數零、浮點數零 |
'' |
空字串 |
'0' |
只有一個字元 0 的字串 |
null |
沒有值(包括還沒定義的變數) |
[] |
空陣列 |
容易誤會的:'0.0'、' '(一個空白)、'false'(五個字母的字串)、-1 都是 true。
字串 '0' 是 false 這一點特別要小心:使用者在表單輸入 0,if ($input) 會把它當成「沒有輸入」。
轉成布林值
第 3、4 行的六個值都是 false;第 7 行的四個值看起來像「假」,其實都是 true。最後的 if 示範字串 '0' 造成的問題。
常數
常數(constant)是設定之後就不能改變的值,適合放「整個程式都用到、不會變」的設定,例如網站名稱、分數上限、稅率。
define('SITE_NAME', 'Uedu CodeLab'); // 用函式定義,名稱是字串
const MAX_SCORE = 100; // 用關鍵字定義
const PASS_SCORE = MAX_SCORE * 0.6; // 可以用其他常數計算
echo SITE_NAME, PHP_EOL; // Uedu CodeLab
echo '及格分數:', PASS_SCORE, PHP_EOL; // 及格分數:60
echo "MAX_SCORE\n"; // 字串裡不會替換,印出 MAX_SCORE
define('SITE_NAME', '別的名稱'); // Warning: Constant SITE_NAME already defined
- 常數名稱沒有
$,慣例全部大寫、用底線分隔字 - 常數名稱區分大小寫;可以在程式的任何地方使用,包括函式裡面
- 已經定義的常數不能重新定義或修改:再
define一次會出現 Warning,值不變 const必須寫在最外層(不能放在if或函式裡),define()可以放在任何地方,例如依條件決定值- 常數不會在雙引號字串裡被替換,要用逗號或
.接起來 defined('NAME')檢查常數是否已經定義
PHP 有很多預定義常數:
| 常數 | 意思 |
|---|---|
PHP_EOL |
這個作業系統的換行字元(Linux 是 "\n") |
PHP_INT_MAX、PHP_INT_MIN |
整數的最大值、最小值 |
PHP_VERSION |
目前 PHP 的版本字串 |
M_PI |
圓周率 |
E_ALL、E_WARNING |
錯誤的種類(設定錯誤回報時用) |
變數不存在怎麼辦:isset、empty 與 ??
讀取一個沒有定義過的變數,PHP 8 會印出警告並當成 null 繼續執行:
Warning: Undefined variable $nothing in /var/www/html/index.php on line 11
警告代表程式有地方沒想清楚。資料可能不存在時(例如使用者沒有填某個欄位),先檢查再使用:
| 寫法 | 什麼時候是 true | 變數不存在時 |
|---|---|---|
isset($x) |
變數存在而且不是 null | false,不會出現警告 |
empty($x) |
變數不存在,或值是假值(0、''、'0'、null、[]、false) |
true,不會出現警告 |
$x ?? '預設值' |
不是運算式,是取值:$x 存在且不是 null 就用 $x,否則用右邊的值 |
用右邊的值,不會出現警告 |
?? 叫做 null 合併運算子,只看「是不是 null 或不存在」:$score = 0 時,$score ?? 60 仍然是 0。
empty() 則會把 0 也當成「空」,所以不適合用來檢查數字是否有填寫。
變數的變數:$$name 會先取出 $name 的值(例如 'score'),再把它當成變數名稱,也就是 $score。
這種寫法讓程式難以閱讀,實務上幾乎都用陣列取代(第 08 課),知道有這種寫法即可。
isset、empty、?? 與未定義的變數
$nothing 和 $nickname 從來沒有定義過。第 6、7、9 行用 isset、empty、?? 檢查它們,不會出現警告;第 11 行直接在字串裡使用 $nothing,就出現 Warning。
想一想:假值與 ??
下面的程式會印出什麼?
<?php
var_dump((bool) '0', (bool) '0.0', (bool) []);
下面的程式會印出什麼?
<?php
$qty = 0;
$note = null;
echo $qty ?? 10, ' ', $note ?? '無', ' ';
var_dump(empty($qty));
下面的程式會印出什麼?
<?php
define('RATE', 0.05);
const TAX = 'RATE';
echo "RATE ", RATE * 100, ' ', TAX;
動手做:把字串轉成數字再計算
從表單收到的資料都是字串。請完成程式:定義運費常數 SHIPPING 為 60;把數量轉成整數、單價轉成浮點數、折扣轉成整數(折扣是空字串,轉成整數就是 0)。執行後應該印出:
數量:int(3)
單價:float(120.5)
折扣:int(0)
總計:421.5
重點回顧
- 變數以
$開頭,名稱以英文字母或底線開頭、區分大小寫;PHP 是動態型別,型別由目前的值決定 - 純量型別:
int、float、string、bool,加上表示「沒有值」的null;陣列與物件在第 08、14 課 - 檢查型別用
var_dump()或get_debug_type();gettype()對浮點數回傳歷史名稱"double" - 整數超過
PHP_INT_MAX會變成浮點數;浮點數有誤差,0.1 + 0.2 == 0.3是 false,不要用==比較浮點數 - 數字字串在算術中自動轉成數字;
'12abc' + 1會有 Warning,'abc' + 1在 PHP 8 丟出 TypeError (int)取開頭的數字、捨去小數;強制轉換不會出現警告- 假值只有:
false、0、0.0、''、'0'、null、[];'0.0'、' '、'false'都是 true - 常數用
define()或const定義,沒有$、慣例全大寫、不能修改;PHP_EOL、PHP_INT_MAX是預定義常數 - 讀取未定義的變數會出現 Warning;先用
isset()、empty()檢查,或用$x ?? 預設值取值