PHP 函數 (Function)
函數是一段可重複使用的程式碼區塊,用來執行特定的任務。函數讓程式碼更有組織、更容易維護和重複使用。
定義函數
使用 function 關鍵字定義函數:
<?php
function greet() {
echo "Hello, World!";
}
// 呼叫函數
greet(); // Hello, World!
greet(); // 可以重複呼叫
?>
函數命名規則
- 函數名稱不區分大小寫(但建議保持一致)
- 必須以字母或底線開頭
- 可以包含字母、數字和底線
- 不能與現有的函數或 PHP 保留字同名
<?php
// 有效的函數名稱
function myFunction() {}
function my_function() {}
function _privateFunction() {}
function function2() {}
// 這三個呼叫的是同一個函數
myFunction();
MyFunction();
MYFUNCTION();
?>
函數參數
函數可以接收參數來處理不同的資料:
<?php
function greet($name) {
echo "Hello, $name!";
}
greet("Alice"); // Hello, Alice!
greet("Bob"); // Hello, Bob!
?>
多個參數
<?php
function add($a, $b) {
return $a + $b;
}
echo add(3, 5); // 8
echo add(10, 20); // 30
?>
預設參數值
<?php
function greet($name, $greeting = "Hello") {
echo "$greeting, $name!";
}
greet("Alice"); // Hello, Alice!
greet("Bob", "Hi"); // Hi, Bob!
greet("Charlie", "Hey"); // Hey, Charlie!
?>
有預設值的參數必須放在沒有預設值的參數後面:
// 正確
function test($a, $b = 10) {}
// 錯誤(會產生警告)
function test($a = 10, $b) {}
參數型別宣告 (PHP 7+)
<?php
function add(int $a, int $b): int {
return $a + $b;
}
echo add(3, 5); // 8
echo add(3.7, 5); // 8(float 會被轉換為 int)
// 嚴格模式
declare(strict_types=1);
function multiply(int $a, int $b): int {
return $a * $b;
}
echo multiply(3, 5); // 15
echo multiply(3.7, 5); // TypeError(嚴格模式下不會自動轉換)
?>
可用的型別宣告
| 型別 | 說明 | PHP 版本 |
|---|---|---|
int | 整數 | 7.0+ |
float | 浮點數 | 7.0+ |
string | 字串 | 7.0+ |
bool | 布林值 | 7.0+ |
array | 陣列 | 7.0+ |
callable | 可呼叫 | 7.0+ |
iterable | 可迭代 | 7.1+ |
object | 物件 | 7.2+ |
mixed | 任意型別 | 8.0+ |
?type | 可為 null | 7.1+ |
type|type | 聯合型別 | 8.0+ |
<?php
// 可為 null 的參數
function findUser(?int $id): ?array {
if ($id === null) {
return null;
}
return ['id' => $id, 'name' => 'User'];
}
// 聯合型別 (PHP 8+)
function process(int|float $number): int|float {
return $number * 2;
}
?>
命名參數 (PHP 8+)
<?php
function createUser(string $name, int $age, string $city = 'Unknown') {
return "Name: $name, Age: $age, City: $city";
}
// 傳統呼叫
echo createUser('Alice', 25, 'Taipei');
// 使用命名參數(可以任意順序)
echo createUser(age: 25, name: 'Bob');
echo createUser(name: 'Charlie', city: 'Kaohsiung', age: 30);
?>
回傳值
使用 return 回傳值:
<?php
function square($n) {
return $n * $n;
}
$result = square(5);
echo $result; // 25
// return 也可以用來提早結束函數
function divide($a, $b) {
if ($b === 0) {
return null; // 提早返回
}
return $a / $b;
}
?>
回傳型別宣告
<?php
function add(int $a, int $b): int {
return $a + $b;
}
function getUser(): array {
return ['name' => 'Alice', 'age' => 25];
}
// 可為 null 的回傳型別
function findById(int $id): ?string {
return $id > 0 ? "User $id" : null;
}
// void - 不回傳任何值
function logMessage(string $message): void {
echo "[LOG] $message\n";
// 不能有 return 值
}
// never - 函數永不正常返回 (PHP 8.1+)
function throwError(): never {
throw new Exception("Error");
}
?>
回傳多個值
PHP 函數只能回傳一個值,但可以使用陣列回傳多個值:
<?php
function getMinMax(array $numbers): array {
return [
'min' => min($numbers),
'max' => max($numbers)
];
}
$result = getMinMax([3, 1, 4, 1, 5, 9]);
echo $result['min']; // 1
echo $result['max']; // 9
// 使用解構
['min' => $min, 'max' => $max] = getMinMax([3, 1, 4, 1, 5, 9]);
echo "$min, $max"; // 1, 9
?>
傳值 vs 傳址
傳值(預設)
參數的值會被複製,函數內的修改不會影響原變數:
<?php
function addOne($num) {
$num += 1;
return $num;
}
$x = 5;
$result = addOne($x);
echo $x; // 5(原變數不變)
echo $result; // 6
?>
傳址(使用 &)
參數是原變數的參考,函數內的修改會影響原變數:
<?php
function addOne(&$num) {
$num += 1;
}
$x = 5;
addOne($x);
echo $x; // 6(原變數被修改)
?>
可變數量參數
使用 ... (Spread Operator)
<?php
function sum(...$numbers): int {
return array_sum($numbers);
}
echo sum(1, 2, 3); // 6
echo sum(1, 2, 3, 4, 5); // 15
// 可以加上型別
function sumInts(int ...$numbers): int {
return array_sum($numbers);
}
?>
搭配固定參數
<?php
function greetAll(string $greeting, string ...$names): void {
foreach ($names as $name) {
echo "$greeting, $name!\n";
}
}
greetAll("Hello", "Alice", "Bob", "Charlie");
?>
展開陣列
<?php
function add($a, $b, $c) {
return $a + $b + $c;
}
$numbers = [1, 2, 3];
echo add(...$numbers); // 6
?>
遞迴函數
函數可以呼叫自己:
<?php
function factorial(int $n): int {
if ($n <= 1) {
return 1;
}
return $n * factorial($n - 1);
}
echo factorial(5); // 120 (5 × 4 × 3 × 2 × 1)
?>
費氏數列
<?php
function fibonacci(int $n): int {
if ($n <= 1) {
return $n;
}
return fibonacci($n - 1) + fibonacci($n - 2);
}
for ($i = 0; $i < 10; $i++) {
echo fibonacci($i) . " ";
}
// 0 1 1 2 3 5 8 13 21 34
?>
遞迴要小心無限迴圈和堆疊溢出。確保有終止條件,並考慮效能問題。
函數是一級公民
PHP 中函數可以:
指派給變數
<?php
function greet($name) {
return "Hello, $name!";
}
$sayHello = 'greet'; // 儲存函數名稱
echo $sayHello('Alice'); // Hello, Alice!
?>
作為參數傳遞
<?php
function applyTwice(callable $func, $value) {
return $func($func($value));
}
function double($n) {
return $n * 2;
}
echo applyTwice('double', 5); // 20 (5 × 2 × 2)
?>
作為回傳值
<?php
function createMultiplier($factor) {
return function($n) use ($factor) {
return $n * $factor;
};
}
$double = createMultiplier(2);
$triple = createMultiplier(3);
echo $double(5); // 10
echo $triple(5); // 15
?>
內建函數
PHP 提供大量內建函數:
<?php
// 字串函數
echo strlen("Hello"); // 5
echo strtoupper("hello"); // HELLO
// 陣列函數
$arr = [3, 1, 4, 1, 5];
echo count($arr); // 5
echo max($arr); // 5
// 數學函數
echo abs(-10); // 10
echo round(3.7); // 4
// 日期函數
echo date("Y-m-d"); // 2024-12-09
echo time(); // 時間戳記
?>
實際應用範例
表單驗證
<?php
function validateEmail(string $email): bool {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
function validatePassword(string $password): array {
$errors = [];
if (strlen($password) < 8) {
$errors[] = "密碼至少需要 8 個字元";
}
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = "密碼需要包含大寫字母";
}
if (!preg_match('/[0-9]/', $password)) {
$errors[] = "密碼需要包含數字";
}
return $errors;
}
$email = "test@example.com";
$password = "abc123";
if (!validateEmail($email)) {
echo "Email 格式不正確\n";
}
$passwordErrors = validatePassword($password);
if (!empty($passwordErrors)) {
foreach ($passwordErrors as $error) {
echo "密碼錯誤:$error\n";
}
}
?>
格式化輸出
<?php
function formatCurrency(float $amount, string $currency = 'TWD'): string {
$symbols = ['TWD' => 'NT$', 'USD' => '$', 'EUR' => '€'];
$symbol = $symbols[$currency] ?? $currency;
return $symbol . number_format($amount, 2);
}
function formatDate(string $date, string $format = 'Y-m-d'): string {
$timestamp = strtotime($date);
return date($format, $timestamp);
}
echo formatCurrency(1234.5); // NT$1,234.50
echo formatCurrency(99.99, 'USD'); // $99.99
echo formatDate('2024-12-09', 'Y年m月d日'); // 2024年12月09日
?>