PHP工程:深入核心概念

发布: (2025年12月8日 GMT+8 21:11)
7 min read
原文: Dev.to

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 能在不把这些变量暴露为全局的情况下执行自定义的范围检查。

Back to Blog

相关文章

阅读更多 »