1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/dolibarr_ynh.git synced 2024-09-03 18:35:53 +02:00
dolibarr_ynh/sources/dolibarr/htdocs/includes/restler/Data/Validator.php

626 lines
21 KiB
PHP
Raw Normal View History

2015-09-28 22:09:38 +02:00
<?php
namespace Luracast\Restler\Data;
use Luracast\Restler\CommentParser;
use Luracast\Restler\Format\HtmlFormat;
use Luracast\Restler\RestException;
use Luracast\Restler\Scope;
use Luracast\Restler\Util;
/**
* Default Validator class used by Restler. It can be replaced by any
* iValidate implementing class by setting Defaults::$validatorClass
*
* @category Framework
* @package Restler
* @author R.Arul Kumaran <arul@luracast.com>
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
*/
class Validator implements iValidate
{
public static $holdException = false;
public static $exceptions = array();
/**
* Validate alphabetic characters.
*
* Check that given value contains only alphabetic characters.
*
* @param $input
* @param ValidationInfo $info
*
* @return string
*
* @throws Invalid
*/
public static function alpha($input, ValidationInfo $info = null)
{
if (ctype_alpha($input)) {
return $input;
}
if ($info && $info->fix) {
//remove non alpha characters
return preg_replace("/[^a-z]/i", "", $input);
}
throw new Invalid('Expecting only alphabetic characters.');
}
/**
* Validate alpha numeric characters.
*
* Check that given value contains only alpha numeric characters.
*
* @param $input
* @param ValidationInfo $info
*
* @return string
*
* @throws Invalid
*/
public static function alphanumeric($input, ValidationInfo $info = null)
{
if (ctype_alnum($input)) {
return $input;
}
if ($info && $info->fix) {
//remove non alpha numeric and space characters
return preg_replace("/[^a-z0-9 ]/i", "", $input);
}
throw new Invalid('Expecting only alpha numeric characters.');
}
/**
* Validate printable characters.
*
* Check that given value contains only printable characters.
*
* @param $input
* @param ValidationInfo $info
*
* @return string
*
* @throws Invalid
*/
public static function printable($input, ValidationInfo $info = null)
{
if (ctype_print($input)) {
return $input;
}
if ($info && $info->fix) {
//remove non printable characters
return preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $input);
}
throw new Invalid('Expecting only printable characters.');
}
/**
* Validate hexadecimal digits.
*
* Check that given value contains only hexadecimal digits.
*
* @param $input
* @param ValidationInfo $info
*
* @return string
*
* @throws Invalid
*/
public static function hex($input, ValidationInfo $info = null)
{
if (ctype_xdigit($input)) {
return $input;
}
throw new Invalid('Expecting only hexadecimal digits.');
}
/**
* Validate Telephone number
*
* Check if the given value is numeric with or without a `+` prefix
*
* @param $input
* @param ValidationInfo $info
*
* @return string
*
* @throws Invalid
*/
public static function tel($input, ValidationInfo $info = null)
{
if (is_numeric($input) && '-' != substr($input, 0, 1)) {
return $input;
}
throw new Invalid('Expecting phone number, a numeric value ' .
'with optional `+` prefix');
}
/**
* Validate Email
*
* Check if the given string is a valid email
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function email($input, ValidationInfo $info = null)
{
$r = filter_var($input, FILTER_VALIDATE_EMAIL);
if ($r) {
return $r;
} elseif ($info && $info->fix) {
$r = filter_var($input, FILTER_SANITIZE_EMAIL);
return static::email($r);
}
throw new Invalid('Expecting email in `name@example.com` format');
}
/**
* Validate IP Address
*
* Check if the given string is a valid ip address
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function ip($input, ValidationInfo $info = null)
{
$r = filter_var($input, FILTER_VALIDATE_IP);
if ($r)
return $r;
throw new Invalid('Expecting IP address in IPV6 or IPV4 format');
}
/**
* Validate Url
*
* Check if the given string is a valid url
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function url($input, ValidationInfo $info = null)
{
$r = filter_var($input, FILTER_VALIDATE_URL);
if ($r) {
return $r;
} elseif ($info && $info->fix) {
$r = filter_var($input, FILTER_SANITIZE_URL);
return static::url($r);
}
throw new Invalid('Expecting url in `http://example.com` format');
}
/**
* MySQL Date
*
* Check if the given string is a valid date in YYYY-MM-DD format
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function date($input, ValidationInfo $info = null)
{
if (
preg_match(
'#^(?P<year>\d{2}|\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$#',
$input,
$date
)
&& checkdate($date['month'], $date['day'], $date['year'])
) {
return $input;
}
throw new Invalid(
'Expecting date in `YYYY-MM-DD` format, such as `'
. date("Y-m-d") . '`'
);
}
/**
* MySQL DateTime
*
* Check if the given string is a valid date and time in YYY-MM-DD HH:MM:SS format
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function datetime($input, ValidationInfo $info = null)
{
if (
preg_match('/^(?P<year>19\d\d|20\d\d)\-(?P<month>0[1-9]|1[0-2])\-' .
'(?P<day>0\d|[1-2]\d|3[0-1]) (?P<h>0\d|1\d|2[0-3]' .
')\:(?P<i>[0-5][0-9])\:(?P<s>[0-5][0-9])$/',
$input, $date)
&& checkdate($date['month'], $date['day'], $date['year'])
) {
return $input;
}
throw new Invalid(
'Expecting date and time in `YYYY-MM-DD HH:MM:SS` format, such as `'
. date("Y-m-d H:i:s") . '`'
);
}
/**
* Alias for Time
*
* Check if the given string is a valid time in HH:MM:SS format
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function time24($input, ValidationInfo $info = null)
{
return static::time($input, $info);
}
/**
* Time
*
* Check if the given string is a valid time in HH:MM:SS format
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function time($input, ValidationInfo $info = null)
{
if (preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/', $input)) {
return $input;
}
throw new Invalid(
'Expecting time in `HH:MM:SS` format, such as `'
. date("H:i:s") . '`'
);
}
/**
* Time in 12 hour format
*
* Check if the given string is a valid time 12 hour format
*
* @param String $input
* @param ValidationInfo $info
*
* @return string
* @throws Invalid
*/
public static function time12($input, ValidationInfo $info = null)
{
if (preg_match(
'/^([1-9]|1[0-2]|0[1-9]){1}(:[0-5][0-9])?\s?([aApP][mM]{1})?$/',
$input)
) {
return $input;
}
throw new Invalid(
'Expecting time in 12 hour format, such as `08:00AM` and `10:05:11`'
);
}
/**
* Unix Timestamp
*
* Check if the given value is a valid timestamp
*
* @param String $input
* @param ValidationInfo $info
*
* @return int
* @throws Invalid
*/
public static function timestamp($input, ValidationInfo $info = null)
{
if ((string)(int)$input == $input
&& ($input <= PHP_INT_MAX)
&& ($input >= ~PHP_INT_MAX)
) {
return (int)$input;
}
throw new Invalid('Expecting unix timestamp, such as ' . time());
}
/**
* Validate the given input
*
* Validates the input and attempts to fix it when fix is requested
*
* @param mixed $input
* @param ValidationInfo $info
* @param null $full
*
* @throws \Exception
* @return array|bool|float|int|mixed|null|number|string
*/
public static function validate($input, ValidationInfo $info, $full = null)
{
$html = Scope::get('Restler')->responseFormat instanceof HtmlFormat;
$name = $html ? "<strong>$info->label</strong>" : "`$info->name`";
try {
if (is_null($input)) {
if ($info->required) {
throw new RestException (400,
"$name is required.");
}
return null;
}
$error = isset ($info->message)
? $info->message
: "Invalid value specified for $name";
//if a validation method is specified
if (!empty($info->method)) {
$method = $info->method;
$info->method = '';
$r = self::validate($input, $info);
return $info->apiClassInstance->{$method} ($r);
}
// when type is an array check if it passes for any type
if (is_array($info->type)) {
//trace("types are ".print_r($info->type, true));
$types = $info->type;
foreach ($types as $type) {
$info->type = $type;
try {
$r = self::validate($input, $info);
if ($r !== false) {
return $r;
}
} catch (RestException $e) {
// just continue
}
}
throw new RestException (400, $error);
}
//patterns are supported only for non numeric types
if (isset ($info->pattern)
&& $info->type != 'int'
&& $info->type != 'float'
&& $info->type != 'number'
) {
if (!preg_match($info->pattern, $input)) {
throw new RestException (400, $error);
}
}
if (isset ($info->choice)) {
if (is_array($input)) {
foreach ($input as $i) {
if (!in_array($i, $info->choice)) {
$error .= ". Expected one of (" . implode(',', $info->choice) . ").";
throw new RestException (400, $error);
}
}
} elseif (!in_array($input, $info->choice)) {
$error .= ". Expected one of (" . implode(',', $info->choice) . ").";
throw new RestException (400, $error);
}
}
if (method_exists($class = get_called_class(), $info->type) && $info->type != 'validate') {
try {
return call_user_func("$class::$info->type", $input, $info);
} catch (Invalid $e) {
throw new RestException(400, $error . '. ' . $e->getMessage());
}
}
switch ($info->type) {
case 'int' :
case 'float' :
case 'number' :
if (!is_numeric($input)) {
$error .= '. Expecting '
. ($info->type == 'int' ? 'integer' : 'numeric')
. ' value';
break;
}
if ($info->type == 'int' && (int)$input != $input) {
if ($info->fix) {
$r = (int)$input;
} else {
$error .= '. Expecting integer value';
break;
}
} else {
$r = $info->numericValue($input);
}
if (isset ($info->min) && $r < $info->min) {
if ($info->fix) {
$r = $info->min;
} else {
$error .= ". Minimum required value is $info->min.";
break;
}
}
if (isset ($info->max) && $r > $info->max) {
if ($info->fix) {
$r = $info->max;
} else {
$error .= ". Maximum allowed value is $info->max.";
break;
}
}
return $r;
case 'string' :
if (!is_string($input)) {
$error .= '. Expecting alpha numeric value';
break;
}
if ($info->required && $input === '') {
$error = "$name is required.";
break;
}
$r = strlen($input);
if (isset ($info->min) && $r < $info->min) {
if ($info->fix) {
$input = str_pad($input, $info->min, $input);
} else {
$char = $info->min > 1 ? 'characters' : 'character';
$error .= ". Minimum $info->min $char required.";
break;
}
}
if (isset ($info->max) && $r > $info->max) {
if ($info->fix) {
$input = substr($input, 0, $info->max);
} else {
$char = $info->max > 1 ? 'characters' : 'character';
$error .= ". Maximum $info->max $char allowed.";
break;
}
}
return $input;
case 'bool':
case 'boolean':
if ($input === 'true' || $input === true) return true;
if (is_numeric($input)) return $input > 0;
return false;
case 'array':
if ($info->fix && is_string($input)) {
$input = explode(CommentParser::$arrayDelimiter, $input);
}
if (is_array($input)) {
$contentType =
Util::nestedValue($info, 'contentType') ? : null;
if ($info->fix) {
if ($contentType == 'indexed') {
$input = $info->filterArray($input, true);
} elseif ($contentType == 'associative') {
$input = $info->filterArray($input, true);
}
} elseif (
$contentType == 'indexed' &&
array_values($input) != $input
) {
$error .= '. Expecting a list of items but an item is given';
break;
} elseif (
$contentType == 'associative' &&
array_values($input) == $input &&
count($input)
) {
$error .= '. Expecting an item but a list is given';
break;
}
$r = count($input);
if (isset ($info->min) && $r < $info->min) {
$item = $info->max > 1 ? 'items' : 'item';
$error .= ". Minimum $info->min $item required.";
break;
}
if (isset ($info->max) && $r > $info->max) {
if ($info->fix) {
$input = array_slice($input, 0, $info->max);
} else {
$item = $info->max > 1 ? 'items' : 'item';
$error .= ". Maximum $info->max $item allowed.";
break;
}
}
if (
isset($contentType) &&
$contentType != 'associative' &&
$contentType != 'indexed'
) {
$name = $info->name;
$info->type = $contentType;
unset($info->contentType);
foreach ($input as $key => $chinput) {
$info->name = "{$name}[$key]";
$input[$key] = static::validate($chinput, $info);
}
}
return $input;
} elseif (isset($contentType)) {
$error .= '. Expecting items of type ' .
($html ? "<strong>$contentType</strong>" : "`$contentType`");
break;
}
break;
case 'mixed':
case 'unknown_type':
case 'unknown':
case null: //treat as unknown
return $input;
default :
if (!is_array($input)) {
break;
}
//do type conversion
if (class_exists($info->type)) {
$input = $info->filterArray($input, false);
$implements = class_implements($info->type);
if (
is_array($implements) &&
in_array('Luracast\\Restler\\Data\\iValueObject', $implements)
) {
return call_user_func(
"{$info->type}::__set_state", $input
);
}
$class = $info->type;
$instance = new $class();
if (is_array($info->children)) {
if (
empty($input) ||
!is_array($input) ||
$input === array_values($input)
) {
$error .= '. Expecting an item of type ' .
($html ? "<strong>$info->type</strong>" : "`$info->type`");
break;
}
foreach ($info->children as $key => $value) {
$cv = new ValidationInfo($value);
if (array_key_exists($key, $input) || $cv->required) {
$instance->{$key} = static::validate(
Util::nestedValue($input, $key),
$cv
);
}
}
}
return $instance;
}
}
throw new RestException (400, $error);
} catch (\Exception $e) {
static::$exceptions[] = $e;
if (static::$holdException) {
return null;
}
throw $e;
}
}
}