第一章:函数?就是你每天都在用的“生活外挂”!
先别被“函数”这俩字吓到!你早就是函数高手了——每天早上你对Siri喊“Hey Siri,今天天气怎样?”的时候,就已经完成了一次完美的函数调用!
生活中的函数类比:
- 点奶茶函数:
makeBubbleTea('芝士奶盖', '少冰', '三分糖') - 发朋友圈函数:
postMoments('深夜加班配图', ['屏蔽老板', '仅同龄人可见']) - 打游戏大招:
releaseUltimateSkill('安琪拉', '大招', '敌方水晶')
看出来了吗?函数就是一组打包好的指令,你告诉它要干嘛(输入),它给你结果(输出),中间过程?你根本不用管!
第二章:解剖你的第一个函数——比拆快递还简单!
2.1 函数的“出生证明”语法
function 你的函数名() {
// 这里写你要它干的事
return '干完了!'; // 可选,给你个回应
}
等等,这语法怎么看着有点...呃...直白?没错!PHP就是这么接地气:
function sayHello() {
echo "哎哟喂,你来啦!<br>";
echo "今天代码写完了吗?<br>";
return "问候完毕!";
}
// 召唤这个函数!
sayHello(); // 页面就会打印那两句话
实战1:打造你的第一个有用函数
function calculateDeliveryFee($distance, $isRaining = false) {
$baseFee = 5; // 起步价5块
$perKm = 2; // 每公里2块
$fee = $baseFee + ($distance * $perKm);
if ($isRaining) {
$fee += 10; // 下雨天加收10块辛苦费
echo "温馨提示:雨天配送,小哥不易,加收10元<br>";
}
if ($distance > 20) {
$fee *= 1.5; // 超过20公里加收50%
echo "超远距离配送警告!<br>";
}
return $fee;
}
// 试试看
echo "配送费:" . calculateDeliveryFee(5) . "元<br>"; // 15元
echo "暴雨配送费:" . calculateDeliveryFee(3, true) . "元<br>"; // 21元
第三章:参数传递的“宫心计”——值、引用和默认值的爱恨情仇
3.1 传值 vs 传引用(这不是在选快递方式!)
// 传值:给你一份复印件,你随便改,原件不变
function addMustache($photo) {
$photo .= "加上两撇小胡子";
return $photo;
}
$mySelfie = "帅气的自拍照";
echo addMustache($mySelfie); // "帅气的自拍照加上两撇小胡子"
echo $mySelfie; // 还是"帅气的自拍照",没被加胡子!
// 传引用:把原件给你改,改完全世界都看到了
function realAddMustache(&$photo) {
$photo .= "加上两撇小胡子";
}
$mySelfie = "帅气的自拍照";
realAddMustache($mySelfie);
echo $mySelfie; // "帅气的自拍照加上两撇小胡子"——永久毁容了!
什么时候用传引用?
当你需要函数直接修改原始数据时,比如:
function promoteToVIP(&$user) {
$user['level'] = 'VIP';
$user['discount'] = 0.8;
$user['privileges'] = ['提前购', '专属客服', '免运费'];
}
$customer = ['name' => '张三', 'level' => '普通'];
promoteToVIP($customer);
print_r($customer); // 张三已经是尊贵的VIP了!
3.2 默认参数:函数的“保底选项”
function makeCoffee($type = '美式', $size = '中杯', $sugar = '正常糖') {
return "您点的{$size}{$type},{$sugar},请稍等!";
}
echo makeCoffee(); // "您点的中杯美式,正常糖,请稍等!"
echo makeCoffee('拿铁', '超大杯'); // "您点的超大杯拿铁,正常糖,请稍等!"
echo makeCoffee('卡布奇诺', '大杯', '少糖'); // "您点的大杯卡布奇诺,少糖,请稍等!"
坑点预警: 默认参数必须放后面!
// 这样写会报错!
// function makeCoffee($type = '美式', $size) { ... }
// 正确写法:带默认值的往后站
function makeCoffee($size, $type = '美式', $sugar = '正常糖') {
// 实现
}
第四章:返回值的72变——不只是一句“好了”
4.1 返回各种奇葩东西
// 1. 返回数组:打包一堆数据
function getUserInfo($userId) {
// 假装查了数据库
return [
'id' => $userId,
'name' => '键盘侠' . $userId,
'level' => rand(1, 100),
'lastLogin' => date('Y-m-d H:i:s'),
'achievements' => ['日更博主', 'bug制造者', '咖啡消耗者']
];
}
$user = getUserInfo(666);
echo "欢迎回来," . $user['name'] . "!";
echo "你的成就:" . implode('、', $user['achievements']);
// 2. 返回布尔值:成功还是失败
function checkPassword($input, $hashed) {
// 假装有加密验证
return $input === $hashed; // 实际应该用password_verify()
}
if (checkPassword($_POST['password'], $user['password_hash'])) {
echo "登录成功!";
} else {
echo "密码错误!";
}
// 3. 返回函数本身?!(高阶玩法)
function createMultiplier($factor) {
// 这个函数返回另一个函数!
return function($number) use ($factor) {
return $number * $factor;
};
}
$double = createMultiplier(2);
$triple = createMultiplier(3);
echo $double(5); // 10
echo $triple(5); // 15
echo $double($triple(4)); // 24 (套娃开始)
第五章:匿名函数和闭包——没有名字的“特种兵”
匿名函数就是没名字的函数,直接赋值给变量:
$greet = function($name) {
return "嘿,{$name}!今天写bug了吗?";
};
echo $greet('老王'); // "嘿,老王!今天写bug了吗?"
闭包:能记住出生环境的匿名函数
function createCounter() {
$count = 0; // 这个变量会被记住
return function() use (&$count) {
$count++;
return "这是第{$count}次调用";
};
}
$counter = createCounter();
echo $counter(); // "这是第1次调用"
echo $counter(); // "这是第2次调用"
echo $counter(); // "这是第3次调用"
// $count变量其实还活着,只是外面访问不到!
实际应用:数组排序自定义规则
$products = [
['name' => 'PHP从入门到放弃', 'price' => 68, 'sales' => 250],
['name' => 'JavaScript防秃指南', 'price' => 88, 'sales' => 180],
['name' => 'CSS禅意花园', 'price' => 59, 'sales' => 320]
];
// 按价格排序
usort($products, function($a, $b) {
return $a['price'] <=> $b['price']; // 太空船运算符,PHP7+
});
// 按综合评分排序(价格低好,销量高好)
usort($products, function($a, $b) {
$scoreA = ($a['sales'] * 0.7) - ($a['price'] * 0.3);
$scoreB = ($b['sales'] * 0.7) - ($b['price'] * 0.3);
return $scoreB <=> $scoreA; // 降序
});
第六章:可变函数和“神奇”的__invoke()
6.1 可变函数:把函数名存起来用
function attack() {
return "普通攻击!";
}
function magicAttack() {
return "魔法攻击!";
}
function heal() {
return "回复生命值!";
}
$action = 'attack';
echo $action(); // "普通攻击!"
// 根据情况选择技能
$availableActions = ['attack', 'magicAttack', 'heal'];
$chosenAction = $availableActions[rand(0, 2)];
echo $chosenAction(); // 随机释放一个技能
6.2 __invoke():让对象可以当函数用
class ChatBot {
private $name;
private $mood = 'happy';
public function __construct($name) {
$this->name = $name;
}
public function setMood($mood) {
$this->mood = $mood;
}
// 当把对象当函数调用时,自动执行这个
public function __invoke($message) {
$responses = [
'happy' => ["{$this->name}:今天心情不错哦!", "{$this->name}:你说得对!"],
'angry' => ["{$this->name}:不想说话!", "{$this->name}:你行你上!"],
'tired' => ["{$this->name}:zzZ...", "{$this->name}:改天再聊吧"]
];
$responseList = $responses[$this->mood];
return $responseList[array_rand($responseList)] . "(你说了:{$message})";
}
}
$bot = new ChatBot('码农小助手');
echo $bot("今天写什么代码?"); // "码农小助手:今天心情不错哦!(你说了:今天写什么代码?)"
$bot->setMood('tired');
echo $bot("这个bug怎么修?"); // "码农小助手:zzZ...(你说了:这个bug怎么修?)"
第七章:实战!构建你的“梗图生成器”函数库
让我们把所有知识用起来,写个实际项目:
<?php
/**
* 梗图生成器函数库
* 功能:根据输入生成程序员梗图描述
*/
// 1. 核心生成函数
function generateMemeTemplate($scene, $characters = ['程序员', '产品经理']) {
$templates = [
'meeting' => "会议室中,{$characters[0]}指着白板:'这里明明应该...',{$characters[1]}微笑点头,然后在需求文档里写下完全不同的话。",
'debug' => "凌晨3点,{$characters[0]}盯着屏幕:'这怎么可能...',突然发现是少了个分号。",
'deploy' => "上线前夕,{$characters[1]}:'就加个小功能',{$characters[0]}看着已成废墟的代码库。",
'test' => "测试人员:'这个功能有问题',{$characters[0]}:'在我电脑上是好的'。",
];
return $templates[$scene] ?? "{$characters[0]}和{$characters[1]}相视一笑,心中各有算盘。";
}
// 2. 随机生成器(使用闭包)
function createRandomGenerator($seed = null) {
$usedTemplates = [];
$count = 0;
return function() use (&$usedTemplates, &$count, $seed) {
$scenes = ['meeting', 'debug', 'deploy', 'test'];
$roles = [
['程序员', '产品经理'],
['前端', '后端'],
['新手', '架构师'],
['你', '你写的bug']
];
// 用种子保证可重复性
if ($seed) {
srand($seed + $count);
}
$scene = $scenes[array_rand($scenes)];
$rolePair = $roles[array_rand($roles)];
$count++;
$meme = generateMemeTemplate($scene, $rolePair);
// 记录已使用的
$usedTemplates[] = $meme;
return [
'id' => $count,
'meme' => $meme,
'scene' => $scene,
'characters' => $rolePair,
'usedCount' => count($usedTemplates)
];
};
}
// 3. 批量生成器
function generateMemeBatch($count, $options = []) {
$defaultOptions = [
'allowDuplicates' => false,
'seed' => null,
'theme' => 'all'
];
$options = array_merge($defaultOptions, $options);
$generator = createRandomGenerator($options['seed']);
$results = [];
$uniqueChecker = [];
for ($i = 0; $i < $count; $i++) {
do {
$result = $generator();
$hash = md5($result['meme']);
} while (!$options['allowDuplicates'] && in_array($hash, $uniqueChecker));
$uniqueChecker[] = $hash;
$results[] = $result;
// 如果指定了主题,过滤一下
if ($options['theme'] !== 'all') {
$results = array_filter($results, function($item) use ($options) {
return strpos($item['meme'], $options['theme']) !== false;
});
}
}
return [
'total' => count($results),
'memes' => $results,
'generatedAt' => date('Y-m-d H:i:s'),
'statistics' => [
'unique' => count($uniqueChecker),
'mostCommonScene' => array_reduce($results, function($carry, $item) {
$carry[$item['scene']] = ($carry[$item['scene']] ?? 0) + 1;
return $carry;
}, [])
]
];
}
// 4. 使用示例
echo "<h2>随机生成5个程序员梗图:</h2>";
$batch = generateMemeBatch(5, ['theme' => '程序员']);
foreach ($batch['memes'] as $meme) {
echo "<div style='border:1px solid #ccc; margin:10px; padding:10px;'>";
echo "<strong>#" . $meme['id'] . "</strong> ";
echo $meme['meme'];
echo "<br><small>场景:" . $meme['scene'] . " | 角色:" .
implode(' vs ', $meme['characters']) . "</small>";
echo "</div>";
}
echo "<hr>";
echo "<h3>生成统计:</h3>";
echo "共生成:" . $batch['total'] . "个<br>";
echo "生成时间:" . $batch['generatedAt'] . "<br>";
echo "场景分布:<pre>" . print_r($batch['statistics']['mostCommonScene'], true) . "</pre>";
// 5. 高级功能:可调用对象
class MemeGenerator {
private $history = [];
private $favorites = [];
public function __invoke($scene = null, $characters = null) {
if ($scene) {
$result = generateMemeTemplate($scene, $characters);
} else {
$gen = createRandomGenerator();
$result = $gen()['meme'];
}
$this->history[] = [
'meme' => $result,
'time' => microtime(true)
];
return $result;
}
public function addToFavorites($index) {
if (isset($this->history[$index])) {
$this->favorites[] = $this->history[$index];
return "已收藏到第" . count($this->favorites) . "个收藏夹";
}
return "找不到这个梗图";
}
public function getHistory() {
return $this->history;
}
public function getFavorites() {
return $this->favorites;
}
}
// 使用可调用对象
echo "<hr><h2>使用可调用对象版本:</h2>";
$myGenerator = new MemeGenerator();
echo $myGenerator('debug', ['你', '昨天的你']);
echo "<br>";
echo $myGenerator(); // 随机生成
echo "<br>";
$myGenerator->addToFavorites(0);
echo "收藏数量:" . count($myGenerator->getFavorites());
?>
第八章:函数的最佳实践和常见“翻车”现场
8.1 你的函数是不是太“胖”了?
反面教材:
function processUserData($userId) {
// 这函数干了太多事!
// 1. 查数据库
$user = queryDatabase("SELECT * FROM users WHERE id = $userId");
// 2. 计算统计数据
$stats = calculateStats($user);
// 3. 发送邮件
sendEmail($user['email'], "你的数据更新了", $stats);
// 4. 写日志
writeLog("用户{$userId}的数据已处理");
// 5. 更新缓存
updateCache("user_{$userId}", $user);
// 6. 还有更多...
return true;
}
改造后:
function processUserData($userId) {
$user = getUserById($userId);
$stats = calculateUserStats($user);
notifyUser($user, $stats);
logUserAction($userId, 'data_processed');
return true;
}
// 每个小函数只做一件事
function getUserById($id) { /* 只查数据库 */ }
function calculateUserStats($user) { /* 只算数据 */ }
function notifyUser($user, $stats) { /* 只发通知 */ }
// ... 以此类推
8.2 错误处理:别让你的函数“静悄悄”地失败
// 糟糕:失败时没反应
function divide($a, $b) {
return $a / $b; // $b为0时会出警告
}
// 好一点:返回特殊值
function safeDivide($a, $b) {
if ($b == 0) {
return INF; // 无穷大
}
return $a / $b;
}
// 更好:抛出异常
function strictDivide($a, $b) {
if ($b == 0) {
throw new InvalidArgumentException("除数不能为0!");
}
return $a / $b;
}
// 使用示例
try {
echo strictDivide(10, 0);
} catch (Exception $e) {
echo "出错了:" . $e->getMessage();
// 可以记录日志、通知用户等
}
第九章:Type Hinting和返回类型声明——让函数更“靠谱”
PHP7+的新特性,让你的函数接口更清晰:
// 指定参数类型和返回类型
function calculateOrderTotal(
array $items,
float $discount = 0.0,
?User $user = null // ?表示可以为null
): float { // : float 指定返回浮点数
$total = 0;
foreach ($items as $item) {
if (!isset($item['price'], $item['quantity'])) {
throw new InvalidArgumentException("商品信息不完整");
}
$total += $item['price'] * $item['quantity'];
}
$total -= $total * $discount;
// 如果是VIP用户,额外折扣
if ($user && $user->isVIP()) {
$total *= 0.9; // 9折
}
return round($total, 2);
}
// 现在调用时,IDE会提示参数类型
// 传错类型会报错,而不是运行时出奇怪问题
第十章:函数调试技巧——当函数“装死”时怎么办
10.1 简单的调试函数
function debugLog($data, $label = '调试') {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$caller = $backtrace[1] ?? ['file' => '未知', 'line' => '未知'];
$output = "[" . date('Y-m-d H:i:s') . "] {$label}";
$output .= " @ {$caller['file']}:{$caller['line']}\n";
$output .= print_r($data, true) . "\n";
$output .= str_repeat("-", 80) . "\n";
// 输出到日志文件
file_put_contents('debug.log', $output, FILE_APPEND);
// 如果是在开发环境,也直接输出
if (php_sapi_name() !== 'cli' && $_SERVER['REMOTE_ADDR'] === '127.0.0.1') {
echo "<pre>" . htmlspecialchars($output) . "</pre>";
}
return $data; // 返回原数据,可以链式调用
}
// 使用示例
$result = debugLog(calculateSomething(), "计算结果")
->then(processData) // 假设有这个方法
->then(debugLog, "处理后的数据");
10.2 性能测试函数
function measureTime(callable $function, ...$args) {
$start = microtime(true);
$startMemory = memory_get_usage();
$result = $function(...$args);
$end = microtime(true);
$endMemory = memory_get_usage();
return [
'result' => $result,
'time' => $end - $start,
'memory' => $endMemory - $startMemory,
'peak_memory' => memory_get_peak_usage()
];
}
// 测试你的函数性能
$stats = measureTime('calculateOrderTotal', $orderItems, 0.1, $user);
echo "耗时:{$stats['time']}秒,内存:{$stats['memory']}字节";
第十一章:完整实战项目——简易聊天机器人函数库
最后,让我们用今天学到的所有知识,写一个完整的聊天机器人:
<?php
/**
* 简易聊天机器人函数库
* 展示函数定义、调用、参数传递、返回值、闭包等所有概念
*/
class ChatBotAdvanced {
private $name;
private $mood = 'neutral';
private $memory = [];
private $skills = [];
public function __construct($name) {
$this->name = $name;
$this->setupDefaultSkills();
}
private function setupDefaultSkills() {
// 技能1:打招呼
$this->addSkill('greet', function($userName) {
$greetings = [
"哟,{$userName}!什么风把你吹来了?",
"{$userName}你好呀!今天写代码了吗?",
"欢迎回来,{$userName}!需要什么帮助?"
];
return $greetings[array_rand($greetings)];
});
// 技能2:讲笑话
$this->addSkill('tellJoke', function() {
$jokes = [
"为什么程序员总是分不清万圣节和圣诞节?因为 Oct 31 == Dec 25!",
"SQL查询走进一家酒吧,走到两张表前问:'我可以JOIN你们吗?'",
"为什么Java开发者戴眼镜?因为他们不会C#!"
];
return $jokes[array_rand($jokes)];
});
// 技能3:天气查询(模拟)
$this->addSkill('checkWeather', function($city) {
$weather = ['晴', '多云', '雨', '雪', '雾', '台风'];
$temp = rand(-10, 40);
return "{$city}的天气:{$weather[array_rand($weather)]},气温{$temp}℃";
});
}
// 添加新技能
public function addSkill($skillName, callable $function) {
$this->skills[$skillName] = $function;
return $this; // 返回$this支持链式调用
}
// 调用技能
public function useSkill($skillName, ...$args) {
if (!isset($this->skills[$skillName])) {
return "{$this->name}:我不会这个技能啊!";
}
// 记录到记忆
$this->memory[] = [
'time' => time(),
'skill' => $skillName,
'args' => $args
];
// 调用函数
return call_user_func_array($this->skills[$skillName], $args);
}
// 让对象可调用:直接对话
public function __invoke($message) {
$responses = $this->analyzeMessage($message);
return $responses[array_rand($responses)];
}
private function analyzeMessage($message) {
$keywords = [
'天气' => 'checkWeather',
'笑话' => 'tellJoke',
'你好' => 'greet',
'嗨' => 'greet'
];
foreach ($keywords as $keyword => $skill) {
if (strpos($message, $keyword) !== false) {
$args = [];
if ($skill === 'checkWeather') {
// 简单提取城市名
preg_match('/.*?(\w+).*天气/', $message, $matches);
$args[] = $matches[1] ?? '北京';
} elseif ($skill === 'greet') {
$args[] = '访客';
}
return [$this->useSkill($skill, ...$args)];
}
}
// 默认回应
return [
"{$this->name}:你说'{$message}'是什么意思?",
"{$this->name}:这个我不太懂呢",
"{$this->name}:你可以问我天气、讲笑话,或者打个招呼"
];
}
// 获取对话历史
public function getMemory($limit = 10) {
return array_slice($this->memory, -$limit);
}
// 设置心情(影响回应风格)
public function setMood($mood) {
$this->mood = $mood;
// 根据心情调整技能
if ($mood === 'happy') {
$this->addSkill('greet', function($userName) {
$happyGreetings = [
"🎉 {$userName}!见到你真高兴!",
"😊 你好呀{$userName}!今天真是美好的一天!",
"🌟 欢迎{$userName}!有什么好事要分享吗?"
];
return $happyGreetings[array_rand($happyGreetings)];
});
}
return $this;
}
}
// 使用示例
echo "<h1>聊天机器人演示</h1>";
$bot = new ChatBotAdvanced('码小聊');
// 方法1:直接调用技能
echo "<h3>直接使用技能:</h3>";
echo $bot->useSkill('greet', '开发者') . "<br>";
echo $bot->useSkill('tellJoke') . "<br>";
echo $bot->useSkill('checkWeather', '上海') . "<br>";
// 方法2:让对象可调用
echo "<h3>对话模式:</h3>";
echo "你:今天北京天气怎样?<br>";
echo "机器人:" . $bot("今天北京天气怎样?") . "<br><br>";
echo "你:讲个笑话吧<br>";
echo "机器人:" . $bot("讲个笑话吧") . "<br><br>";
echo "你:写代码好难<br>";
echo "机器人:" . $bot("写代码好难") . "<br><br>";
// 方法3:添加自定义技能
$bot->addSkill('calculate', function(...$numbers) {
$sum = array_sum($numbers);
$count = count($numbers);
$avg = $sum / $count;
return "求和:{$sum},平均:{$avg},最大值:" . max($numbers);
});
echo "<h3>自定义计算技能:</h3>";
echo $bot->useSkill('calculate', 1, 2, 3, 4, 5) . "<br>";
// 方法4:设置心情
$bot->setMood('happy');
echo "<h3>快乐模式的问候:</h3>";
echo $bot->useSkill('greet', '朋友') . "<br>";
// 查看历史记录
echo "<h3>对话历史:</h3>";
echo "<pre>";
print_r($bot->getMemory());
echo "</pre>";
// 链式调用演示
echo "<h3>链式调用:</h3>";
$bot->setMood('happy')
->addSkill('cheer', function() {
return "加油!你可以的!💪";
});
echo $bot->useSkill('cheer');
?>
第十二章:总结——你的函数修炼手册
学了这么多,最后记住这几点核心心法:
- 函数是你的代码“乐高积木”——小而专,才能灵活组合
- 起名要像给宠物起名——看一眼就知道它是干嘛的
- 一个函数只做一件事——如果你说不清它做什么,说明它做太多了
- 参数越少越好——超过3个就该想想是不是设计有问题了
- 要么返回值,要么改状态,不要都干——保持纯粹
最后的小测试:
看看这个函数有什么问题?
function doEverything($data) {
// 验证数据
if (empty($data)) return false;
// 处理业务逻辑
$result = complexBusinessLogic($data);
// 保存到数据库
saveToDatabase($result);
// 发邮件通知
sendEmailNotification($result);
// 写日志
writeLog($result);
// 更新缓存
updateCache($result);
// 返回成功
return true;
}
答案是:它干了太多事了! 应该拆分成多个小函数,每个只负责一个明确的任务。
现在,你已经是PHP函数的小高手了!下次写代码时,记得多想想:“这个能封装成函数吗?” 很快你就会发现,你的代码越来越简洁,维护起来越来越轻松,同事们看你的眼神都会充满崇拜!
记住,好的函数就像好的笑话——简短、精炼,而且能在别的地方重复使用!现在,去写那些优雅如诗的代码吧! 🚀
1万+

被折叠的 条评论
为什么被折叠?



