@@ -70,19 +70,9 @@ public function logKernelException(ExceptionEvent $event): void
7070 }
7171
7272 // There's no specific status code defined in the configuration for this exception
73- if (!$ throwable instanceof HttpExceptionInterface) {
74- $ class = new \ReflectionClass ($ throwable );
75-
76- do {
77- if ($ attributes = $ class ->getAttributes (WithHttpStatus::class, \ReflectionAttribute::IS_INSTANCEOF )) {
78- /** @var WithHttpStatus $instance */
79- $ instance = $ attributes [0 ]->newInstance ();
80-
81- $ throwable = HttpException::fromStatusCode ($ instance ->statusCode , $ throwable ->getMessage (), $ throwable , $ instance ->headers );
82- $ event ->setThrowable ($ throwable );
83- break ;
84- }
85- } while ($ class = $ class ->getParentClass ());
73+ if (!$ throwable instanceof HttpExceptionInterface && $ withHttpStatus = $ this ->getInheritedAttribute ($ throwable ::class, WithHttpStatus::class)) {
74+ $ throwable = HttpException::fromStatusCode ($ withHttpStatus ->statusCode , $ throwable ->getMessage (), $ throwable , $ withHttpStatus ->headers );
75+ $ event ->setThrowable ($ throwable );
8676 }
8777
8878 $ e = FlattenException::createFromThrowable ($ throwable );
@@ -200,16 +190,9 @@ private function resolveLogLevel(\Throwable $throwable): string
200190 }
201191 }
202192
203- $ class = new \ReflectionClass ($ throwable );
204-
205- do {
206- if ($ attributes = $ class ->getAttributes (WithLogLevel::class)) {
207- /** @var WithLogLevel $instance */
208- $ instance = $ attributes [0 ]->newInstance ();
209-
210- return $ instance ->level ;
211- }
212- } while ($ class = $ class ->getParentClass ());
193+ if ($ withLogLevel = $ this ->getInheritedAttribute ($ throwable ::class, WithLogLevel::class)) {
194+ return $ withLogLevel ->level ;
195+ }
213196
214197 if (!$ throwable instanceof HttpExceptionInterface || $ throwable ->getStatusCode () >= 500 ) {
215198 return LogLevel::CRITICAL ;
@@ -233,4 +216,45 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re
233216
234217 return $ request ;
235218 }
219+
220+ /**
221+ * @template T
222+ *
223+ * @param class-string<T> $attribute
224+ *
225+ * @return T|null
226+ */
227+ private function getInheritedAttribute (string $ class , string $ attribute ): ?object
228+ {
229+ $ class = new \ReflectionClass ($ class );
230+ $ interfaces = [];
231+ $ attributeReflector = null ;
232+ $ parentInterfaces = [];
233+ $ ownInterfaces = [];
234+
235+ do {
236+ if ($ attributes = $ class ->getAttributes ($ attribute , \ReflectionAttribute::IS_INSTANCEOF )) {
237+ $ attributeReflector = $ attributes [0 ];
238+ $ parentInterfaces = class_implements ($ class ->name );
239+ break ;
240+ }
241+
242+ $ interfaces [] = class_implements ($ class ->name );
243+ } while ($ class = $ class ->getParentClass ());
244+
245+ while ($ interfaces ) {
246+ $ ownInterfaces = array_diff_key (array_pop ($ interfaces ), $ parentInterfaces );
247+ $ parentInterfaces += $ ownInterfaces ;
248+
249+ foreach ($ ownInterfaces as $ interface ) {
250+ $ class = new \ReflectionClass ($ interface );
251+
252+ if ($ attributes = $ class ->getAttributes ($ attribute , \ReflectionAttribute::IS_INSTANCEOF )) {
253+ $ attributeReflector = $ attributes [0 ];
254+ }
255+ }
256+ }
257+
258+ return $ attributeReflector ?->newInstance();
259+ }
236260}
0 commit comments