Skip to content

Commit

Permalink
bug #10687 [Validator] Fixed string conversion in constraint violatio…
Browse files Browse the repository at this point in the history
…ns (eagleoneraptor, webmozart)

This PR was merged into the 2.3 branch.

Discussion
----------

[Validator] Fixed string conversion in constraint violations

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #10675
| License       | MIT
| Doc PR        | -

Commits
-------

32ae95b [Validator] Added more detailed inline documentation
08ea6d3 [Validator] Removed information from the violation output if the value is an array, object or resource
d6a783f [Validator] Renamed valueToString() to formatValue(); added missing formatValue() calls
71897d7 [Validator] Fixed CS
cea4155 [Validator] Fixed date-to-string conversion tests to match ICU 51
5aa7e6d [Validator] Added "{{ value }}" parameters where they were missing
f329552 [Validator] Simplified and explained the LuhnValidator
bff09f2 [Validator] Simplified IssnValidator
224e70f [Validator] Fixed and simplified IsbnValidator
fd58870 [Validator] Simplified IBAN validation algorithm
97243bc [Validator] Fixed value-to-string conversion in constraint violations
75e8815 [Validator] Fix constraint violation message parameterization
  • Loading branch information
webmozart committed Jul 30, 2014
2 parents 71edf38 + 32ae95b commit 7d7b5c7
Show file tree
Hide file tree
Showing 59 changed files with 544 additions and 277 deletions.
106 changes: 106 additions & 0 deletions src/Symfony/Component/Validator/ConstraintValidator.php
Expand Up @@ -32,4 +32,110 @@ public function initialize(ExecutionContextInterface $context)
{
$this->context = $context;
}

/**
* Returns a string representation of the type of the value.
*
* This method should be used if you pass the type of a value as
* message parameter to a constraint violation. Note that such
* parameters should usually not be included in messages aimed at
* non-technical people.
*
* @param mixed $value The value to return the type of
*
* @return string The type of the value
*/
protected function formatTypeOf($value)
{
return is_object($value) ? get_class($value) : gettype($value);
}

/**
* Returns a string representation of the value.
*
* This method returns the equivalent PHP tokens for most scalar types
* (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped
* in double quotes ("). Objects, arrays and resources are formatted as
* "object", "array" and "resource". If the parameter $prettyDateTime
* is set to true, {@link \DateTime} objects will be formatted as
* RFC-3339 dates ("Y-m-d H:i:s").
*
* Be careful when passing message parameters to a constraint violation
* that (may) contain objects, arrays or resources. These parameters
* should only be displayed for technical users. Non-technical users
* won't know what an "object", "array" or "resource" is and will be
* confused by the violation message.
*
* @param mixed $value The value to format as string
* @param bool $prettyDateTime Whether to format {@link \DateTime}
* objects as RFC-3339 dates ("Y-m-d H:i:s")
*
* @return string The string representation of the passed value
*/
protected function formatValue($value, $prettyDateTime = false)
{
if ($prettyDateTime && $value instanceof \DateTime) {
if (class_exists('IntlDateFormatter')) {
$locale = \Locale::getDefault();
$formatter = new \IntlDateFormatter($locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT);

return $formatter->format($value);
}

return $value->format('Y-m-d H:i:s');
}

if (is_object($value)) {
return 'object';
}

if (is_array($value)) {
return 'array';
}

if (is_string($value)) {
return '"'.$value.'"';
}

if (is_resource($value)) {
return 'resource';
}

if (null === $value) {
return 'null';
}

if (false === $value) {
return 'false';
}

if (true === $value) {
return 'true';
}

return (string) $value;
}

/**
* Returns a string representation of a list of values.
*
* Each of the values is converted to a string using
* {@link formatValue()}. The values are then concatenated with commas.
*
* @param array $values A list of values
* @param bool $prettyDateTime Whether to format {@link \DateTime}
* objects as RFC-3339 dates ("Y-m-d H:i:s")
*
* @return string The string representation of the value list
*
* @see formatValue()
*/
protected function formatValues(array $values, $prettyDateTime = false)
{
foreach ($values as $key => $value) {
$values[$key] = $this->formatValue($value, $prettyDateTime);
}

return implode(', ', $values);
}
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/Validator/ConstraintViolation.php
Expand Up @@ -96,9 +96,9 @@ public function __construct($message, $messageTemplate, array $messageParameters
public function __toString()
{
if (is_object($this->root)) {
$class = get_class($this->root);
$class = 'Object('.get_class($this->root).')';
} elseif (is_array($this->root)) {
$class = "Array";
$class = 'Array';
} else {
$class = (string) $this->root;
}
Expand Down
Expand Up @@ -32,41 +32,13 @@ public function validate($value, Constraint $constraint)

if (!$this->compareValues($value, $constraint->value)) {
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->valueToString($constraint->value),
'{{ compared_value }}' => $this->valueToString($constraint->value),
'{{ compared_value_type }}' => $this->valueToType($constraint->value)
'{{ value }}' => $this->formatValue($value, true),
'{{ compared_value }}' => $this->formatValue($constraint->value, true),
'{{ compared_value_type }}' => $this->formatTypeOf($constraint->value)
));
}
}

/**
* Returns a string representation of the type of the value.
*
* @param mixed $value
*
* @return string
*/
private function valueToType($value)
{
return is_object($value) ? get_class($value) : gettype($value);
}

/**
* Returns a string representation of the value.
*
* @param mixed $value
*
* @return string
*/
private function valueToString($value)
{
if ($value instanceof \DateTime) {
return $value->format('Y-m-d H:i:s');
}

return var_export($value, true);
}

/**
* Compares the two given values to find if their relationship is valid
*
Expand Down
Expand Up @@ -27,7 +27,9 @@ class BlankValidator extends ConstraintValidator
public function validate($value, Constraint $constraint)
{
if ('' !== $value && null !== $value) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value)
));
}
}
}
Expand Up @@ -108,7 +108,9 @@ public function validate($value, Constraint $constraint)
}

if (!is_numeric($value)) {
$this->context->addViolation($constraint->message);
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));

return;
}
Expand All @@ -124,6 +126,8 @@ public function validate($value, Constraint $constraint)
}
}

$this->context->addViolation($constraint->message);
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}
16 changes: 12 additions & 4 deletions src/Symfony/Component/Validator/Constraints/ChoiceValidator.php
Expand Up @@ -59,25 +59,33 @@ public function validate($value, Constraint $constraint)
if ($constraint->multiple) {
foreach ($value as $_value) {
if (!in_array($_value, $choices, $constraint->strict)) {
$this->context->addViolation($constraint->multipleMessage, array('{{ value }}' => $_value));
$this->context->addViolation($constraint->multipleMessage, array(
'{{ value }}' => $this->formatValue($_value),
));
}
}

$count = count($value);

if ($constraint->min !== null && $count < $constraint->min) {
$this->context->addViolation($constraint->minMessage, array('{{ limit }}' => $constraint->min), null, (int) $constraint->min);
$this->context->addViolation($constraint->minMessage, array(
'{{ limit }}' => $constraint->min
), null, (int) $constraint->min);

return;
}

if ($constraint->max !== null && $count > $constraint->max) {
$this->context->addViolation($constraint->maxMessage, array('{{ limit }}' => $constraint->max), null, (int) $constraint->max);
$this->context->addViolation($constraint->maxMessage, array(
'{{ limit }}' => $constraint->max
), null, (int) $constraint->max);

return;
}
} elseif (!in_array($value, $choices, $constraint->strict)) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value)
));
}
}
}
Expand Up @@ -48,7 +48,7 @@ public function validate($value, Constraint $constraint)
}
} elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) {
$this->context->addViolationAt('['.$field.']', $constraint->missingFieldsMessage, array(
'{{ field }}' => $field
'{{ field }}' => $this->formatValue($field)
), null);
}
}
Expand All @@ -57,7 +57,7 @@ public function validate($value, Constraint $constraint)
foreach ($value as $field => $fieldValue) {
if (!isset($constraint->fields[$field])) {
$this->context->addViolationAt('['.$field.']', $constraint->extraFieldsMessage, array(
'{{ field }}' => $field
'{{ field }}' => $this->formatValue($field)
), $fieldValue);
}
}
Expand Down
Expand Up @@ -42,7 +42,9 @@ public function validate($value, Constraint $constraint)
$countries = Intl::getRegionBundle()->getCountryNames();

if (!isset($countries[$value])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}
}
Expand Up @@ -42,7 +42,9 @@ public function validate($value, Constraint $constraint)
$currencies = Intl::getCurrencyBundle()->getCurrencyNames();

if (!isset($currencies[$value])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}
}
Expand Up @@ -40,7 +40,9 @@ public function validate($value, Constraint $constraint)
$value = (string) $value;

if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}
}
Expand Up @@ -50,7 +50,9 @@ public function validate($value, Constraint $constraint)
}

if (!$valid) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}

Expand Down
Expand Up @@ -30,6 +30,8 @@ public function validate($value, Constraint $constraint)
return;
}

$this->context->addViolation($constraint->message);
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->formatValue($value),
));
}
}
22 changes: 13 additions & 9 deletions src/Symfony/Component/Validator/Constraints/FileValidator.php
Expand Up @@ -96,13 +96,17 @@ public function validate($value, Constraint $constraint)
$path = $value instanceof FileObject ? $value->getPathname() : (string) $value;

if (!is_file($path)) {
$this->context->addViolation($constraint->notFoundMessage, array('{{ file }}' => $path));
$this->context->addViolation($constraint->notFoundMessage, array(
'{{ file }}' => $this->formatValue($path)
));

return;
}

if (!is_readable($path)) {
$this->context->addViolation($constraint->notReadableMessage, array('{{ file }}' => $path));
$this->context->addViolation($constraint->notReadableMessage, array(
'{{ file }}' => $this->formatValue($path)
));

return;
}
Expand All @@ -126,10 +130,10 @@ public function validate($value, Constraint $constraint)

if ($size > $limit) {
$this->context->addViolation($constraint->maxSizeMessage, array(
'{{ size }}' => $size,
'{{ limit }}' => $limit,
'{{ suffix }}' => $suffix,
'{{ file }}' => $path,
'{{ size }}' => $size,
'{{ limit }}' => $limit,
'{{ suffix }}' => $suffix,
'{{ file }}' => $this->formatValue($path),
));

return;
Expand Down Expand Up @@ -161,9 +165,9 @@ public function validate($value, Constraint $constraint)

if (false === $valid) {
$this->context->addViolation($constraint->mimeTypesMessage, array(
'{{ type }}' => '"'.$mime.'"',
'{{ types }}' => '"'.implode('", "', $mimeTypes) .'"',
'{{ file }}' => $path,
'{{ type }}' => $this->formatValue($mime),
'{{ types }}' => $this->formatValues($mimeTypes),
'{{ file }}' => $this->formatValue($path),
));
}
}
Expand Down

0 comments on commit 7d7b5c7

Please sign in to comment.