13
13
use PhpParser \Node \Name ;
14
14
use PhpParser \Node \NullableType ;
15
15
use PhpParser \Node \Param ;
16
+ use PhpParser \Node \Scalar \Float_ ;
17
+ use PhpParser \Node \Scalar \Int_ ;
18
+ use PhpParser \Node \Scalar \String_ ;
16
19
use PhpParser \Node \Stmt \ClassMethod ;
17
20
use PhpParser \Node \Stmt \Function_ ;
18
21
use PhpParser \Node \UnionType ;
22
+ use Rector \PhpParser \Node \Value \ValueResolver ;
19
23
use Rector \Rector \AbstractRector ;
20
24
use Rector \ValueObject \PhpVersionFeature ;
21
25
use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
26
30
*/
27
31
final class OptionalParametersAfterRequiredRector extends AbstractRector implements MinPhpVersionInterface
28
32
{
33
+ /**
34
+ * @readonly
35
+ */
36
+ private ValueResolver $ valueResolver ;
37
+ public function __construct (ValueResolver $ valueResolver )
38
+ {
39
+ $ this ->valueResolver = $ valueResolver ;
40
+ }
29
41
public function getRuleDefinition () : RuleDefinition
30
42
{
31
- return new RuleDefinition ('Add null default value when a required parameter follows an optional one ' , [new CodeSample (<<<'CODE_SAMPLE'
43
+ return new RuleDefinition ('Add reasonable default value when a required parameter follows an optional one ' , [new CodeSample (<<<'CODE_SAMPLE'
32
44
class SomeObject
33
45
{
34
- public function run($optional = 1, $required)
46
+ public function run($optional = 1, int $required)
35
47
{
36
48
}
37
49
}
38
50
CODE_SAMPLE
39
51
, <<<'CODE_SAMPLE'
40
52
class SomeObject
41
53
{
42
- public function run($optional = 1, $required = null )
54
+ public function run($optional = 1, int $required = 0 )
43
55
{
44
56
}
45
57
}
@@ -73,30 +85,7 @@ public function refactor(Node $node)
73
85
$ previousParam = $ node ->params [$ key - 1 ] ?? null ;
74
86
if ($ previousParam instanceof Param && $ previousParam ->default instanceof Expr) {
75
87
$ hasChanged = \true;
76
- $ param ->default = new ConstFetch (new Name ('null ' ));
77
- if (!$ param ->type instanceof Node) {
78
- continue ;
79
- }
80
- if ($ param ->type instanceof NullableType) {
81
- continue ;
82
- }
83
- if ($ param ->type instanceof UnionType) {
84
- foreach ($ param ->type ->types as $ unionedType ) {
85
- if ($ unionedType instanceof Identifier && $ this ->isName ($ unionedType , 'null ' )) {
86
- continue 2 ;
87
- }
88
- }
89
- $ param ->type ->types [] = new Identifier ('null ' );
90
- continue ;
91
- }
92
- if ($ param ->type instanceof IntersectionType) {
93
- $ param ->type = new UnionType ([$ param ->type , new Identifier ('null ' )]);
94
- continue ;
95
- }
96
- if ($ param ->type instanceof ComplexType) {
97
- continue ;
98
- }
99
- $ param ->type = new NullableType ($ param ->type );
88
+ $ this ->processParam ($ param );
100
89
}
101
90
}
102
91
return $ hasChanged ? $ node : null ;
@@ -105,4 +94,80 @@ public function provideMinPhpVersion() : int
105
94
{
106
95
return PhpVersionFeature::NULLABLE_TYPE ;
107
96
}
97
+ /**
98
+ * Look first found type reasonable value
99
+ *
100
+ * @param Node[] $types
101
+ */
102
+ private function mapReasonableParamValue (array $ types ) : Expr
103
+ {
104
+ foreach ($ types as $ type ) {
105
+ if ($ this ->isName ($ type , 'string ' )) {
106
+ return new String_ ('' );
107
+ }
108
+ if ($ this ->isName ($ type , 'int ' )) {
109
+ return new Int_ (0 );
110
+ }
111
+ if ($ this ->isName ($ type , 'float ' )) {
112
+ return new Float_ (0.0 );
113
+ }
114
+ if ($ this ->isName ($ type , 'bool ' )) {
115
+ return $ this ->nodeFactory ->createFalse ();
116
+ }
117
+ if ($ this ->isName ($ type , 'array ' )) {
118
+ return $ this ->nodeFactory ->createArray ([]);
119
+ }
120
+ if ($ this ->isName ($ type , 'true ' )) {
121
+ return $ this ->nodeFactory ->createTrue ();
122
+ }
123
+ if ($ this ->isName ($ type , 'false ' )) {
124
+ return $ this ->nodeFactory ->createFalse ();
125
+ }
126
+ }
127
+ return new ConstFetch (new Name ('null ' ));
128
+ }
129
+ private function processParam (Param $ param ) : void
130
+ {
131
+ if (!$ param ->type instanceof Node) {
132
+ $ param ->default = new ConstFetch (new Name ('null ' ));
133
+ return ;
134
+ }
135
+ if ($ param ->type instanceof NullableType) {
136
+ $ param ->default = new ConstFetch (new Name ('null ' ));
137
+ return ;
138
+ }
139
+ if ($ param ->type instanceof IntersectionType) {
140
+ $ param ->default = new ConstFetch (new Name ('null ' ));
141
+ $ param ->type = new UnionType ([$ param ->type , new Identifier ('null ' )]);
142
+ return ;
143
+ }
144
+ if ($ param ->type instanceof UnionType) {
145
+ foreach ($ param ->type ->types as $ unionedType ) {
146
+ if ($ unionedType instanceof Identifier && $ this ->isName ($ unionedType , 'null ' )) {
147
+ $ param ->default = new ConstFetch (new Name ('null ' ));
148
+ return ;
149
+ }
150
+ }
151
+ $ reasonableValue = $ this ->mapReasonableParamValue ($ param ->type ->types );
152
+ if ($ this ->valueResolver ->isNull ($ reasonableValue )) {
153
+ $ param ->default = new ConstFetch (new Name ('null ' ));
154
+ $ param ->type ->types [] = new Identifier ('null ' );
155
+ return ;
156
+ }
157
+ $ param ->default = $ reasonableValue ;
158
+ return ;
159
+ }
160
+ if ($ param ->type instanceof ComplexType) {
161
+ return ;
162
+ }
163
+ $ reasonableValue = $ this ->mapReasonableParamValue ([$ param ->type ]);
164
+ if ($ this ->valueResolver ->isNull ($ reasonableValue )) {
165
+ if (!$ param ->type instanceof Identifier || !$ this ->isNames ($ param ->type , ['null ' , 'mixed ' ])) {
166
+ $ param ->type = new NullableType ($ param ->type );
167
+ }
168
+ $ param ->default = new ConstFetch (new Name ('null ' ));
169
+ return ;
170
+ }
171
+ $ param ->default = $ reasonableValue ;
172
+ }
108
173
}
0 commit comments