# 函數
[toc]
## 1. 語法
函數是實現代碼復用的重要方式,在所有編程語言中均如此
```php
function 函數名稱(參數列表)
{
// 函數體
return 返回值;
}
```
| 序號 | 名稱 | 描述 |
| ---- | ----------- | ------------------------------------------ |
| 1 | `function` | 聲明函數 |
| 2 | `函數名稱` | 符合 PHP 標識符命名規范,不區分大小寫 |
| 2 | `參數列表` | 零個或多個接收外部傳入到函數的變量 |
| 2 | `{...` | 創建出一個封閉的函數作用域 |
| 2 | `函數體` | 由零個可多個合法的 PHP 語句組成 |
| 2 | `return 值` | 將執行結果返回函數調用者[可選] |
| 2 | `...}` | 函數執行結束,如果沒有`return`,則返回`null` |
示例代碼: `demo1.php`
```php
# 函數語法
// 聲明: function 關鍵字
function func1($a, $b)
{
return $a + $b;
}
// 調用: 按名調用
echo func1(10, 20);
```
---
## 2. 類型
| 序號 | 類型 | 語法 | 描述 |
| ---- | ---------- | ------------------------- | --------------------------------------- |
| 1 | 自定義函數 | `function getName(){...}` | 用戶根據業務需求創建 |
| 2 | 系統函數 | `substr(), count()...` | 也叫預定義函數,不必聲明直接調用 |
| 3 | 可變函數 | `$funcName();` | 函數名使用變量表示 |
| 4 | 匿名函數 | `$f = function (){...}` | 也叫"閉包"或"函數表達式",常用做回調處理 |
示例代碼: `demo2.php`
```php
<?php
# 函數類型
// 1. 自定義函數
function getPrice($money, $discount)
{
return $money * $discount;
}
echo '實付金額 = ' . getPrice(5000, 0.8);
echo '<hr>';
// 2. 系統函數
$str = '鐘南山團隊入圍2020年度國家科技獎提名';
// 僅獲取前5個中文字符
echo mb_substr($str, 0, 5);
echo '<hr>';
// 3. 可變函數
// 仍以前面已聲明的函數: getPrice()為例
$funcName = 'getPrice';
// 被調用的函數名稱保存到一個變量中
// 這個功能很實現,在后面課程中會看到更多應用
echo '實付金額 = ' . $funcName(8000, 0.7);
echo '<hr>';
// 3. 匿名函數
$f = function ($money, $num) {
return $money * $num;
};
echo '實付金額 = ' . $f(100, 20);
echo '<br>';
// 匿名函數也叫閉包,可以繼承父作用域中的變量
$discount = 0.8;
// $discount 是父作用域中的變量,這個父作用域可以是全局, 也可以是父函數
// 父作用域是全局
$getAmount = function ($money, $num) use ($discount) {
$amount = $money * $num;
return $amount > 2000 ? $amount * $discount : $amount;
};
echo '實付金額 = ' . $getAmount(120, 20);
echo '<br>';
// 父作用域是函數
$f = function ($discount) {
$getAmount = function ($money, $num) use ($discount) {
$amount = $money * $num;
return $amount > 2000 ? $amount * $discount : $amount;
};
return $getAmount;
};
echo '實付金額 = ' . $f($discount)(120, 20);
```
---
## 3. 返回值
- 函數必須要有返回值
- 函數必須是遵守**單值返回**原則
| 序號 | 場景 | 描述 |
| ---- | ---------- | --------------------------------- |
| 1 | `return` | 可以返回任何類型的值,包括函數類型 |
| 2 | 無`return` | 遇到`}`也會返回, 默認返回`null` |
- 如果需要返回多個值,可以通過以下手段
| 序號 | 返回值類型 | 描述 |
| ---- | ----------- | ------------ |
| 1 | `string` | 字符串拼接 |
| 2 | `array` | 數組 |
| 3 | `json` | JSON 字符串 |
| 4 | `serialize` | 序列化字符串 |
> json 和序列化,使用時需要進行解碼操作
示例代碼: `demo3.php`
```php
<?php
# 函數返回值
// 單值返回,前面已經有了許多案例,重點放在多值返回上
// 1. 通過拼裝字符串實現
function demo1()
{
$status = 1;
$message = '成功';
return $status . ', ' . $message;
}
echo demo1();
// 字符串拼裝返回非常適合處理大量變量與html標簽的混寫
// 字符串拼裝返回多值有許多限制,并且對返回值的解析處理非常麻煩
echo '<hr>';
// 2. 通過數組返回多值
function demo2()
{
return ['status' => 1, 'message' => '驗證成功'];
}
echo demo2()['status'] === 1 ? demo2()['message'] : '驗證失敗';
// 返回值如果仍在php程序中處理,這樣做很方便
// 開發中, 通常返回值都是要交給前端處理的,轉為JSON格式更方便
echo '<hr>';
// 3. 通過JSON格式返回多值
// json是一種通用的,輕量級數據表示格式,使用javascript對象字面量表示數據
// 幾乎所有編程程序,都支持定義和解析json格式的數據,請放心使用
// 當前后端時行數據交互時, json格式幾乎成了除字符串之外的唯一選擇
// json本質上仍是字符串, 只不過具有一些特殊格式,需要進行解析罷了
function demo3()
{
// json_encode(): json編碼函數,將數據編碼為json格式字符串返回
return json_encode(['status' => 1, 'message' => '驗證成功']);
}
// json_decode():json解碼函數, 解析json格式字符串,默認返回對象,true表示返回數組
$data = json_decode(demo3(), true);
echo '<pre>' . print_r($data, true) . '</pre>';
echo '<hr>';
// 4. 通過序列化返回多值
// 序列化可以將程序中的變量/數據做持久化保存,可以進行傳輸,保存到文件中等
// 與json一樣, 序列化也有序列化與反序列二種操作
function demo4()
{
return serialize(['status' => 1, 'message' => '驗證成功']);
}
// 查看序列化的格式化字符串
echo demo4(), '<br>';
// 反序列化
$data = unserialize(demo4());
echo '<pre>' . print_r($data, true) . '</pre>';
```
---
## 4. 參數
- 調用者可以通過參數將數據傳遞到函數中
- 參數是以逗號分隔的表達式列表
- 參數按照從左到右的順序求值
參數類型
| 序號 | 類型 | 描述 |
| ---- | -------- | -------------------- |
| 1 | 值參數 | 默認傳參方式 |
| 2 | 引用參數 | 改變原始調用參數值 |
| 3 | 默認參數 | 調用時允許省略的參數 |
| 4 | 剩余參數 | 調用參數數量不確定 |
示例代碼: `demo4.php`
```php
<?php
# 函數參數
// 1. 值參數: 默認
function demo1($arg)
{
return $arg *= 2;
}
$value = 100;
echo demo1($value), '<br>';
echo $value, '<br>';
echo '<hr>';
// 2. 引用參數
// 引用傳參: 參數前添加地址引用符
function demo2(&$arg)
{
// 函數內部更新了調用參數,則直接影響到原值
return $arg *= 2;
}
$value = 100;
echo demo2($value), '<br>';
echo $value, '<br>';
echo '<hr>';
// 3. 默認參數
// 參數列表中,允許有必選參數和默認參數二大類, 默認參數必須排列在必選參數的后面
// 默認參數決定了函數的默認行為,如果函數默認行為滿足要求,顯然不傳參調用更方便
// 默認參數也允許用戶根據自身需求,傳入自定義參數,對函數的行為進行干預
function demo3($a, $b, $opt = '+')
{
$res = 0;
switch ($opt) {
case '+':
$res = "$a + $b = " . ($a + $b);
break;
case '-':
$res = "$a - $b = " . ($a - $b);
break;
case '*':
$res = "$a * $b = " . ($a * $b);
break;
case '/':
$res = "$a / $b = " . ($a / $b);
break;
default:
$res = '非法操作符';
}
return $res;
}
// 如果用戶不傳第三個參數,則使用默認參數,即默認執行:+ 加法
echo demo3(10, 20), '<br>';
// 傳入用戶自定義操作, 如*, 乘法
echo demo3(10, 20, '*'), '<br>';
// 傳入非法數據: #
echo demo3(10, 20, '#'), '<br>';
echo '<hr>';
// 4. 剩余參數
function demo4($a, $b, $c)
{
return $a + $b + $c;
}
// 計算三數之和
echo demo4(1, 2, 3), '<br>';
// 如果計算五數之和,甚至更多數的求和,難道要這樣一直寫下去呢?
// 其實函數的參數列表中的參數,只是占位符罷了,可以為空的
// 函數內部可以通過其它方式來獲取到調用參數的
// php5.5之前,可以用以下三個函數來獲取調用參數
// func_num_args(),func_get_arg(), func_get_args()
// 現在已經用不到它們了,我們可以使用一種更加優雅的方式來獲取:剩余參數
// 剩余參數的概念與語法與JavaScript中是一樣的, 熟悉js的同學一定不陌生
// 計算任意個數據之和
function sum(...$args)
{
print_r($args);
echo '<br>';
return array_sum($args);
}
$arr = [2, 3, 4, 6, 88, 23, 1, 24];
// ...: 剩余參數展開與收集操作符
// 調用時,執行展開操作, 傳參時執行收集操作
echo sum(...$arr);
```
---
## 5. 回調函數
| 語法 | 類型 | 執行方式 | 應用場景 |
| -------- | ------------- | -------- | -------- |
| 匿名函數 | 閉包`Closure` | 異步 | 函數參數 |
> 異步執行,是指當前函數的執行并不會中斷當前程序的執行流程
示例代碼: `demo5.php`
```php
<?php
# 回調函數
$data = range(1, 100);
// print_r($data);
// 使用回調,可以異步的處理海量數據,并不中斷當前程序
$arr1 = array_map(function ($item) {
if ($item % 2 === 0) return $item;
}, $data);
echo '<hr>';
print_r($arr1);
// 過濾掉空值
$arr2 = array_filter($arr1, function ($item) {
return $item;
});
echo '<hr>';
print_r($arr2);
```
---
## 6. 命名空間
- 使用目錄來整理文檔, 允許將同名文檔,存儲在不同的目錄下面即可
- 不同目錄下的同名文件,訪問時必須帶上的它的目錄名稱,以未區別
- 命名空間采用類似的思想,同名函數,只要聲明在不同空間中即可
- 同樣, 訪問這些函數時, 也需要帶上它的命名空間才可以
示例代碼: `demo6.php`
```php
# 函數命名空間
// function demo1()
// {
// return __FUNCTION__;
// }
// function demo1()
// {
// return __FUNCTION__;
// }
// 報函數重復命名錯誤
// 可以在不同命名空間中創建同名函數
namespace ns1 {
function demo1()
{
return __FUNCTION__;
}
}
namespace ns2 {
function demo1()
{
return __FUNCTION__;
}
}
namespace {
// 帶有空間的函數,調用時需要帶上它的空間名稱
echo \ns1\demo1();
echo '<hr>';
echo \ns2\demo1();
}
```