
本文详解“only variables should be passed by reference”错误成因,指出bind_param()要求传入变量而非函数返回值,并提供修复方案;同时强调弃用sha-1,推荐使用php原生password_hash()/password_verify()实现安全密码存储与验证。
本文详解“only variables should be passed by reference”错误成因,指出bind_param()要求传入变量而非函数返回值,并提供修复方案;同时强调弃用sha-1,推荐使用php原生password_hash()/password_verify()实现安全密码存储与验证。
在使用MySQLi预处理语句(Prepared Statement)时,bind_param()方法要求所有参数必须是可被引用的变量(lvalue),而不能是表达式、字面量或函数调用结果(如sha1($_POST['l_pass']))。该限制源于PHP底层对引用传递的语法要求——只有变量才能被取地址并传递给内部函数。
上述报错代码第44行:
$query->bind_param("ss", $_POST['l_user'], sha1($_POST['l_pass'])); // ❌ 错误:函数调用不可传引用
sha1($_POST['l_pass']) 是一个临时计算结果,不是变量,因此无法满足bind_param()的引用约束。
✅ 正确做法是先计算哈希值并赋给一个变量,再绑定该变量:
if (isset($_POST['l_login'])) {
$query = $con->prepare("SELECT id FROM librarian WHERE username = ? AND password = ?");
$username = $_POST['l_user'];
$password_hash = sha1($_POST['l_pass']); // ✅ 先赋值为变量
$query->bind_param("ss", $username, $password_hash);
$query->execute();
$result = $query->get_result();
if ($result->num_rows !== 1) {
echo error_without_field("Invalid username/password combination");
} else {
// 登录成功逻辑
$row = $result->fetch_assoc();
$_SESSION['librarian_id'] = $row['id'];
header("Location: dashboard.php");
exit;
}
}
⚠️ 重要安全提醒:
sha1() 已被密码学界彻底废弃——自2005年起即存在实用碰撞攻击,2017年Google更公开演示了SHA-1碰撞(SHAttered攻击)。绝不可用于密码哈希。现代PHP应使用内置的、自适应的、带盐的密码哈希方案:
? 推荐方案(PHP 5.5+):
// 注册时:生成强哈希(自动加盐、支持算法升级)
$hash = password_hash($_POST['l_pass'], PASSWORD_ARGON2ID, [
'memory_cost' => 65536, // 64MB 内存
'time_cost' => 4,
'threads' => 3
]);
// 登录时:安全比对(自动处理盐与算法)
if (password_verify($_POST['l_pass'], $stored_hash)) {
// 密码正确
}
若环境不支持Argon2(如PHP
$hash = password_hash($_POST['l_pass'], PASSWORD_DEFAULT); // 验证方式不变:password_verify($input, $hash) → 始终安全
? 总结:
- bind_param() 只接受变量,禁止直接传函数调用结果;
- SHA-1、MD5、crypt()(无盐)等均属不安全旧方案,必须淘汰;
- 坚持使用 password_hash() + password_verify(),它们已内建最佳实践(随机盐、可配置强度、向后兼容算法升级);
- 预处理语句防SQL注入 + 安全哈希防彩虹表/碰撞攻击,二者缺一不可。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











