PHP 闭包函数、use、回调函数、bind 完整知识点手册

117 阅读2分钟

🧠 PHP 闭包函数、use、回调函数、bind 完整知识点手册


✅ 一、闭包函数(Closure)

📌 定义:

闭包函数就是没有名字的函数,写在代码里的“临时小工具”。

🔹 特点:

  • 没有函数名;
  • 可以赋值给变量或作为参数传递;
  • 可以使用 use 引入外部变量;
  • 可以通过 bind 控制作用域。

✅ 示例:

$greet = function($name) {
    echo "你好, $name\n";
};

$greet("小明"); // 输出: 你好, 小明

✅ 二、use 关键字

📌 定义:

在闭包中使用外部变量时,必须用 use 把它带进来。

🔹 类型:

类型示例
普通变量use ($var)
引用变量use (&$var)
多个变量use ($a, $b)

✅ 示例1:引入普通变量

$name = "小张";

$sayHi = function() use ($name) {
    echo "你好, $name\n";
};

$sayHi(); // 输出: 你好, 小张

✅ 示例2:修改外部变量(需加 &)

$count = 0;

$increase = function() use (&$count) {
    $count++;
};

$increase();
echo $count; // 输出: 1

✅ 示例3:命名空间中导入类

use App\Services\UserService;

$userService = new UserService();

✅ 示例4:起别名(as)

use App\Models\User as UserModel;
use Admin\Models\User as AdminUser;

$user = new UserModel();
$admin = new AdminUser();

✅ 三、回调函数(Callback)

📌 定义:

回调函数是“被别人调用的函数”,你先定义好,等着别人来用。

🔹 常见使用场景:

场景示例
数组处理array_maparray_filterarray_reduce
路由分发根据 URL 动态调用控制器方法
插件系统允许插件注册自己的处理函数
事件监听注册钩子函数,在特定事件发生时触发

✅ 示例1:基本用法

function sayHello($name) {
    echo "你好呀, $name\n";
}

call_user_func('sayHello', '小红');
// 输出: 你好呀, 小红

✅ 示例2:使用闭包作为回调

call_user_func(function() {
    echo "这是个匿名回调函数\n";
});

✅ 示例3:数组过滤

$numbers = [1, 2, 3, 4, 5];

$evenNumbers = array_filter($numbers, function($num) {
    return $num % 2 === 0;
});

print_r($evenNumbers);
// 输出: Array ( [1] => 2 [3] => 4 )

✅ 四、bind 方法(\Closure::bind)

📌 定义:

bind 是用来改变闭包的作用域绑定,让它可以访问某个类的私有或受保护成员。

🔹 参数说明:

\Closure::bind(Closure $closure, ?object $newThis, mixed $newScope)
参数说明
$closure要绑定的闭包
$newThis新的 $this 对象;null 表示不绑定对象
$newScope新的作用域(类名或对象);null 表示绑定到全局作用域

✅ 示例1:绑定到类实例(允许访问私有属性)

class User {
    private $secret = "我是秘密信息";

    public function getSecretGetter() {
        return \Closure::bind(function() {
            return $this->secret;
        }, $this, $this);
    }
}

$user = new User();
$getSecret = $user->getSecretGetter();

echo $getSecret(); // 输出: 我是秘密信息

✅ 示例2:绑定到 null(防止访问类成员)

self::$includeFile = \Closure::bind(static function($file) {
    include $file;
}, null, null);
  • 第一个 null:表示不绑定对象;
  • 第二个 null:表示不绑定类作用域;
  • 这样做是为了防止被包含文件访问 $this 或类成员。

✅ 示例3:绑定到指定类(但不绑定对象)

class MyClass {
    private static $value = "静态私有值";
}

$fn = \Closure::bind(function() {
    echo MyClass::$value . "\n";
}, null, MyClass::class);

$fn(); // 输出: 静态私有值

🧩 五、综合示例:闭包 + use + bind + 回调 的组合使用

class Loader {
    private $prefixes = [];

    public function setPrefixes(array $prefixes) {
        $this->prefixes = $prefixes;
    }

    public function getLoaderClosure($data) {
        return \Closure::bind(function() use ($data) {
            $this->prefixes = $data;
        }, $this, $this);
    }
}

$loader = new Loader();
$setData = $loader->getLoaderClosure(['App\' => 'src/']);

$setData(); // 执行闭包,设置 prefix 数据

print_r($loader->prefixes); // 输出: Array ( [App] => src/ )

📌 总结一句话记忆法

闭包是没名字的函数,回调是别人调用的函数,use 是把外面的变量带进闭包里,bind 是给闭包发通行证,让它能访问类的私密区域。


🧠 小贴士:什么时候该用哪个?

场景推荐方式
想写一个临时的小函数✅ 闭包函数
把函数当作参数传给别的函数✅ 回调函数
在闭包里要用外面的变量✅ use
想修改外部变量✅ use (&$var)
要访问类的私有属性✅ bind(this,this, this)
防止闭包访问类成员✅ bind(null, null)
简化命名空间类名✅ use App\Class
类名冲突时起别名✅ use Class as Alias
批量导入多个类✅ use App{A, B, C}