外观
PHP函数
约 4162 字大约 14 分钟
PHPWeb后端入门
2026-03-04
张华同学作为班里的学习委员,最近眉头紧锁。面对班级里堆积如山的成绩单,他每次都需要手动计算平均分、核对权重、评定等级,这让他深刻感受到人工计算的痛苦。为了将自己从繁琐的计算中解放出来,他决定利用 PHP 语言开发一个“学生成绩计算器”。借此机会,我们也将跟随张华的脚步,一起探索编程世界中能够极大提升效率的秘密武器——函数。
1. 初识函数:代码界的“打工人”
💡 什么是函数?
在编程的浩瀚宇宙中,函数就像是技艺高超的 工匠 ,每一个都拥有自己独一无二的姓名和专属技能。它们不抱怨、不摸鱼,只要你下达指令,它们就会精准执行。 所谓函数,就是将一段具有独立功能的代码块封装起来,以便在不同的地方重复调用。它就像是一个黑盒,你只需关心放进去什么,以及拿出来什么。
我们可以通过一个简单的互动动画来直观理解 内部算法逻辑 的运转过程:
2
5
⚙️
⚙️
💤 等待加工...
函数体: sum(...)
返回值
引入函数能够为我们的 程序设计 带来诸多不可替代的优势:
- 代码重用性:一次编写,多处调用,彻底告别“复制粘贴”的原始时代。
- 代码模块化:将复杂的大问题拆解为若干个小模块,如同搭积木般构建系统。
- 易读性与可维护性:让程序的排错和单元测试变得轻松惬意。
依据不同的标准,我们可以对这些“打工人”进行多维度分类:
- 按来源:自定义函数 vs 预定义函数 (系统自带)
- 按参数:有参函数 vs 无参函数
- 按结果:有返回值的函数 vs 无返回值的函数
2. 自定义函数:打造你的专属工具
既然市面上买不到完全符合心意的工具,那我们就亲自动手,利用关键字 function 来打造专属的自定义工具。
函数的定义
在语言内部,创建一个函数需要遵循特定的语法规则。其基本结构如下方代码所示:
<?php
// function 是声明函数的关键字
// functionName 是你为这个函数起的唯一名字(建议使用小驼峰命名法)
function functionName([parameter1, parameter2, ...])
{
// 这里是函数体,包含具体的执行逻辑
return $result; // 可选的返回值
}
?>例如,我们可以定义一个简单的无参函数来向世界问好:
<?php
function sayHello() {
echo "Hello World!";
}
// 调用示例
sayHello();
?>函数的参数
为了让函数更具灵活性,我们需要向其传递数据,这些数据就是 参数 。参数分为多种类型,玩法各不相同。
- 必需参数:调用时绝对不能缺少的,缺少就会引发系统的无情警告。
- 可选参数:我们在定义时赋予它一个默认值,如果调用者忘记提供,它就会默默使用备胎数据。
- 可变数量参数:遇到不确定数量的数据时,利用
...语法(splat运算符),将涌来的数据统统打包进一个数组中。
<?php
// 使用 ... 语法接收任意数量的参数
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $number) {
$total += $number;
}
return $total;
}
echo sum(1, 2, 3, 4); // 用法示例,输出 10
?>💡 进阶:值传递与引用传递
默认情况下,参数是通过“值传递”的,相当于给函数发了一份数据的复印件。如果我们想让函数直接修改原文件,就需要用到 引用传递,只需在参数前加上 & 符号即可。
来看看下面这个直观的值传递 vs 引用传递演示:
按值传递:复印件模式
函数获得的是原数据的“复印件”,内部修改不影响外部原件。
外部存储
$a: Hello
函数内部存储
等待传参...
函数的调用
唤醒沉睡函数的方式被称为 函数调用 。最常见的是直接调用,直接呼唤其大名即可。当函数携带着劳动成果(返回值)归来时,我们可以使用赋值调用,将结果暂存到变量中。
更有趣的是 嵌套调用 ,即在一个函数的肚子里召唤另一个函数。而当一个函数疯狂到开始呼唤它自己时,这就形成了传说中的 递归调用。在处理诸如阶乘计算等层层递进的问题时,递归往往能发挥奇效。
n!=n×(n−1)!
这种抽象数学公式转换为代码逻辑时,简直是为递归量身定制的。
<?php
// 1. 直接/赋值调用
$result = sum(5, 10);
echo "5 + 10 = " . $result;
// 2. 嵌套调用演示
function formatResult($val) {
return "【" . $val . "】";
}
function printSum($a, $b) {
echo formatResult(sum($a, $b)); // 在printSum中调用了formatResult和sum
}
// 3. 递归调用演示(计算阶乘)
function factorial($n) {
if ($n <= 1) return 1; // 递归出口
return $n * factorial($n - 1); // 疯狂地呼唤自己
}
echo "5的阶乘是: " . factorial(5); // 输出 120
?>变量的作用域
🚧 变量的“领地意识”
在代码的世界里,变量也是有领地意识的,这就是 作用域 。在函数外部定义的全局变量,虽然名气大,但函数内部一般不买账,无法直接访问。而在函数内部诞生的局部变量,一旦函数执行完毕,就会被系统无情销毁,仿佛从未存在过。
如果非要在函数内部动用外部的全局变量,必须亮出 global 令牌,或者求助于 $GLOBALS 超全局数组。让我们通过这个互动组件来亲身体验:
🌍 全局作用域 (主程序)
在函数外部诞生的变量,属于全局天地。
📦 局部作用域 (函数内部)
函数就是一个黑盒,它有自己独立的地盘。
函数未调用或已结束执行
💡
点击上方按钮,一步步观察变量作用域的神奇变化。
匿名函数
有时候我们只需要一个临时工,连名字都懒得起,这就是 闭包函数 (匿名函数)。它常被作为参数传递给其他高级函数。如果要让匿名函数借用外部的变量,记得使用 use 语句牵线搭桥。
<?php
// 1. 基本匿名函数
$greet = function($name) {
echo "你好," . $name . "!<br>";
};
$greet("PHP大侠"); // 调用匿名函数
// 2. 作为参数传递(常见于数组处理,如 array_map)
$numbers = [1, 2, 3, 4];
$squared = array_map(function($n) {
return $n * $n;
}, $numbers);
print_r($squared); // 输出: Array ( [0] => 1 [1] => 4 [2] => 9 [3] => 16 )
// 3. 使用 use 引入外部变量
$taxRate = 0.05;
$calcTax = function($amount) use ($taxRate) {
return $amount * $taxRate;
};
echo "100元的税费是: " . $calcTax(100); // 输出 5
?>3. 数据搬运工:处理 GET 请求与预定义变量
当用户在浏览器中点击链接、提交表单(方式为GET)或者直接在地址栏输入带有参数的网址时,都会触发一次 GET请求 。这是一种向服务器索要数据的轻量级通信方式。
在服务器端,系统贴心地准备了一个名为 $_GET 的超全局变量。它本质上是一个关联数组,默默收集着通过 URL 传递过来的所有数据。比如用户访问了 index.php?name=ZhangHua,我们在代码中只需优雅地取出 $_GET['name'],就能获取到“ZhangHua”这个值。
4. 站在巨人的肩膀上:预定义函数
除了自己造轮子,语言本身为我们提供了一个庞大且免费的内置函数宝库。熟练掌握它们,能让你的开发效率起飞。
📚 点击展开:常用的内置函数手册
变量函数
在与各种数据打交道时,类型检测是必不可少的环节。
empty():专门用来揪出那些不存在、为零或为空的家伙。isset():负责确认变量是否已经安家落户且不为null。unset():如果看某个变量不顺眼,可直接将其从内存中抹除。print_r()和var_dump():想要窥探变量的内部构造,它们是你最忠实的侦探。
数学函数
处理数字时,数学运算函数库是我们的好帮手。
abs():取绝对值。round():负责四舍五入。ceil():暴力向上取整,默默守护着数据的精度。
时间和日期函数
时间是一根看不见的线,而 UNIX时间戳 就是时间轴上的刻度尺。
time():能瞬间获取此刻的秒数。date():面对冷冰冰的数字刻度,施展格式化魔法,将其转换为人类能看懂的形式。
5. 安全扩展:警惕代码中的“内鬼”
🚨 安全警告
在搭建强大功能的同时,我们绝不能忽视 网络安全 。有些函数虽然能力通天,但如果不加限制,就会成为黑客入侵的后门。
危险一:代码执行与命令注入
比如那个臭名昭著的 eval() 函数,它能把传进来的任意字符串当做 PHP 代码执行。如果你将用户输入直接喂给它,黑客就可以长驱直入。
<?php
// ❌ 极度危险的示例(绝对不要这样写!)
$userInput = $_GET['code'];
// 如果黑客访问: index.php?code=system('rm -rf /');
eval($userInput); // 😱 后果:黑客执行了系统命令,你的服务器文件瞬间被清空,完全沦陷!
?>防范手段:在生产环境中,我们通常会在 php.ini 配置文件中通过 disable_functions = eval,system,exec 强行将这些危险函数封印。
危险二:明文或过时的密码存储
此外,在涉及密码存储时,直接存明文或者用 md5() 和 sha1() 这些老掉牙的算法早已不再安全。黑客只需几秒钟就能通过“彩虹表”反查出密码。
<?php
// ❌ 危险的古代做法:
$unsafe_pwd = md5("123456");
// 后果:黑客脱库后,拿到这个哈希值可以在彩虹表网站瞬间查出原密码是123456
// ✅ 现代网络安全推荐做法:
// 1. 注册时:使用 password_hash 生成带盐的哈希值
$hashed_pwd = password_hash("123456", PASSWORD_DEFAULT);
// 存入数据库的是形如 $2y$10$abcdefghijklmnopqrst... 这种每次都不同、无法反解的乱码
// 2. 登录时:使用 password_verify 核对真相
if (password_verify("123456", $hashed_pwd)) {
echo "密码正确,允许登录!";
} else {
echo "密码错误!";
}
?>如今的密码散列届,是 password_hash() 和 password_verify() 兄弟俩的天下,它们内置加盐机制,让破解变得难如登天。
6. 综合项目实战:学生成绩计算器
纸上得来终觉浅,绝知此事要躬行。现在,让我们将上述零碎的知识点融合在一起,帮助张华完成他的旷世巨作——学生成绩计算器。
我们的需求很明确:收集用户的平时成绩和期末成绩,通过自定义函数进行数据验证和权重计算(平时占40%,期末占60%),最后评定出令人心跳加速的成绩等级。
calc.php
<?php
// 【任务 1】:定义计算加权总分的专属函数
function calcWeightedTotal($assignScore, $examScore)
{
// 利用算术运算符分配权重
$wAssignScore = $assignScore * 0.4;
$wExamScore = $examScore * 0.6;
return $wAssignScore + $wExamScore; // 返回计算后的加权总分
}
// 【任务 2】:定义评估成绩等级的逻辑函数
function evalGrade($wTotal)
{
// 使用条件语句进行等级划分
if ($wTotal >= 90) return "优秀";
if ($wTotal >= 80) return "良好";
if ($wTotal >= 70) return "中等";
if ($wTotal >= 60) return "及格";
return "不及格";
}
// 【任务 3】:核心处理流程(假设用户已通过 GET 请求提交数据)
// 使用 isset() 函数确保数据存在,避免报错
if (isset($_GET['aScore']) && isset($_GET['eScore'])) {
$aScore = $_GET['aScore'];
$eScore = $_GET['eScore'];
// 严谨的数据范围验证
if ($aScore < 0 || $aScore > 100 || $eScore < 0 || $eScore > 100) {
echo "警告:分数必须在0~100之间,请不要试图挑战系统的底线!";
exit;
}
// 调用自定义函数
$wTotal = calcWeightedTotal($aScore, $eScore);
$grade = evalGrade($wTotal);
// 优雅地输出最终结果
echo "亲爱的同学,你的加权总分是:{$wTotal}分<br>";
echo "你的最终成绩等级被评定为:{$grade}<br>";
} else {
echo "请在URL中传入你的成绩参数哦!";
}
?>用法说明:将上述代码保存为PHP文件,通过浏览器访问并在地址栏末尾拼接 ?aScore=85&eScore=92 即可看到魔法般的结果。

项目扩展:给计算器穿上“可视化的网页外衣”
纯靠在地址栏手动输入 ?aScore=85&eScore=92 还是有点极客。现在让我们使用 HTML表单 来重构这个计算器,让它变成一个对普通用户更友好的网页应用。
这需要前后端的结合:
- 前端:绘制一个表单,提交方式设置为
GET。 - 后端 (PHP):复用上述的功能逻辑。默认情况下给用户展示表单输入框,如果接收到了数据,则隐藏表单直接展示成绩计算结果和重试按钮。
1. 前端 HTML 交互界面
这是呈现给用户的可视化表单代码:
index.php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>学生成绩智能计算器</title>
<style>
body { font-family: sans-serif; padding: 20px; background: #f5f6fa; }
.card { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 400px; margin: 0 auto;}
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; }
button { background: #3498db; color: white; border: none; padding: 10px 20px; cursor: pointer; border-radius: 4px; width: 100%; font-size: 16px;}
button:hover { background: #2980b9; }
.result-box { text-align: center; font-size: 1.2rem; }
.grade { font-size: 2rem; color: #e74c3c; font-weight: bold; margin: 15px 0; }
.back-btn { background: #95a5a6; margin-top: 20px;}
</style>
</head>
<body>
<div class="card">
<!-- 下方的 PHP 代码负责决定显示哪一块内容 -->
<?php include 'logic.php'; ?>
</div>
</body>
</html>
2. 后端 PHP 核心控制逻辑
点击展开下方代码,看看 PHP 是如何通过判断 $_GET 超全局变量的存在与否,来决定在网页上输出哪一块内容的:
🛠️ 点击展开:查看完整的 PHP 后端切换逻辑 (logic.php)
logic.php
<?php
// 此前定义的两个核心计算函数(复用)
function calcWeightedTotal($assignScore, $examScore) {
return ($assignScore * 0.4) + ($examScore * 0.6);
}title="logic.php"
function evalGrade($wTotal) {
if ($wTotal >= 90) return "优秀";
if ($wTotal >= 80) return "良好";
if ($wTotal >= 70) return "中等";
if ($wTotal >= 60) return "及格";
return "不及格";
}
// 核心控制流
if (isset($_GET['aScore']) && isset($_GET['eScore'])) {
// -----------------------------------------
// 场景 A:接收到了参数,展示计算结果页面
// -----------------------------------------
$aScore = floatval($_GET['aScore']); // floatval 可过滤恶意非数字字符
$eScore = floatval($_GET['eScore']);
if ($aScore < 0 || $aScore > 100 || $eScore < 0 || $eScore > 100) {
echo "<h3 style='color:red;'>⚠️ 错误:分数必须在0~100之间!</h3>";
echo "<button class='back-btn' onclick='history.back()'>返回修改</button>";
} else {
$wTotal = calcWeightedTotal($aScore, $eScore);
$grade = evalGrade($wTotal);
// 动态生成渲染的结果页面
echo "<div class='result-box'>";
echo "<h2>🎉 计算完成 🎉</h2>";
echo "<p>您的加权总分为:<strong>{$wTotal}分</strong></p>";
echo "<p>最终评定等级:</p>";
echo "<div class='grade'>{$grade}</div>";
echo "<button class='back-btn' onclick='window.location.href=`index.php`'>重新计算</button>";
echo "</div>";
}
} else {
// -----------------------------------------
// 场景 B:没有参数,默认展示输入表单页面
// -----------------------------------------
?>
<h2 style="text-align:center; margin-top:0;">成绩计算器</h2>
<!-- 表单 action 为空代表提交到当前页面 -->
<form action="" method="GET">
<div class="form-group">
<label>平时成绩 (占40%)</label>
<input type="number" name="aScore" placeholder="请输入0-100的数字" required min="0" max="100">
</div>
<div class="form-group">
<label>期末成绩 (占60%)</label>
<input type="number" name="eScore" placeholder="请输入0-100的数字" required min="0" max="100">
</div>
<button type="submit">立即计算</button>
</form>
<?php
} // 结束 if...else 块
?>
7. 课程知识点总结
| 知识模块 | 核心概念解析 | 实战应用场景 |
|---|---|---|
| 自定义函数 | function 封装与调用,参数支持默认值 | 封装学生成绩的加权计算公式,方便多处复用 |
| 参数传递方式 | 值传递(发复印件)与引用传递(发原件) | 高效处理复杂数据并在函数内直接修改原变量 |
| 变量作用域 | 局部变量不可见,global 穿透作用域 | 避免不同函数模块之间的命名冲突 |
| 匿名函数 | 闭包机制与 use 引入外部变量 | 配合数组处理函数如 array_map 进行优雅的批量计算 |
| PHP 安全防线 | eval() 的危害,密码哈希的最佳实践 | 使用 password_hash() 保障电商系统的账户安全 |
8. 后续进阶建议
- 思考:为什么在现代密码学中强烈推荐
password_hash()而非传统的md5()进行密码加密保存? - 挑战:编写一个递归函数,模拟遍历一个具有多层级深度的无限级分类目录结构(例如文件夹树状图)。
