PHP工程:深入核心概念
Source: Dev.to
PHP 中的变量作用域(变量的生与死)
作用域定义了变量被声明和可以访问的上下文。
局部作用域:函数的私有空间
在函数(或方法)内部声明的变量是 局部作用域。函数开始时创建,函数执行完毕后销毁。
function calculateArea($width, $height) {
// $area、$width 和 $height 都是局部变量。
$area = $width * $height;
echo "Area is: " . $area;
}
// echo $area; // Fatal Error: Undefined variable $area
类比: 像 安全会议室里的私人笔记本——内容只对本次会议有意义。
全局作用域:公共声明
在任何函数或类之外声明的变量属于 全局作用域。默认情况下,函数不能直接访问全局变量。
$site_name = "MyApp"; // 全局变量
function showSite() {
// 显式将全局变量导入函数的局部作用域
global $site_name;
echo "Welcome to " . $site_name;
}
showSite(); // 输出: Welcome to MyApp
重要提示: 将变量作为函数参数传递通常比使用
global更可取。
类比: 办公室前厅的公告板——所有人都能看到,但必须明确告诉 PHP 去函数外面查找。
静态作用域:函数记忆的持久化
在函数内部声明为 static 的变量仍然是局部的,但 在多次调用之间保持。
function counter() {
static $count = 0; // 只初始化一次
$count++;
return $count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
实用示例:简单的 ID 生成器
静态变量可以在不使用全局计数器的情况下生成唯一的顺序 ID。
function generate_id() {
static $current_id = 0; // 只初始化一次
$current_id++;
return $current_id;
}
echo "User ID: " . generate_id() . "\n"; // User ID: 1
echo "Order ID: " . generate_id() . "\n"; // Order ID: 2
实用示例:单例模式(数据库连接)
单例模式依赖静态属性来确保类只有一个实例。
class Database
{
private static $instance = null;
public static function getInstance(): Database
{
if (self::$instance === null) {
// 只创建一次连接
self::$instance = new self();
}
return self::$instance;
}
// 私有构造函数防止直接实例化
private function __construct() {}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance(); // $db1 和 $db2 是同一个对象
为什么重要: 静态作用域对于在不依赖全局变量或类属性的情况下跟踪状态(如调用计数或 ID 生成器)至关重要。
类比: 离开后仍留在桌面上的便利贴——你回来时它们仍在。
引用(&)——操作原始数据
在 PHP 中,大多数变量是 按值传递(会复制一份)。引用允许你为同一块数据创建别名,从而实现按引用传递。
普通复制(按值传递)
$a = 10;
$b = $a; // $b 得到 10 的副本
$b = 20; // 只改变 $b
echo $a; // 输出: 10
引用版(共享内存)
$a = 10;
$b =& $a; // $b 现在是 $a 的别名
$b = 20; // 改变 $b 也会改变 $a
echo $a; // 输出: 20
类比: 同一台电视的两个遥控器。按下任意一个遥控器的按钮都会影响同一台电视。
函数中的引用(显式按引用传递)
function addOne(&$num) {
$num++;
}
$x = 5;
addOne($x);
echo $x; // 输出: 6
foreach 中的引用(危险区)
使用 foreach ($arr as &$v) 会让 $v 成为每个数组元素的别名。循环结束后务必解除引用。
$arr = [1, 2, 3];
foreach ($arr as &$v) {
$v *= 2;
}
unset($v); // 解除引用
闭包 —— 能记住上下文的函数
闭包 是一种匿名函数,既可以存入变量,也可以作为参数传递,并且 能够访问创建它时所在作用域的变量。
$greet = function ($name) {
return "Hello " . $name;
};
echo $greet("Mamu"); // Hello Mamu
类比: 可携带的微芯片,内置预设的操作,你可以随时拿出来执行。
use —— 在闭包内部捕获变量
匿名函数默认不能访问外部作用域的变量。use 关键字可以把这些变量导入闭包。
按值捕获
$name = "Mamu";
$fn = function () use ($name) {
return $name;
};
$name = "Ali"; // 捕获后再修改
echo $fn(); // 输出: Mamu
按引用捕获
$count = 0;
$fn = function () use (&$count) {
$count++;
};
$fn();
$fn();
echo $count; // 输出: 2
类比: 闭包持有一根 通向原变量的活线,而不是单纯的快照。
词法作用域(出生地规则)
词法作用域指闭包访问的是它 编写时 所在的作用域,而不是 执行时 所在的作用域。
$message = "Outside"; // 作用域 A
$fn = function () use ($message) {
echo $message; // 从作用域 A 捕获
};
function run($cb) {
$message = "Inside"; // 作用域 B
$cb();
}
run($fn); // 打印 "Outside"
记忆规则: 闭包使用的是它们 创建时 的变量(词法),而不是调用点的变量(动态)。
实战示例:创建动态数组过滤器
function makeFilter($min, $max) {
return function ($value) use ($min, $max) {
return $value >= $min && $value 12 [2] => 17 )
在这个例子中,闭包按值捕获 $min 和 $max,从而让 array_filter 能在不把这些变量暴露为全局的情况下执行自定义的范围检查。