<?php
namespace schlaus\schimile;
/**
*
* Schimile is a validation class
*
*/
class schimile {
private static $_ruleset = array();
private static $_errorHandler = null;
private static $_latestErrors = null;
private static $_currentInstance;
private $_currentIndex = 0;
private $_optional = array();
private $_nextIndex = 0;
private $_inputType;
private $_not;
private $_queue = array();
private $_queueIndex = 0;
private $_errors = array();
private $_haltOnError = false;
private $_currentValue;
private $_currentValidatorAccepts = array("boolean", "integer", "double", "string", "array", "object");
public function __construct($inputType, $index)
{
$this->_inputType = $inputType;
$this->_updateIndex($index);
}
private function _accept()
{
$shortForms = array(
"arr" => "array",
"str" => "string",
"bool" => "boolean",
"obj" => "object",
"int" => "integer",
"float" => "double"
);
$accepted = func_get_args();
if (empty($accepted)) $accepted = array("bool", "int", "float", "str", "arr", "obj");
if (count($accepted) == 1 && is_string($accepted)) {
$accepted = preg_split('/(, |,|\|)/', $accepted, -1, PREG_SPLIT_NO_EMPTY);
} elseif (is_array($accepted[0])) {
$accepted = $accepted[0];
}
foreach ($accepted as &$val) {
if (array_key_exists($val, $shortForms)) $val = $shortForms[$val];
}
$this->_currentValidatorAccepts = $accepted;
}
private function _isValidType($var, $accepted)
{
return in_array(gettype($var), $accepted);
}
private function _pushToQueue($validator, $nonNegatedMessage, $negatedMessage, $params = array())
{
$message = ($this->_not) ? $negatedMessage : $nonNegatedMessage;
$this->_queue[$this->_queueIndex][] = array(
"validator" => $validator,
"message" => $message,
"negate" => $this->_not,
"inputType" => $this->_inputType,
"accepts" => $this->_currentValidatorAccepts,
"useIndex" => $this->_currentIndex,
"params" => $params
);
$this->_accept();
}
private function _reset()
{
$this->_currentIndex = 0;
$this->_nextIndex = 0;
$this->_queue = array();
$this->_queueIndex = 0;
$this->_errors = array();
}
public static function assert($ruleset, $input)
{
if (is_object($ruleset)) {
$instance = $ruleset;
} elseif (is_string($ruleset) && array_key_exists($ruleset, self::$_ruleset)) {
$instance = clone self::$_ruleset[$ruleset];
} else {
throw new InvalidArgumentException("Invalid argument provided for schimile::loadRuleset() (Ruleset object or name expected)");
}
return $instance->execute($input);
}
public static function getErrors()
{
return self::$_latestErrors;
}
private function _handleErrors()
{
self::$_latestErrors = $this->_errors;
return false;
}
private function _pushError($item, $message)
{
$this->_errors[$item][] = $message;
}
public function execute($input)
{
foreach ($this->_queue as $index => $testArrays) {
foreach ($testArrays as $testNr => $testBlock) {
switch ($testBlock['inputType']) {
case "input":
$value = &$input;
break;
case "val":
if (is_object($input)) {
$value =& $input->$testBlock['useIndex'];
} else {
$value =& $input[$testBlock['useIndex']];
}
break;
case "key":
$value = key($input);
break;
}
$matcher = !$testBlock['negate'];
array_unshift($testBlock['params'], $value);
if ($this->_isValidType($value, $testBlock['accepts'])) {
try {
$result = call_user_func_array($testBlock['validator'], $testBlock['params']);
if (!is_bool($result)) {
$value = $result;
$result = true;
}
} catch (ValidationFailException $e) {
$this->_haltOnError = true;
$result = null;
}
} else {
throw new InvalidInputTypeException($testBlock['accepts'], $value);
}
if ($result === $matcher) {
//pass
} else {
//fail
$this->_pushError($this->_currentIndex, $testBlock['message']);
if ($this->_haltOnError) {
break 2;
}
}
}
}
if (!empty($this->_errors)) {
if (is_null(self::$_errorHandler)) return $this->_handleErrors();
else return self::$_errorHandler($this->_errors);
}
return true;
}
private function _composite($params)
{
$not = $this->_not;
foreach ($params as $test) {
if (isset($test['negate']) && $test['negate']) {
$this->isNot();
} else {
$this->is();
}
$params = (empty($test['params'])) ? array() : $test['params'];
call_user_func_array(array($this, $test['test']), $params);
}
$this->_not = $not;
return $this;
}
public static function registerErrorHandler($fn)
{
if (is_callable($fn)) {
self::$_errorHandler = $fn;
} else {
throw new InvalidArgumentException("Invalid argument provided for schimile::registerErrorHandler() (Callable expetcer, "
. gettype($fn) . " received).");
}
}
/**
* Gets the currently loaded ruleset
* @return Array
*/
public static function getRuleset()
{
return self::$_ruleset;
}
public static function loadRuleset($ruleset)
{
if (is_object($ruleset)) {
self::$_ruleset[] = $ruleset;
end(self::$_ruleset[]);
return key(self::$_ruleset[]);
} elseif (is_array($ruleset)) {
self::$_ruleset = array_merge(self::$_ruleset, $ruleset);
} else {
throw new InvalidArgumentException("Invalid argument provided for schimile::loadRuleset() (Array or Object expected, "
. gettype($ruleset) . " received).");
}
}
private static function _init($type, $index = false)
{
$self = __CLASS__;
$self::$_currentInstance = new $self($type, $index);
}
private function _nextItem($type, $index = false)
{
$this->_inputType = $type;
$this->_queueIndex++;
$this->_updateIndex($index);
}
private function _updateIndex($index)
{
if ($index === false) {
$this->_currentIndex = $this->_nextIndex++;
} else {
$this->_currentIndex = $index;
}
}
public static function input()
{
self::_init("input");
return self::$_currentInstance;
}
public static function val($index = false)
{
self::_init("val", $index);
return self::$_currentInstance;
}
public function next($index = false)
{
$this->_updateIndex($index);
return $this;
}
public function arr()
{
$this->_pushToQueue(function($input) {
return is_array($input);
}, "{{name}} must be an array", "{{name}} can't be an array");
return $this;
}
public function top()
{
$this->_inputType = "input";
$this->_updateIndex(0);
return $this;
}
public function validPer($ruleset)
{
$this->_pushToQueue(function($input, $ruleset) {
$self = __CLASS__;
return $self::assert($ruleset, $input);
}, "null", "null", array($ruleset));
return $this;
}
public function is()
{
$this->_not = false;
return $this;
}
public function isNot()
{
$this->_not = true;
return $this;
}
public function noneOf()
{
$rulesets = func_get_args();
$this->_pushToQueue(function($input, $rulesets) {
$self = __CLASS__;
$status = false;
foreach ($rulesets as $ruleset) {
$status = ($status || $self::assert($ruleset, $input));
}
return !$status;
}, "null", "null", array($rulesets));
return $this;
}
public function allOf()
{
$rulesets = func_get_args();
$this->_pushToQueue(function($input, $rulesets) {
$self = __CLASS__;
$status = true;
foreach ($rulesets as $ruleset) {
$status = ($status && $self::assert($ruleset, $input));
}
return $status;
}, "null", "null", array($rulesets));
return $this;
}
public function oneOf()
{
$rulesets = func_get_args();
$this->_pushToQueue(function($input, $rulesets) {
$self = __CLASS__;
$status = false;
foreach ($rulesets as $ruleset) {
$status = ($status || $self::assert($ruleset, $input));
}
return $status;
}, "null", "null", array($rulesets));
return $this;
}
public function haltOnFail()
{
$this->_haltOnError = true;
return $this;
}
public function continueOnFail()
{
$this->_haltOnError = false;
return $this;
}
public function required()
{
if ($key = array_search($this->_currentIndex, $this->_optional) !== false) {
unset($this->_optional[$key]);
}
return $this;
}
public function optional()
{
$this->_optional[] = $this->_currentIndex;
return $this;
}
public function string()
{
//$this->_accept("string", $strict);
$this->_pushToQueue(function($input) {
return is_string($input);
}, "{{name}} must be a string", "{{name}} can't be a string");
return $this;
}
public function int()
{
$this->_pushToQueue(function($input) {
return is_int($input);
}, "{{name}} must be an integer", "{{name}} can't be an integer");
return $this;
}
public function atStart($param)
{
$this->_pushToQueue(function($input, $param) {
return strpos($input, $param) === 0;
}, "{{name}} must start with $param", "{{name}} can't start with $param", array($param));
return $this;
}
public function atEnd($param)
{
$this->_pushToQueue(function($input, $param) {
return substr($haystack, -strlen($needle)) === $needle;
}, "{{name}} must end with $param", "{{name}} can't end with $param", array($param));
return $this;
}
public function contains($param)
{
$this->_pushToQueue(function($input, $param) {
return strpos($input, $param) !== false;
}, "{{name}} must start with $param", "{{name}} can't start with $param", array($param));
return $this;
}
public function like($type, $keepType = true)
{
$this->_pushToQueue(function($input, $type) {
switch($type) {
case "float":
case "double":
$pattern = '/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/';
break;
case "int":
case "integer":
$pattern = '/^[-+]?[0-9]+$/';
break;
default:
throw new InvalidInputTypeException("Invalid comparison type provided for schimile->like() operator");
break;
}
if (preg_match($pattern, $input)) {
if ($keepType) return true;
if ($type === "float") return (float) $input;
return (int) $input;
} else {
if ($keepType) return false;
throw new ValidationFailException();
}
}, "{{name}} must be a valid $type", "{{name}} can't be a $type", array($type));
return $this;
}
public function likeRegex($pattern)
{
$this->_pushToQueue(function($input, $pattern) {
return (bool) preg_match($pattern, $input);
}, "{{name}} must match regex $pattern", "{{name}} can't match regex $pattern", array($pattern));
return $this;
}
public function numeric()
{
$this->_pushToQueue(function($input) {
return is_numeric($input);
}, "{{name}} must be numeric", "{{name}} can't be numeric");
return $this;
}
public function alnum()
{
$this->_pushToQueue(function($input) {
return ctype_alnum($input);
}, "{{name}} must consist of only letters and numbers", "{{name}} can't consist of just letters or numbers");
return $this;
}
public function charList($characters)
{
$this->_pushToQueue(function($input, $characters) {
$incl = array();
$pattern = preg_replace_callback('/([a-z]\-[a-z])|([0-9]\-[0-9])|([A-Z]\-[A-Z])/', function($matches) use (&$incl) {
$incl = array_merge($incl, $matches);
return "";
}, $characters);
$pattern = preg_quote($pattern);
$pattern = $pattern . implode("", array_unique($incl));
$pattern = '/^['.$pattern.']+$/';
return (bool) preg_match($pattern, $input);
}, "{{name}} can only contain the characters $characters", "{{name}} can't consist entirely of the characters $characters", array($characters));
return $this;
}
public function lengthBetween($min = 0, $max = null)
{
$this->_pushToQueue(function($input, $min, $max) {
$resultmin = false;
$resultmax = false;
if (strlen($input) >= $min) $resultmin = true;
if ($max === null || strlen($input) <= $max) $resultmax = true;
return $resultmin && $resultmax;
}, "{{name}} must be between $min and $max characters", "{{name}} can't be between $min and $max characters", array($min, $max));
return $this;
}
public function email()
{
$this->_pushToQueue(function($input) {
return (bool) preg_match('/^.+@.+\..+$/', $input);
}, "{{name}} must be a valid e-mail address", "{{name}} can't be an e-mail address");
return $this;
}
public function equalTo($comparison, $strict = true)
{
$this->_pushToQueue(function($input, $comparison, $strict) {
if ($strict) {
return $input === $comparison;
} else {
return $input == $comparison;
}
}, "{{name}} must be equal to $comparison", "{{name}} can't be $comparison", array($comparison, $strict));
return $this;
}
public function entropyMin($value)
{
$this->_pushToQueue(function($input, $value) {
$h=0;
$size = strlen($string);
foreach (count_chars($string, 1) as $v) {
$p = $v/$size;
$h -= $p*log($p)/log(2);
}
return $h >= $value;
}, "{{name}} must be equal to $comparison", "{{name}} can't be $comparison", array($value));
return $this;
}
}
preferences:
35.73 ms | 402 KiB | 5 Q