respect-validation/library/Exceptions/ValidationException.php
Henrique Moody 748b280c34 Update conversion to strings on exceptions
Many changes were made on `ValidationException::stringify`:
- Add support for instances of `Exception`;
- Add support for instances of `Traversable`;
- Add support for resources;
- Improve `Array` conversion;
- Improve `Object` conversion;
- Improve conversion of all values by using JSON.

Now, all the parameters of the exception classes are just converted to
string when replacing parameters on exceptions, so the exception classes
now keep the original value of all parameters.
2015-09-04 17:11:40 -03:00

327 lines
7.7 KiB
PHP

<?php
/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
*
* For the full copyright and license information, please view the "LICENSE.md"
* file that was distributed with this source code.
*/
namespace Respect\Validation\Exceptions;
use DateTime;
use Exception;
use InvalidArgumentException;
use Traversable;
class ValidationException extends InvalidArgumentException implements ValidationExceptionInterface
{
const MODE_DEFAULT = 1;
const MODE_NEGATIVE = 2;
const STANDARD = 0;
public static $defaultTemplates = array(
self::MODE_DEFAULT => array(
self::STANDARD => 'Data validation failed for %s',
),
self::MODE_NEGATIVE => array(
self::STANDARD => 'Data validation failed for %s',
),
);
/**
* @var int
*/
private static $maxDepthStringify = 5;
/**
* @var int
*/
private static $maxCountStringify = 10;
/**
* @var string
*/
private static $maxReplacementStringify = '...';
protected $id = 'validation';
protected $mode = self::MODE_DEFAULT;
protected $name = '';
protected $template = '';
protected $params = array();
public static function format($template, array $vars = array())
{
return preg_replace_callback(
'/{{(\w+)}}/',
function ($match) use ($vars) {
if (!isset($vars[$match[1]])) {
return $match[0];
}
$value = $vars[$match[1]];
if ('name' == $match[1]) {
return $value;
}
return ValidationException::stringify($value);
},
$template
);
}
/**
* @param mixed $value
* @param int $depth
*
* @return string
*/
public static function stringify($value, $depth = 1)
{
if ($depth >= self::$maxDepthStringify) {
return self::$maxReplacementStringify;
}
if (is_array($value)) {
return static::stringifyArray($value, $depth);
}
if (is_object($value)) {
return static::stringifyObject($value, $depth);
}
if (is_resource($value)) {
return sprintf('`[resource] (%s)`', get_resource_type($value));
}
if (is_float($value)) {
if (is_infinite($value)) {
return ($value > 0 ? '' : '-').'INF';
}
if (is_nan($value)) {
return 'NaN';
}
}
$options = 0;
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
$options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
return (@json_encode($value, $options) ?: $value);
}
/**
* @param array $value
* @param int $depth
*
* @return string
*/
public static function stringifyArray(array $value, $depth = 1)
{
$nextDepth = ($depth + 1);
if ($nextDepth >= self::$maxDepthStringify) {
return self::$maxReplacementStringify;
}
if (empty($value)) {
return '{ }';
}
$total = count($value);
$string = '';
$current = 0;
foreach ($value as $childKey => $childValue) {
if ($current++ >= self::$maxCountStringify) {
$string .= self::$maxReplacementStringify;
break;
}
if (!is_int($childKey)) {
$string .= sprintf('%s: ', static::stringify($childKey, $nextDepth));
}
$string .= static::stringify($childValue, $nextDepth);
if ($current !== $total) {
$string .= ', ';
}
}
return sprintf('{ %s }', $string);
}
/**
* @param mixed $value
* @param int $depth
*
* @return string
*/
public static function stringifyObject($value, $depth = 2)
{
$nextDepth = $depth + 1;
if ($value instanceof DateTime) {
return sprintf('"%s"', $value->format('Y-m-d H:i:s'));
}
$class = get_class($value);
if ($value instanceof Traversable) {
return sprintf('`[traversable] (%s: %s)`', $class, static::stringify(iterator_to_array($value), $nextDepth));
}
if ($value instanceof Exception) {
$properties = array(
'message' => $value->getMessage(),
'code' => $value->getCode(),
'file' => $value->getFile().':'.$value->getLine(),
);
return sprintf('`[exception] (%s: %s)`', $class, static::stringify($properties, $nextDepth));
}
if (method_exists($value, '__toString')) {
return static::stringify($value->__toString(), $nextDepth);
}
$properties = static::stringify(get_object_vars($value), $nextDepth);
return sprintf('`[object] (%s: %s)`', $class, str_replace('`', '', $properties));
}
public function __toString()
{
return $this->getMainMessage();
}
public function chooseTemplate()
{
return key(static::$defaultTemplates[$this->mode]);
}
public function configure($name, array $params = array())
{
$this->setName($name);
$this->setParams($params);
$this->message = $this->getMainMessage();
$this->setId($this->guessId());
return $this;
}
public function getName()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
public function getMainMessage()
{
$vars = $this->getParams();
$vars['name'] = $this->getName();
$template = $this->getTemplate();
if (isset($vars['translator']) && is_callable($vars['translator'])) {
$template = call_user_func($vars['translator'], $template);
}
return static::format($template, $vars);
}
public function getParam($name)
{
return $this->hasParam($name) ? $this->params[$name] : false;
}
public function getParams()
{
return $this->params;
}
public function getTemplate()
{
if (!empty($this->template)) {
return $this->template;
} else {
return $this->template = $this->buildTemplate();
}
}
public function hasParam($name)
{
return isset($this->params[$name]);
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setMode($mode)
{
$this->mode = $mode;
$this->template = $this->buildTemplate();
return $this;
}
public function setParam($key, $value)
{
$this->params[$key] = $value;
return $this;
}
public function setParams(array $params)
{
foreach ($params as $key => $value) {
$this->setParam($key, $value);
}
return $this;
}
public function setTemplate($template)
{
$this->template = $template;
return $this;
}
protected function buildTemplate()
{
$templateKey = $this->chooseTemplate();
return static::$defaultTemplates[$this->mode][$templateKey];
}
protected function guessId()
{
if (!empty($this->id) && $this->id != 'validation') {
return $this->id;
}
$pieces = explode('\\', get_called_class());
$exceptionClassShortName = end($pieces);
$ruleClassShortName = str_replace('Exception', '', $exceptionClassShortName);
$ruleName = lcfirst($ruleClassShortName);
return $ruleName;
}
}