24
24
* and will throw an exception if a file is found but does
25
25
* not declare the class.
26
26
*
27
+ * It can also patch classes to turn docblocks into actual return types.
28
+ * This behavior is controlled by the SYMFONY_PATCH_TYPE_DECLARATIONS env var,
29
+ * which is a url-encoded array with the follow parameters:
30
+ * - force: any value enables deprecation notices - can be any of:
31
+ * - "docblock" to patch only docblock annotations
32
+ * - "object" to turn union types to the "object" type when possible (not recommended)
33
+ * - "1"/"0" to enable/disable patching of return types
34
+ * - php: the target version of PHP - e.g. "7.1" doesn't generate "object" types
35
+ *
36
+ * Note that patching doesn't care about any coding style so you'd better to run
37
+ * php-cs-fixer after, with rules "phpdoc_trim_consecutive_blank_line_separation"
38
+ * and "no_superfluous_phpdoc_tags" enabled typically.
39
+ *
27
40
* @author Fabien Potencier <fabien@symfony.com>
28
41
* @author Christophe Coevoet <stof@notk.org>
29
42
* @author Nicolas Grekas <p@tchwork.com>
@@ -141,6 +154,7 @@ class DebugClassLoader
141
154
private $ isFinder ;
142
155
private $ loaded = [];
143
156
private $ patchTypes ;
157
+
144
158
private static $ caseCheck ;
145
159
private static $ checkedClasses = [];
146
160
private static $ final = [];
@@ -160,6 +174,10 @@ public function __construct(callable $classLoader)
160
174
$ this ->classLoader = $ classLoader ;
161
175
$ this ->isFinder = \is_array ($ classLoader ) && method_exists ($ classLoader [0 ], 'findFile ' );
162
176
parse_str (getenv ('SYMFONY_PATCH_TYPE_DECLARATIONS ' ) ?: '' , $ this ->patchTypes );
177
+ $ this ->patchTypes += [
178
+ 'force ' => null ,
179
+ 'php ' => null ,
180
+ ];
163
181
164
182
if (!isset (self ::$ caseCheck )) {
165
183
$ file = file_exists (__FILE__ ) ? __FILE__ : rtrim (realpath ('. ' ), \DIRECTORY_SEPARATOR );
@@ -551,15 +569,14 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
551
569
}
552
570
}
553
571
554
- $ forcePatchTypes = $ this ->patchTypes ['force ' ] ?? null ;
572
+ $ forcePatchTypes = $ this ->patchTypes ['force ' ];
555
573
556
- if ($ canAddReturnType = null !== $ forcePatchTypes && false === strpos ($ method ->getFileName (), \DIRECTORY_SEPARATOR .'vendor ' .\DIRECTORY_SEPARATOR )) {
574
+ if ($ canAddReturnType = $ forcePatchTypes && false === strpos ($ method ->getFileName (), \DIRECTORY_SEPARATOR .'vendor ' .\DIRECTORY_SEPARATOR )) {
557
575
if ('void ' !== (self ::MAGIC_METHODS [$ method ->name ] ?? 'void ' )) {
558
576
$ this ->patchTypes ['force ' ] = $ forcePatchTypes ?: 'docblock ' ;
559
577
}
560
578
561
- $ canAddReturnType = ($ this ->patchTypes ['force ' ] ?? false )
562
- || false !== strpos ($ refl ->getFileName (), \DIRECTORY_SEPARATOR .'Tests ' .\DIRECTORY_SEPARATOR )
579
+ $ canAddReturnType = false !== strpos ($ refl ->getFileName (), \DIRECTORY_SEPARATOR .'Tests ' .\DIRECTORY_SEPARATOR )
563
580
|| $ refl ->isFinal ()
564
581
|| $ method ->isFinal ()
565
582
|| $ method ->isPrivate ()
@@ -570,20 +587,20 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
570
587
}
571
588
572
589
if (null !== ($ returnType = self ::$ returnTypes [$ class ][$ method ->name ] ?? self ::MAGIC_METHODS [$ method ->name ] ?? null ) && !$ method ->hasReturnType () && !($ doc && preg_match ('/\n\s+\* @return +(\S+)/ ' , $ doc ))) {
573
- if ('void ' === $ returnType ) {
590
+ list ($ normalizedType , $ returnType , $ declaringClass , $ declaringFile ) = \is_string ($ returnType ) ? [$ returnType , $ returnType , '' , '' ] : $ returnType ;
591
+
592
+ if ('void ' === $ normalizedType ) {
574
593
$ canAddReturnType = false ;
575
594
}
576
595
577
- list ($ normalizedType , $ returnType , $ declaringClass , $ declaringFile ) = \is_string ($ returnType ) ? [$ returnType , $ returnType , '' , '' ] : $ returnType ;
578
-
579
- if ($ canAddReturnType && 'docblock ' !== ($ this ->patchTypes ['force ' ] ?? false )) {
596
+ if ($ canAddReturnType && 'docblock ' !== $ this ->patchTypes ['force ' ]) {
580
597
$ this ->patchMethod ($ method , $ returnType , $ declaringFile , $ normalizedType );
581
598
}
582
599
583
600
if (strncmp ($ ns , $ declaringClass , $ len )) {
584
- if ($ canAddReturnType && 'docblock ' === ( $ this ->patchTypes ['force ' ] ?? false ) && false === strpos ($ method ->getFileName (), \DIRECTORY_SEPARATOR .'vendor ' .\DIRECTORY_SEPARATOR )) {
601
+ if ($ canAddReturnType && 'docblock ' === $ this ->patchTypes ['force ' ] && false === strpos ($ method ->getFileName (), \DIRECTORY_SEPARATOR .'vendor ' .\DIRECTORY_SEPARATOR )) {
585
602
$ this ->patchMethod ($ method , $ returnType , $ declaringFile , $ normalizedType );
586
- } elseif ('' !== $ declaringClass ) {
603
+ } elseif ('' !== $ declaringClass && null !== $ this -> patchTypes [ ' force ' ] ) {
587
604
$ 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 , $ className );
588
605
}
589
606
}
@@ -820,7 +837,7 @@ private function setReturnType(string $types, \ReflectionMethod $method, ?string
820
837
} 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 )) {
821
838
if ($ iterable ) {
822
839
$ normalizedType = $ returnType = 'iterable ' ;
823
- } elseif ($ object ) {
840
+ } elseif ($ object && ' object ' === $ this -> patchTypes [ ' force ' ] ) {
824
841
$ normalizedType = $ returnType = 'object ' ;
825
842
} else {
826
843
// ignore multi-types return declarations
@@ -947,7 +964,7 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri
947
964
}
948
965
}
949
966
950
- if ('docblock ' === ( $ this ->patchTypes ['force ' ] ?? null ) || ('object ' === $ normalizedType && ( $ this ->patchTypes ['php71-compat ' ] ?? false ) )) {
967
+ if ('docblock ' === $ this ->patchTypes ['force ' ] || ('object ' === $ normalizedType && ' 7.1 ' === $ this ->patchTypes ['php ' ] )) {
951
968
$ returnType = implode ('| ' , $ returnType );
952
969
953
970
if ($ method ->getDocComment ()) {
@@ -1015,7 +1032,7 @@ private static function getUseStatements(string $file): array
1015
1032
1016
1033
private function fixReturnStatements (\ReflectionMethod $ method , string $ returnType )
1017
1034
{
1018
- if (( $ this ->patchTypes ['php71-compat ' ] ?? false ) && 'object ' === ltrim ($ returnType , '? ' ) && 'docblock ' !== ( $ this ->patchTypes ['force ' ] ?? null ) ) {
1035
+ if (' 7.1 ' === $ this ->patchTypes ['php ' ] && 'object ' === ltrim ($ returnType , '? ' ) && 'docblock ' !== $ this ->patchTypes ['force ' ]) {
1019
1036
return ;
1020
1037
}
1021
1038
@@ -1026,7 +1043,7 @@ private function fixReturnStatements(\ReflectionMethod $method, string $returnTy
1026
1043
$ fixedCode = $ code = file ($ file );
1027
1044
$ i = (self ::$ fileOffsets [$ file ] ?? 0 ) + $ method ->getStartLine ();
1028
1045
1029
- if ('? ' !== $ returnType && 'docblock ' !== ( $ this ->patchTypes ['force ' ] ?? null ) ) {
1046
+ if ('? ' !== $ returnType && 'docblock ' !== $ this ->patchTypes ['force ' ]) {
1030
1047
$ fixedCode [$ i - 1 ] = preg_replace ('/\)(;?\n)/ ' , "): $ returnType \\1 " , $ code [$ i - 1 ]);
1031
1048
}
1032
1049
0 commit comments