强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

PHP 完全指南 / 第 7 章 — 函数

第 7 章 — 函数:命名参数、可变参数、箭头函数与闭包

7.1 函数基础

<?php
declare(strict_types=1);

// 基本函数定义
function greet(string $name): string
{
    return "Hello, {$name}!";
}

echo greet('Alice');  // Hello, Alice!

// 函数命名规范:camelCase 或 snake_case(PSR 推荐 camelCase)
function calculateTotal(float $price, int $quantity): float
{
    return $price * $quantity;
}

7.2 命名参数(Named Arguments)— PHP 8.0+

<?php
function createUser(
    string $name,
    int $age = 18,
    string $email = '',
    bool $active = true,
): array {
    return compact('name', 'age', 'email', 'active');
}

// 传统调用 — 必须按顺序
createUser('Alice', 30, 'alice@example.com', false);

// 命名参数 — 可跳过默认参数,顺序任意
createUser('Bob', email: 'bob@example.com');
createUser(
    active: false,
    name: 'Eve',
    age: 25,
);

// 配合内置函数
htmlspecialchars($string, double_encode: false);
str_contains($haystack, 'needle', offset: 5);
array_map(callback: fn($n) => $n * 2, array: [1, 2, 3]);

注意:命名参数与函数签名参数名绑定,重命名参数属于 BC Break(向后不兼容)。


7.3 默认参数值

<?php
function sendEmail(
    string $to,
    string $subject,
    string $body = '',
    string $from = 'noreply@example.com',
    bool $isHtml = false,
    int $priority = 3,
): bool {
    // 发送逻辑...
    return true;
}

// 使用默认值
sendEmail('user@example.com', 'Welcome');

// 覆盖部分默认值
sendEmail(
    'user@example.com',
    'Report',
    body: '<h1>月报</h1>',
    isHtml: true,
);

7.4 可变参数(Variadic Arguments)

7.4.1 ... 运算符

<?php
// 接收不定数量参数
function sum(int ...$numbers): int
{
    return array_sum($numbers);
}

echo sum(1, 2, 3);        // 6
echo sum(1, 2, 3, 4, 5);  // 15

// 合并数组为参数
$values = [10, 20, 30];
echo sum(...$values);      // 60(展开运算符)

7.4.2 带类型约束的可变参数

<?php
function log(string $level, string ...$messages): void
{
    $timestamp = date('Y-m-d H:i:s');
    foreach ($messages as $msg) {
        echo "[{$timestamp}] [{$level}] {$msg}\n";
    }
}

log('INFO', 'User logged in', 'Session started');

7.4.3 引用可变参数

<?php
function incrementAll(int &...$values): void
{
    foreach ($values as &$value) {
        $value++;
    }
}

$a = 1; $b = 2; $c = 3;
incrementAll($a, $b, $c);
echo "$a, $b, $c";  // 2, 3, 4

7.5 箭头函数(Arrow Functions)— PHP 7.4+

<?php
// 语法:fn(参数) => 表达式
$double = fn(int $x): int => $x * 2;
echo $double(5);  // 10

// 自动捕获外部变量(不需要 use)
$factor = 3;
$triple = fn(int $x): int => $x * $factor;
echo $triple(5);  // 15

// 数组操作中常用
$prices = [100, 200, 300];

// 提取价格加税
$withTax = array_map(fn($p) => $p * 1.13, $prices);

// 过滤大于 150 的价格
$expensive = array_filter($prices, fn($p) => $p > 150);

// 排序
$sorted = array_values($prices);
usort($sorted, fn($a, $b) => $a <=> $b);

// 链式调用
$result = array_map(
    fn($user) => $user['name'],
    array_filter(
        $users,
        fn($user) => $user['age'] >= 18
    )
);

箭头函数 vs 匿名函数

特性箭头函数 fn() =>匿名函数 function() {}
语法单表达式多语句代码块
外部变量自动捕获需要 use
return隐式必须显式
适合场景简单回调复杂逻辑

7.6 闭包(Closures)

<?php
// 基本闭包
$greet = function(string $name): string {
    return "Hello, {$name}!";
};
echo $greet('World');

// 使用 use 捕获外部变量
$multiplier = 10;
$scale = function(int $x) use ($multiplier): int {
    return $x * $multiplier;
};
echo $scale(5);  // 50

// 引用捕获
$count = 0;
$increment = function() use (&$count): int {
    return ++$count;
};
$increment(); $increment(); $increment();
echo $count;  // 3

// 闭包作为返回值 — 函数工厂
function createAdder(int $base): Closure
{
    return function(int $value) use ($base): int {
        return $base + $value;
    };
}

$add5  = createAdder(5);
$add10 = createAdder(10);
echo $add5(3);   // 8
echo $add10(3);  // 13

7.6.1 闭包绑定(bind/bindTo)

<?php
class Logger
{
    private array $logs = [];

    public function getLogs(): array
    {
        return $this->logs;
    }
}

$logAccessor = Closure::bind(
    function(Logger $logger, string $message) {
        $logger->logs[] = "[{$this->timestamp()}] {$message}";
    },
    new class {  // 绑定到匿名类(可访问其 $this)
        private function timestamp(): string
        {
            return date('Y-m-d H:i:s');
        }
    },
    Logger::class
);

$logger = new Logger();
$logAccessor($logger, 'Test message');
print_r($logger->getLogs());

7.7 第一类可调用语法(First-class Callable)— PHP 8.1+

<?php
// 将函数/方法转为闭包对象
$strlen = strlen(...);         // 函数
echo $strlen('hello');         // 5

$trim = trim(...);
$clean = array_map($trim, ['  hello ', ' world  ']);

// 静态方法
$validate = Email::validate(...);

// 对象方法
$obj = new Calculator();
$add = $obj->add(...);

// 数学函数
$square = fn(int $x) => ($x ** 2);
$sum = array_sum(array_map($square, [1, 2, 3, 4]));  // 30

7.8 递归函数

<?php
// 阶乘
function factorial(int $n): int
{
    if ($n <= 1) return 1;
    return $n * factorial($n - 1);
}
echo factorial(5);  // 120

// 斐波那契(带缓存)
function fibonacci(int $n, array &$memo = []): int
{
    if (isset($memo[$n])) return $memo[$n];
    if ($n <= 1) return $n;
    $memo[$n] = fibonacci($n - 1, $memo) + fibonacci($n - 2, $memo);
    return $memo[$n];
}
echo fibonacci(10);  // 55

// 递归遍历目录
function scanDir(string $dir): array
{
    $results = [];
    foreach (new DirectoryIterator($dir) as $file) {
        if ($file->isDot()) continue;
        $path = $file->getPathname();
        if ($file->isDir()) {
            $results = array_merge($results, scanDir($path));
        } else {
            $results[] = $path;
        }
    }
    return $results;
}

7.9 生成器函数(简介)

<?php
// 生成器使用 yield 产生值
function range2(int $start, int $end, int $step = 1): Generator
{
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}

foreach (range2(1, 10, 2) as $num) {
    echo "{$num} ";
}
// 1 3 5 7 9

详见 第 14 章 — 生成器


7.10 类型化返回值进阶

<?php
// never 返回类型(PHP 8.1+)— 函数永不正常返回
function throwError(string $message): never
{
    throw new RuntimeException($message);
}

// 交叉类型返回(PHP 8.1+)
interface Countable {}
interface Iterator {}

function createCollection(): Countable&Iterator
{
    return new ArrayObject([1, 2, 3]);
}

7.11 业务场景:中间件管道

<?php
declare(strict_types=1);

// 闭包实现中间件模式
type Middleware = Closure;

function createPipeline(): Closure
{
    return function (array $request, Closure $next) use (&$middlewares) {
        // 管道会依次调用每个中间件
    };
}

function buildMiddlewareStack(array $middlewares): Closure
{
    // 将中间件链组合为单个闭包
    $core = fn(array $request): array => $request;

    return array_reduce(
        array_reverse($middlewares),
        fn(Closure $next, Closure $middleware): Closure =>
            fn(array $request) => $middleware($request, $next),
        $core
    );
}

// 定义中间件
$logMiddleware = function(array $request, Closure $next): array {
    echo "[LOG] {$request['method']} {$request['path']}\n";
    $response = $next($request);
    echo "[LOG] Status: {$response['status']}\n";
    return $response;
};

$authMiddleware = function(array $request, Closure $next): array {
    if (!isset($request['user'])) {
        return ['status' => 401, 'body' => 'Unauthorized'];
    }
    return $next($request);
};

$timingMiddleware = function(array $request, Closure $next): array {
    $start = microtime(true);
    $response = $next($request);
    $elapsed = round((microtime(true) - $start) * 1000, 2);
    $response['elapsed_ms'] = $elapsed;
    return $response;
};

// 构建管道
$pipeline = buildMiddlewareStack([
    $logMiddleware,
    $timingMiddleware,
    $authMiddleware,
]);

// 执行
$response = $pipeline([
    'method' => 'GET',
    'path'   => '/api/users',
    'user'   => ['id' => 1, 'name' => 'Alice'],
]);

print_r($response);

7.12 扩展阅读


上一章第 6 章 — 控制流 下一章第 8 章 — 数组