PHP魔术常量

魔术常量是PHP中的一组特殊常量,它们在不同的上下文中会被自动替换为不同的值。与普通常量不同,魔术常量不区分大小写,但为了代码的可读性,通常使用大写形式。本教程将详细介绍PHP中所有魔术常量的用法、特性和应用场景。

PHP魔术常量概述

PHP中的魔术常量以双下划线(__)开头和结尾。它们会根据它们在代码中的位置自动获取不同的值。以下是PHP中常用的魔术常量:

  • __LINE__:返回当前行号
  • __FILE__:返回当前文件的完整路径和文件名
  • __DIR__:返回当前文件所在的目录
  • __FUNCTION__:返回当前函数的名称
  • __CLASS__:返回当前类的名称(包括命名空间)
  • __METHOD__:返回当前方法的名称(包括类名)
  • __NAMESPACE__:返回当前命名空间的名称
  • __TRAIT__:返回当前trait的名称(包括命名空间)
  • __COMPILER_HALT_OFFSET__:返回编译器停止解析的位置

常用魔术常量详解

__LINE__

__LINE__魔术常量返回它在代码中所在的行号,是一个整数。它常用于调试和日志记录。

<?php
echo "<h4>__LINE__魔术常量示例</h4>";

echo "这是第 " . __LINE__ . " 行代码<br>";
// 输出: 这是第 5 行代码

echo "这是第 " . __LINE__ . " 行代码<br>";
// 输出: 这是第 8 行代码

// 在条件语句中使用
if (true) {
    echo "这个条件语句在第 " . __LINE__ . " 行<br>";
    // 输出: 这个条件语句在第 12 行
}

// 在函数中使用
function debug($message) {
    echo "[行号: " . __LINE__ . "] " . $message . "<br>";
}

debug("这是一条调试信息");
// 输出: [行号: 19] 这是一条调试信息
?>

__FILE__

__FILE__魔术常量返回当前文件的完整路径和文件名,包括文件扩展名。在PHP 4.0.2及以上版本中,它返回的路径是绝对路径(在CLI模式下,总是返回绝对路径)。

<?php
echo "<h4>__FILE__魔术常量示例</h4>";

echo "当前文件的完整路径: " . __FILE__ . "<br>";
// 输出: 当前文件的完整路径: E:\myProject\php\php-magic-constants.html

// 获取文件名(不包含路径)
echo "当前文件名: " . basename(__FILE__) . "<br>";
// 输出: 当前文件名: php-magic-constants.html

// 用于包含其他文件(使用相对路径)
$includePath = dirname(__FILE__) . '/includes/functions.php';
echo "包含文件的路径: " . $includePath . "<br>";
// 输出: 包含文件的路径: E:\myProject\php\includes\functions.php
?>

__DIR__

__DIR__魔术常量返回当前文件所在的目录的路径,不包含文件名。它相当于dirname(__FILE__)。在PHP 5.3.0及以上版本中可用。

<?php
echo "<h4>__DIR__魔术常量示例</h4>";

echo "当前文件所在的目录: " . __DIR__ . "<br>";
// 输出: 当前文件所在的目录: E:\myProject\php

echo "与dirname(__FILE__)比较: " . dirname(__FILE__) . "<br>";
// 输出: 与dirname(__FILE__)比较: E:\myProject\php

// 包含上层目录的文件
$parentIncludePath = __DIR__ . '/../config.php';
echo "上层目录文件的路径: " . $parentIncludePath . "<br>";
// 输出: 上层目录文件的路径: E:\myProject\php\..\config.php

// 构建应用程序根路径
$appRoot = __DIR__;
echo "应用程序根路径: " . $appRoot . "<br>";
// 输出: 应用程序根路径: E:\myProject\php
?>

__FUNCTION__

__FUNCTION__魔术常量返回当前函数的名称。在PHP 5及以上版本中,如果在类方法中使用,它只返回方法名,不包含类名。

<?php
echo "<h4>__FUNCTION__魔术常量示例</h4>";

// 在普通函数中使用
function testFunction() {
    echo "当前函数名: " . __FUNCTION__ . "<br>";
}

testFunction();
// 输出: 当前函数名: testFunction

// 在递归函数中使用
function recursiveFunction($count) {
    echo "在 " . __FUNCTION__ . " 中, 计数: " . $count . "<br>";
    if ($count < 3) {
        recursiveFunction($count + 1);
    }
}

recursiveFunction(1);
// 输出:
// 在 recursiveFunction 中, 计数: 1
// 在 recursiveFunction 中, 计数: 2
// 在 recursiveFunction 中, 计数: 3

// 用于日志记录
function logAction($action) {
    $logMessage = date('Y-m-d H:i:s') . " - [" . __FUNCTION__ . "]: " . $action;
    echo $logMessage . "<br>";
    // 在实际应用中,这里会写入日志文件
}

logAction("用户登录成功");
// 输出: 2023-04-15 14:30:25 - [logAction]: 用户登录成功
?>

__CLASS__

__CLASS__魔术常量返回当前类的名称,包括命名空间(如果有)。在PHP 5及以上版本中可用。如果在类的外部使用,它将返回空字符串。

<?php
echo "<h4>__CLASS__魔术常量示例</h4>";

// 定义一个类
class User {
    public function getClassName() {
        return __CLASS__;
    }
    
    public static function getStaticClassName() {
        return __CLASS__;
    }
}

// 创建对象并测试
$user = new User();
echo "通过实例方法获取类名: " . $user->getClassName() . "<br>";
// 输出: 通过实例方法获取类名: User

echo "通过静态方法获取类名: " . User::getStaticClassName() . "<br>";
// 输出: 通过静态方法获取类名: User

// 测试继承
class Admin extends User {
    public function getClassName() {
        return __CLASS__;
    }
}

$admin = new Admin();
echo "子类的类名: " . $admin->getClassName() . "<br>";
// 输出: 子类的类名: Admin

echo "子类调用父类静态方法: " . $admin->getStaticClassName() . "<br>";
// 输出: 子类调用父类静态方法: User
?>

__METHOD__

__METHOD__魔术常量返回当前方法的名称,包括类名。在PHP 5及以上版本中可用。

<?php
echo "<h4>__METHOD__魔术常量示例</h4>";

// 定义一个类
class Calculator {
    public function add($a, $b) {
        echo "正在执行: " . __METHOD__ . "<br>";
        return $a + $b;
    }
    
    public static function multiply($a, $b) {
        echo "正在执行静态方法: " . __METHOD__ . "<br>";
        return $a * $b;
    }
}

// 创建对象并测试
$calculator = new Calculator();
$result = $calculator->add(5, 3);
echo "结果: " . $result . "<br>";
// 输出:
// 正在执行: Calculator::add
// 结果: 8

// 调用静态方法
$result = Calculator::multiply(5, 3);
echo "结果: " . $result . "<br>";
// 输出:
// 正在执行静态方法: Calculator::multiply
// 结果: 15

// 与__FUNCTION__比较
class TestClass {
    public function testMethod() {
        echo "__FUNCTION__: " . __FUNCTION__ . "<br>";
        echo "__METHOD__: " . __METHOD__ . "<br>";
    }
}

$test = new TestClass();
$test->testMethod();
// 输出:
// __FUNCTION__: testMethod
// __METHOD__: TestClass::testMethod
?>

__NAMESPACE__

__NAMESPACE__魔术常量返回当前命名空间的名称。在PHP 5.3.0及以上版本中可用。如果在全局命名空间中使用,它将返回空字符串。

<?php
echo "<h4>__NAMESPACE__魔术常量示例</h4>";

// 输出当前命名空间(全局命名空间)
echo "当前命名空间: '" . __NAMESPACE__ . "'<br>";
// 输出: 当前命名空间: ''

// 定义一个命名空间
namespace App\Utils;

echo "在命名空间内: '" . __NAMESPACE__ . "'<br>";
// 输出: 在命名空间内: 'App\Utils'

// 在命名空间中定义一个类
class Helper {
    public function getNamespace() {
        return __NAMESPACE__;
    }
    
    public function getFullClassName() {
        return __NAMESPACE__ . '\\' . __CLASS__;
    }
}

// 创建对象并测试
$helper = new Helper();
echo "通过方法获取命名空间: '" . $helper->getNamespace() . "'<br>";
// 输出: 通过方法获取命名空间: 'App\Utils'

echo "完整类名: '" . $helper->getFullClassName() . "'<br>";
// 输出: 完整类名: 'App\Utils\Helper'

// 返回到全局命名空间
namespace;

echo "回到全局命名空间: '" . __NAMESPACE__ . "'<br>";
// 输出: 回到全局命名空间: ''

// 使用带命名空间的类
$utilsHelper = new App\Utils\Helper();
echo "跨命名空间访问: '" . $utilsHelper->getNamespace() . "'<br>";
// 输出: 跨命名空间访问: 'App\Utils'
?>

其他魔术常量

__TRAIT__

__TRAIT__魔术常量返回当前trait的名称,包括命名空间。在PHP 5.4.0及以上版本中可用。

<?php
echo "<h4>__TRAIT__魔术常量示例</h4>";

// 定义一个trait
namespace App\Traits;

trait Loggable {
    public function getTraitName() {
        return __TRAIT__;
    }
    
    public function log($message) {
        echo "[" . __TRAIT__ . "] " . $message . "<br>";
    }
}

// 返回到全局命名空间
namespace;

// 定义一个使用trait的类
class LoggedClass {
    // 使用trait
    use App\Traits\Loggable;
}

// 创建对象并测试
$loggedObject = new LoggedClass();
echo "Trait名称: " . $loggedObject->getTraitName() . "<br>";
// 输出: Trait名称: App\Traits\Loggable

$loggedObject->log("这是一条日志消息");
// 输出: [App\Traits\Loggable] 这是一条日志消息
?>
__COMPILER_HALT_OFFSET__

__COMPILER_HALT_OFFSET__魔术常量返回编译器停止解析的位置,即__HALT_COMPILER();语句在文件中的偏移量。这个常量主要用于PHP的phar打包工具,普通开发中较少使用。

<?php
echo "<h4>__COMPILER_HALT_OFFSET__魔术常量示例</h4>";

// 这是一个简单的示例,演示__COMPILER_HALT_OFFSET__的用法
echo "编译器停止解析的位置: " . __COMPILER_HALT_OFFSET__ . "<br>";

// 在实际应用中,这可能被用来嵌入二进制数据
// 例如:
/*
$binaryData = file_get_contents(__FILE__, false, null, __COMPILER_HALT_OFFSET__);
echo "嵌入的二进制数据长度: " . strlen($binaryData) . "字节<br>";
*/

__HALT_COMPILER();
// 这里是编译器停止解析的位置
// 之后的任何内容都不会被PHP解析
// 在phar文件中,这里通常是二进制数据
?>

魔术常量的应用场景

调试和日志记录

魔术常量在调试和日志记录中非常有用,可以帮助开发者了解代码执行的位置和上下文。

<?php
echo "<h4>魔术常量在调试和日志记录中的应用</h4>";

// 自定义调试函数
function debug($message, $level = 'info') {
    $timestamp = date('Y-m-d H:i:s');
    $line = __LINE__ - 1; // 减去函数定义占用的行
    $file = basename(__FILE__);
    $function = __FUNCTION__;
    
    echo "[$timestamp] [$level] [$file:$line] [$function] $message<br>";
}

// 测试调试函数
debug("这是一条调试信息");
// 输出示例: [2023-04-15 14:45:10] [info] [php-magic-constants.html:582] [debug] 这是一条调试信息

debug("出现错误", 'error');
// 输出示例: [2023-04-15 14:45:10] [error] [php-magic-constants.html:585] [debug] 出现错误

// 类中的日志记录
class Logger {
    public function log($message, $level = 'info') {
        $timestamp = date('Y-m-d H:i:s');
        $line = __LINE__ - 1;
        $file = basename(__FILE__);
        $class = __CLASS__;
        $method = __METHOD__;
        
        echo "[$timestamp] [$level] [$file:$line] [$class::$method] $message<br>";
    }
}

// 使用日志类
$logger = new Logger();
$logger->log("用户登录");
// 输出示例: [2023-04-15 14:45:10] [info] [php-magic-constants.html:604] [Logger::log] 用户登录
$logger->log("数据库连接失败", 'error');
// 输出示例: [2023-04-15 14:45:10] [error] [php-magic-constants.html:606] [Logger::log] 数据库连接失败
?>

自动加载和文件包含

魔术常量在实现自动加载和文件包含时非常有用,可以基于当前文件的位置构建正确的路径。

<?php
echo "<h4>魔术常量在自动加载和文件包含中的应用</h4>";

// 简单的自动加载函数
function myAutoloader($className) {
    // 假设类名与文件名相同,位于classes目录下
    $baseDir = __DIR__ . '/classes/';
    $file = $baseDir . str_replace('\\', '/', $className) . '.php';
    
    echo "尝试加载: $file<br>";
    
    if (file_exists($file)) {
        include_once $file;
        echo "成功加载类: $className<br>";
        return true;
    }
    
    echo "无法加载类: $className<br>";
    return false;
}

// 注册自动加载函数
spl_autoload_register('myAutoloader');

// 包含配置文件
function loadConfig() {
    $configFile = __DIR__ . '/config/config.php';
    
    echo "尝试包含配置文件: $configFile<br>";
    
    if (file_exists($configFile)) {
        include_once $configFile;
        echo "配置文件加载成功<br>";
        return true;
    }
    
    echo "配置文件不存在<br>";
    return false;
}

// 测试配置文件加载
loadConfig();

// 构建应用程序路径
function getAppPath($relativePath = '') {
    return __DIR__ . ($relativePath ? '/' . ltrim($relativePath, '/') : '');
}

echo "应用程序根路径: " . getAppPath() . "<br>";
echo "图片目录路径: " . getAppPath('images') . "<br>";
echo "CSS文件路径: " . getAppPath('css/style.css') . "<br>";
?>

动态类和方法调用

魔术常量可以用于实现动态类和方法调用,增强代码的灵活性和可扩展性。

<?php
echo "<h4>魔术常量在动态类和方法调用中的应用</h4>";

// 基础控制器类
class BaseController {
    public function render($view, $data = []) {
        $controllerName = str_replace('Controller', '', __CLASS__);
        echo "渲染控制器 '$controllerName' 的视图 '$view'<br>";
        echo "传递的数据: " . json_encode($data) . "<br>";
        // 实际应用中,这里会包含对应的视图文件
    }
    
    // 动态调用方法
    public function callAction($action, $params = []) {
        $method = $action . 'Action';
        $controller = __CLASS__;
        
        echo "调用控制器 '$controller' 的方法 '$method'<br>";
        
        if (method_exists($this, $method)) {
            call_user_func_array([$this, $method], $params);
            return true;
        }
        
        echo "方法 '$method' 不存在<br>";
        return false;
    }
}

// 测试基础控制器
$baseController = new BaseController();
$baseController->render('index', ['title' => '首页']);
// 输出:
// 渲染控制器 'Base' 的视图 'index'
// 传递的数据: {"title":"首页"}

$baseController->callAction('list', [1, 'all']);
// 输出:
// 调用控制器 'BaseController' 的方法 'listAction'
// 方法 'listAction' 不存在

// 用户控制器(继承基础控制器)
class UserController extends BaseController {
    public function indexAction() {
        echo "执行用户控制器的indexAction方法<br>";
        $this->render('user/index');
    }
    
    public function viewAction($id) {
        echo "执行用户控制器的viewAction方法,ID: $id<br>";
        $this->render('user/view', ['id' => $id]);
    }
}

// 测试用户控制器
$userController = new UserController();
$userController->callAction('index');
// 输出:
// 调用控制器 'UserController' 的方法 'indexAction'
// 执行用户控制器的indexAction方法
// 渲染控制器 'User' 的视图 'user/index'
// 传递的数据: []

$userController->callAction('view', [123]);
// 输出:
// 调用控制器 'UserController' 的方法 'viewAction'
// 执行用户控制器的viewAction方法,ID: 123
// 渲染控制器 'User' 的视图 'user/view'
// 传递的数据: {"id":123}
?>

单例模式和工厂模式

魔术常量在实现设计模式如单例模式和工厂模式时也很有用,可以动态获取类名。

<?php
echo "<h4>魔术常量在设计模式中的应用</h4>";

// 单例模式示例
class Database {
    private static $instance;
    
    // 私有构造函数,防止外部实例化
    private function __construct() {
        echo "初始化数据库连接 (" . __CLASS__ . ")<br>";
        // 实际应用中,这里会建立数据库连接
    }
    
    // 获取单例实例
    public static function getInstance() {
        if (!isset(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    // 执行查询
    public function query($sql) {
        echo "执行SQL查询: $sql<br>";
        // 实际应用中,这里会执行真实的SQL查询
        return true;
    }
}

// 获取数据库实例
$db1 = Database::getInstance();
$db1->query("SELECT * FROM users");

$db2 = Database::getInstance();
$db2->query("SELECT * FROM products");

echo "db1 和 db2 是否是同一个实例: " . ($db1 === $db2 ? "是" : "否") . "<br>";
// 输出: db1 和 db2 是否是同一个实例: 是

// 工厂模式示例
class LoggerFactory {
    // 创建日志记录器
    public static function createLogger($type = 'file') {
        $className = ucfirst($type) . 'Logger';
        
        echo "尝试创建日志记录器: $className<br>";
        
        if (class_exists($className)) {
            return new $className();
        }
        
        echo "不支持的日志记录器类型: $type<br>";
        return null;
    }
}

// 文件日志记录器
class FileLogger {
    public function log($message) {
        echo "[FileLogger] " . $message . "<br>";
        // 实际应用中,这里会将日志写入文件
    }
}

// 数据库日志记录器
class DatabaseLogger {
    public function log($message) {
        echo "[DatabaseLogger] " . $message . "<br>";
        // 实际应用中,这里会将日志存储到数据库
    }
}

// 使用工厂创建日志记录器
$fileLogger = LoggerFactory::createLogger('file');
$fileLogger->log("这是一条文件日志");
// 输出:
// 尝试创建日志记录器: FileLogger
// [FileLogger] 这是一条文件日志

$dbLogger = LoggerFactory::createLogger('database');
$dbLogger->log("这是一条数据库日志");
// 输出:
// 尝试创建日志记录器: DatabaseLogger
// [DatabaseLogger] 这是一条数据库日志

$invalidLogger = LoggerFactory::createLogger('console');
// 输出:
// 尝试创建日志记录器: ConsoleLogger
// 不支持的日志记录器类型: console
?>

PHP魔术常量的最佳实践

  1. 调试和日志记录:使用__LINE____FILE____FUNCTION__等魔术常量来增强日志信息,便于追踪问题。
  2. 文件路径处理:使用__DIR____FILE__来构建可靠的文件路径,避免使用相对路径带来的问题。
  3. 动态代码生成:利用魔术常量来实现动态类和方法调用,提高代码的灵活性和可扩展性。
  4. 设计模式实现:在实现单例模式、工厂模式等设计模式时,使用魔术常量获取类名,减少硬编码。
  5. 自动加载机制:结合魔术常量和PHP的自动加载机制,实现类的自动加载。
  6. 避免过度使用:虽然魔术常量很有用,但也应该避免过度使用,特别是在可以使用普通变量或参数的情况下。
  7. 性能考虑:魔术常量的解析会有一定的性能开销,但通常影响很小。在对性能要求极高的场景中,可以考虑缓存魔术常量的值。
  8. 代码可读性:确保使用魔术常量的代码易于理解和维护,添加适当的注释说明。

PHP魔术常量综合示例

<?php
// 1. 基本魔术常量使用
echo "<h4>1. 基本魔术常量使用</h4>";

echo "当前行号: " . __LINE__ . "<br>";
echo "当前文件: " . __FILE__ . "<br>";
echo "当前目录: " . __DIR__ . "<br>";
echo "当前命名空间: '" . __NAMESPACE__ . "'<br>";

// 2. 创建一个简单的调试日志系统
echo "<h4>2. 简单的调试日志系统</h4>";

// 定义日志级别
define('LOG_LEVEL_DEBUG', 1);
define('LOG_LEVEL_INFO', 2);
define('LOG_LEVEL_WARNING', 3);
define('LOG_LEVEL_ERROR', 4);

// 当前日志级别
$currentLogLevel = LOG_LEVEL_DEBUG;

// 日志函数
function logMessage($message, $level = LOG_LEVEL_INFO) {
    global $currentLogLevel;
    
    // 检查日志级别
    if ($level < $currentLogLevel) {
        return;
    }
    
    // 获取日志级别名称
    $levelNames = [
        LOG_LEVEL_DEBUG => 'DEBUG',
        LOG_LEVEL_INFO => 'INFO',
        LOG_LEVEL_WARNING => 'WARNING',
        LOG_LEVEL_ERROR => 'ERROR'
    ];
    
    $levelName = isset($levelNames[$level]) ? $levelNames[$level] : 'UNKNOWN';
    
    // 获取调用信息
    $trace = debug_backtrace();
    $caller = $trace[0];
    $line = $caller['line'];
    $file = basename($caller['file']);
    $function = isset($caller['function']) ? $caller['function'] : 'main';
    
    // 格式化日志消息
    $timestamp = date('Y-m-d H:i:s');
    $logMessage = "[$timestamp] [$levelName] [$file:$line] [$function] $message";
    
    // 输出日志
    echo $logMessage . "<br>";
    
    // 在实际应用中,这里可能会将日志写入文件或数据库
}

// 测试日志函数
logMessage("这是一条信息日志", LOG_LEVEL_INFO);
logMessage("这是一条调试日志", LOG_LEVEL_DEBUG);
logMessage("这是一条警告日志", LOG_LEVEL_WARNING);
logMessage("这是一条错误日志", LOG_LEVEL_ERROR);

// 在函数中使用日志
function processData($data) {
    logMessage("开始处理数据: " . json_encode($data), LOG_LEVEL_DEBUG);
    
    // 处理数据...
    
    if (empty($data)) {
        logMessage("数据为空", LOG_LEVEL_WARNING);
        return false;
    }
    
    logMessage("数据处理完成", LOG_LEVEL_INFO);
    return true;
}

// 测试带日志的函数
processData(["name" => "张三", "age" => 30]);
processData([]);

// 3. 使用魔术常量实现简单的路由系统
echo "<h4>3. 简单的路由系统</h4>";

// 路由类
class Router {
    private $routes = [];
    
    // 添加路由
    public function addRoute($pattern, $controller, $action) {
        logMessage("添加路由: $pattern -> $controller::$action", LOG_LEVEL_DEBUG);
        $this->routes[$pattern] = [
            'controller' => $controller,
            'action' => $action
        ];
    }
    
    // 匹配路由
    public function match($url) {
        logMessage("尝试匹配URL: $url", LOG_LEVEL_DEBUG);
        
        foreach ($this->routes as $pattern => $route) {
            // 简单的路由匹配(实际应用中可能更复杂)
            if ($pattern === $url) {
                logMessage("找到匹配的路由: $pattern", LOG_LEVEL_INFO);
                return $route;
            }
        }
        
        logMessage("未找到匹配的路由", LOG_LEVEL_WARNING);
        return null;
    }
    
    // 调度请求
    public function dispatch($url) {
        logMessage("调度请求: $url", LOG_LEVEL_INFO);
        
        $route = $this->match($url);
        
        if ($route) {
            $controllerName = $route['controller'];
            $actionName = $route['action'];
            
            logMessage("实例化控制器: $controllerName", LOG_LEVEL_DEBUG);
            
            // 检查控制器是否存在
            if (class_exists($controllerName)) {
                $controller = new $controllerName();
                
                // 检查方法是否存在
                if (method_exists($controller, $actionName)) {
                    logMessage("调用方法: $controllerName::$actionName", LOG_LEVEL_DEBUG);
                    $controller->$actionName();
                    return true;
                } else {
                    logMessage("方法不存在: $controllerName::$actionName", LOG_LEVEL_ERROR);
                }
            } else {
                logMessage("控制器不存在: $controllerName", LOG_LEVEL_ERROR);
            }
        }
        
        logMessage("请求调度失败", LOG_LEVEL_ERROR);
        return false;
    }
}

// 控制器基类
class BaseController {
    // 渲染视图
    protected function render($view, $data = []) {
        $controllerName = str_replace('Controller', '', __CLASS__);
        logMessage("渲染视图: $view (控制器: $controllerName)", LOG_LEVEL_DEBUG);
        
        echo "<div class='view'>";
        echo "<h3>视图: $view</h3>";
        echo "<p>控制器: $controllerName</p>";
        
        if (!empty($data)) {
            echo "<p>数据: " . json_encode($data) . "</p>";
        }
        
        echo "</div>";
    }
}

// 首页控制器
class HomeController extends BaseController {
    public function index() {
        logMessage("执行首页控制器的index方法", LOG_LEVEL_INFO);
        $this->render('home/index', ["title" => "网站首页"]);
    }
    
    public function about() {
        logMessage("执行首页控制器的about方法", LOG_LEVEL_INFO);
        $this->render('home/about', ["title" => "关于我们"]);
    }
}

// 用户控制器
class UserController extends BaseController {
    public function list() {
        logMessage("执行用户控制器的list方法", LOG_LEVEL_INFO);
        $this->render('user/list', ["title" => "用户列表"]);
    }
    
    public function view() {
        logMessage("执行用户控制器的view方法", LOG_LEVEL_INFO);
        $this->render('user/view', ["title" => "查看用户", "id" => 123]);
    }
}

// 创建路由实例并添加路由
$router = new Router();
$router->addRoute('/', 'HomeController', 'index');
$router->addRoute('/about', 'HomeController', 'about');
$router->addRoute('/users', 'UserController', 'list');
$router->addRoute('/users/view', 'UserController', 'view');

// 测试路由调度
/*
echo "<h5>测试路由: /</h5>";
$router->dispatch('/');

echo "<h5>测试路由: /about</h5>";
$router->dispatch('/about');

echo "<h5>测试路由: /users</h5>";
$router->dispatch('/users');

echo "<h5>测试路由: /unknown</h5>";
$router->dispatch('/unknown');
*/

// 4. 使用魔术常量实现一个简单的依赖注入容器
echo "<h4>4. 简单的依赖注入容器</h4>";

// 依赖注入容器
class Container {
    private $instances = [];
    private $bindings = [];
    
    // 绑定一个类型到容器
    public function bind($key, $resolver) {
        logMessage("绑定类型到容器: $key", LOG_LEVEL_DEBUG);
        $this->bindings[$key] = $resolver;
    }
    
    // 从容器中获取一个实例
    public function make($key) {
        logMessage("从容器中获取实例: $key", LOG_LEVEL_DEBUG);
        
        // 检查实例是否已经存在
        if (isset($this->instances[$key])) {
            logMessage("返回缓存的实例: $key", LOG_LEVEL_DEBUG);
            return $this->instances[$key];
        }
        
        // 检查是否有绑定
        if (isset($this->bindings[$key])) {
            $resolver = $this->bindings[$key];
            logMessage("使用解析器创建实例: $key", LOG_LEVEL_DEBUG);
            
            // 创建实例
            $instance = $resolver($this);
            
            // 缓存实例
            $this->instances[$key] = $instance;
            
            return $instance;
        }
        
        // 尝试直接实例化
        if (class_exists($key)) {
            logMessage("直接实例化类: $key", LOG_LEVEL_DEBUG);
            
            // 创建实例
            $instance = new $key();
            
            // 缓存实例
            $this->instances[$key] = $instance;
            
            return $instance;
        }
        
        logMessage("无法解析: $key", LOG_LEVEL_ERROR);
        return null;
    }
}

// 数据库连接类
class DatabaseConnection {
    private $config;
    
    public function __construct() {
        $this->config = [
            'host' => 'localhost',
            'port' => 3306,
            'database' => 'test',
            'username' => 'root',
            'password' => ''
        ];
        
        logMessage("初始化数据库连接", LOG_LEVEL_INFO);
    }
    
    public function connect() {
        logMessage("建立数据库连接: " . $this->config['host'] . ":" . $this->config['port'], LOG_LEVEL_DEBUG);
        // 实际应用中,这里会建立真实的数据库连接
        return true;
    }
    
    public function query($sql) {
        logMessage("执行SQL: $sql", LOG_LEVEL_DEBUG);
        // 实际应用中,这里会执行真实的SQL查询
        return ["success" => true, "data" => []];
    }
}

// 用户服务类
class UserService {
    private $db;
    
    public function __construct(DatabaseConnection $db) {
        $this->db = $db;
        logMessage("初始化用户服务", LOG_LEVEL_INFO);
    }
    
    public function getUsers() {
        logMessage("获取用户列表", LOG_LEVEL_INFO);
        $result = $this->db->query("SELECT * FROM users");
        // 实际应用中,这里会处理查询结果
        return ["users" => ["张三", "李四", "王五"]];
    }
}

// 创建容器实例
$container = new Container();

// 绑定服务到容器
$container->bind('DatabaseConnection', function($c) {
    $db = new DatabaseConnection();
    $db->connect();
    return $db;
});

$container->bind('UserService', function($c) {
    $db = $c->make('DatabaseConnection');
    return new UserService($db);
});

// 从容器中获取服务
$userService = $container->make('UserService');
$users = $userService->getUsers();

echo "获取的用户列表: " . json_encode($users) . "<br>";

// 验证单例模式
$userService2 = $container->make('UserService');
echo "userService 和 userService2 是否是同一个实例: " . ($userService === $userService2 ? "是" : "否") . "<br>";

// 5. 使用魔术常量实现一个简单的事件系统
echo "<h4>5. 简单的事件系统</h4>";

// 事件管理器
class EventManager {
    private $listeners = [];
    
    // 注册事件监听器
    public function on($event, callable $listener) {
        logMessage("注册事件监听器: $event", LOG_LEVEL_DEBUG);
        
        if (!isset($this->listeners[$event])) {
            $this->listeners[$event] = [];
        }
        
        $this->listeners[$event][] = $listener;
    }
    
    // 触发事件
    public function trigger($event, $data = null) {
        logMessage("触发事件: $event", LOG_LEVEL_INFO);
        
        if (!isset($this->listeners[$event])) {
            logMessage("没有监听器监听事件: $event", LOG_LEVEL_DEBUG);
            return;
        }
        
        // 调用所有监听器
        foreach ($this->listeners[$event] as $index => $listener) {
            logMessage("调用事件监听器 #$index for $event", LOG_LEVEL_DEBUG);
            $listener($data);
        }
    }
}

// 创建事件管理器实例
$eventManager = new EventManager();

// 注册事件监听器
$eventManager->on('user_registered', function($user) {
    logMessage("用户注册事件处理 - 发送欢迎邮件", LOG_LEVEL_INFO);
    // 实际应用中,这里会发送欢迎邮件
    echo "已向用户 '" . ($user['email'] ?? 'unknown') . "' 发送欢迎邮件<br>";
});

$eventManager->on('user_registered', function($user) {
    logMessage("用户注册事件处理 - 添加到统计", LOG_LEVEL_INFO);
    // 实际应用中,这里会更新统计数据
    echo "已将用户 '" . ($user['name'] ?? 'unknown') . "' 添加到统计系统<br>";
});

$eventManager->on('user_logged_in', function($user) {
    logMessage("用户登录事件处理 - 更新最后登录时间", LOG_LEVEL_INFO);
    // 实际应用中,这里会更新用户的最后登录时间
    echo "已更新用户 '" . ($user['name'] ?? 'unknown') . "' 的最后登录时间<br>";
});

// 触发事件
echo "<h5>触发用户注册事件</h5>";
$eventManager->trigger('user_registered', ["name" => "张三", "email" => "zhangsan@example.com"]);

echo "<h5>触发用户登录事件</h5>";
$eventManager->trigger('user_logged_in', ["name" => "张三", "id" => 123]);

echo "<h5>触发未注册的事件</h5>";
$eventManager->trigger('user_logged_out');
?>