@@ -13,9 +13,7 @@ final class Exporter
1313 public static function closure (\Closure $ closure ): string
1414 {
1515 $ ref = new \ReflectionFunction ($ closure );
16- $ code = static ::getFunctionCode ($ ref );
17-
18- return preg_replace ('/^.*?function(\s+[^\s \\(]+?)?\s* \\((.+) \\}.*?\s*$/s ' , 'function($2} ' , $ code );
16+ return static ::getClosureSource ($ ref );
1917 }
2018
2119 /**
@@ -38,20 +36,50 @@ public static function helpers(Context $context): string
3836 return "[ $ ret] " ;
3937 }
4038
41- public static function getFunctionCode (\ReflectionFunction $ refobj ): string
39+ public static function getClosureSource (\ReflectionFunction $ fn ): string
4240 {
43- $ fname = $ refobj ->getFileName ();
44- $ lines = file_get_contents ($ fname );
45- $ file = new \SplFileObject ($ fname );
41+ $ fileContents = file_get_contents ($ fn ->getFileName ());
42+ $ startLine = $ fn ->getStartLine ();
43+ $ endLine = $ fn ->getEndLine ();
44+ $ enteredFnToken = null ;
45+ $ depth = 0 ;
46+ $ code = '' ;
4647
47- $ start = $ refobj ->getStartLine () - 1 ;
48- $ end = $ refobj ->getEndLine ();
48+ foreach (\PhpToken::tokenize ($ fileContents ) as $ token ) {
49+ if ($ token ->line < $ startLine ) {
50+ continue ;
51+ } elseif ($ token ->line > $ endLine ) {
52+ break ;
53+ } elseif (!$ enteredFnToken ) {
54+ if ($ token ->id !== T_FUNCTION && $ token ->id !== T_FN ) {
55+ continue ;
56+ }
57+ $ enteredFnToken = $ token ;
58+ }
4959
50- $ file ->seek ($ start );
51- $ spos = $ file ->ftell ();
52- $ file ->seek ($ end );
53- $ epos = $ file ->ftell ();
60+ $ name = $ token ->getTokenName ();
61+
62+ if (in_array ($ name , ['( ' , '[ ' , '{ ' , 'T_CURLY_OPEN ' ])) {
63+ $ depth ++;
64+ } elseif (in_array ($ name , [') ' , '] ' , '} ' ])) {
65+ if ($ depth === 0 && $ enteredFnToken ->id === T_FN ) {
66+ return rtrim ($ code );
67+ }
68+ $ depth --;
69+ }
70+
71+ if ($ depth === 0 ) {
72+ if ($ enteredFnToken ->id === T_FUNCTION && $ name === '} ' ) {
73+ $ code .= $ token ->text ;
74+ return $ code ;
75+ } elseif ($ enteredFnToken ->id === T_FN && in_array ($ name , ['; ' , ', ' ])) {
76+ return $ code ;
77+ }
78+ }
79+
80+ $ code .= $ token ->text ;
81+ }
5482
55- return substr ( $ lines , $ spos , $ epos - $ spos ) ;
83+ return $ code ;
5684 }
5785}
0 commit comments