@@ -39,7 +39,9 @@ public function __construct(
3939 * Decorates a service with stopwatch timing interceptors.
4040 *
4141 * @template T of object
42+ *
4243 * @param T $service
44+ *
4345 * @return T
4446 */
4547 public function decorate (object $ service ): object
@@ -50,13 +52,14 @@ public function decorate(object $service): object
5052 return $ service ;
5153 }
5254
53- $ className = $ class ->getName ();
54- [$ prefixInterceptors , $ suffixInterceptors ] = $ this ->getPrefixAndSuffixInterceptors ($ class , $ className );
55+ [$ prefixInterceptors , $ suffixInterceptors ] = $ this ->getPrefixAndSuffixInterceptors ($ class );
5556
5657 /** @var T */
5758 return $ this ->createProxyWithInterceptors ($ service , $ prefixInterceptors , $ suffixInterceptors );
5859 }
5960
61+ // Validation methods
62+
6063 private function shouldSkipDecoration (ReflectionClass $ class ): bool
6164 {
6265 return $ class ->getFileName () === false
@@ -71,6 +74,71 @@ private function isExcludedClassName(string $className): bool
7174 || str_contains ($ className , 'StopwatchProxy_ ' );
7275 }
7376
77+ // Interceptor creation methods
78+
79+ /**
80+ * @return array{0: array<string, Closure>, 1: array<string, Closure>}
81+ */
82+ private function getPrefixAndSuffixInterceptors (ReflectionClass $ class ): array
83+ {
84+ $ className = $ class ->getName ();
85+
86+ $ prefixInterceptors = [];
87+ $ suffixInterceptors = [];
88+
89+ $ methods = $ this ->getProxyableMethods ($ class );
90+
91+ $ stopwatch = $ this ->stopwatch ;
92+ $ decorator = $ this ;
93+
94+ foreach ($ methods as $ method ) {
95+ $ methodName = $ method ->getName ();
96+ $ eventName = "{$ class ->getShortName ()}-> {$ methodName }" ;
97+
98+ $ prefixInterceptors [$ methodName ] = $ this ->createPrefixInterceptor ($ eventName , $ className , $ stopwatch );
99+ $ suffixInterceptors [$ methodName ] = $ this ->createSuffixInterceptor ($ eventName , $ stopwatch , $ decorator );
100+ }
101+
102+ return [$ prefixInterceptors , $ suffixInterceptors ];
103+ }
104+
105+ private function createPrefixInterceptor (string $ eventName , string $ className , Stopwatch $ stopwatch ): Closure
106+ {
107+ return static function () use ($ eventName , $ className , $ stopwatch ): void {
108+ $ stopwatch ->start ($ eventName , $ className );
109+ };
110+ }
111+
112+ private function createSuffixInterceptor (
113+ string $ eventName ,
114+ Stopwatch $ stopwatch ,
115+ self $ decorator ,
116+ ): Closure {
117+ return static function (
118+ mixed $ proxy ,
119+ mixed $ instance ,
120+ mixed $ method ,
121+ mixed $ params ,
122+ mixed &$ returnValue ,
123+ ) use (
124+ $ eventName ,
125+ $ stopwatch ,
126+ $ decorator ,
127+ ): void {
128+ $ stopwatch ->stop ($ eventName );
129+ $ decorator ->decorateReturnValue ($ returnValue );
130+ };
131+ }
132+
133+ private function decorateReturnValue (mixed &$ returnValue ): void
134+ {
135+ if (is_object ($ returnValue ) && !$ returnValue instanceof EntityInterface) {
136+ $ returnValue = $ this ->decorate ($ returnValue );
137+ }
138+ }
139+
140+ // Proxy creation methods
141+
74142 /**
75143 * @param array<string, Closure> $prefixInterceptors
76144 * @param array<string, Closure> $suffixInterceptors
@@ -129,6 +197,8 @@ private function createProxy(
129197 }
130198 }
131199
200+ // Proxy class generation methods
201+
132202 private function generateProxyClass (
133203 string $ proxyClassName ,
134204 string $ originalClassName ,
@@ -182,6 +252,39 @@ private function generateProxyMethod(ReflectionMethod $method): string
182252 return $ code ;
183253 }
184254
255+ private function generateMethodBody (string $ methodName , string $ argsList , bool $ isVoid ): string
256+ {
257+ return $ isVoid
258+ ? $ this ->generateVoidMethodBody ($ methodName , $ argsList )
259+ : $ this ->generateNonVoidMethodBody ($ methodName , $ argsList );
260+ }
261+
262+ private function generateVoidMethodBody (string $ methodName , string $ argsList ): string
263+ {
264+ $ code = " \$this->wrappedInstance-> {$ methodName }( {$ argsList }); \n" ;
265+ $ code .= " if (isset( \$this->suffixInterceptors[' {$ methodName }'])) { \n" ;
266+ $ code .= " \$returnValue = null; \n" ;
267+ $ code .= " ( \$this->suffixInterceptors[' {$ methodName }']) " ;
268+ $ code .= "(null, null, null, func_get_args(), \$returnValue); \n" ;
269+ $ code .= " } \n" ;
270+
271+ return $ code ;
272+ }
273+
274+ private function generateNonVoidMethodBody (string $ methodName , string $ argsList ): string
275+ {
276+ $ code = " \$returnValue = \$this->wrappedInstance-> {$ methodName }( {$ argsList }); \n" ;
277+ $ code .= " if (isset( \$this->suffixInterceptors[' {$ methodName }'])) { \n" ;
278+ $ code .= " ( \$this->suffixInterceptors[' {$ methodName }']) " ;
279+ $ code .= "(null, null, null, func_get_args(), \$returnValue); \n" ;
280+ $ code .= " } \n" ;
281+ $ code .= " return \$returnValue; \n" ;
282+
283+ return $ code ;
284+ }
285+
286+ // Method parameter handling
287+
185288 /**
186289 * @return array{0: string, 1: string}
187290 */
@@ -247,6 +350,8 @@ private function getDefaultValueString(\ReflectionParameter $param): string
247350 return $ result ;
248351 }
249352
353+ // Return type handling
354+
250355 /**
251356 * @return array{0: string, 1: bool}
252357 */
@@ -268,36 +373,7 @@ private function getMethodReturnType(ReflectionMethod $method): array
268373 return [$ returnType , $ isVoid ];
269374 }
270375
271- private function generateMethodBody (string $ methodName , string $ argsList , bool $ isVoid ): string
272- {
273- return $ isVoid
274- ? $ this ->generateVoidMethodBody ($ methodName , $ argsList )
275- : $ this ->generateNonVoidMethodBody ($ methodName , $ argsList );
276- }
277-
278- private function generateVoidMethodBody (string $ methodName , string $ argsList ): string
279- {
280- $ code = " \$this->wrappedInstance-> {$ methodName }( {$ argsList }); \n" ;
281- $ code .= " if (isset( \$this->suffixInterceptors[' {$ methodName }'])) { \n" ;
282- $ code .= " \$returnValue = null; \n" ;
283- $ code .= " ( \$this->suffixInterceptors[' {$ methodName }']) " ;
284- $ code .= "(null, null, null, func_get_args(), \$returnValue); \n" ;
285- $ code .= " } \n" ;
286-
287- return $ code ;
288- }
289-
290- private function generateNonVoidMethodBody (string $ methodName , string $ argsList ): string
291- {
292- $ code = " \$returnValue = \$this->wrappedInstance-> {$ methodName }( {$ argsList }); \n" ;
293- $ code .= " if (isset( \$this->suffixInterceptors[' {$ methodName }'])) { \n" ;
294- $ code .= " ( \$this->suffixInterceptors[' {$ methodName }']) " ;
295- $ code .= "(null, null, null, func_get_args(), \$returnValue); \n" ;
296- $ code .= " } \n" ;
297- $ code .= " return \$returnValue; \n" ;
298-
299- return $ code ;
300- }
376+ // Method filtering
301377
302378 /**
303379 * @return array<ReflectionMethod>
@@ -317,63 +393,4 @@ private function isProxyableMethod(ReflectionMethod $method): bool
317393 && !$ method ->isConstructor ()
318394 && !$ method ->isDestructor ();
319395 }
320-
321- /**
322- * @return array{0: array<string, Closure>, 1: array<string, Closure>}
323- */
324- private function getPrefixAndSuffixInterceptors (ReflectionClass $ class , string $ className ): array
325- {
326- $ prefixInterceptors = [];
327- $ suffixInterceptors = [];
328-
329- $ methods = $ this ->getProxyableMethods ($ class );
330-
331- $ stopwatch = $ this ->stopwatch ;
332- $ decorator = $ this ;
333-
334- foreach ($ methods as $ method ) {
335- $ methodName = $ method ->getName ();
336- $ eventName = "{$ class ->getShortName ()}-> {$ methodName }" ;
337-
338- $ prefixInterceptors [$ methodName ] = $ this ->createPrefixInterceptor ($ eventName , $ className , $ stopwatch );
339- $ suffixInterceptors [$ methodName ] = $ this ->createSuffixInterceptor ($ eventName , $ stopwatch , $ decorator );
340- }
341-
342- return [$ prefixInterceptors , $ suffixInterceptors ];
343- }
344-
345- private function createPrefixInterceptor (string $ eventName , string $ className , Stopwatch $ stopwatch ): Closure
346- {
347- return static function () use ($ eventName , $ className , $ stopwatch ): void {
348- $ stopwatch ->start ($ eventName , $ className );
349- };
350- }
351-
352- private function createSuffixInterceptor (
353- string $ eventName ,
354- Stopwatch $ stopwatch ,
355- self $ decorator ,
356- ): Closure {
357- return static function (
358- mixed $ p ,
359- mixed $ i ,
360- mixed $ m ,
361- mixed $ params ,
362- mixed &$ returnValue ,
363- ) use (
364- $ eventName ,
365- $ stopwatch ,
366- $ decorator ,
367- ): void {
368- $ stopwatch ->stop ($ eventName );
369- $ decorator ->decorateReturnValue ($ returnValue );
370- };
371- }
372-
373- private function decorateReturnValue (mixed &$ returnValue ): void
374- {
375- if (is_object ($ returnValue ) && !$ returnValue instanceof EntityInterface) {
376- $ returnValue = $ this ->decorate ($ returnValue );
377- }
378- }
379396}
0 commit comments