mirror of
https://github.com/YunoHost-Apps/dolibarr_ynh.git
synced 2024-09-03 18:35:53 +02:00
626 lines
21 KiB
PHP
626 lines
21 KiB
PHP
|
<?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;
|
||
|
}
|
||
|
}
|
||
|
}
|