@@ -67,7 +67,7 @@ class DebugClassLoader
67
67
private $ classLoader ;
68
68
private $ isFinder ;
69
69
private $ loaded = [];
70
- private $ compatPatch ;
70
+ private $ patchTypes ;
71
71
private static $ caseCheck ;
72
72
private static $ checkedClasses = [];
73
73
private static $ final = [];
@@ -85,7 +85,7 @@ public function __construct(callable $classLoader)
85
85
{
86
86
$ this ->classLoader = $ classLoader ;
87
87
$ this ->isFinder = \is_array ($ classLoader ) && method_exists ($ classLoader [0 ], 'findFile ' );
88
- $ this -> compatPatch = getenv ('SYMFONY_PATCH_TYPE_DECLARATIONS_COMPAT ' ) ?: null ;
88
+ parse_str ( getenv ('SYMFONY_PATCH_TYPE_DECLARATIONS ' ) ?: '' , $ this -> patchTypes ) ;
89
89
90
90
if (!isset (self ::$ caseCheck )) {
91
91
$ file = file_exists (__FILE__ ) ? __FILE__ : rtrim (realpath ('. ' ), \DIRECTORY_SEPARATOR );
@@ -160,13 +160,22 @@ public static function disable(): void
160
160
spl_autoload_unregister ($ function );
161
161
}
162
162
163
+ $ loader = null ;
164
+
163
165
foreach ($ functions as $ function ) {
164
166
if (\is_array ($ function ) && $ function [0 ] instanceof self) {
167
+ $ loader = $ function [0 ];
165
168
$ function = $ function [0 ]->getClassLoader ();
166
169
}
167
170
168
171
spl_autoload_register ($ function );
169
172
}
173
+
174
+ if (null !== $ loader ) {
175
+ foreach (array_merge (get_declared_interfaces (), get_declared_traits (), get_declared_classes ()) as $ class ) {
176
+ $ loader ->checkClass ($ class );
177
+ }
178
+ }
170
179
}
171
180
172
181
public function findFile (string $ class ): ?string
@@ -348,7 +357,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
348
357
if (trait_exists ($ class )) {
349
358
$ file = $ refl ->getFileName ();
350
359
351
- foreach ($ refl ->getMethods (\ReflectionMethod:: IS_PUBLIC | \ReflectionMethod:: IS_PROTECTED ) as $ method ) {
360
+ foreach ($ refl ->getMethods () as $ method ) {
352
361
if ($ method ->getFileName () === $ file ) {
353
362
self ::$ methodTraits [$ file ][$ method ->getStartLine ()] = $ class ;
354
363
}
@@ -370,7 +379,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
370
379
}
371
380
}
372
381
373
- foreach ($ refl ->getMethods (\ReflectionMethod:: IS_PUBLIC | \ReflectionMethod:: IS_PROTECTED ) as $ method ) {
382
+ foreach ($ refl ->getMethods () as $ method ) {
374
383
if ($ method ->class !== $ class ) {
375
384
continue ;
376
385
}
@@ -413,18 +422,24 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
413
422
}
414
423
}
415
424
425
+ if ($ canAddReturnType = '' !== ($ this ->patchTypes ['class-prefix ' ] ?? '' ) && 0 === strpos ($ class , $ this ->patchTypes ['class-prefix ' ])) {
426
+ $ canAddReturnType = false !== strpos ($ refl ->getFileName (), \DIRECTORY_SEPARATOR .'Tests ' .\DIRECTORY_SEPARATOR )
427
+ || $ refl ->isFinal ()
428
+ || $ method ->isFinal ()
429
+ || $ method ->isPrivate ()
430
+ || ('' === (self ::$ internal [$ class ] ?? null ) && !$ refl ->isAbstract ())
431
+ || '' === (self ::$ final [$ class ] ?? null )
432
+ || preg_match ('/@(final|internal)$/m ' , $ doc );
433
+ }
434
+
416
435
if (isset (self ::$ returnTypes [$ class ][$ method ->name ]) && !$ method ->hasReturnType () && !($ doc && preg_match ('/\n\s+\* @return +(\S+)/ ' , $ doc ))) {
417
436
list ($ normalizedType , $ returnType , $ declaringClass , $ declaringFile ) = self ::$ returnTypes [$ class ][$ method ->name ];
418
437
419
- if (null !== $ this -> compatPatch && 0 === strpos ( $ class , $ this -> compatPatch ) ) {
420
- self :: fixReturnStatements ($ method , $ normalizedType );
438
+ if ($ canAddReturnType ) {
439
+ $ this -> patchMethod ($ method, $ returnType , $ declaringFile , $ normalizedType );
421
440
}
422
441
423
442
if (strncmp ($ ns , $ declaringClass , $ len )) {
424
- if (null !== $ this ->compatPatch && 0 === strpos ($ class , $ this ->compatPatch )) {
425
- self ::patchMethod ($ method , $ returnType , $ declaringFile );
426
- }
427
-
428
443
$ deprecations [] = sprintf ('Method "%s::%s()" will return "%s" as of its next major version. Doing the same in child class "%s" will be required when upgrading. ' , $ declaringClass , $ method ->name , $ normalizedType , $ class );
429
444
}
430
445
}
@@ -436,11 +451,19 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
436
451
if (!$ method ->hasReturnType () && false !== strpos ($ doc , '@return ' ) && preg_match ('/\n\s+\* @return +(\S+)/ ' , $ doc , $ matches )) {
437
452
$ this ->setReturnType ($ matches [1 ], $ method , $ parent );
438
453
439
- if (null !== $ this ->compatPatch && 0 === strpos ($ class , $ this ->compatPatch )) {
440
- self ::fixReturnStatements ($ method , self ::$ returnTypes [$ class ][$ method ->name ][0 ] ?? '? ' );
454
+ if (isset (self ::$ returnTypes [$ class ][$ method ->name ][0 ]) && $ canAddReturnType ) {
455
+ $ this ->fixReturnStatements ($ method , self ::$ returnTypes [$ class ][$ method ->name ][0 ]);
456
+ }
457
+
458
+ if ($ method ->isPrivate ()) {
459
+ unset(self ::$ returnTypes [$ class ][$ method ->name ]);
441
460
}
442
461
}
443
462
463
+ if ($ method ->isPrivate ()) {
464
+ continue ;
465
+ }
466
+
444
467
$ finalOrInternal = false ;
445
468
446
469
foreach (['final ' , 'internal ' ] as $ annotation ) {
@@ -630,7 +653,7 @@ private function setReturnType(string $types, \ReflectionMethod $method, ?string
630
653
} elseif ('null ' === $ normalizedType ) {
631
654
$ normalizedType = $ t ;
632
655
$ returnType = $ t ;
633
- } elseif ($ n !== $ normalizedType ) {
656
+ } elseif ($ n !== $ normalizedType || ! preg_match ( ' /^ \\\\ ?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?: \\\\ [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/ ' , $ n ) ) {
634
657
// ignore multi-types return declarations
635
658
return ;
636
659
}
@@ -680,7 +703,7 @@ private function normalizeType(string $type, string $class, ?string $parent): st
680
703
/**
681
704
* Utility method to add @return annotations to the Symfony code-base where it triggers a self-deprecations.
682
705
*/
683
- private static function patchMethod (\ReflectionMethod $ method , string $ returnType , string $ declaringFile )
706
+ private function patchMethod (\ReflectionMethod $ method , string $ returnType , string $ declaringFile, string $ normalizedType )
684
707
{
685
708
static $ patchedMethods = [];
686
709
static $ useStatements = [];
@@ -690,8 +713,10 @@ private static function patchMethod(\ReflectionMethod $method, string $returnTyp
690
713
}
691
714
692
715
$ patchedMethods [$ file ][$ startLine ] = true ;
693
- $ patchedMethods [$ file ][0 ] = $ patchedMethods [$ file ][0 ] ?? 0 ;
694
- $ startLine += $ patchedMethods [$ file ][0 ] - 2 ;
716
+ $ fileOffset = $ patchedMethods [$ file ][0 ] ?? 0 ;
717
+ $ startLine += $ fileOffset - 2 ;
718
+ $ nullable = '? ' === $ normalizedType [0 ] ? '? ' : '' ;
719
+ $ normalizedType = ltrim ($ normalizedType , '? ' );
695
720
$ returnType = explode ('| ' , $ returnType );
696
721
$ code = file ($ file );
697
722
@@ -737,32 +762,42 @@ private static function patchMethod(\ReflectionMethod $method, string $returnTyp
737
762
if (!isset ($ useMap [$ alias ])) {
738
763
$ useStatements [$ file ][2 ][$ alias ] = $ type ;
739
764
$ code [$ useOffset ] = "use $ type; \n" .$ code [$ useOffset ];
740
- ++$ patchedMethods [ $ file ][ 0 ] ;
765
+ ++$ fileOffset ;
741
766
} elseif ($ useMap [$ alias ] !== $ type ) {
742
767
$ alias .= 'FIXME ' ;
743
768
$ useStatements [$ file ][2 ][$ alias ] = $ type ;
744
769
$ code [$ useOffset ] = "use $ type as $ alias; \n" .$ code [$ useOffset ];
745
- ++$ patchedMethods [ $ file ][ 0 ] ;
770
+ ++$ fileOffset ;
746
771
}
747
772
748
773
$ returnType [$ i ] = null !== $ format ? sprintf ($ format , $ alias ) : $ alias ;
774
+
775
+ if (!isset (self ::SPECIAL_RETURN_TYPES [$ normalizedType ]) && !isset (self ::SPECIAL_RETURN_TYPES [$ returnType [$ i ]])) {
776
+ $ normalizedType = $ returnType [$ i ];
777
+ }
749
778
}
750
779
751
- $ returnType = implode ('| ' , $ returnType );
780
+ if ('object ' === $ normalizedType && ($ this ->patchTypes ['keep-compat-with-php71 ' ] ?? false )) {
781
+ $ returnType = implode ('| ' , $ returnType );
752
782
753
- if ($ method ->getDocComment ()) {
754
- $ code [$ startLine ] = " * @return $ returnType \n" .$ code [$ startLine ];
755
- } else {
756
- $ code [$ startLine ] .= <<<EOTXT
783
+ if ($ method ->getDocComment ()) {
784
+ $ code [$ startLine ] = " * @return $ returnType \n" .$ code [$ startLine ];
785
+ } else {
786
+ $ code [$ startLine ] .= <<<EOTXT
757
787
/**
758
788
* @return $ returnType
759
789
*/
760
790
761
791
EOTXT ;
792
+ }
793
+
794
+ $ fileOffset += substr_count ($ code [$ startLine ], "\n" ) - 1 ;
762
795
}
763
796
764
- $ patchedMethods [$ file ][0 ] += substr_count ( $ code [ $ startLine ], "\n" ) - 1 ;
797
+ $ patchedMethods [$ file ][0 ] = $ fileOffset ;
765
798
file_put_contents ($ file , $ code );
799
+
800
+ $ this ->fixReturnStatements ($ method , $ nullable .$ normalizedType , $ patchedMethods [$ file ][0 ]);
766
801
}
767
802
768
803
private static function getUseStatements (string $ file ): array
@@ -808,15 +843,25 @@ private static function getUseStatements(string $file): array
808
843
return [$ namespace , $ useOffset , $ useMap ];
809
844
}
810
845
811
- private static function fixReturnStatements (\ReflectionMethod $ method , string $ returnType )
846
+ private function fixReturnStatements (\ReflectionMethod $ method , string $ returnType, int $ i = 0 )
812
847
{
848
+ if (($ this ->patchTypes ['keep-compat-with-php71 ' ] ?? false ) && 'object ' === ltrim ($ returnType , '? ' )) {
849
+ return ;
850
+ }
851
+
813
852
if (!file_exists ($ file = $ method ->getFileName ())) {
814
853
return ;
815
854
}
816
855
817
856
$ fixedCode = $ code = file ($ file );
857
+ $ i += $ method ->getStartLine ();
858
+
859
+ if ('? ' !== $ returnType ) {
860
+ $ fixedCode [$ i - 1 ] = preg_replace ('/\)(;?\n)/ ' , "): $ returnType \\1 " , $ code [$ i - 1 ]);
861
+ }
862
+
818
863
$ end = $ method ->getEndLine ();
819
- for ($ i = $ method -> getStartLine () ; $ i < $ end ; ++$ i ) {
864
+ for (; $ i < $ end ; ++$ i ) {
820
865
if ('void ' === $ returnType ) {
821
866
$ fixedCode [$ i ] = str_replace (' return null; ' , ' return; ' , $ code [$ i ]);
822
867
} elseif ('mixed ' === $ returnType || '? ' === $ returnType [0 ]) {
0 commit comments