1.3 函数:可复用的作业班组


文档摘要

1.3 函数:可复用的作业班组 本节摘要:函数是把一段处理流程编成固定班组的手段:起个名字、定好入口参数与出口结果,之后随时点名调用。本节沿 1.2 的表单校验场景继续推进,讲参数传递、类型声明、作用域与匿名函数,最后把散落的校验代码整编成一个可复用的校验班组——它是第 2.1 节面向对象的前身。 重复的校验代码该往哪放 上一节结束时,报名表单的校验长长地摊在主流程里:姓名判空、年龄卡范围,一个页面这么写还行,十个页面都这么写,改一处就得追十处。服务端开发的朴素道理是:同一段逻辑出现第二次,就该编成班组(函数),主流程只负责点名。这就是函数存在的理由——不神秘,就是给代码块命名并复用。 第一行读法: 声明、名字 、括号里是参数清单、冒号后面 是返回值类型声明。类型声明不是装饰——1.

1.3 函数:可复用的作业班组

本节摘要:函数是把一段处理流程编成固定班组的手段:起个名字、定好入口参数与出口结果,之后随时点名调用。本节沿 1.2 的表单校验场景继续推进,讲参数传递、类型声明、作用域与匿名函数,最后把散落的校验代码整编成一个可复用的校验班组——它是第 2.1 节面向对象的前身。

重复的校验代码该往哪放

上一节结束时,报名表单的校验长长地摊在主流程里:姓名判空、年龄卡范围,一个页面这么写还行,十个页面都这么写,改一处就得追十处。服务端开发的朴素道理是:同一段逻辑出现第二次,就该编成班组(函数),主流程只负责点名。这就是函数存在的理由——不神秘,就是给代码块命名并复用。

<?php // 最简单的班组:输入一个值,给一个结论 function isBlank(string $value): bool { return trim($value) === ''; } var_dump(isBlank(' ')); // bool(true) var_dump(isBlank('张三')); // bool(false)

第一行读法:function 声明、名字 isBlank、括号里是参数清单、冒号后面 bool 是返回值类型声明。类型声明不是装饰——1.2 节开过严格模式,参数类型不对会当场报错,班组边界由此变得清晰。

参数、返回值与作用域

参数可以带默认值,调用时省略即取默认;PHP 8 还支持按名字传参,跳过中间的默认项直接指定目标参数:

<?php function ticket(int $from, int $to, string $seat = '二等座', bool $fast = true): string { return "{$from} 到 {$to} 的{$seat}" . ($fast ? '(直达)' : '(普快)'); } echo ticket(1001, 2002), "\n"; // 1001 到 2002 的二等座(直达) echo ticket(1001, 2002, fast: false), "\n"; // 1001 到 2002 的二等座(普快)

参数数量不定时用可变参数,PHP 把它们收进数组;返回值不止一种可能时用联合类型声明。这两件在处理请求参数时都常用:

<?php function sum(int ...$nums): int { return array_sum($nums); } echo sum(1, 2, 3), "\n"; // 6 function findUser(int|string $key): ?array { // ?array 表示"数组或 null" // 实际项目里这里查数据库,演示直接返回 null return null; } var_dump(findUser(7)); // NULL

作用域是新手最容易摔的一跤:函数内部默认看不见外部的变量。想在班组里引用外面的东西,要么作为参数传进来(首选,边界最清楚),要么用 static 让函数在多次调用间记住某个值(比如计数器):

<?php function nextId(): int { static $id = 0; // 只在首次调用时初始化,随后跨调用保留 return ++$id; } echo nextId(), nextId(), nextId(), "\n"; // 123

参数传入是默认选择的原因很直接:依赖都摆在签名上,谁调用、传什么,一眼可查;藏在函数体里的外部依赖,排查问题时得钻进去翻。

匿名函数与箭头函数:随身携带的小工具

并非所有逻辑都值得起名字。PHP 支持匿名函数(闭包)与更短的箭头函数,两者最常出现在"把一段行为当参数传给别的函数"的场景里,比如数组加工:

<?php $fares = [120, 80, 240, 60]; $over100 = array_filter($fares, function (int $f): bool { return $f >= 100; }); print_r($over100); // [0 => 120, 2 => 240] $withTax = array_map(fn(int $f): int => (int) round($f * 1.1), $fares); print_r($withTax); // [132, 88, 264, 66]

两种写法的差别在捕获外部变量:匿名函数要用 use 显式搬进来,箭头函数自动捕获父作用域变量(按值)。演示对比:

<?php $rate = 0.1; $old = function (int $f) use ($rate): float { return $f * (1 + $rate); }; $new = fn(int $f): float => $f * (1 + $rate); echo $old(100), ' ', $new(100), "\n"; // 110 110,行为一致

选择建议就一句:表达式能一行写完用箭头函数,逻辑长了用命名函数。匿名函数体一旦超过几行,取名成正式班组反而更好找。

实战:把校验逻辑整编成班组

回到报名表单,把 1.2 节摊在主流程的校验整编成一组函数。输入输出都在签名上,主流程瘦成三行:

<?php declare(strict_types=1); function checkName(string $name): ?string { return trim($name) === '' ? '姓名不能为空' : null; } function checkAge(int $age): ?string { return ($age < 1 || $age > 120) ? '年龄不在合理范围' : null; } /** 跑一遍全部检查项,返回错误清单(空数组即通过) */ function validate(array $input): array { $checks = [ checkName((string) ($input['name'] ?? '')), checkAge((int) ($input['age'] ?? 0)), ]; return array_filter($checks, fn(?string $e): bool => $e !== null); } $errors = validate($_POST ?? []); if ($errors) { foreach ($errors as $e) { echo "退件:{$e}\n"; } exit(1); } echo "受理成功\n";

这个 validate 骨架的妙处在扩展性:以后加"邮箱格式""手机号格式",只需再写一个 check 函数、往数组里添一行,主流程纹丝不动。第 2.1 节把这类"同一批规则"升级成类的属性与方法时,你会看到今天的班组如何变成固定岗位。中途若想验证某条规则,单独调用对应的 check 函数即可,不必跑全流程。

常见坑

⚠️ 函数里偷偷用外部变量:全局变量一旦进函数体,复用到别的页面就可能翻车,依赖一律从参数走。

⚠️ 返回值类型漂移:有时返回数组、有时返回 false,调用方写得提心吊胆;统一"返回数组(可能为空)或可空类型",调用方代码立刻清爽。

本节要点回顾

  • 函数即命名复用:逻辑第二次出现就编班组,签名(参数与返回类型)就是班组的工作边界。
  • 严格模式配类型声明:把类型错误提前到调用瞬间暴露,排错成本骤降。
  • 作用域默认封闭:依赖从参数进、结果从返回值出,static 只留给计数这类极少数场景。
  • 闭包与箭头函数:把行为当参数传时使用,一行表达式选箭头函数,逻辑复杂就转正命名。
  • 校验班组骨架:检查项返回错误信息或 null,汇入数组统一退件——这个骨架会一路用到框架章。

下一节看数组:请求数据在 PHP 里几乎都以数组形态出现,它是行李清单,也是接下来所有批处理动作的对象。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U