mirror of
https://github.com/justinrainbow/json-schema.git
synced 2025-05-02 04:18:09 +02:00
* Added two optional arguments to addError method to make it easier for custom error messages
* Added more information to the error message of StringConstraint * Added more information to the Constraint addError * Unknown format does not generate error * Updated the test so that unknown format does not throw an error * Make messages to start with a capital letter consistently
This commit is contained in:
parent
a4bee9f4b3
commit
acdaf80a9c
@ -24,12 +24,12 @@ class CollectionConstraint extends Constraint
|
||||
{
|
||||
// Verify minItems
|
||||
if (isset($schema->minItems) && count($value) < $schema->minItems) {
|
||||
$this->addError($path, "There must be a minimum of " . $schema->minItems . " items in the array");
|
||||
$this->addError($path, "There must be a minimum of " . $schema->minItems . " items in the array", 'minItems', array('minItems' => $schema->minItems,));
|
||||
}
|
||||
|
||||
// Verify maxItems
|
||||
if (isset($schema->maxItems) && count($value) > $schema->maxItems) {
|
||||
$this->addError($path, "There must be a maximum of " . $schema->maxItems . " items in the array");
|
||||
$this->addError($path, "There must be a maximum of " . $schema->maxItems . " items in the array", 'maxItems', array('maxItems' => $schema->maxItems,));
|
||||
}
|
||||
|
||||
// Verify uniqueItems
|
||||
@ -39,7 +39,7 @@ class CollectionConstraint extends Constraint
|
||||
$unique = array_map(function($e) { return var_export($e, true); }, $value);
|
||||
}
|
||||
if (count(array_unique($unique)) != count($value)) {
|
||||
$this->addError($path, "There are no duplicates allowed in the array");
|
||||
$this->addError($path, "There are no duplicates allowed in the array", 'uniqueItems');
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ class CollectionConstraint extends Constraint
|
||||
$this->checkUndefined($v, $schema->additionalItems, $path, $k);
|
||||
} else {
|
||||
$this->addError(
|
||||
$path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items');
|
||||
$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
|
||||
|
@ -61,12 +61,20 @@ abstract class Constraint implements ConstraintInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function addError($path, $message)
|
||||
public function addError($path, $message, $constraint='', array $more=null)
|
||||
{
|
||||
$this->errors[] = array(
|
||||
$error = array(
|
||||
'property' => $path,
|
||||
'message' => $message
|
||||
'message' => $message,
|
||||
'constraint' => $constraint,
|
||||
);
|
||||
|
||||
if (is_array($more) && count($more) > 0)
|
||||
{
|
||||
$error += $more;
|
||||
}
|
||||
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,10 +33,12 @@ interface ConstraintInterface
|
||||
/**
|
||||
* adds an error
|
||||
*
|
||||
* @param $path
|
||||
* @param $message
|
||||
* @param string $path
|
||||
* @param string $message
|
||||
* @param string $constraint the constraint/rule that is broken, e.g.: 'minLength'
|
||||
* @param array $more more array elements to add to the error
|
||||
*/
|
||||
public function addError($path, $message);
|
||||
public function addError($path, $message, $constraint='', array $more=null);
|
||||
|
||||
/**
|
||||
* checks if the validator has not raised errors
|
||||
|
@ -41,6 +41,6 @@ class EnumConstraint extends Constraint
|
||||
}
|
||||
}
|
||||
|
||||
$this->addError($path, "Does not have a value in the enumeration " . print_r($schema->enum, true));
|
||||
$this->addError($path, "Does not have a value in the enumeration " . print_r($schema->enum, true), 'enum', array('enum' => $schema->enum,));
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ class FormatConstraint extends Constraint
|
||||
switch ($schema->format) {
|
||||
case 'date':
|
||||
if (!$date = $this->validateDateTime($element, 'Y-m-d')) {
|
||||
$this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)));
|
||||
$this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'time':
|
||||
if (!$this->validateDateTime($element, 'H:i:s')) {
|
||||
$this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)));
|
||||
$this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -45,74 +45,79 @@ class FormatConstraint extends Constraint
|
||||
!$this->validateDateTime($element, 'Y-m-d\TH:i:sP') &&
|
||||
!$this->validateDateTime($element, 'Y-m-d\TH:i:sO')
|
||||
) {
|
||||
$this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)));
|
||||
$this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'utc-millisec':
|
||||
if (!$this->validateDateTime($element, 'U')) {
|
||||
$this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)));
|
||||
$this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'regex':
|
||||
if (!$this->validateRegex($element)) {
|
||||
$this->addError($path, 'Invalid regex format ' . $element);
|
||||
$this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
if (!$this->validateColor($element)) {
|
||||
$this->addError($path, "Invalid color");
|
||||
$this->addError($path, "Invalid color", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'style':
|
||||
if (!$this->validateStyle($element)) {
|
||||
$this->addError($path, "Invalid style");
|
||||
$this->addError($path, "Invalid style", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'phone':
|
||||
if (!$this->validatePhone($element)) {
|
||||
$this->addError($path, "Invalid phone number");
|
||||
$this->addError($path, "Invalid phone number", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'uri':
|
||||
if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
|
||||
$this->addError($path, "Invalid URL format");
|
||||
$this->addError($path, "Invalid URL format", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
if (null === filter_var($element, FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE)) {
|
||||
$this->addError($path, "Invalid email");
|
||||
$this->addError($path, "Invalid email", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ip-address':
|
||||
case 'ipv4':
|
||||
if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
|
||||
$this->addError($path, "Invalid IP address");
|
||||
$this->addError($path, "Invalid IP address", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ipv6':
|
||||
if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
|
||||
$this->addError($path, "Invalid IP address");
|
||||
$this->addError($path, "Invalid IP address", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'host-name':
|
||||
case 'hostname':
|
||||
if (!$this->validateHostname($element)) {
|
||||
$this->addError($path, "Invalid hostname");
|
||||
$this->addError($path, "Invalid hostname", 'format', array('format' => $schema->format,));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing so that custom formats can be used.
|
||||
// Empty as it should be:
|
||||
// The value of this keyword is called a format attribute. It MUST be a string.
|
||||
// A format attribute can generally only validate a given set of instance types.
|
||||
// If the type of the instance to validate is not in this set, validation for
|
||||
// this format attribute and instance SHOULD succeed.
|
||||
// http://json-schema.org/latest/json-schema-validation.html#anchor105
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -26,40 +26,40 @@ class NumberConstraint extends Constraint
|
||||
if (isset($schema->exclusiveMinimum)) {
|
||||
if (isset($schema->minimum)) {
|
||||
if ($schema->exclusiveMinimum && $element === $schema->minimum) {
|
||||
$this->addError($path, "Must have a minimum value greater than boundary value of " . $schema->minimum);
|
||||
$this->addError($path, "Must have a minimum value greater than boundary value of " . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum,));
|
||||
} else if ($element < $schema->minimum) {
|
||||
$this->addError($path, "Must have a minimum value of " . $schema->minimum);
|
||||
$this->addError($path, "Must have a minimum value of " . $schema->minimum, 'minimum', array('minimum' => $schema->minimum,));
|
||||
}
|
||||
} else {
|
||||
$this->addError($path, "Use of exclusiveMinimum requires presence of minimum");
|
||||
$this->addError($path, "Use of exclusiveMinimum requires presence of minimum", 'missingMinimum');
|
||||
}
|
||||
} else if (isset($schema->minimum) && $element < $schema->minimum) {
|
||||
$this->addError($path, "Must have a minimum value of " . $schema->minimum);
|
||||
$this->addError($path, "Must have a minimum value of " . $schema->minimum, 'minimum', array('minimum' => $schema->minimum,));
|
||||
}
|
||||
|
||||
// Verify maximum
|
||||
if (isset($schema->exclusiveMaximum)) {
|
||||
if (isset($schema->maximum)) {
|
||||
if ($schema->exclusiveMaximum && $element === $schema->maximum) {
|
||||
$this->addError($path, "Must have a maximum value less than boundary value of " . $schema->maximum);
|
||||
$this->addError($path, "Must have a maximum value less than boundary value of " . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum,));
|
||||
} else if ($element > $schema->maximum) {
|
||||
$this->addError($path, "Must have a maximum value of " . $schema->maximum);
|
||||
$this->addError($path, "Must have a maximum value of " . $schema->maximum, 'maximum', array('maximum' => $schema->maximum,));
|
||||
}
|
||||
} else {
|
||||
$this->addError($path, "Use of exclusiveMaximum requires presence of maximum");
|
||||
$this->addError($path, "Use of exclusiveMaximum requires presence of maximum", 'missingMinimum');
|
||||
}
|
||||
} else if (isset($schema->maximum) && $element > $schema->maximum) {
|
||||
$this->addError($path, "Must have a maximum value of " . $schema->maximum);
|
||||
$this->addError($path, "Must have a maximum value of " . $schema->maximum, 'maximum', array('maximum' => $schema->maximum,));
|
||||
}
|
||||
|
||||
// Verify divisibleBy - Draft v3
|
||||
if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) {
|
||||
$this->addError($path, "Is not divisible by " . $schema->divisibleBy);
|
||||
$this->addError($path, "Is not divisible by " . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy,));
|
||||
}
|
||||
|
||||
// Verify multipleOf - Draft v4
|
||||
if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) {
|
||||
$this->addError($path, "Must be a multiple of " . $schema->multipleOf);
|
||||
$this->addError($path, "Must be a multiple of " . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf,));
|
||||
}
|
||||
|
||||
$this->checkFormat($element, $schema, $path, $i);
|
||||
|
@ -46,7 +46,7 @@ class ObjectConstraint extends Constraint
|
||||
foreach ($patternProperties as $pregex => $schema) {
|
||||
// Validate the pattern before using it to test for matches
|
||||
if (@preg_match('/'. $pregex . '/', '') === false) {
|
||||
$this->addError($path, 'The pattern "' . $pregex . '" is invalid');
|
||||
$this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex,));
|
||||
continue;
|
||||
}
|
||||
foreach ($element as $i => $value) {
|
||||
@ -77,7 +77,7 @@ class ObjectConstraint extends Constraint
|
||||
|
||||
// no additional properties allowed
|
||||
if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) {
|
||||
$this->addError($path, "The property - " . $i . " - is not defined and the definition does not allow additional properties");
|
||||
$this->addError($path, "The property " . $i . " is not defined and the definition does not allow additional properties", 'additionalProp');
|
||||
}
|
||||
|
||||
// additional properties defined
|
||||
@ -92,7 +92,7 @@ class ObjectConstraint extends Constraint
|
||||
// property requires presence of another
|
||||
$require = $this->getProperty($definition, 'requires');
|
||||
if ($require && !$this->getProperty($element, $require)) {
|
||||
$this->addError($path, "The presence of the property " . $i . " requires that " . $require . " also be present");
|
||||
$this->addError($path, "The presence of the property " . $i . " requires that " . $require . " also be present", 'requires');
|
||||
}
|
||||
|
||||
if (!$definition) {
|
||||
|
@ -24,17 +24,23 @@ class StringConstraint extends Constraint
|
||||
{
|
||||
// Verify maxLength
|
||||
if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) {
|
||||
$this->addError($path, "Must be at most " . $schema->maxLength . " characters long");
|
||||
$this->addError($path, "Must be at most " . $schema->maxLength . " characters long", 'maxLength', array(
|
||||
'maxLength' => $schema->maxLength,
|
||||
));
|
||||
}
|
||||
|
||||
//verify minLength
|
||||
if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) {
|
||||
$this->addError($path, "Must be at least " . $schema->minLength . " characters long");
|
||||
$this->addError($path, "Must be at least " . $schema->minLength . " characters long", 'minLength', array(
|
||||
'minLength' => $schema->minLength,
|
||||
));
|
||||
}
|
||||
|
||||
// Verify a regex pattern
|
||||
if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#', $element)) {
|
||||
$this->addError($path, "Does not match the regex pattern " . $schema->pattern);
|
||||
$this->addError($path, "Does not match the regex pattern " . $schema->pattern, 'pattern', array(
|
||||
'pattern' => $schema->pattern,
|
||||
));
|
||||
}
|
||||
|
||||
$this->checkFormat($element, $schema, $path, $i);
|
||||
|
@ -82,7 +82,7 @@ class TypeConstraint extends Constraint
|
||||
implode(', ', array_filter(self::$wording)))
|
||||
);
|
||||
}
|
||||
$this->addError($path, gettype($value) . " value found, but " . self::$wording[$type] . " is required");
|
||||
$this->addError($path, ucwords(gettype($value)) . " value found, but " . self::$wording[$type] . " is required", 'type');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,18 +117,17 @@ class UndefinedConstraint extends Constraint
|
||||
|
||||
// Verify required values
|
||||
if (is_object($value)) {
|
||||
|
||||
if (!($value instanceof UndefinedConstraint) && isset($schema->required) && is_array($schema->required) ) {
|
||||
// Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...]
|
||||
foreach ($schema->required as $required) {
|
||||
if (!property_exists($value, $required)) {
|
||||
$this->addError($required, "The property " . $required . " is required");
|
||||
$this->addError($required, "The property " . $required . " is required", 'required');
|
||||
}
|
||||
}
|
||||
} else if (isset($schema->required) && !is_array($schema->required)) {
|
||||
// Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true}
|
||||
if ( $schema->required && $value instanceof UndefinedConstraint) {
|
||||
$this->addError($path, "Is missing and it is required");
|
||||
$this->addError($path, "Is missing and it is required", 'required');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,7 +147,7 @@ class UndefinedConstraint extends Constraint
|
||||
|
||||
// if no new errors were raised it must be a disallowed value
|
||||
if (count($this->getErrors()) == count($initErrors)) {
|
||||
$this->addError($path, "Disallowed value was matched");
|
||||
$this->addError($path, "Disallowed value was matched", 'disallow');
|
||||
} else {
|
||||
$this->errors = $initErrors;
|
||||
}
|
||||
@ -160,7 +159,7 @@ class UndefinedConstraint extends Constraint
|
||||
|
||||
// if no new errors were raised then the instance validated against the "not" schema
|
||||
if (count($this->getErrors()) == count($initErrors)) {
|
||||
$this->addError($path, "Matched a schema which it should not");
|
||||
$this->addError($path, "Matched a schema which it should not", 'not');
|
||||
} else {
|
||||
$this->errors = $initErrors;
|
||||
}
|
||||
@ -170,12 +169,12 @@ class UndefinedConstraint extends Constraint
|
||||
if (is_object($value)) {
|
||||
if (isset($schema->minProperties)) {
|
||||
if (count(get_object_vars($value)) < $schema->minProperties) {
|
||||
$this->addError($path, "Must contain a minimum of " . $schema->minProperties . " properties");
|
||||
$this->addError($path, "Must contain a minimum of " . $schema->minProperties . " properties", 'minProperties', array('minProperties' => $schema->minProperties,));
|
||||
}
|
||||
}
|
||||
if (isset($schema->maxProperties)) {
|
||||
if (count(get_object_vars($value)) > $schema->maxProperties) {
|
||||
$this->addError($path, "Must contain no more than " . $schema->maxProperties . " properties");
|
||||
$this->addError($path, "Must contain no more than " . $schema->maxProperties . " properties", 'maxProperties', array('maxProperties' => $schema->maxProperties,));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,7 +208,7 @@ class UndefinedConstraint extends Constraint
|
||||
$isValid = $isValid && (count($this->getErrors()) == count($initErrors));
|
||||
}
|
||||
if (!$isValid) {
|
||||
$this->addError($path, "Failed to match all schemas");
|
||||
$this->addError($path, "Failed to match all schemas", 'allOf');
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +223,7 @@ class UndefinedConstraint extends Constraint
|
||||
}
|
||||
}
|
||||
if (!$isValid) {
|
||||
$this->addError($path, "Failed to match at least one schema");
|
||||
$this->addError($path, "Failed to match at least one schema", 'anyOf');
|
||||
} else {
|
||||
$this->errors = $startErrors;
|
||||
}
|
||||
@ -248,7 +247,8 @@ class UndefinedConstraint extends Constraint
|
||||
$allErrors,
|
||||
array(array(
|
||||
'property' => $path,
|
||||
'message' => "failed to match exactly one schema"
|
||||
'message' => "Failed to match exactly one schema",
|
||||
'constraint' => 'oneOf',
|
||||
),),
|
||||
$startErrors
|
||||
)
|
||||
@ -274,13 +274,13 @@ class UndefinedConstraint extends Constraint
|
||||
if (is_string($dependency)) {
|
||||
// Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"}
|
||||
if (!property_exists($value, $dependency)) {
|
||||
$this->addError($path, "$key depends on $dependency and $dependency is missing");
|
||||
$this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies');
|
||||
}
|
||||
} else if (is_array($dependency)) {
|
||||
// Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]}
|
||||
foreach ($dependency as $d) {
|
||||
if (!property_exists($value, $d)) {
|
||||
$this->addError($path, "$key depends on $d and $d is missing");
|
||||
$this->addError($path, "$key depends on $d and $d is missing", 'dependencies');
|
||||
}
|
||||
}
|
||||
} else if (is_object($dependency)) {
|
||||
|
@ -35,8 +35,9 @@ class AdditionalPropertiesTest extends BaseTestCase
|
||||
null,
|
||||
array(
|
||||
array(
|
||||
'property' => '',
|
||||
'message' => 'The property - additionalProp - is not defined and the definition does not allow additional properties'
|
||||
'property' => '',
|
||||
'message' => 'The property additionalProp is not defined and the definition does not allow additional properties',
|
||||
'constraint' => 'additionalProp',
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -120,6 +120,7 @@ class FormatTest extends BaseTestCase
|
||||
array('www.example.com', 'host-name'),
|
||||
|
||||
array('anything', '*'),
|
||||
array('unknown', '*'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -160,6 +161,7 @@ class FormatTest extends BaseTestCase
|
||||
array(':::ff', 'ipv6'),
|
||||
|
||||
array('localhost', 'host-name'),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,17 +74,19 @@ class OfPropertiesTest extends BaseTestCase
|
||||
Validator::CHECK_MODE_NORMAL,
|
||||
array(
|
||||
array(
|
||||
"property" => "prop2",
|
||||
"message" => "array value found, but a string is required",
|
||||
|
||||
"property" => "prop2",
|
||||
"message" => "Array value found, but a string is required",
|
||||
"constraint" => "type",
|
||||
),
|
||||
array(
|
||||
"property" => "prop2",
|
||||
"message" => "array value found, but a number is required",
|
||||
"property" => "prop2",
|
||||
"message" => "Array value found, but a number is required",
|
||||
"constraint" => "type",
|
||||
),
|
||||
array(
|
||||
"property" => "prop2",
|
||||
"message" => "failed to match exactly one schema",
|
||||
"property" => "prop2",
|
||||
"message" => "Failed to match exactly one schema",
|
||||
"constraint" => "oneOf",
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -43,7 +43,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$constraint = new TypeConstraint();
|
||||
$constraint->check($value, (object)array('type' => $type));
|
||||
$this->assertTypeConstraintError("$label value found, but $wording $type is required", $constraint);
|
||||
$this->assertTypeConstraintError(ucwords($label)." value found, but $wording $type is required", $constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user