src/Component.php
<?php
namespace BootPress\Validator;
use BootPress\Page\Component as Page;
class Component
{
/** @var callable[] Custom validation rules that you would like to apply. */
public $rules = array();
/** @var string[] Before you check if ``$this->certified()``, these are the error messages associated with each validation rule. After ``$this->certified()``, these are all the errors we encountered (if any). You can customize and add as you see fit. */
public $errors = array(
'remote' => 'Please fix this field.',
'required' => 'This field is required.',
'equalTo' => 'Please enter the same value again.',
'notEqualTo' => 'Please enter a different value, values must not be the same.',
'number' => 'Please enter a valid number.',
'integer' => 'A positive or negative non-decimal number please.',
'digits' => 'Please enter only digits.',
'min' => 'Please enter a value greater than or equal to {0}.',
'max' => 'Please enter a value less than or equal to {0}.',
'range' => 'Please enter a value between {0} and {1}.',
'alphaNumeric' => 'Letters, numbers, and underscores only please.',
'minLength' => 'Please enter at least {0} characters.',
'maxLength' => 'Please enter no more than {0} characters.',
'rangeLength' => 'Please enter a value between {0} and {1} characters long.',
'minWords' => 'Please enter at least {0} words.',
'maxWords' => 'Please enter {0} words or less.',
'rangeWords' => 'Please enter between {0} and {1} words.',
'pattern' => 'Invalid format.',
'date' => 'Please enter a valid date.',
'email' => 'Please enter a valid email address.',
'url' => 'Please enter a valid URL.',
'ipv4' => 'Please enter a valid IP v4 address.',
'ipv6' => 'Please enter a valid IP v6 address.',
'inList' => 'Please make a valid selection.',
'noWhiteSpace' => 'No white space please.',
);
/** @var array A field's array of acceptable values which, if specified here, do not need to be included '**inList**'. */
public $menu = array();
/** @var array This is where we save all of the information ``$this->set()``ed for each field. */
protected $data = array();
/** @var array These are the user submitted values for each field. */
protected $values = array();
/** @var null|bool Whether the form has been submitted or not. Null if we don't know. */
protected $submitted = null;
/** @var false|array Either false or an array of all the submitted values. */
protected $certified = false;
/** @var string[] The rules we reserve for validation until the end. */
protected $reserved = array('default', 'required', 'equalTo', 'notEqualTo');
/** @var string[] The rules we define in-house. */
protected $methods = array('number', 'integer', 'digits', 'min', 'max', 'range', 'alphaNumeric', 'minLength', 'maxLength', 'rangeLength', 'minWords', 'maxWords', 'rangeWords', 'pattern', 'date', 'email', 'url', 'ipv4', 'ipv6', 'inList', 'yesNo', 'trueFalse', 'noWhiteSpace', 'singleSpace');
/** @var string[] So that we have something to work with no matter what happens to ``$this->errors`` (anything can happen) public property. */
protected $default_errors = array();
/**
* Get the value of a protected property.
*
* @param string $name
*
* @return mixed
*/
public function __get($name)
{
switch ($name) {
case 'data':
case 'values':
case 'submitted':
case 'certified':
return $this->$name;
break;
}
}
/**
* Determine if a protected property is available.
*
* @param string $name
*
* @return mixed
*/
public function __isset($name)
{
switch ($name) {
case 'data':
case 'values':
case 'submitted':
case 'certified':
return true;
break;
}
return false;
}
/**
* Pass an array of values to be validated.
*
* @param array $values
*
* ```php
* $validator = new Validator($_POST);
* ```
*/
public function __construct(array $values = array())
{
$this->values = $values;
$this->default_errors = $this->errors;
}
/**
* This allows you to set individually (or all at once) the validation rules and filters for each form field. The value of every $field you set here is automatically ``trim()``ed and returned when ``$this->certified()``.
*
* @param string $field The name of your form field. If this is an ``array($field => $rules, $field, ...)`` then we loop through each one and call this method ourselves over and over.
*
* Your $field names can be an array by adding brackets to the end ie. '**name[]**'. They can also be multi-dimensional arrays such as '**name[first]**', or '**name[players][]**', or '**name[parent][child]**', etc. The important thing to remember is that you must always use the exact name given here when referencing them in other methods.
* @param string|array $rules A pipe delimited set (or an array) of rules to validate and filter the $field with. You can also specify custom messages by making this an ``array($rule => $message, ...)``. Parameters are comma-delimited, and placed within '**[]**' two brackets. The available options are:
*
* - '**remote[rule]**' - Set ``$validator->rules['rule'] = function($value){}`` to determine the validity of a submitted value. The function should return a boolean true or false.
* - '**default**' - A default value if the field is empty, or not even set.
* - '**required**' - This field must have a value, and cannot be empty.
* - '**equalTo[field]**' - Must match the same value as contained in the other form field.
* - '**notEqualTo[field]**' - Must NOT match the same value as contained in the other form field.
* - Numbers:
* - '**number**' - Must be a valid decimal number, positive or negative, integer or float, commas okay. Defaults to 0.
* - '**integer**' - Must be a postive or negative integer number, no commas. Defaults to 0.
* - '**digits**' - Must be a positive integer number, no commas. Defaults to 0.
* - '**min[number]**' - Must be greater than or equal to [number].
* - '**max[number]**' - Must be less than or equal to [number].
* - '**range[min, max]**' - Must be greater than or equal to [min], and less than or equal to [max].
* - Strings:
* - '**alphaNumeric**' - Alpha (a-z), numeric (0-9), and underscore (_) characters only.
* - '**minLength[integer]**' - String length must be greater than or equal to [integer].
* - '**maxLength[integer]**' - String length must be less than or equal to [integer].
* - '**rangeLength[minLength, maxLength]**' - String length must be greater than or equal to [minLength], and less than or equal to [maxLength].
* - '**minWords[integer]**' - Number of words must be greater than or equal to [integer].
* - '**maxWords[integer]**' - Number of words must be less than or equal to [integer].
* - '**rangeWords[minWords, maxWords]**' - Number of words must be greater than or equal to [minWords], and less than or equal to [maxWords].
* - '**pattern[regex]**' - Must match the supplied [ECMA Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) compatible [regex].
* - '**date**' - Must be a valid looking date. No particular format is enforced.
* - '**email**' - Must be a valid looking email.
* - '**url**' - Must be a valid looking url.
* - '**ipv4**' - Must be a valid looking ipv4 address.
* - '**ipv6**' - Must be a valid looking ipv6 address.
* - '**inList[1,2,3]**' - Must be one of a comma-separated list of acceptable values.
* - '**noWhiteSpace**' - Must contain no white space.
* - Filters:
* - '**singleSpace**' - Removes any doubled-up whitespace so that you only have single spaces between words.
* - '**trueFalse**' - Returns a **1** (true) or **0** (false) integer.
* - '**yesNo**' - Returns a '**Y**' or '**N**' value.
*
* ```php
* $validator->set('name', 'required');
*
* $validator->set('email', 'required|email');
*
* $validator->set(array(
* 'password' => 'required|alphaNumeric|minLength[5]|noWhiteSpace',
* 'confim' => array('required', 'matches[password]'),
* ));
*
* $validator->set('field', array('required' => 'Do this or else.'));
* ```
*/
public function set($field, $rules = '')
{
if (is_array($field)) {
foreach ($field as $name => $rules) {
if (is_numeric($name) && is_string($rules)) {
$this->set($rules);
} else {
$this->set($name, $rules);
}
}
return;
}
$page = Page::html();
$process = (is_array($rules)) ? $rules : array_map('trim', explode('|', $rules));
$custom = array();
$rules = array();
foreach ($process as $rule => $message) {
if (is_numeric($rule)) {
$rule = $message;
$message = false;
}
$param = true;
if (preg_match("/(.*?)\[(.*)\]/", $rule, $match)) {
$rule = $match[1];
$param = $match[2];
if (strpos($param, ',') !== false) {
$param = array_map('trim', explode(',', $param));
}
}
if ($message) {
$custom[$rule] = $message;
}
$rules[$rule] = $param;
}
$validate = array();
foreach ($rules as $rule => $param) {
switch ($rule) {
case 'remote':
if ($value = $page->get($this->base($field))) {
return $page->sendJson($this->remote($value, $param));
}
$validate['remote'] = $page->url();
break;
case 'required':
$validate['required'] = 'true';
break;
case 'equalTo':
$validate['equalTo'] = '#'.$this->id($param);
break;
case 'notEqualTo':
$validate['notEqualTo'] = '#'.$this->id($param);
break;
case 'number':
$validate['number'] = 'true';
$rules['default'] = 0;
break;
case 'integer':
$validate['integer'] = 'true';
$rules['default'] = 0;
break;
case 'digits':
$validate['digits'] = 'true';
$rules['default'] = 0;
break;
case 'min':
$validate['min'] = $param;
break;
case 'max':
$validate['max'] = $param;
break;
case 'range':
$validate['range'] = $param;
break;
case 'alphaNumeric':
$validate['alphanumeric'] = 'true';
break;
case 'minLength':
$validate['minlength'] = $param;
break;
case 'maxLength':
$validate['maxlength'] = $param;
break;
case 'rangeLength':
$validate['rangelength'] = $param;
break;
case 'minWords':
$validate['minWords'] = $param;
break;
case 'maxWords':
$validate['maxWords'] = $param;
break;
case 'rangeWords':
$validate['rangeWords'] = $param;
break;
case 'pattern':
$validate['pattern'] = $param;
break; // must use javascript ecma regex
case 'date':
$validate['date'] = 'true';
break;
case 'email':
$validate['email'] = 'true';
break;
case 'url':
$validate['url'] = 'true';
break;
case 'ipv4':
$validate['ipv4'] = 'true';
break;
case 'ipv6':
$validate['ipv6'] = 'true';
break;
case 'inList':
if ($param === true) {
$param = (isset($this->menu[$field]) && is_array($this->menu[$field])) ? $this->menu[$field] : array(); // Predetermined acceptable values
} elseif (!is_array($param)) {
$param = array($param); // A single acceptable value
}
$rules[$rule] = $param; // An array of acceptable values
$validate['inList'] = implode(',', $param);
// json string arrays do not play nicely with data-rule-... attributes
$page->jquery('jQuery.validator.addMethod("inList", function(value, element, params) { return this.optional(element) || $.inArray(value, params.split(",")) !== -1; }, "Please make a valid selection.");');
break;
case 'noWhiteSpace':
$validate['nowhitespace'] = 'true';
break;
case 'trueFalse':
$rules['default'] = 0;
break;
case 'yesNo':
$rules['default'] = 'N';
break;
}
}
foreach ($validate as $rule => $param) {
if (is_array($param)) {
$validate[$rule] = implode(',', $param);
}
}
$messages = array();
foreach ($rules as $rule => $param) {
if (isset($custom[$rule]) || isset($this->errors[$rule])) {
$error = (isset($custom[$rule])) ? $custom[$rule] : $this->errors[$rule];
if (!isset($this->default_errors[$rule]) || $this->default_errors[$rule] != $error) {
$messages[$rule] = $error;
}
}
}
$indexes = array();
sscanf($field, '%[^[][', $indexes[0]);
$value = null;
if ((bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) {
foreach ($matches[1] as $key) {
if ($key !== '') {
$indexes[] = $key;
}
}
$value = $this->reduce($this->values, $indexes);
} elseif (isset($this->values[$field])) {
$value = $this->values[$field];
}
$error = null;
if (!is_null($value)) {
$value = (is_array($value)) ? array_map('trim', $value) : trim($value);
foreach ($rules as $rule => $param) {
list($value, $error) = $this->validate($value, $rule, $param);
if (empty($value) || !empty($error)) {
break;
}
}
}
$this->data[$field] = array(
'id' => '#'.$this->id($field),
'field' => $indexes,
'rules' => $rules,
'validate' => (!empty($validate)) ? $validate : null,
'messages' => (!empty($messages)) ? $messages : null,
'value' => $value,
'error' => $error,
);
}
/**
* This method goes through all of the fields you set above, determines if the form has been sent, and picks out any errors.
*
* @return false|array Returns an array of validated and filtered form values for every ``$validator->set('field')`` IF the form was submitted (ie. at least one field has it's $_GET or $_POST counterpart), AND there were no errors.
*
* ```php
* if ($vars = $validator->certified()) {
* // process $vars
* }
* ```
*/
public function certified()
{
if (!is_null($this->submitted)) {
return $this->certified; // so we don't overwrite error messages
}
$errors = array();
$this->values = array();
$this->submitted = false;
foreach ($this->data as $field => $data) {
if (!is_null($data['value'])) {
$this->submitted = true;
}
if (!empty($data['value'])) {
$this->values[$field] = $data['value'];
} elseif (strpos($field, '[]') !== false) {
$this->values[$field] = array();
} else {
$this->values[$field] = (isset($data['rules']['default'])) ? $data['rules']['default'] : '';
}
if (!is_null($data['error'])) {
$errors[$field] = $data['error'];
}
}
if ($this->submitted) {
$submitted = array();
foreach ($this->data as $field => $data) {
$value = &$submitted;
foreach ($data['field'] as $index) {
$value = &$value[$index];
}
$value = $this->values[$field];
if (isset($data['rules']['required']) && empty($value)) {
$errors[$field] = $this->errorMessage('required');
} elseif (!isset($errors[$field])) {
if (isset($data['rules']['equalTo']) && $value != $this->value($data['rules']['equalTo'])) {
$errors[$field] = $this->errorMessage('equalTo');
} elseif (isset($data['rules']['notEqualTo']) && $value == $this->value($data['rules']['notEqualTo'])) {
$errors[$field] = $this->errorMessage('notEqualTo');
}
}
}
$this->certified = (!empty($errors)) ? false : $submitted;
}
$this->errors = $errors;
return $this->certified;
}
/**
* Allows you to know if a form $field has been required, or not.
*
* @param string $field
*
* @return bool Whether the field is required or not.
*/
public function required($field)
{
return (isset($this->data[$field]['rules']['required'])) ? true : false;
}
/**
* Returns the submitted value of the $field that should be used when displaying the form.
*
* The array feature comes in handy when you want to save the values to your database.
*
* @param string|array $field
*
* @return mixed
*/
public function value($field)
{
if (is_array($field)) {
$values = array();
foreach ($field as $key => $value) {
$values[$key] = $this->value($value);
}
return $values;
}
return ($this->submitted && isset($this->values[$field])) ? $this->values[$field] : null;
}
/**
* Returns an error message (if any) that should be used when displaying the form.
*
* @param string $field
*
* @return null|string
*/
public function error($field)
{
return ($this->submitted && isset($this->errors[$field])) ? $this->errors[$field] : null;
}
/**
* Returns all of the rules set up for the $field.
*
* @param string $field
*
* @return string[]
*
* ```php
* foreach ($validator->rules($field) as $validate => $param) {
* $attributes["data-rule-{$validate}"] = htmlspecialchars($param);
* }
* ```
*/
public function rules($field)
{
return (isset($this->data[$field]) && $validate = $this->data[$field]['validate']) ? $validate : array();
}
/**
* Returns a $field's rules and associated error messages.
*
* @param string $field
*
* @return string[]
*
* ```php
* foreach ($validator->messages($field) as $rule => $message) {
* $attributes["data-msg-{$rule}"] = htmlspecialchars($message);
* }
* ```
*/
public function messages($field)
{
return (isset($this->data[$field]) && $messages = $this->data[$field]['messages']) ? $messages : array();
}
/**
* Includes Jörn's jQuery Validation code that this component was meant to sync perfectly with.
*
* @param string $form The jQuery identifier of your form.
* @param string[] $options The rules and custom messages should be added to each inputs data-... attributes using ``$this->rules()`` and ``$this->messages()``. Any other fine-tuning can be done here. The $options values must be pre json encoded ie. quotes around strings ('"string"'), brackets for arrays ('[]'), quoted bools ('false'). The reason for this is because we cannot json_encode functions properly ('function(){}').
*
* ```php
* $validator->jquery('#form', array('debug'=>'true'));
* ```
*
* @see https://jqueryvalidation.org/validate/
*/
public function jquery($form, array $options = array())
{
$page = Page::html();
foreach ($options as $key => $value) {
$options[$key] = $key.':'.$value;
}
$page->jquery('$("'.$form.'").validate({'.implode(', ', $options).'});');
$page->link('https://cdn.jsdelivr.net/jquery.validation/1.15.0/jquery.validate.min.js');
$page->link('https://cdn.jsdelivr.net/jquery.validation/1.15.0/additional-methods.min.js');
}
/**
* Returns the unique id assigned to the $field.
*
* @param string $field
*
* @return string
*/
public function id($field)
{
static $ids = array();
if (!isset($ids[$field])) {
$ids[$field] = Page::html()->id($this->base($field));
}
return $ids[$field];
}
/**
* Determines if the $value is a valid decimal number. Can be positive or negative, integer or float, and commas to separate thousandths are okay.
*
* @param string $value
*
* @return bool
*/
public static function number($value)
{
return (bool) preg_match('/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/', (string) $value);
}
/**
* Deterimes if the $value is a positive or negative integer number, no commas.
*
* @param string $value
*
* @return bool
*/
public static function integer($value)
{
return (bool) preg_match('/^-?\d+$/', $value);
}
/**
* Deterimes if the $value is a positive integer number, no commas.
*
* @param string $value
*
* @return bool
*/
public static function digits($value)
{
return (bool) preg_match('/^\d+$/', $value);
}
/**
* Determines if the $value is greater than or equal to $param.
*
* @param float $value
* @param float $param
*
* @return bool
*/
public static function min($value, $param)
{
return is_numeric($value) ? ($value >= $param) : false;
}
/**
* Determines if the $value is less than or equal to $param.
*
* @param float $value
* @param float $param
*
* @return bool
*/
public static function max($value, $param)
{
return is_numeric($value) ? ($value <= $param) : false;
}
/**
* Determines if the $value is greater than or equal to $param[0], and less than or equal to $param[1].
*
* @param float $value
* @param float[] $param
*
* @return bool
*/
public static function range($value, array $param)
{
return is_numeric($value) ? ($value >= $param[0] && $value <= $param[1]) : false;
}
/**
* Deterimes if the $value has alpha (a-z), numeric (0-9), and underscore (_) characters only.
*
* @param string $value
*
* @return bool
*/
public static function alphaNumeric($value)
{
return (bool) preg_match('/^\w+$/', $value);
}
/**
* Determines if the $value's length is greater than or equal to $param.
*
* @param string $value
* @param int $param
*
* @return bool
*/
public static function minLength($value, $param)
{
$length = is_array($value) ? count($value) : mb_strlen($value);
return $length >= $param;
}
/**
* Determines if the $value's length is less than or equal to $param.
*
* @param string $value
* @param int $param
*
* @return bool
*/
public static function maxLength($value, $param)
{
$length = is_array($value) ? count($value) : mb_strlen($value);
return $length <= $param;
}
/**
* Determines if the $value's length is greater than or equal to $param[0], and less than or equal to $param[1].
*
* @param string $value
* @param int[] $param
*
* @return bool
*/
public static function rangeLength($value, array $param)
{
$length = is_array($value) ? count($value) : mb_strlen($value);
return $length >= $param[0] && $length <= $param[1];
}
/**
* Determines if the number of $value's words are greater than or equal to $param.
*
* @param string $value
* @param int $param
*
* @return bool
*/
public static function minWords($value, $param)
{
$count = self::numWords($value);
return $count >= $param;
}
/**
* Determines if the number of $value's words are less than or equal to $param.
*
* @param string $value
* @param int $param
*
* @return bool
*/
public static function maxWords($value, $param)
{
$count = self::numWords($value);
return $count <= $param;
}
/**
* Determines if the number of $value's words are greater than or equal to $param[0], and less than or equal to $param[1].
*
* @param string $value
* @param int[] $param
*
* @return bool
*/
public static function rangeWords($value, array $param)
{
$count = self::numWords($value);
return $count >= $param[0] && $count <= $param[1];
}
/**
* Determines if the $value matches the supplied regex ($param).
*
* @param string $value
* @param string $param
*
* @return bool
*/
public static function pattern($value, $param)
{
return (bool) preg_match($param, $value);
}
/**
* Determines if the $value is a parseable date.
*
* @param string $value
*
* @return bool
*/
public static function date($value)
{
return (bool) strtotime($value);
}
/**
* Determines if the $value is a valid looking email.
*
* @param string $value
*
* @return bool
*/
public static function email($value)
{
return (bool) preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/', $value);
}
/**
* Determines if the $value is a valid looking url.
*
* @param string $value
*
* @return bool
*/
public static function url($value)
{
return (bool) preg_match('_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$_iuS', $value);
}
/**
* Determines if the $value is a valid looking ipv4 address.
*
* @param string $value
*
* @return bool
*/
public static function ipv4($value)
{
return (bool) preg_match('/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i', $value);
}
/**
* Determines if the $value is a valid looking ipv6 address.
*
* @param string $value
*
* @return bool
*/
public static function ipv6($value)
{
return (bool) preg_match('/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i', $value);
}
/**
* Determines if the $value exists in the $param array.
*
* @param string $value
* @param array $param
*
* @return bool
*/
public static function inList($value, array $param)
{
return in_array($value, $param);
}
/**
* Determines if the $value contains any white space.
*
* @param string $value
*
* @return bool
*/
public static function noWhiteSpace($value)
{
return (empty($value)) ? true : (bool) preg_match('/^\S+$/i', $value);
}
/**
* Removes any doubled-up whitespace from the $value.
*
* @param string $value
*
* @return string
*/
public static function singleSpace($value)
{
return preg_replace('/\s(?=\s)/', '', $value);
}
/**
* Returns a **1** (true) or **0** (false) integer depending on the $value.
*
* @param mixed $value
*
* @return int
*/
public static function trueFalse($value)
{
if ((is_numeric($value) && $value > 0) || strtoupper($value) == 'Y') {
return 1;
}
return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
}
/**
* Returns a '**Y**' or '**N**' string depending on the $value.
*
* @param mixed $value
*
* @return string
*/
public static function yesNo($value)
{
if ((is_numeric($value) && $value > 0) || strtoupper($value) == 'Y') {
return 'Y';
}
return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'Y' : 'N';
}
/**
* Returns the number of words in $value.
*
* @param string $value
*
* @return int
*/
private static function numWords($value)
{
$value = preg_replace('/<.[^<>]*?>/', ' ', $value);
$value = preg_replace('/ | /i', ' ', $value);
$value = preg_replace('/[.(),;:!?%#$\'\"_+=\/\-“”’]*/', '', $value);
preg_match_all('/\b\w+\b/', $value, $words);
return count($words[0]);
}
/**
* A helper method to validate a $value via a callable $param.
*
* @param string $value
* @param string $param
*
* @return string
*/
private function remote($value, $param)
{
$value = (isset($this->rules[$param]) && is_callable($this->rules[$param])) ? $this->rules[$param]($value) : false;
if (!is_string($value)) {
$value = ($value) ? 'true' : 'false';
}
return $value;
}
/**
* A helper method to determine the value in an array based on a string eg. 'array[value]'.
*
* @param mixed $array
* @param array $indexes
* @param int $i
*
* @return mixed
*/
private function reduce($array, array $indexes, $i = 0)
{
if (is_array($array) && isset($indexes[$i])) {
return isset($array[$indexes[$i]]) ? $this->reduce($array[$indexes[$i]], $indexes, ($i + 1)) : null;
}
return ($array === '') ? null : $array;
}
/**
* A helper method to remove any reference to an array ie. 'array[value]' would be just 'array'.
*
* @param string $field
*
* @return string
*/
private function base($field)
{
return ($split = strpos($field, '[')) ? substr($field, 0, $split) : $field;
}
/**
* A helper method to validate a string or an array of values based on it's $rule's and $param's.
*
* @param mixed $value
* @param string $rule
* @param mixed $param
*
* @return array The derived value, and error (if any).
*/
private function validate($value, $rule, $param)
{
if (in_array($rule, $this->reserved)) {
return array($value, null);
}
if (is_array($value) && !in_array($rule, array('minLength', 'maxLength', 'rangeLength'))) {
$values = $value;
$errors = array();
foreach ($values as $key => $value) {
list($value, $error) = $this->validate($value, $rule, $param);
$values[$key] = $value;
if ($error) {
$errors[$key] = $error;
}
}
return array($values, !empty($errors) ? array_shift($errors) : null);
}
$error = null;
if ($rule == 'remote') {
if ('true' != $result = $this->remote($value, $param)) {
$error = ($result == 'false') ? $this->errorMessage($rule, $param) : $result;
}
} elseif (isset($this->rules[$rule]) && is_callable($this->rules[$rule])) {
if (!is_bool($result = $this->rules[$rule]($value, $param))) {
$value = $result;
}
if ($result === false) {
$error = $this->errorMessage($rule, $param);
}
} elseif (in_array($rule, $this->methods)) {
if (!is_bool($result = self::$rule($value, $param))) {
$value = $result;
}
if ($result === false) {
$error = $this->errorMessage($rule, $param);
}
}
return array($value, $error);
}
/**
* A helper method to retrieve the associated error message when something goes wrong.
*
* @param string $rule
* @param mixed $param
*
* @return null|string
*/
private function errorMessage($rule, $param = null)
{
if ($rule == 'remote' && isset($this->errors[$param])) {
return $this->errorMessage($param);
}
if (is_null($param)) {
$param = '';
}
$params = array_pad((array) $param, 2, '');
return (isset($this->errors[$rule])) ? str_replace(array('{0}', '{1}'), $params, $this->errors[$rule]) : null;
}
}