Skip to content

Commit 44731dc

Browse files
committed
Added strict mode support
1 parent 351c96b commit 44731dc

2 files changed

Lines changed: 73 additions & 6 deletions

File tree

src/Validator.php

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@
1010
class Validator
1111
{
1212
protected $types = [];
13+
protected $strict;
1314

1415
/**
1516
* Constructor.
17+
*
18+
* @param $options
1619
*/
17-
public function __construct()
20+
public function __construct(array $options = [])
1821
{
22+
$this->strict = $options['strict'] ?? false;
23+
1924
$this->types = $this->getBuiltInTypeValidators();
2025
}
2126

@@ -111,7 +116,6 @@ protected function getNormalizedPath()
111116
{
112117
return strtr(implode('.', $this->path), [
113118
'.[' => '[',
114-
'].' => ']',
115119
]);
116120
}
117121

@@ -180,17 +184,54 @@ protected function matchArray(array $data, $definition)
180184

181185
protected function matchObject($data, $definition)
182186
{
187+
if ($this->strict && !$this->matchObjectKeys($data, $definition)) {
188+
return false;
189+
}
190+
191+
$hasErrors = false;
183192
foreach ($definition as $name => $type) {
184193
array_push($this->path, $name);
185194
$result = $this->matchInternal($data[$name] ?? null, $type);
186195
array_pop($this->path);
187196

188197
if (!$result) {
189-
return false;
198+
$hasErrors = true;
190199
}
191200
}
192201

193-
return true;
202+
return !$hasErrors;
203+
}
204+
205+
protected function matchObjectKeys($data, $definition)
206+
{
207+
$requiredKeys = array_keys($definition);
208+
$providedKeys = array_keys($data);
209+
210+
sort($requiredKeys);
211+
sort($providedKeys);
212+
213+
if ($requiredKeys === $providedKeys) {
214+
return true;
215+
}
216+
217+
$absenceKeys = array_diff($requiredKeys, $providedKeys);
218+
$notRequiredKeys = array_diff($providedKeys, $requiredKeys);
219+
220+
$message = "The object keys doesn't match the type definition";
221+
222+
if ($absenceKeys) {
223+
$absenceKeys = implode(',', $absenceKeys);
224+
$message .= ": '$absenceKeys' are absent";
225+
}
226+
227+
if ($notRequiredKeys) {
228+
$notRequiredKeys = implode(',', $notRequiredKeys);
229+
$message .= ": '$notRequiredKeys' are not required";
230+
}
231+
232+
$this->addError($this->getNormalizedPath(), $message);
233+
234+
return false;
194235
}
195236

196237
protected function matchInternal($data, $type)

tests/JsonTypeTest.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
*/
1313
class JsonTypeTest extends PHPUnit_Framework_TestCase
1414
{
15-
protected function createValidator()
15+
protected function createValidator($strictMode = false)
1616
{
17-
$validator = new Validator();
17+
$validator = new Validator(['strict' => $strictMode]);
1818

1919
$validator->addType('timestamp', $this->timestampValidator());
2020

@@ -202,4 +202,30 @@ public function testErrorMessages($value, $type, $key, $message)
202202
$this->assertFalse($json->matches($value, $type));
203203
$this->assertEquals($message, $json->getErrors()[$key] ?? '');
204204
}
205+
206+
public function typeStrictData()
207+
{
208+
return [
209+
[
210+
['key1' => 'v1', 'key3' => 'v3'],
211+
[
212+
'key1' => 'string',
213+
'key2' => 'string',
214+
],
215+
'$',
216+
"The object keys doesn't match the type definition: 'key2' are absent: 'key3' are not required",
217+
]
218+
];
219+
}
220+
221+
/**
222+
* @dataProvider typeStrictData
223+
*/
224+
public function testStrictMode($value, $type, $key, $message)
225+
{
226+
$json = $this->createValidator(true);
227+
228+
$this->assertFalse($json->matches($value, $type));
229+
$this->assertEquals($message, $json->getErrors()[$key] ?? '');
230+
}
205231
}

0 commit comments

Comments
 (0)