Symfony应用全局函数正确定义方法及缓存加载问题咨询
Symfony 全局可调用函数实现方案
出于故障排查需求,需要在Symfony应用任意位置调用自定义_logger()日志函数,目标函数定义如下:
function _logger(?string $msg=null, int $offset=0, bool $time=false):void { $d1 = debug_backtrace()[$offset+1]; $d2 = debug_backtrace()[$offset+2]; syslog(LOG_INFO, sprintf('_logger%s: %s::%s(%s) called by %s::%s on line %s%s', $time?'('.date("Y-m-d H:i:s").')':'', $d1['class'], $d1['function'], json_encode($d1['args']), $d2['class'], $d2['function'], $d2['line'], $msg?' | '.$msg:'')); }
前期尝试的异常原因
先后试过三种实现方案均存在问题,各方案的异常根因如下:
- 写入前端控制器
index.php
直接在文件主体定义函数会在自动加载器初始化阶段和Runtime组件运行流程冲突,触发无明确提示的致命错误;将函数写在index.php返回的闭包内时,函数仅属于闭包内部作用域,只有HTTP请求触发闭包执行时才会完成定义,无法覆盖CLI命令、队列消费等不经过该前端闭包的场景,对应代码结构如下:<?php use App\Kernel; // 此处直接定义函数会触发致命错误 require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { // 此处定义的函数仅HTTP场景可调用 return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; - 写入
Kernel.phpKernel.php头部声明了namespace App;,文件内定义的函数默认归属App命名空间,不属于全局作用域,因此调用时必须写完整路径\App\_logger(),无法实现全局直接调用,对应代码结构如下:<?php declare(strict_types=1); namespace App; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\HttpKernel\Kernel as BaseKernel; class Kernel extends BaseKernel { use MicroKernelTrait; } if(!function_exists('_logger')) { // 此处定义的函数属于App命名空间,必须带命名空间前缀调用 } - 在
src目录下新建独立文件存放函数
Symfony默认的服务自动加载机制会按照PSR-4规范扫描src目录下所有PHP文件,尝试加载对应命名空间的类。文件名为_logger.php时,框架会预期文件内存在App\_logger类,文件内仅存函数定义时就会抛出类不存在的错误;缓存未失效时会跳过文件扫描因此偶现正常,缓存重建时重新扫描就会触发报错,对应错误信息如下:Expected to find class "App_logger" in file "/var/www/src/_logger.php" while importing services from resource "../src/", but it was not found! Check the namespace prefix used with the resource in /var/www/config/services.yaml (which is being imported from "/var/www/src/Kernel.php"). (500 Internal Server Error)
标准实现方式
PHP生态下全局函数的标准加载方式是通过Composer的files自动加载规则实现,和框架逻辑解耦,兼容所有运行场景,不会和Symfony缓存机制冲突,实现步骤如下:
- 新建函数存放文件,推荐路径为
src/functions/global.php,注意文件不要声明任何命名空间,函数定义外层包裹function_exists判断避免重复定义:<?php declare(strict_types=1); if (!function_exists('_logger')) { function _logger(?string $msg=null, int $offset=0, bool $time=false):void { $d1 = debug_backtrace()[$offset+1]; $d2 = debug_backtrace()[$offset+2]; syslog(LOG_INFO, sprintf('_logger%s: %s::%s(%s) called by %s::%s on line %s%s', $time?'('.date("Y-m-d H:i:s").')':'', $d1['class'], $d1['function'], json_encode($d1['args']), $d2['class'], $d2['function'], $d2['line'], $msg?' | '.$msg:'')); } } - 打开项目根目录的
composer.json,在autoload配置段中添加files规则,指定全局函数文件路径:"autoload": { "psr-4": { "App\\": "src/" }, "files": [ "src/functions/global.php" ] } - 执行命令
composer dump-autoload重新生成自动加载文件,完成后即可在应用任意位置(HTTP请求、CLI命令、异步队列等所有场景)直接调用_logger(),无需加任何命名空间前缀。
补充说明:如果要将函数文件直接放在src根目录,需要修改config/services.yaml的服务自动加载排除规则,将函数文件路径加入排除列表,避免框架将其作为类文件扫描,但这种方式不如Composer自动加载通用,不推荐使用。
内容的提问来源于stack exchange,提问作者user1032531
相关产品推荐
相关产品推荐

