kode/facade

A robust, universal Facade component for PHP 8.1+, compatible with Laravel, Symfony, ThinkPHP, Webman, and KodePHP.

v1.2.1 2025-09-27 05:36 UTC

This package is auto-updated.

Last update: 2025-09-27 05:36:47 UTC


README

Package Name: kode/facade
Version: 1.0.0 (Stable)
PHP Version: >=8.1
Author: KodePHP Team
License: Apache-2.0
IDE Support: PhpStorm, VS Code

📦 概述

kode/facade 是一个健壮、通用、轻量级的 PHP Facade 抽象组件,专为 KodePHP 框架设计,同时兼容 Laravel、Symfony、ThinkPHP 8、Webman、自研框架 等主流 PHP 框架。

该组件提供:

  • 静态代理 实现服务容器绑定的动态调用
  • ✅ 支持 PHP 8.1+ 所有新特性(协变、逆变、枚举、只读类等)
  • ✅ 完全无副作用,不影响协程、多线程、多进程模型
  • ✅ 使用 反射 + 缓存 实现安全、快速的方法调用
  • ✅ 类名与方法名简洁、易记、不与 PHP 原生冲突
  • ✅ 支持跨框架调用,可作为通用市场组件发布

🧩 核心设计理念

特性 说明
🔐 无全局状态污染 不使用 static::$app 全局赋值,通过 Container 接口注入
性能优化 方法映射缓存 + 反射缓存,避免重复解析
🔄 协变逆变支持 接口返回类型与参数支持 PHP 泛型风格协变(out)与逆变(in)
🧱 解耦设计 不依赖任何具体容器实现,仅依赖 Psr\Container\ContainerInterface
🔧 高度可配置 支持自定义容器实现、Facade 映射、方法缓存等
🔧 高度可扩展 支持自定义 Facade 映射、方法缓存等

📚 安装方式

composer require kode/facade

🧠 核心类与 API

1. Facade 抽象类(核心)

所有 Facade 的基类,提供静态代理能力。

namespace Kode\Facade;

use Psr\Container\ContainerInterface;

abstract class Facade
```{
    /**
     * 获取当前 Facade 对应的服务名(在容器中的 key)
     * @return string
     */
    protected static function id(): string;

    /**
     * 设置服务容器
     * @param ContainerInterface $container
     * @return void
     */
    public static function setContainer(ContainerInterface $container): void;

    /**
     * 清除当前 Facade 的代理实例(用于测试或重置)
     * @return void
     */
    public static function clear(): void;

    /**
     * 检查门面是否已解析
     * @return bool
     */
    public static function isResolved(): bool;

    /**
     * 获取此门面的服务ID
     * @return string
     */
    public static function getServiceId(): string;

    /**
     * 使用参数数组调用门面实例上的方法
     * @param string $method
     * @param array $args
     * @return mixed
     */
    public static function call(string $method, array $args = []);

    /**
     * 检查门面实例上是否存在指定方法
     * @param string $method
     * @return bool
     */
    public static function hasMethod(string $method): bool;

    /**
     * 动态静态调用转发
     * @param string $method
     * @param array $args
     * @return mixed
     */
    public static function __callStatic(string $method, array $args);
}

2. FacadeProxy(内部代理管理器)

内部使用,管理 Facade 到真实实例的映射。

namespace Kode\Facade;

class FacadeProxy
{
    private static array $instances = [];
    private static array $ids = [];

    /**
     * 绑定服务 ID 到 Facade
     * @template T
     * @param class-string<T> $facade
     * @param string $serviceId
     * @return void
     */
    public static function bind(string $facade, string $serviceId): void;

    /**
     * 检查门面是否绑定到服务ID
     * @param string $facade
     * @return bool
     */
    public static function isBound(string $facade): bool;

    /**
     * 获取门面的服务ID
     * @param string $facade
     * @return string|null
     */
    public static function getServiceId(string $facade): ?string;

    /**
     * 获取所有绑定的门面
     * @return array<string, string>
     */
    public static function getBindings(): array;

    /**
     * 获取 Facade 对应的实例
     * @param string $facade
     * @return object
     */
    public static function getInstance(string $facade): object;
}

🛠 使用示例

Step 1:定义一个服务接口

namespace App\Service;

interface MailerInterface
{
    public function send(string $to, string $subject, string $body): bool;
}

Step 2:实现服务

namespace App\Service;

class SmtpMailer implements MailerInterface
{
    public function send(string $to, string $subject, string $body): bool
    {
        // 发送逻辑...
        return true;
    }
}

Step 3:创建 Facade

namespace App\Facade;
```php
use Kode\Facade\Facade;

/**
 * @method static bool send(string $to, string $subject, string $body)
 */
class Mail extends Facade
```{
    protected static function id(): string
    {
        return 'mailer'; // 对应容器中的服务 key
    }
}

Step 4:在任意框架中使用

Laravel / Symfony / ThinkPHP / Webman 示例

// 假设你已获取容器实例 $container(实现 Psr\Container\ContainerInterface)

use App\Facade\Mail;
use Kode\Facade\FacadeProxy;

// 绑定 Facade 到服务 ID
FacadeProxy::bind(\App\Facade\Mail::class, 'mailer');

// 设置容器
Mail::setContainer($container);

// 使用静态调用
Mail::send('user@example.com', 'Hello', 'Welcome!');

🧩 增强功能

✅ 门面状态检查

现在可以检查门面是否已解析以及是否绑定到服务:

// 检查门面是否已解析
if (Mail::isResolved()) {
    // 门面已解析
}

// 检查门面是否绑定到服务ID
if (FacadeProxy::isBound(\App\Facade\Mail::class)) {
    // 门面已绑定
}

✅ 获取门面信息

可以获取门面的服务ID和所有绑定信息:

// 获取门面的服务ID
$serviceId = Mail::getServiceId();

// 获取门面的服务ID(通过代理)
$serviceId = FacadeProxy::getServiceId(\App\Facade\Mail::class);

// 获取所有绑定的门面
$bindings = FacadeProxy::getBindings();

✅ 方法调用增强

提供了更灵活的方法调用方式:

// 使用call方法调用,参数以数组形式传递
$result = Mail::call('send', ['user@example.com', 'Subject', 'Body']);

// 检查门面实例上是否存在指定方法
if (Mail::hasMethod('send')) {
    // 方法存在
}

✅ 上下文安全模式(Context-Safe Mode)

在协程环境(如Swoole、Swow、PHP 8.1+ Fiber)中,可以启用上下文安全模式以确保门面实例在不同协程间隔离:

// 启用上下文安全模式
Mail::enableContextSafeMode();

// 现在每个协程将拥有独立的门面实例缓存
// 避免不同协程间的实例污染问题

上下文安全模式特性:

  • ✅ 每个协程/上下文拥有独立的实例缓存
  • ✅ 支持PHP Fiber、Swoole、Swow等协程环境
  • ✅ 与原有API完全兼容
  • ✅ 可随时启用或禁用
// 检查是否启用了上下文安全模式
if (Mail::isContextSafeMode()) {
    // 上下文安全模式已启用
}

// 禁用上下文安全模式
Mail::disableContextSafeMode();

🧩 高级特性

✅ 协变(Covariance)支持

interface ResponseFactory
{
    public function make(): Response; // 返回基类
}

interface JsonResponseFactory extends ResponseFactory
{
    public function make(): JsonResponse; // 子类返回更具体的类型(协变)
}

kode/facade 完全支持此类返回类型的协变。

✅ 逆变(Contravariance)支持

interface EventDispatcher
{
    public function dispatch(object $event): void;
}

interface SpecificEventDispatcher extends EventDispatcher
{
    public function dispatch(SpecificEvent $event): void; // 参数更具体(逆变)
}

✅ 参数类型的逆变在反射调用中被正确处理。

✅ 反射安全调用(带缓存)

内部使用 ReflectionMethod + APCu/Array 缓存:

$reflector = new ReflectionMethod($instance, $method);
$reflector->invokeArgs($instance, $args);

调用信息缓存于 static 数组,避免重复反射。

🧪 测试与兼容性

框架 兼容性 说明
Laravel 9+ 使用 app()Container 注入
Symfony 6+ 通过 ServiceContainer 传入
ThinkPHP 8 使用 app() 兼容 PSR 容器
Webman 1+ 支持 Workerman 多进程模型
Swoole 协程 无全局变量,协程安全
多线程(ZTS) 不使用静态实例缓存线程局部存储

📌 最佳实践建议

  1. Facade 类名:使用单数、动词或名词,如 Mail, Cache, Log, DB
  2. 方法名:保持与服务接口一致,避免动词重复(如 getGet
  3. 不覆盖 __callStatic:避免破坏代理机制
  4. 绑定在启动时完成:在 bootstrap.phpServiceProvider 中调用 FacadeProxy::bind()
  5. 测试时使用 clear():避免测试间状态污染

📞 联系与贡献

kode/facade —— 简单、安全、通用、高性能的 PHP Facade 解决方案
为未来协程、多线程、多进程架构打下坚实基础。