本文目录导读:

- use 关键字(闭包/匿名函数)
- 依赖注入(构造函数/方法注入)
- 全局变量(不推荐,但有特殊用途)
- 静态属性/服务容器
- 魔术方法 __invoke 和闭包
- Generators 上下文传递
- 现代框架中的上下文(PSR-11 容器)
- 最佳实践建议
- 完整示例:用户认证上下文
在 PHP 中,上下文传递(Context Passing)主要有以下几种方式,我会逐一详细说明:
use 关键字(闭包/匿名函数)
最常用的上下文传递方式,用于在闭包中捕获外部变量:
<?php
$userId = 123;
$userName = "Alice";
// 基本使用
$greeting = function() use ($userId, $userName) {
return "User {$userId}: {$userName}";
};
echo $greeting(); // 输出: User 123: Alice
// 按引用传递
$counter = 0;
$increment = function() use (&$counter) {
$counter++;
};
$increment();
$increment();
echo $counter; // 输出: 2
// 捕获对象
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$query = function($sql) use ($db) {
return $db->query($sql);
};
?>
依赖注入(构造函数/方法注入)
通过构造函数或方法参数传递上下文:
<?php
class UserService
{
private $db;
private $logger;
public function __construct(PDO $db, Logger $logger)
{
$this->db = $db;
$this->logger = $logger;
}
public function getUser($id)
{
$this->logger->info("Getting user: $id");
// 使用 $this->db
}
}
// 实例化时传递依赖
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$logger = new FileLogger('/var/log/app.log');
$service = new UserService($db, $logger);
?>
全局变量(不推荐,但有特殊用途)
<?php
// 全局配置
$GLOBALS['config'] = [
'db_host' => 'localhost',
'db_name' => 'test',
'debug' => true
];
// 全局错误处理器
function customErrorHandler($errno, $errstr, $errfile, $errline) {
global $GLOBALS;
error_log($errstr, 3, $GLOBALS['config']['log_file']);
}
// 全局变量访问(尽量避免)
$config = $GLOBALS['config'];
echo $config['db_host'];
?>
静态属性/服务容器
使用静态属性或服务容器模式:
<?php
class AppContext
{
private static $instance = null;
private $db;
private $user;
private $session;
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function setDb($db)
{
$this->db = $db;
return $this;
}
public function getDb()
{
return $this->db;
}
public function setUser($user)
{
$this->user = $user;
return $this;
}
public function getUser()
{
return $this->user;
}
}
// 使用
AppContext::getInstance()
->setDb($db)
->setUser($currentUser);
// 在任何地方获取
$user = AppContext::getInstance()->getUser();
?>
魔术方法 __invoke 和闭包
<?php
class ContextHandler
{
private $context;
public function __invoke($data)
{
// 使用上下文处理数据
if ($this->context['debug']) {
var_dump($data);
}
return $this->processWithContext($data);
}
private function processWithContext($data)
{
// 使用 $this->context 逻辑
return $data;
}
}
$handler = new ContextHandler();
$handler->context = ['debug' => true, 'app' => 'MyApp'];
// 直接调用
$result = $handler(['key' => 'value']);
?>
Generators 上下文传递
<?php
function processItems($items, $context = []) {
foreach ($items as $item) {
// 使用上下文
yield $context['prefix'] . $item;
}
}
$items = ['apple', 'banana', 'orange'];
$context = ['prefix' => 'fruit: '];
foreach (processItems($items, $context) as $processed) {
echo $processed . PHP_EOL;
}
?>
现代框架中的上下文(PSR-11 容器)
使用 PSR-11 依赖注入容器:
<?php
// 使用容器接口
interface ContainerInterface
{
public function get($id);
public function has($id);
}
class MyContainer implements ContainerInterface
{
private $services = [];
public function get($id)
{
if (!$this->has($id)) {
throw new Exception("Service not found: $id");
}
return $this->services[$id]();
}
public function set($id, $factory)
{
$this->services[$id] = $factory;
return $this;
}
public function has($id)
{
return isset($this->services[$id]);
}
}
// 使用
$container = new MyContainer();
$container->set('db', function() {
return new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
});
// 获取服务
$db = $container->get('db');
?>
最佳实践建议
- 优先使用依赖注入:让类显式声明依赖
- 避免全局变量:会增加耦合度,难以测试
- 使用容器管理复杂依赖:在大型应用中使用
- 闭包捕获注意性能:大量使用闭包捕获可能影响性能
- 考虑作用域:明确变量在何处被使用
完整示例:用户认证上下文
<?php
// 定义 UserContext 类
class UserContext
{
private $user;
private $roles;
private $ip;
private $session;
public function __construct($user, $session, $ip) {
$this->user = $user;
$this->session = $session;
$this->ip = $ip;
}
public function getUser() { return $this->user; }
public function hasRole($role) { return in_array($role, $this->roles); }
public function getIp() { return $this->ip; }
}
// 在框架中传递
class RequestHandler {
public function handle(Request $request, UserContext $userContext) {
if ($userContext->hasRole('admin')) {
// 管理员逻辑
} else {
// 普通用户逻辑
}
}
}
// 创建上下文
$context = new UserContext($user, $session, $request->getClientIp());
$handler = new RequestHandler();
$handler->handle($request, $context);
?>
选择合适的上下文传递方式取决于:
- 应用规模和复杂度
- 测试难度要求
- 代码可维护性
- 性能需求
通常推荐组合使用多种方式,但要有明确的设计原则。