@@ -192,8 +192,7 @@ private function loadMethodBodies(\ReflectionClass $from): array
192192 foreach ($ nodeFinder ->findInstanceOf ($ class , Node \Stmt \ClassMethod::class) as $ method ) {
193193 /** @var Node\Stmt\ClassMethod $method */
194194 if ($ method ->stmts ) {
195- $ start = $ method ->stmts [0 ]->getAttribute ('startFilePos ' );
196- $ body = substr ($ code , $ start , end ($ method ->stmts )->getAttribute ('endFilePos ' ) - $ start + 1 );
195+ $ body = $ this ->extractBody ($ nodeFinder , $ code , $ method ->stmts );
197196 $ bodies [$ method ->name ->toString ()] = Helpers::indentPhp ($ body , -2 );
198197 }
199198 }
@@ -208,17 +207,60 @@ private function loadFunctionBody(\ReflectionFunction $from): string
208207 }
209208
210209 [$ code , $ stmts ] = $ this ->parse ($ from );
210+
211+ $ nodeFinder = new PhpParser \NodeFinder ;
211212 /** @var Node\Stmt\Function_ $function */
212- $ function = ( new PhpParser \ NodeFinder ) ->findFirst ($ stmts , function (Node $ node ) use ($ from ) {
213+ $ function = $ nodeFinder ->findFirst ($ stmts , function (Node $ node ) use ($ from ) {
213214 return $ node instanceof Node \Stmt \Function_ && $ node ->namespacedName ->toString () === $ from ->name ;
214215 });
215216
216- $ start = $ function ->stmts [0 ]->getAttribute ('startFilePos ' );
217- $ body = substr ($ code , $ start , end ($ function ->stmts )->getAttribute ('endFilePos ' ) - $ start + 1 );
217+ $ body = $ this ->extractBody ($ nodeFinder , $ code , $ function ->stmts );
218218 return Helpers::indentPhp ($ body , -1 );
219219 }
220220
221221
222+ /**
223+ * @param Node[] $statements
224+ */
225+ private function extractBody (PhpParser \NodeFinder $ nodeFinder , string $ originalCode , array $ statements ): string
226+ {
227+ $ start = $ statements [0 ]->getAttribute ('startFilePos ' );
228+ $ body = substr ($ originalCode , $ start , end ($ statements )->getAttribute ('endFilePos ' ) - $ start + 1 );
229+
230+ $ replacements = [];
231+ // name-nodes => resolved fully-qualified name
232+ foreach ($ nodeFinder ->findInstanceOf ($ statements , Node \Name::class) as $ node ) {
233+ if ($ node ->hasAttribute ('resolvedName ' )
234+ && $ node ->getAttribute ('resolvedName ' ) instanceof Node \Name \FullyQualified
235+ ) {
236+ $ replacements [] = [
237+ $ node ->getStartFilePos (),
238+ $ node ->getEndFilePos (),
239+ $ node ->getAttribute ('resolvedName ' )->toCodeString (),
240+ ];
241+ }
242+ }
243+
244+ //sort collected resolved names by position in file
245+ usort ($ replacements , function ($ a , $ b ) {
246+ return $ a [0 ] <=> $ b [0 ];
247+ });
248+ $ correctiveOffset = -$ start ;
249+ //replace changes body length so we need correct offset
250+ foreach ($ replacements as [$ startPos , $ endPos , $ replacement ]) {
251+ $ replacingStringLength = $ endPos - $ startPos + 1 ;
252+ $ body = substr_replace (
253+ $ body ,
254+ $ replacement ,
255+ $ correctiveOffset + $ startPos ,
256+ $ replacingStringLength
257+ );
258+ $ correctiveOffset += strlen ($ replacement ) - $ replacingStringLength ;
259+ }
260+ return $ body ;
261+ }
262+
263+
222264 private function parse ($ from ): array
223265 {
224266 $ file = $ from ->getFileName ();
@@ -235,7 +277,7 @@ private function parse($from): array
235277 $ stmts = $ parser ->parse ($ code );
236278
237279 $ traverser = new PhpParser \NodeTraverser ;
238- $ traverser ->addVisitor (new PhpParser \NodeVisitor \NameResolver );
280+ $ traverser ->addVisitor (new PhpParser \NodeVisitor \NameResolver ( null , [ ' replaceNodes ' => false ]) );
239281 $ stmts = $ traverser ->traverse ($ stmts );
240282
241283 return [$ code , $ stmts ];
0 commit comments