Skip to content

[Validator] Fixed string conversion in constraint violations #10687

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

Merged
merged 12 commits into from
Jul 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions src/Symfony/Component/Validator/ConstraintValidator.php
Original file line number Diff line number Diff line change
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some more explanations about when/why/how this is ever called? Something along the lines of:

"This method is used when a constraint error message contains the {{ value }} placeholder. By default, Symfony never includes it, but the developer can override those error messages. It's up to the developer to make sure it actually makes sense to include such string representation of submitted values in error messages."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

*
* 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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
));
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
));
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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),
));
}
}
}
Original file line number Diff line number Diff line change
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),
));
}
}
}
Original file line number Diff line number Diff line change
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),
));
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading