| Server IP : 172.67.179.166 / Your IP : 172.64.213.35 Web Server : nginx/1.20.2 System : Linux 172-104-110-161.ip.linodeusercontent.com 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64 User : www ( 1000) PHP Version : 8.1.9 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /www/wwwroot/data.driver-hp.download/framework-4.1.3/system/Validation/ |
Upload File : |
<?php
/**
* This file is part of the CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CodeIgniter\Validation;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\Validation\Exceptions\ValidationException;
use CodeIgniter\View\RendererInterface;
use Config\Validation as ValidationConfig;
use InvalidArgumentException;
/**
* Validator
*/
class Validation implements ValidationInterface
{
/**
* Files to load with validation functions.
*
* @var array
*/
protected $ruleSetFiles;
/**
* The loaded instances of our validation files.
*
* @var array
*/
protected $ruleSetInstances = [];
/**
* Stores the actual rules that should
* be ran against $data.
*
* @var array
*/
protected $rules = [];
/**
* The data that should be validated,
* where 'key' is the alias, with value.
*
* @var array
*/
protected $data = [];
/**
* Any generated errors during validation.
* 'key' is the alias, 'value' is the message.
*
* @var array
*/
protected $errors = [];
/**
* Stores custom error message to use
* during validation. Where 'key' is the alias.
*
* @var array
*/
protected $customErrors = [];
/**
* Our configuration.
*
* @var ValidationConfig
*/
protected $config;
/**
* The view renderer used to render validation messages.
*
* @var RendererInterface
*/
protected $view;
/**
* Validation constructor.
*
* @param ValidationConfig $config
* @param RendererInterface $view
*/
public function __construct($config, RendererInterface $view)
{
$this->ruleSetFiles = $config->ruleSets;
$this->config = $config;
$this->view = $view;
}
/**
* Runs the validation process, returning true/false determining whether
* validation was successful or not.
*
* @param array|null $data The array of data to validate.
* @param string|null $group The predefined group of rules to apply.
* @param string|null $dbGroup The database group to use.
*
* @return boolean
*/
public function run(array $data = null, string $group = null, string $dbGroup = null): bool
{
$data = $data ?? $this->data;
// i.e. is_unique
$data['DBGroup'] = $dbGroup;
$this->loadRuleSets();
$this->loadRuleGroup($group);
// If no rules exist, we return false to ensure
// the developer didn't forget to set the rules.
if (empty($this->rules))
{
return false;
}
// Replace any placeholders (e.g. {id}) in the rules with
// the value found in $data, if any.
$this->rules = $this->fillPlaceholders($this->rules, $data);
// Need this for searching arrays in validation.
helper('array');
// Run through each rule. If we have any field set for
// this rule, then we need to run them through!
foreach ($this->rules as $field => $setup)
{
// Blast $rSetup apart, unless it's already an array.
$rules = $setup['rules'] ?? $setup;
if (is_string($rules))
{
$rules = $this->splitRules($rules);
}
$values = dot_array_search($field, $data);
$values = is_array($values) ? $values : [$values];
if ($values === [])
{
// We'll process the values right away if an empty array
$this->processRules($field, $setup['label'] ?? $field, $values, $rules, $data);
}
foreach ($values as $value)
{
// Otherwise, we'll let the loop do the job
$this->processRules($field, $setup['label'] ?? $field, $value, $rules, $data);
}
}
return $this->getErrors() === [];
}
/**
* Runs the validation process, returning true or false
* determining whether validation was successful or not.
*
* @param mixed $value
* @param string $rule
* @param string[] $errors
*
* @return boolean
*/
public function check($value, string $rule, array $errors = []): bool
{
$this->reset();
return $this->setRule('check', null, $rule, $errors)->run(['check' => $value]);
}
/**
* Runs all of $rules against $field, until one fails, or
* all of them have been processed. If one fails, it adds
* the error to $this->errors and moves on to the next,
* so that we can collect all of the first errors.
*
* @param string $field
* @param string|null $label
* @param string|array $value
* @param array|null $rules
* @param array $data
*
* @return boolean
*/
protected function processRules(string $field, string $label = null, $value, $rules = null, array $data = null): bool
{
if (is_null($data))
{
throw new InvalidArgumentException('You must supply the parameter: data.');
}
if (in_array('if_exist', $rules, true))
{
$flattenedData = array_flatten_with_dots($data);
$ifExistField = $field;
if (strpos($field, '.*') !== false)
{
// We'll change the dot notation into a PCRE pattern
// that can be used later
$ifExistField = str_replace('\.\*', '\.(?:[^\.]+)', preg_quote($field, '/'));
$dataIsExisting = array_reduce(array_keys($flattenedData), static function ($carry, $item) use ($ifExistField) {
$pattern = sprintf('/%s/u', $ifExistField);
return $carry || preg_match($pattern, $item) === 1;
}, false);
}
else
{
$dataIsExisting = array_key_exists($ifExistField, $flattenedData);
}
unset($ifExistField, $flattenedData);
if (! $dataIsExisting)
{
// we return early if `if_exist` is not satisfied. we have nothing to do here.
return true;
}
// Otherwise remove the if_exist rule and continue the process
$rules = array_diff($rules, ['if_exist']);
}
if (in_array('permit_empty', $rules, true))
{
if (! in_array('required', $rules, true) && (is_array($value) ? empty($value) : (trim($value) === '')))
{
$passed = true;
foreach ($rules as $rule)
{
if (preg_match('/(.*?)\[(.*)\]/', $rule, $match))
{
$rule = $match[1];
$param = $match[2];
if (! in_array($rule, ['required_with', 'required_without'], true))
{
continue;
}
// Check in our rulesets
foreach ($this->ruleSetInstances as $set)
{
if (! method_exists($set, $rule))
{
continue;
}
$passed = $passed && $set->$rule($value, $param, $data);
break;
}
}
}
if ($passed === true)
{
return true;
}
}
$rules = array_diff($rules, ['permit_empty']);
}
foreach ($rules as $rule)
{
$isCallable = is_callable($rule);
$passed = false;
$param = false;
if (! $isCallable && preg_match('/(.*?)\[(.*)\]/', $rule, $match))
{
$rule = $match[1];
$param = $match[2];
}
// Placeholder for custom errors from the rules.
$error = null;
// If it's a callable, call and and get out of here.
if ($isCallable)
{
$passed = $param === false ? $rule($value) : $rule($value, $param, $data);
}
else
{
$found = false;
// Check in our rulesets
foreach ($this->ruleSetInstances as $set)
{
if (! method_exists($set, $rule))
{
continue;
}
$found = true;
$passed = $param === false ? $set->$rule($value, $error) : $set->$rule($value, $param, $data, $error);
break;
}
// If the rule wasn't found anywhere, we
// should throw an exception so the developer can find it.
if (! $found)
{
throw ValidationException::forRuleNotFound($rule);
}
}
// Set the error message if we didn't survive.
if ($passed === false)
{
// if the $value is an array, convert it to as string representation
if (is_array($value))
{
$value = '[' . implode(', ', $value) . ']';
}
$this->errors[$field] = is_null($error)
? $this->getErrorMessage($rule, $field, $label, $param, $value)
: $error; // @phpstan-ignore-line
return false;
}
}
return true;
}
/**
* Takes a Request object and grabs the input data to use from its
* array values.
*
* @param RequestInterface|IncomingRequest $request
*
* @return ValidationInterface
*/
public function withRequest(RequestInterface $request): ValidationInterface
{
/** @var IncomingRequest $request */
if (strpos($request->getHeaderLine('Content-Type'), 'application/json') !== false)
{
$this->data = $request->getJSON(true);
return $this;
}
if (in_array($request->getMethod(), ['put', 'patch', 'delete'], true)
&& strpos($request->getHeaderLine('Content-Type'), 'multipart/form-data') === false
)
{
$this->data = $request->getRawInput();
}
else
{
$this->data = $request->getVar() ?? [];
}
return $this;
}
/**
* Sets an individual rule and custom error messages for a single field.
*
* The custom error message should be just the messages that apply to
* this field, like so:
*
* [
* 'rule' => 'message',
* 'rule' => 'message'
* ]
*
* @param string $field
* @param string|null $label
* @param string $rules
* @param array $errors
*
* @return $this
*/
public function setRule(string $field, string $label = null, string $rules, array $errors = [])
{
$this->rules[$field] = [
'label' => $label,
'rules' => $rules,
];
$this->customErrors = array_merge($this->customErrors, [
$field => $errors,
]);
return $this;
}
/**
* Stores the rules that should be used to validate the items.
* Rules should be an array formatted like:
*
* [
* 'field' => 'rule1|rule2'
* ]
*
* The $errors array should be formatted like:
* [
* 'field' => [
* 'rule' => 'message',
* 'rule' => 'message
* ],
* ]
*
* @param array $rules
* @param array $errors // An array of custom error messages
*
* @return ValidationInterface
*/
public function setRules(array $rules, array $errors = []): ValidationInterface
{
$this->customErrors = $errors;
foreach ($rules as $field => &$rule)
{
if (! is_array($rule))
{
continue;
}
if (! array_key_exists('errors', $rule))
{
continue;
}
$this->customErrors[$field] = $rule['errors'];
unset($rule['errors']);
}
$this->rules = $rules;
return $this;
}
/**
* Returns all of the rules currently defined.
*
* @return array
*/
public function getRules(): array
{
return $this->rules;
}
/**
* Checks to see if the rule for key $field has been set or not.
*
* @param string $field
*
* @return boolean
*/
public function hasRule(string $field): bool
{
return array_key_exists($field, $this->rules);
}
/**
* Get rule group.
*
* @param string $group Group.
*
* @return string[] Rule group.
*
* @throws InvalidArgumentException If group not found.
*/
public function getRuleGroup(string $group): array
{
if (! isset($this->config->$group))
{
throw ValidationException::forGroupNotFound($group);
}
if (! is_array($this->config->$group))
{
throw ValidationException::forGroupNotArray($group);
}
return $this->config->$group;
}
/**
* Set rule group.
*
* @param string $group Group.
*
* @throws InvalidArgumentException If group not found.
*/
public function setRuleGroup(string $group)
{
$rules = $this->getRuleGroup($group);
$this->setRules($rules);
$errorName = $group . '_errors';
if (isset($this->config->$errorName))
{
$this->customErrors = $this->config->$errorName;
}
}
/**
* Returns the rendered HTML of the errors as defined in $template.
*
* @param string $template
*
* @return string
*/
public function listErrors(string $template = 'list'): string
{
if (! array_key_exists($template, $this->config->templates))
{
throw ValidationException::forInvalidTemplate($template);
}
return $this->view
->setVar('errors', $this->getErrors())
->render($this->config->templates[$template]);
}
/**
* Displays a single error in formatted HTML as defined in the $template view.
*
* @param string $field
* @param string $template
*
* @return string
*/
public function showError(string $field, string $template = 'single'): string
{
if (! array_key_exists($field, $this->getErrors()))
{
return '';
}
if (! array_key_exists($template, $this->config->templates))
{
throw ValidationException::forInvalidTemplate($template);
}
return $this->view
->setVar('error', $this->getError($field))
->render($this->config->templates[$template]);
}
/**
* Loads all of the rulesets classes that have been defined in the
* Config\Validation and stores them locally so we can use them.
*/
protected function loadRuleSets()
{
if (empty($this->ruleSetFiles))
{
throw ValidationException::forNoRuleSets();
}
foreach ($this->ruleSetFiles as $file)
{
$this->ruleSetInstances[] = new $file();
}
}
/**
* Loads custom rule groups (if set) into the current rules.
*
* Rules can be pre-defined in Config\Validation and can
* be any name, but must all still be an array of the
* same format used with setRules(). Additionally, check
* for {group}_errors for an array of custom error messages.
*
* @param string|null $group
*
* @return array|ValidationException|null
*/
public function loadRuleGroup(string $group = null)
{
if (empty($group))
{
return null;
}
if (! isset($this->config->$group))
{
throw ValidationException::forGroupNotFound($group);
}
if (! is_array($this->config->$group))
{
throw ValidationException::forGroupNotArray($group);
}
$this->setRules($this->config->$group);
// If {group}_errors exists in the config file,
// then override our custom errors with them.
$errorName = $group . '_errors';
if (isset($this->config->$errorName))
{
$this->customErrors = $this->config->$errorName;
}
return $this->rules;
}
/**
* Replace any placeholders within the rules with the values that
* match the 'key' of any properties being set. For example, if
* we had the following $data array:
*
* [ 'id' => 13 ]
*
* and the following rule:
*
* 'required|is_unique[users,email,id,{id}]'
*
* The value of {id} would be replaced with the actual id in the form data:
*
* 'required|is_unique[users,email,id,13]'
*
* @param array $rules
* @param array $data
*
* @return array
*/
protected function fillPlaceholders(array $rules, array $data): array
{
$replacements = [];
foreach ($data as $key => $value)
{
$replacements["{{$key}}"] = $value;
}
if (! empty($replacements))
{
foreach ($rules as &$rule)
{
if (is_array($rule))
{
foreach ($rule as &$row)
{
// Should only be an `errors` array
// which doesn't take placeholders.
if (is_array($row))
{
continue;
}
$row = strtr($row, $replacements);
}
continue;
}
$rule = strtr($rule, $replacements);
}
}
return $rules;
}
/**
* Checks to see if an error exists for the given field.
*
* @param string $field
*
* @return boolean
*/
public function hasError(string $field): bool
{
return array_key_exists($field, $this->getErrors());
}
/**
* Returns the error(s) for a specified $field (or empty string if not
* set).
*
* @param string $field Field.
*
* @return string Error(s).
*/
public function getError(string $field = null): string
{
if ($field === null && count($this->rules) === 1)
{
$field = array_key_first($this->rules);
}
return array_key_exists($field, $this->getErrors()) ? $this->errors[$field] : '';
}
/**
* Returns the array of errors that were encountered during
* a run() call. The array should be in the following format:
*
* [
* 'field1' => 'error message',
* 'field2' => 'error message',
* ]
*
* @return array<string,string>
*
* Excluded from code coverage because that it always run as cli
*
* @codeCoverageIgnore
*/
public function getErrors(): array
{
// If we already have errors, we'll use those.
// If we don't, check the session to see if any were
// passed along from a redirect_with_input request.
if (empty($this->errors) && ! is_cli() && isset($_SESSION, $_SESSION['_ci_validation_errors']))
{
$this->errors = unserialize($_SESSION['_ci_validation_errors']);
}
return $this->errors ?? [];
}
/**
* Sets the error for a specific field. Used by custom validation methods.
*
* @param string $field
* @param string $error
*
* @return ValidationInterface
*/
public function setError(string $field, string $error): ValidationInterface
{
$this->errors[$field] = $error;
return $this;
}
/**
* Attempts to find the appropriate error message
*
* @param string $rule
* @param string $field
* @param string|null $label
* @param string $param
* @param string $value The value that caused the validation to fail.
*
* @return string
*/
protected function getErrorMessage(string $rule, string $field, string $label = null, string $param = null, string $value = null): string
{
// Check if custom message has been defined by user
if (isset($this->customErrors[$field][$rule]))
{
$message = lang($this->customErrors[$field][$rule]);
}
else
{
// Try to grab a localized version of the message...
// lang() will return the rule name back if not found,
// so there will always be a string being returned.
$message = lang('Validation.' . $rule);
}
$message = str_replace('{field}', empty($label) ? $field : lang($label), $message);
$message = str_replace('{param}', empty($this->rules[$param]['label']) ? $param : lang($this->rules[$param]['label']), $message);
return str_replace('{value}', $value, $message);
}
/**
* Split rules string by pipe operator.
*
* @param string $rules
*
* @return array
*/
protected function splitRules(string $rules): array
{
$nonEscapeBracket = '((?<!\\\\)(?:\\\\\\\\)*[\[\]])';
$pipeNotInBracket = sprintf(
'/\|(?=(?:[^\[\]]*%s[^\[\]]*%s)*(?![^\[\]]*%s))/',
$nonEscapeBracket,
$nonEscapeBracket,
$nonEscapeBracket
);
$_rules = preg_split($pipeNotInBracket, $rules);
return array_unique($_rules);
}
// Misc
/**
* Resets the class to a blank slate. Should be called whenever
* you need to process more than one array.
*
* @return ValidationInterface
*/
public function reset(): ValidationInterface
{
$this->data = [];
$this->rules = [];
$this->errors = [];
$this->customErrors = [];
return $this;
}
}