| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 |
- <?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/
- *
- */
- class Validator implements iValidate
- {
- public static $holdException = false;
- public static $exceptions = array();
- public static $preFilters = array(
- //'*' => 'some_global_filter', //applied to all parameters
- 'string' => 'trim', //apply filter function by type (string)
- //'string' => 'strip_tags',
- //'string' => 'htmlspecialchars',
- //'int' => 'abs',
- //'float' => 'abs',
- //'CustomClass' => 'MyFilterClass::custom',
- // please note that you wont get an instance
- // of CustomClass. you will get an array instead
- );
- /**
- * 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 UUID strings.
- *
- * Check that given value contains only alpha numeric characters and the length is 36 chars.
- *
- * @param $input
- * @param ValidationInfo $info
- *
- * @return string
- *
- * @throws Invalid
- */
- public static function uuid($input, ValidationInfo $info = null)
- {
- if (is_string($input) && preg_match(
- '/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i',
- $input
- )) {
- return strtolower($input);
- }
- throw new Invalid('Expecting a Universally Unique IDentifier (UUID) string.');
- }
- /**
- * 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.');
- }
- /**
- * Color specified as hexadecimals
- *
- * Check that given value contains only color.
- *
- * @param $input
- * @param ValidationInfo|null $info
- *
- * @return string
- * @throws Invalid
- */
- public static function color($input, ValidationInfo $info = null)
- {
- if (preg_match('/^#[a-f0-9]{6}$/i', $input)) {
- return $input;
- }
- throw new Invalid('Expecting color as 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`";
- if (
- isset(static::$preFilters['*']) &&
- is_scalar($input) &&
- is_callable($func = static::$preFilters['*'])
- ) {
- $input = $func($input);
- }
- if (
- isset(static::$preFilters[$info->type]) &&
- (is_scalar($input) || !empty($info->children)) &&
- is_callable($func = static::$preFilters[$info->type])
- ) {
- $input = $func($input);
- }
- 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 (!$info->required && empty($input)) {
- //since its optional, and empty let it pass.
- $input = null;
- } elseif (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') {
- if (!$info->required && empty($input)) {
- //optional parameter with a empty value assume null
- return null;
- }
- 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' :
- case 'password' : //password fields with string
- case 'search' : //search field with string
- if (is_bool($input)) $input = $input ? 'true' : 'false';
- 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 (is_bool($input)) {
- return $input;
- }
- if (is_numeric($input)) {
- if ($input == 1) {
- return true;
- }
- if ($input == 0) {
- return false;
- }
- } elseif (is_string($input)) {
- switch (strtolower($input)) {
- case 'true':
- return true;
- case 'false':
- return false;
- }
- }
- if ($info->fix) {
- return $input ? true : false;
- }
- $error .= '. Expecting boolean value';
- break;
- 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, false);
- }
- } 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);
- unset($info->min);
- unset($info->max);
- 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);
- $cv->name = "{$info->name}[$key]";
- 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[$info->name] = $e;
- if (static::$holdException) {
- return null;
- }
- throw $e;
- }
- }
- }
|