Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

Commit d60963f

Browse files
committed
Add shortcut for optional nullable param, closes #5
1 parent 7895d85 commit d60963f

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

src/Specs/Builder/ParameterSpecBuilder.php

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

tests/Specs/Builder/FunctionSpecBuilderTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,18 @@ public function testBuildSpecWithParams()
117117
$this->assertFalse($spec->needsExecutionContext());
118118
$this->assertContainsOnlyInstancesOf(ParameterSpecInterface::class, $spec->getParameters()->getParameters());
119119
$this->assertCount(3, $spec->getParameters()->getParameters());
120+
}
121+
122+
public function testBuildSpecWithNullableParams()
123+
{
124+
$this->parameterSpecBuilderShouldBuildOn('one: param', 'two?: param');
125+
126+
$spec = $this->builder->build('(one: param, two?: param)');
120127

128+
$this->assertInstanceOf(FunctionSpecInterface::class, $spec);
129+
$this->assertFalse($spec->needsExecutionContext());
130+
$this->assertContainsOnlyInstancesOf(ParameterSpecInterface::class, $spec->getParameters()->getParameters());
131+
$this->assertCount(2, $spec->getParameters()->getParameters());
121132
}
122133

123134
// Test throws spec

tests/Specs/Builder/ParameterSpecBuilderTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,44 @@ public function testBuildingVariadicParameter()
134134

135135
/**
136136
* @expectedException \Pinepain\JsSandbox\Specs\Builder\Exceptions\ParameterSpecBuilderException
137-
* @expectedExceptionMessage Variadic parameter should have no default value
137+
* @expectedExceptionMessage Variadic parameter could have no default value
138138
*/
139139
public function testBuildingVariadicParameterWithDefaultValueShouldThrowException()
140140
{
141141
$this->builder->build('...param = []: type');
142142
}
143143

144+
/**
145+
* @expectedException \Pinepain\JsSandbox\Specs\Builder\Exceptions\ParameterSpecBuilderException
146+
* @expectedExceptionMessage Variadic parameter could not be nullable
147+
*/
148+
public function testBuildingVariadicParameterWithNullableShouldThrowException()
149+
{
150+
$this->builder->build('...param?: type');
151+
}
152+
153+
/**
154+
* @expectedException \Pinepain\JsSandbox\Specs\Builder\Exceptions\ParameterSpecBuilderException
155+
* @expectedExceptionMessage Nullable parameter could not have default value
156+
*/
157+
public function testBuildingNullableParameterWithDefaultValueShouldThrowException()
158+
{
159+
$this->builder->build('param? = "default": type');
160+
}
161+
162+
public function testBuildingNullableParameter()
163+
{
164+
$this->extractorDefinitionShouldBuildOn('type');
165+
166+
$spec = $this->builder->build('param? : type');
167+
168+
$this->assertInstanceOf(OptionalParameterSpec::class, $spec);
169+
170+
$this->assertSame('param', $spec->getName());
171+
$this->assertNull($spec->getDefaultValue());
172+
$this->assertInstanceOf(ExtractorDefinitionInterface::class, $spec->getExtractorDefinition());
173+
}
174+
144175
/**
145176
* @expectedException \Pinepain\JsSandbox\Specs\Builder\Exceptions\ParameterSpecBuilderException
146177
* @expectedExceptionMessage Unable to parse definition because of extractor failure: ExtractorDefinitionBuilder exception for testing

0 commit comments

Comments
 (0)