@@ -35,6 +35,9 @@ class ParameterSpecBuilder implements ParameterSpecBuilderInterface
3535 \s*
3636 )?
3737 (?<name>[_a-z]\w*)
38+ \s*
39+ (?<nullable>\?)?
40+ \s*
3841 (?:
3942 \s* = \s*
4043 (?<default>
@@ -86,13 +89,20 @@ public function build(string $definition): ParameterSpecInterface
8689 }
8790
8891 if (preg_match ($ this ->regexp , $ definition , $ matches )) {
92+
93+ $ this ->validateDefinition ($ definition , $ matches );
94+
8995 try {
90- if ($ matches[ ' rest ' ] ?? false ) {
96+ if ($ this -> hasRest ( $ matches) ) {
9197 return $ this ->buildVariadicParameterSpec ($ matches );
9298 }
9399
94- if ($ matches ['default ' ] ?? false ) {
95- return $ this ->buildOptionalParameterSpec ($ matches );
100+ if ($ this ->hasDefault ($ matches )) {
101+ return $ this ->buildOptionalParameterSpec ($ matches , $ matches ['default ' ]);
102+ }
103+
104+ if ($ this ->hasNullable ($ matches )) {
105+ return $ this ->buildOptionalParameterSpec ($ matches , null );
96106 }
97107
98108 return $ this ->buildMandatoryParameterSpec ($ matches );
@@ -106,16 +116,14 @@ public function build(string $definition): ParameterSpecInterface
106116
107117 protected function buildVariadicParameterSpec (array $ matches ): VariadicParameterSpec
108118 {
109- if (isset ($ matches ['default ' ]) && '' !== $ matches ['default ' ]) {
110- throw new ParameterSpecBuilderException ('Variadic parameter should have no default value ' );
111- }
112-
113119 return new VariadicParameterSpec ($ matches ['name ' ], $ this ->builder ->build ($ matches ['type ' ]));
114120 }
115121
116- protected function buildOptionalParameterSpec (array $ matches ): OptionalParameterSpec
122+ protected function buildOptionalParameterSpec (array $ matches, ? string $ default ): OptionalParameterSpec
117123 {
118- $ default = $ this ->buildDefaultValue ($ matches ['default ' ]);
124+ if (null !== $ default ) {
125+ $ default = $ this ->buildDefaultValue ($ matches ['default ' ]);
126+ }
119127
120128 return new OptionalParameterSpec ($ matches ['name ' ], $ this ->builder ->build ($ matches ['type ' ]), $ default );
121129 }
@@ -166,6 +174,7 @@ protected function buildDefaultValue(string $definition)
166174 }
167175 }
168176
177+ // UNEXPECTED
169178 // Less likely we will ever get here because it should fail at a parsing step, but just in case
170179 throw new ParameterSpecBuilderException ("Unknown default value format ' {$ definition }' " );
171180 }
@@ -176,4 +185,34 @@ private function wrappedWith(string $definition, string $starts, $ends)
176185
177186 return $ starts == $ definition [0 ] && $ ends == $ definition [-1 ];
178187 }
188+
189+ protected function validateDefinition (string $ definition , array $ matches ): void
190+ {
191+ if ($ this ->hasNullable ($ matches ) && $ this ->hasRest ($ matches )) {
192+ throw new ParameterSpecBuilderException ("Variadic parameter could not be nullable " );
193+ }
194+
195+ if ($ this ->hasNullable ($ matches ) && $ this ->hasDefault ($ matches )) {
196+ throw new ParameterSpecBuilderException ("Nullable parameter could not have default value " );
197+ }
198+
199+ if ($ this ->hasRest ($ matches ) && $ this ->hasDefault ($ matches )) {
200+ throw new ParameterSpecBuilderException ('Variadic parameter could have no default value ' );
201+ }
202+ }
203+
204+ private function hasNullable (array $ matches ): bool
205+ {
206+ return isset ($ matches ['nullable ' ]) && '' !== $ matches ['nullable ' ];
207+ }
208+
209+ private function hasRest (array $ matches ): bool
210+ {
211+ return isset ($ matches ['rest ' ]) && '' !== $ matches ['rest ' ];
212+ }
213+
214+ private function hasDefault (array $ matches ): bool
215+ {
216+ return isset ($ matches ['default ' ]) && '' !== $ matches ['default ' ];
217+ }
179218}
0 commit comments