National Yunlin University of Science and Technology
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

雲林科技大學 AQI 56 27°C PM2.5 16
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 04

運算子

算術、指派、比較(== 與 ===)、邏輯、字串串接、三元與 null 合併運算子
Interaction實作 25 minute(s)

Learning Objectives

  • 使用算術運算子,說明 / 的結果何時是浮點數、intdiv 與 % 的用法,以及除以零會丟出 DivisionByZeroError
  • 使用複合指派、遞增遞減(分辨前置與後置)與字串串接 . 和 .=
  • 分辨 == 與 ===,說明 PHP 8 的字串與數字比較規則,使用 <=> 太空船運算子
  • 使用 &&、||、!,說明短路求值與 and/or 優先順序比 = 低的陷阱
  • 使用三元運算子、?: 、?? 與 ??=,並依優先順序判斷運算式的計算順序

運算子:讓值變成新的值

運算子(operator)把一個或幾個值變成一個新的值:3 + 4 得到 7、$age >= 18 得到 true、'Hi' . '!' 得到 'Hi!'。 被運算的值叫運算元(operand)。

PHP 的運算子大多和 C、Java、JavaScript 長得一樣,但有幾個地方要特別注意:

  • 字串串接用 .,不是 +(+ 只做數字加法)
  • == 會先轉換型別再比較,=== 才是「型別和值都相同」
  • and、or 和 &&、|| 意思相同,但優先順序差很多
  • ?? 專門處理「可能不存在或是 null」的值

這一課每一組運算子都有一個可以逐步執行的範例,最後整理優先順序。

算術運算子

運算子 意思 例子 結果
+、-、* 加、減、乘 7 * 2 14
/ 除 7 / 2、6 / 2 3.5(float)、3(int)
% 取餘數(模數) 7 % 3 1
** 次方 2 ** 10 1024
-(單元) 取負號 -$x

幾個細節:

  • / 的兩邊都是整數而且整除時結果是 int,否則是 float。PHP 沒有「整數除法」運算子,要取商用 intdiv(7, 2)(得到 3)
  • % 先把兩邊轉成整數再算;結果的正負號跟著左邊:-7 % 3 是 -1、7 % -3 是 1。浮點數取餘數用 fmod()
  • ** 的優先順序比負號高,所以 -2 ** 2 是 -(2 ** 2),等於 -4
  • 除以零:PHP 8 起,/ 或 % 的右邊是 0,會丟出 DivisionByZeroError,程式停止(PHP 7 的 / 只給警告)。除之前先確認除數不是 0
Fatal error: Uncaught DivisionByZeroError: Division by zero in /var/www/html/index.php:2

除法、取餘數與次方

注意第 3、4 行:同樣是 /,結果的型別不同。最後四行是 intdiv 與 % 最常見的用法:把總分鐘數換成「幾小時幾分鐘」。

指派、遞增遞減與字串串接

= 是指派:先算右邊,再把結果存進左邊的變數。$n = $n + 5 可以縮寫成複合指派 $n += 5:

寫法 等於
$n += 5、$n -= 5、$n *= 5、$n /= 5、$n %= 5、$n **= 2 $n = $n 運算子 值
$s .= '!' $s = $s . '!'(在字串後面接上)
$n ??= 0 $n 不存在或是 null 時才指派(本課後面介紹)

遞增、遞減把變數加 1 或減 1,分成前置與後置:

寫法 運算式的值 變數
++$i 加 1 之後的值 加 1
$i++ 加 1 之前的值 加 1
--$i、$i-- 同上,減 1 減 1

單獨寫成一行 $i++; 時前置、後置沒有差別;只有把它放在運算式裡(例如 $a = $i++;)才有差。

字串串接用 .:'Hello' . ' ' . 'PHP'。PHP 8 起,. 的優先順序比 +、- 低, 所以 '總分:' . 80 + 5 會先算 80 + 5,得到 '總分:85'(PHP 7 會先串接,再拿字串去加 5)。 混用時加上括號最清楚:'總分:' . (80 + 5)。

複合指派與 $i++、++$i

看左邊 $n 每一行的變化。第 11 到 13 行要同時看兩個變數:$a、$b、$c 拿到的值,和 $i 本身的變化。

比較運算子:== 與 ===

運算子 意思
== 相等(先轉換型別再比較,寬鬆比較)
=== 全等:型別相同而且值相等(嚴格比較)
!=、<> 不相等(寬鬆);兩種寫法意思相同
!== 不全等(嚴格)
<、>、<=、>= 大小比較
<=> 太空船運算子:左邊小回傳 -1、相等回傳 0、左邊大回傳 1

== 的轉換規則很容易出錯。PHP 8 改了字串與數字的比較規則:

  • 字串是數字字串('1'、'01'、'1e2'、' 42')時,轉成數字比較,所以 '1' == '01'、100 == '1e2' 都是 true
  • 字串不是數字字串時,改成把數字轉成字串再比較字串:0 == 'a' 是 false、0 == '' 也是 false

在 PHP 7 以前,0 == 'a' 是 true('a' 被轉成 0),這是很多安全漏洞與錯誤的來源。網路上的舊文章可能還寫著這個舊規則。

比較 PHP 7 PHP 8
0 == 'a' true false
0 == '' true false
42 == '42abc' true false
'1' == '01' true true
0 == '0' true true

其他常見情況:null == false 是 true(兩邊都轉成 bool),'abc' == 'ABC' 是 false(字串比較區分大小寫), 兩個字串用 <、> 比較時依字元的編碼一個一個比('apple' < 'banana')。

原則:除非你確定需要轉換,一律用 === 和 !==。

寬鬆比較與嚴格比較

每個 var_dump 印出好幾個比較的結果,一個一行。先猜猜看每一個是 true 還是 false,再對照輸出。

想一想:算術、遞增與比較

1.

下面的程式會印出什麼?

<?php
echo 10 / 4, ' ', intdiv(10, 4), ' ', 10 % 4, ' ', -10 % 4;
2.

下面的程式會印出什麼?

<?php
$i = 3;
$a = $i++;
$b = --$i;
echo "$a $b $i";
3.

在 PHP 8.3 執行,下面的程式會印出什麼?

<?php
var_dump(0 == 'a', '1' == '01', 100 == '1e2', null == 0);
第 1 次作答

邏輯運算子

  • $a && $b 或 $a and $b:而且,兩邊都是 true 才是 true
  • $a || $b 或 $a or $b:或者,至少一邊是 true 就是 true
  • !$a:不是,$a 是 false 時為 true
  • $a xor $b:互斥或,剛好一邊是 true 才是 true

結果一定是 bool;運算元不是 bool 時,依上一課的「假值」規則轉換。

短路求值:&& 的左邊已經是 false,結果一定是 false,右邊不會執行;|| 的左邊已經是 true,右邊也不會執行。 常用來保護後面的運算,例如 $x !== 0 && 10 / $x > 1:$x 是 0 時不會去做除法。

and、or 的陷阱:它們的意思和 &&、|| 相同,但優先順序比 = 還低:

$r = false or true;    // 等於 ($r = false) or true;  $r 是 false!
$r = false || true;    // 等於 $r = (false || true);  $r 是 true

建議一律使用 &&、||,就不會遇到這個問題。

短路求值與 or 的陷阱

第 8 行的 10 / $x 如果真的執行,會因為除以零而停止;但 && 左邊已經是 false,右邊根本不會算。再看第 11 行執行完,$r 是 false,不是 true。

三元運算子與 null 合併運算子

三元運算子 條件 ? 值1 : 值2:條件成立取值1,否則取值2。適合「二選一」的簡短判斷:

$result = $score >= 60 ? '及格' : '不及格';
寫法 意思 左邊是 ''、0 時 左邊不存在時
$a ?: $b $a 是真值就用 $a,否則用 $b($a ? $a : $b 的簡寫) 用 $b 出現 Warning,用 $b
$a ?? $b $a 存在而且不是 null 就用 $a,否則用 $b 用 $a 不會有警告,用 $b
$a ??= $b $a 不存在或是 null 時,才把 $b 指派給 $a 不變 指派

選擇的原則:要「沒有值才用預設值」用 ??;要「空的、零也換成預設值」用 ?:。 ?? 可以串接:$a ?? $b ?? '預設',從左邊開始,取第一個存在而且不是 null 的值。 三元運算子不要巢狀(PHP 8 起,沒有加括號的巢狀三元運算子會直接報錯),條件複雜時改用 if(第 05 課)或 match。

? :、?:、?? 與 ??=

第 8、9 行對同一個空字串 $nick 分別用 ?: 和 ??,結果不同。第 12、13 行連續兩次 ??=,只有第一次會指派。

運算子優先順序

一個運算式裡有好幾個運算子時,優先順序高的先算;同一級的依結合性決定從左或從右算。常用的運算子由高到低:

  1. **(最高;從右往左結合,2 ** 3 ** 2 是 2 ** 9)
  2. ++、--、-(負號)、(int) 等轉型
  3. !
  4. *、/、%
  5. +、-
  6. <<、>>
  7. .(PHP 8 起比 +、- 低)
  8. <、<=、>、>=
  9. ==、!=、===、!==、<>、<=>
  10. 位元運算子 &,接著 ^,接著 |
  11. &&
  12. ||
  13. ??(從右往左結合)
  14. ? :
  15. =、+=、.=、??= 等指派(從右往左結合,$a = $b = 0 先把 0 給 $b)
  16. and,接著 xor,接著 or(最低)

沒有特別註明的,同一級都是從左往右算:10 - 3 - 2 是 (10 - 3) - 2。

例子:2 + 3 * 2 ** 2 先算 2 ** 2 = 4,再算 3 * 4 = 12,最後 2 + 12 = 14。 不確定的時候就加括號,括號裡的永遠先算,別人讀程式也比較輕鬆。完整的表格在 PHP 官方手冊的「Operator Precedence」。

位元運算子把整數當成一串二進位的位元來運算,一般網頁程式很少用到,認得就好:

  • &、|、^:逐位元的 AND、OR、XOR,例如 6 & 3 是 2、6 | 3 是 7、6 ^ 3 是 5(6 是二進位 110、3 是 011)
  • ~:每個位元反轉,~0 是 -1
  • <<、>>:左移、右移,1 << 3 是 8、16 >> 2 是 4

最常見的用途是錯誤回報的設定,例如 E_ALL & ~E_NOTICE(全部,但不包括 Notice)。

想一想:邏輯、null 合併與除以零

1.

下面的程式會印出什麼?

<?php
$r = false or true;
var_dump($r);
2.

下面的程式會印出什麼?

<?php
$n = 0;
echo $n ?: 5, ' ', $n ?? 5;
3.

在 PHP 8.3 執行下面的程式,結果是什麼?

<?php
echo 10 / 0;
第 1 次作答

動手做:換算時間

影片長度是 $seconds 秒。請用 intdiv 與 % 算出幾小時、幾分、幾秒;用三元運算子判斷是否超過一小時;使用者名稱 $user 是 null 時顯示「訪客」。執行後應該印出:

1 小時 2 分 5 秒
超過一小時:是
使用者:訪客
你的輸出
預期輸出

重點回顧

  • / 整除時是 int,否則是 float;取商用 intdiv();% 的正負號跟著左邊;** 是次方,比負號優先
  • PHP 8 起除以零(/ 與 %)丟出 DivisionByZeroError
  • 複合指派 +=、.=、??=;$i++ 的值是加 1 前、++$i 的值是加 1 後
  • 字串串接用 .;PHP 8 起 . 比 +、- 低,混用時加括號
  • == 先轉換型別再比較,=== 型別與值都要相同;預設用 ===、!==
  • PHP 8:數字與非數字字串比較時,把數字轉成字串比,所以 0 == 'a' 是 false
  • <=> 回傳 -1、0、1
  • &&、|| 會短路;and、or 優先順序比 = 低,$r = false or true; 讓 $r 是 false,一律用 &&、||
  • ?: 看真假、?? 只看 null 或不存在;不確定優先順序就加括號
這一課完成了!
Next lesson:控制結構
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。