-
Notifications
You must be signed in to change notification settings - Fork 356
Better coercion #336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better coercion #336
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,10 +19,24 @@ | |
*/ | ||
class CollectionConstraint extends Constraint | ||
{ | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function check($value, $schema = null, JsonPointer $path = null, $i = null) | ||
{ | ||
$this->_check($value, $schema, $path, $i); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function coerce(&$value, $schema = null, JsonPointer $path = null, $i = null) | ||
{ | ||
$this->_check($value, $schema, $path, $i, true); | ||
} | ||
|
||
protected function _check(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mh maybe we can make it even more BC by just providing a CoerceConstraint (which wraps the underlying Constraint) and does all the type checking. so in my educated guess we only pass the coerce flag to the factory so it wraps the wanted constraint with the CoerceConstraint. The Facade-Pattern it is ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so all other constraints are mostly untouched... class CoerceConstraint
{
public function __construct(Constraint $constraint)
{
$this->constraint = $constraint;
}
public function check(...)
{
$result = $this->constraint->check(...);
//do the coercion here...
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting idea, but unfortunately any kind of coercion that depends on |
||
{ | ||
// Verify minItems | ||
if (isset($schema->minItems) && count($value) < $schema->minItems) { | ||
|
@@ -47,7 +61,7 @@ public function check($value, $schema = null, JsonPointer $path = null, $i = nul | |
|
||
// Verify items | ||
if (isset($schema->items)) { | ||
$this->validateItems($value, $schema, $path, $i); | ||
$this->validateItems($value, $schema, $path, $i, $coerce); | ||
} | ||
} | ||
|
||
|
@@ -58,8 +72,9 @@ public function check($value, $schema = null, JsonPointer $path = null, $i = nul | |
* @param \stdClass $schema | ||
* @param JsonPointer|null $path | ||
* @param string $i | ||
* @param boolean $coerce | ||
*/ | ||
protected function validateItems($value, $schema = null, JsonPointer $path = null, $i = null) | ||
protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false) | ||
{ | ||
if (is_object($schema->items)) { | ||
// just one type definition for the whole array | ||
|
@@ -74,27 +89,27 @@ protected function validateItems($value, $schema = null, JsonPointer $path = nul | |
) { | ||
// performance optimization | ||
$type = $schema->items->type; | ||
$validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type); | ||
$typeValidator = $this->factory->createInstanceFor('type'); | ||
$validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type); | ||
|
||
foreach ($value as $k => $v) { | ||
$k_path = $this->incrementPath($path, $k); | ||
$k_path = $this->incrementPath($path, $k); | ||
if($coerce) { | ||
$typeValidator->coerce($v, $schema->items, $k_path, $i); | ||
} else { | ||
$typeValidator->check($v, $schema->items, $k_path, $i); | ||
} | ||
|
||
if (($type === 'string' && !is_string($v)) | ||
|| ($type === 'number' && !(is_numeric($v) && !is_string($v))) | ||
|| ($type === 'integer' && !is_int($v)) | ||
){ | ||
$this->addError($k_path, ucwords(gettype($v)) . " value found, but $type is required", 'type'); | ||
} else { | ||
$validator->check($v, $schema, $k_path, $i); | ||
} | ||
$validator->check($v, $schema->items, $k_path, $i); | ||
} | ||
$this->addErrors($typeValidator->getErrors()); | ||
$this->addErrors($validator->getErrors()); | ||
} else { | ||
foreach ($value as $k => $v) { | ||
$initErrors = $this->getErrors(); | ||
|
||
// First check if its defined in "items" | ||
$this->checkUndefined($v, $schema->items, $path, $k); | ||
$this->checkUndefined($v, $schema->items, $path, $k, $coerce); | ||
|
||
// Recheck with "additionalItems" if the first test fails | ||
if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) { | ||
|
@@ -114,27 +129,27 @@ protected function validateItems($value, $schema = null, JsonPointer $path = nul | |
// Defined item type definitions | ||
foreach ($value as $k => $v) { | ||
if (array_key_exists($k, $schema->items)) { | ||
$this->checkUndefined($v, $schema->items[$k], $path, $k); | ||
$this->checkUndefined($v, $schema->items[$k], $path, $k, $coerce); | ||
} else { | ||
// Additional items | ||
if (property_exists($schema, 'additionalItems')) { | ||
if ($schema->additionalItems !== false) { | ||
$this->checkUndefined($v, $schema->additionalItems, $path, $k); | ||
$this->checkUndefined($v, $schema->additionalItems, $path, $k, $coerce); | ||
} else { | ||
$this->addError( | ||
$path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items', 'additionalItems', array('additionalItems' => $schema->additionalItems,)); | ||
} | ||
} else { | ||
// Should be valid against an empty schema | ||
$this->checkUndefined($v, new \stdClass(), $path, $k); | ||
$this->checkUndefined($v, new \stdClass(), $path, $k, $coerce); | ||
} | ||
} | ||
} | ||
|
||
// Treat when we have more schema definitions than values, not for empty arrays | ||
if (count($value) > 0) { | ||
for ($k = count($value); $k < count($schema->items); $k++) { | ||
$this->checkUndefined($this->factory->createInstanceFor('undefined'), $schema->items[$k], $path, $k); | ||
$this->checkUndefined($this->factory->createInstanceFor('undefined'), $schema->items[$k], $path, $k, $coerce); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,6 @@ abstract class Constraint implements ConstraintInterface | |
|
||
const CHECK_MODE_NORMAL = 0x00000001; | ||
const CHECK_MODE_TYPE_CAST = 0x00000002; | ||
const CHECK_MODE_COERCE = 0x00000004; | ||
|
||
/** | ||
* @var Factory | ||
|
@@ -124,11 +123,16 @@ protected function incrementPath(JsonPointer $path = null, $i) | |
* @param mixed $schema | ||
* @param JsonPointer|null $path | ||
* @param mixed $i | ||
* @param boolean $coerce | ||
*/ | ||
protected function checkArray($value, $schema = null, JsonPointer $path = null, $i = null) | ||
protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing PHPDoc for parameter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good eye, thank you. I've added phpdocs, following the patterns and style already in place. As for adding $coerce as a Now that I'm driving it through an explicit function call, I was actually interested in your reaction to what I did in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes look reasonable. Type check is more solid with this approach.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review! If you're satisfied, please approve. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no write permissions, can't approve. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A LGTM will help |
||
{ | ||
$validator = $this->factory->createInstanceFor('collection'); | ||
$validator->check($value, $schema, $path, $i); | ||
if($coerce) { | ||
$validator->coerce($value, $schema, $path, $i); | ||
} else { | ||
$validator->check($value, $schema, $path, $i); | ||
} | ||
|
||
$this->addErrors($validator->getErrors()); | ||
} | ||
|
@@ -141,11 +145,16 @@ protected function checkArray($value, $schema = null, JsonPointer $path = null, | |
* @param JsonPointer|null $path | ||
* @param mixed $i | ||
* @param mixed $patternProperties | ||
* @param boolean $coerce | ||
*/ | ||
protected function checkObject($value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null) | ||
protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null, $coerce = false) | ||
{ | ||
$validator = $this->factory->createInstanceFor('object'); | ||
$validator->check($value, $schema, $path, $i, $patternProperties); | ||
if($coerce){ | ||
$validator->coerce($value, $schema, $path, $i, $patternProperties); | ||
} else { | ||
$validator->check($value, $schema, $path, $i, $patternProperties); | ||
} | ||
|
||
$this->addErrors($validator->getErrors()); | ||
} | ||
|
@@ -157,11 +166,16 @@ protected function checkObject($value, $schema = null, JsonPointer $path = null, | |
* @param mixed $schema | ||
* @param JsonPointer|null $path | ||
* @param mixed $i | ||
* @param boolean $coerce | ||
*/ | ||
protected function checkType($value, $schema = null, JsonPointer $path = null, $i = null) | ||
protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false) | ||
{ | ||
$validator = $this->factory->createInstanceFor('type'); | ||
$validator->check($value, $schema, $path, $i); | ||
if($coerce) { | ||
$validator->coerce($value, $schema, $path, $i); | ||
} else { | ||
$validator->check($value, $schema, $path, $i); | ||
} | ||
|
||
$this->addErrors($validator->getErrors()); | ||
} | ||
|
@@ -173,12 +187,17 @@ protected function checkType($value, $schema = null, JsonPointer $path = null, $ | |
* @param mixed $schema | ||
* @param JsonPointer|null $path | ||
* @param mixed $i | ||
* @param boolean $coerce | ||
*/ | ||
protected function checkUndefined($value, $schema = null, JsonPointer $path = null, $i = null) | ||
protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false) | ||
{ | ||
$validator = $this->factory->createInstanceFor('undefined'); | ||
|
||
$validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i); | ||
if($coerce){ | ||
$validator->coerce($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i); | ||
} else { | ||
$validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i); | ||
} | ||
|
||
$this->addErrors($validator->getErrors()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should fix the indentation here. maybe we should create a PR with php-cs-fixer & style-ci