在Laravel应用中获取调用辅助函数的控制器和方法

在Laravel应用中获取调用辅助函数的控制器和方法

本文探讨了在Laravel辅助函数中,如何在不显式传递参数的情况下,动态获取调用该辅助函数的控制器名称及其方法。通过利用PHP的调试回溯机制,特别是借助spatie/backtrace包,可以高效且可靠地实现这一目标。文章提供了两种实现方案:直接在辅助函数中集成回溯逻辑,以及通过修改Laravel的全局异常处理器,实现更集中、自动化的错误上下文日志记录。

1. 问题背景:在辅助函数中识别调用者

laravel应用开发中,我们经常会创建一些通用的辅助函数(helper functions),例如用于统一记录数据库操作异常的logdatabaseerror($exception)。这个辅助函数可能被多个控制器中的多个方法调用。在记录日志时,我们通常希望包含更多上下文信息,例如哪个控制器和方法触发了异常。然而,每次调用时都手动传递控制器和方法名作为参数,会增加代码冗余和维护成本。

例如,一个典型的数据库错误日志辅助函数可能如下:

// helpers.php function logDatabaseError ($exception) {     // 如何在此处动态获取 $controller 和 $function?     $controller = '???';     $function = '???';      $log_string = "TIME: ".now().PHP_EOL;     $log_string.= "User ID: ".(Auth::user() ? Auth::user()->id : 'Guest').PHP_EOL;     $log_string.= "Controller->Action:".$controller."->".$function.PHP_EOL;     $log_string.= $exception->getMessage().PHP_EOL; // 获取异常消息     $log_string.= $exception->getTraceAsString().PHP_EOL; // 完整堆栈追踪      Storage::disk('logs')->append('database.log', $log_string); }

而控制器中的调用方式:

// BestControllerEverController.php class BestControllerEver extends Controller {     function writeStuffToDatabase (Request $request) {         try {             DB::table('some_table')->insert(['data' => 'value']);         }         catch(IlluminateDatabaseQueryException $exception) {             logDatabaseError($exception); // 希望在此处自动识别 BestControllerEver 和 writeStuffToDatabase         }     } }

虽然异常对象本身包含堆栈追踪信息,但直接解析其字符串表示既不优雅也不可靠。因此,我们需要一种更结构化的方法来获取这些信息。

2. 解决方案一:在辅助函数内部使用 spatie/backtrace

PHP内置的debug_backtrace()函数可以获取程序执行的堆栈信息。然而,直接使用它需要手动解析数组,较为繁琐。spatie/backtrace是一个优秀的PHP包,它封装了debug_backtrace(),提供了更面向对象和易于使用的API来处理堆栈追踪。

2.1 安装 spatie/backtrace

首先,通过Composer将spatie/backtrace安装到您的Laravel项目中:

composer require spatie/backtrace

2.2 修改辅助函数 logDatabaseError

在logDatabaseError辅助函数中,我们可以使用SpatieBacktraceBacktrace::create()来生成一个堆栈追踪实例,然后遍历其帧(frames)来找到负责的控制器和方法。

// helpers.php use SpatieBacktraceBacktrace; use SpatieBacktraceFrame as SpatieBacktraceFrame; // 避免与 Laravel 内部 Frame 冲突  function logDatabaseError ($exception) {     $backtrace = Backtrace::create();      // 过滤堆栈帧,找到第一个继承自 AppHttpControllersController 的类     $controllerResponsible = collect($backtrace->frames())         ->filter(function (SpatieBacktraceFrame $frame) {             return (bool)$frame->class; // 确保帧有类名         })         ->filter(function (SpatieBacktraceFrame $frame) {             // 检查该类是否是 AppHttpControllersController 的子类             // 注意:您的控制器必须继承 AppHttpControllersController             return is_subclass_of($frame->class, AppHttpControllersController::class);         })         ->first(); // 获取第一个匹配的控制器帧      $log_string = "TIME: " . now()->format('Y-m-d H:i:s') . PHP_EOL;     $log_string .= "User ID: " . (auth()->check() ? auth()->id() : 'Guest') . PHP_EOL;      if ($controllerResponsible) {         $log_string .= "Controller->Action:" . $controllerResponsible->class . "->" . $controllerResponsible->method . PHP_EOL;     } else {         $log_string .= "Controller->Action: N/A (Could not determine from backtrace)" . PHP_EOL;     }      $log_string .= "Exception: " . $exception->getMessage() . PHP_EOL;     $log_string .= "File: " . $exception->getFile() . " Line: " . $exception->getLine() . PHP_EOL;     $log_string .= "Trace: " . $exception->getTraceAsString() . PHP_EOL; // 包含完整堆栈追踪      IlluminateSupportFacadesStorage::disk('logs')->append('database.log', $log_string);      // 如果需要使用 Laravel 8.66.0+ 的按需日志功能,可以取消注释以下代码     /*     IlluminateSupportFacadesLog::build([         'driver' => 'single',         'path' => storage_path('logs/database.log'),     ])->info($log_string);     */ }

注意事项:

  • 控制器继承: 您的所有控制器必须继承AppHttpControllersController,这是is_subclass_of函数能够正确识别的关键。
  • SpatieBacktraceFrame与Laravel内部的IlluminateRoutingRoute可能存在命名冲突,因此在use语句中进行别名处理是个好习惯。
  • auth()-youjiankuohaophpcnid()在用户未登录时可能返回null,建议进行判断。

3. 解决方案二:通过全局异常处理器实现集中化日志(推荐)

对于更大型的应用,将错误处理逻辑分散在各个try-catch块中并不理想。Laravel提供了强大的全局异常处理机制,通过修改app/Exceptions/Handler.php文件,我们可以实现更优雅、集中化的错误上下文日志记录。这种方法不仅移除了控制器中的try-catch样板代码,还能确保所有报告的异常都包含调用上下文。

在Laravel应用中获取调用辅助函数的控制器和方法

Timebolt

视频静态过滤器,可以快速自动删除沉默镜头

在Laravel应用中获取调用辅助函数的控制器和方法26

查看详情 在Laravel应用中获取调用辅助函数的控制器和方法

3.1 移除控制器中的 try-catch 块

首先,从您的控制器方法中移除所有的try-catch块,让异常自然地冒泡到全局异常处理器。

// BestControllerEverController.php class BestControllerEver extends Controller {     function writeStuffToDatabase (Request $request) {         // 移除 try-catch,让异常被全局处理器捕获         DB::table('myunavialbetable')->get(); // 假设这会抛出 QueryException     } }

3.2 修改 app/Exceptions/Handler.php

在app/Exceptions/Handler.php中,我们将利用reportable方法在异常被报告时捕获堆栈信息,并通过context方法将这些信息添加到日志上下文中。

<?php  namespace AppExceptions;  use IlluminateFoundationExceptionsHandler as ExceptionHandler; use Throwable; use SpatieBacktraceBacktrace as SpatieBacktrace; use SpatieBacktraceFrame as SpatieBacktraceFrame; // 避免命名冲突  class Handler extends ExceptionHandler {     /**      * 用于在 reportable 回调中存储控制器负责信息。      *      * @var SpatieBacktraceFrame|null      */     public $controllerResponsible = null;      /**      * A list of the exception types that are not reported.      *      * @var array<int, class-string<Throwable>>      */     protected $dontReport = [         //     ];      /**      * A list of the inputs that are never flashed for validation exceptions.      *      * @var array<int, string>      */     protected $dontFlash = [         'current_password',         'password',         'password_confirmation',     ];      /**      * Register the exception handling callbacks for the application.      *      * @return void      */     public function register(): void     {         $this->reportable(function (Throwable $e) {             // 在异常被报告时,创建回溯实例并查找负责的控制器             $backtraceInstance = SpatieBacktrace::createForThrowable($e);              $controllerResponsible = collect($backtraceInstance->frames())                 ->filter(function (SpatieBacktraceFrame $frame) {                     return (bool)$frame->class;                 })                 ->filter(function (SpatieBacktraceFrame $frame) {                     return is_subclass_of($frame->class, AppHttpControllersController::class);                 })                 ->first();              // 将找到的控制器帧存储起来,以便在 context 方法中使用             $this->controllerResponsible = $controllerResponsible;         });     }      /**      * Get the default context variables for logging.      *      * @return array<string, mixed>      */     protected function context(): array     {         $extraContext = [];          // 如果找到了负责的控制器,则将其信息添加到日志上下文中         if ($this->controllerResponsible instanceof SpatieBacktraceFrame) {             $extraContext['controller'] = $this->controllerResponsible->class;             $extraContext['method'] = $this->controllerResponsible->method;             $extraContext['controller@method'] = $this->controllerResponsible->class . '@' . $this->controllerResponsible->method;         }          // 合并父类的上下文和我们添加的额外上下文         return array_merge(parent::context(), $extraContext);     } }

工作原理:

  1. register() 方法中的 reportable() 回调: 当任何异常被Laravel的异常处理器捕获并准备报告(例如写入日志)时,reportable() 定义的回调函数就会被执行。在这个回调中,我们利用SpatieBacktrace::createForThrowable($e)获取异常的堆栈追踪,并从中筛选出调用异常的控制器和方法。找到的信息被存储在$this->controllerResponsible属性中。
  2. context() 方法: 这个方法用于向所有日志消息(包括异常日志)添加额外的上下文信息。我们在这里检查$this->controllerResponsible是否包含有效数据,如果存在,就将其作为controller、method和controller@method键值对添加到日志上下文中。

通过这种方式,当任何异常发生并被报告时,Laravel的日志系统会自动包含触发该异常的控制器和方法信息,无需在业务逻辑中手动处理。

4. 总结与最佳实践

本文介绍了两种在Laravel中动态获取调用辅助函数或触发异常的控制器和方法的方法:

  1. 直接在辅助函数中集成 spatie/backtrace: 适用于需要特定辅助函数内部进行上下文感知的场景,但需要在每个相关辅助函数中重复逻辑。
  2. 通过全局异常处理器 app/Exceptions/Handler.php 实现集中化日志: 这是更推荐的方案,它将错误上下文的捕获和注入逻辑集中管理,减少了业务代码中的try-catch块,使得控制器更简洁,同时确保所有报告的异常都能自动包含丰富的上下文信息。

无论选择哪种方法,关键在于利用PHP的堆栈追踪机制(通过spatie/backtrace进行优化),并确保您的控制器都遵循了继承AppHttpControllersController的约定。第二种方案尤其适合构建健壮、易于调试的生产级Laravel应用。

以上就是在Laravel应用中获取调用辅助函数的控制器和方法的详细内容,更多请关注php word laravel composer 处理器 cad app 应用开发 键值对 php laravel composer NULL 面向对象 封装 try catch register 回调函数 字符串 继承 对象 this 数据库 http 自动化 应用开发

大家都在看:

php word laravel composer 处理器 cad app 应用开发 键值对 php laravel composer NULL 面向对象 封装 try catch register 回调函数 字符串 继承 对象 this 数据库 http 自动化 应用开发

app
上一篇
下一篇
text=ZqhQzanResources