prewritten input sanitization classes???
Posted: Wed Oct 26, 2005 6:55 pm
does any one know of any good ones? free or not free? thanks.
A community of PHP developers offering assistance, advice, discussion, and friendship.
http://forums.devnetwork.net/
Check code snippets, I wrote a simple validation class, Jcart added to it, and perhaps others have since...fendtele83 wrote:does any one know of any good ones? free or not free? thanks.
Code: Select all
$suspect_array = array('foo'=>'bar');
$validator =& new ArrayValidator;
$validator->setRuleAt('foo', new FooRule);
$validator->setTarget($suspect_array);
$foo = $validator->getValidated('foo');Code: Select all
class Rule
{
function isValid() {}
function getError() {}
}Code: Select all
/*
Validate array values & keys.
Support for:
- arrays up to 2D
- alien key checks
- missing key checks & optional keys
- optional form fields (which don't have to validate)
*/
class ArrayValidator
{
var $value_errors = array();
var $reqval_errors = array(); // indexes whose values are required
// to validate but did not
var $missing_keys = array();
var $alien_keys = array();
var $unvalidated = array(); // any keys which were not checked
var $_allowed_keys = array();
var $_specs = array();
var $_target = array();
/*
param (array) target array index
param (array) an array of Rule objects
param (bool) $req_exists: is $index required to exist in the
target array or is it an optional key?
param (bool) $req_val: is the value required to validate if
the target array is to be considered valid.
*/
function setRulesAt($index, &$rules, $req_exists = true, $req_val = true)
{
$spec =& $this->_getAt('_specs', $index);
$spec['rules'] =& $rules;
$spec['req_exists'] = $req_exists;
$spec['req_val'] = $req_val;
if( !in_array($index, $this->_allowed_keys)) {
$this->_allowed_keys[] = $index;
}
}
/*
param (string) target array index [edit: type should be "array"]
param (object) a Rule object
param (bool) $req_exists: is $index required to exist in the
target array or is it an optional key?
param (bool) $req_val: is the value required to validate if
the target array is to be considered valid.
*/
function setRuleAt($index, &$rule, $req_exists = true, $req_val = true)
{
$spec =& $this->_getAt('_specs', $index);
$spec['rules'][] =& $rule;
$spec['req_exists'] = $req_exists;
$spec['req_val'] = $req_val;
if(!in_array($index, $this->_allowed_keys)) {
$this->_allowed_keys[] = $index;
}
}
function &_getAt($property, $index)
{
$p =& $this->$property;
if(count($index) == 1) {
return $p[$index[0]];
}
if(count($index) == 2) {
return $p[$index[0]][$index[1]];
}
}
/*
param (array)
*/
function setTarget($target)
{
$this->_target = $target;
$this->value_errors = array();
$this->reqval_errors = array();
$this->missing_keys = array();
$this->unvalidated = array();
$this->alien_keys = array();
$this->_alienKeyCheck();
}
function _alienKeyCheck()
{
foreach(array_keys($this->_target) as $target_key_1st_dim) {
if(is_array($this->_target[$target_key_1st_dim])) {
foreach(array_keys($this->_target[$target_key_1st_dim])
as $target_key_2nd_dim) {
$this->_checkIndex(array(
$target_key_1st_dim,
$target_key_2nd_dim));
}
} else {
$this->_checkIndex(array($target_key_1st_dim));
}
}
}
function _checkIndex($index)
{
if( !in_array($index, $this->_allowed_keys)) {
$this->_logAlienKey($index);
}
}
/*
param (array) a target array index
return (mixed) value at $index if it validates; null if not
*/
function getValidated($index)
{
$spec =& $this->_getAt('_specs', $index);
$suspect = $this->_getTargetValueAt($index, $spec['req_exists']);
if( !is_null($suspect)) {
$validated = $this->_validateValue(
$spec,
$index,
$suspect);
} else {
$validated = null;
}
return $validated;
}
function _getTargetValueAt($index, $req_exists)
{
if( count($index) == 1
and array_key_exists($index[0], $this->_target)) {
return $this->_target[$index[0]];
}
if( count($index) == 2
and array_key_exists($index[0], $this->_target)
and array_key_exists($index[1], $this->_target[$index[0]])) {
return $this->_target[$index[0]][$index[1]];
}
if($req_exists) {
$this->_logMissingKey($index);
}
return null; #!! values === null not supported
}
function _validateValue(&$spec, $index, $suspect)
{
if( !is_array($spec['rules'])) {
return null;
}
$errors = array();
foreach(array_keys($spec['rules']) as $key) {
if( !$spec['rules'][$key]->isValid($suspect)) {
$errors[] = $spec['rules'][$key]->getError();
}
}
if( !count($errors)) {
return $suspect;
} else {
$this->_logValueErrors($errors, $index);
if($spec['req_val']) {
$this->_logRequiredToValidateError($index);
}
return null;
}
}
function _logAlienKey($index)
{
$this->alien_keys[] = $index;
}
function _logMissingKey($index)
{
$this->missing_keys[] = $index;
}
function _logValueErrors($errors, $index)
{
$ref =& $this->_getAt('value_errors', $index);
$ref = $errors;
}
function _logRequiredToValidateError($index)
{
$this->reqval_errors[] = $index;
}
/*
return (bool)
*/
function hasValueErrors()
{
return (bool)count($this->value_errors);
}
/*
return (bool)
*/
function hasRequiredValueErrors()
{
return (bool)count($this->reqval_errors);
}
/*
return (bool)
*/
function hasMissingKeys()
{
return (bool)count($this->missing_keys);
}
/*
return (bool)
*/
function hasAlienKeys()
{
return (bool)count($this->alien_keys);
}
/*
return (array)
*/
function getMissingKeys()
{
return $this->missing_keys;
}
/*
return (array)
*/
function getAlienKeys()
{
return $this->alien_keys;
}
/*
return (array)
*/
function getRequiredValueErrors()
{
return $this->reqval_errors;
}
/*
return (array)
*/
function getValueErrors()
{
return $this->value_errors;
}
/*
param (array)
return (array)
*/
function getValueErrorsAt($index)
{
$errors = array();
if(count($index) == 1) {
if(array_key_exists($index[0], $this->value_errors)) {
$errors = $this->value_errors[$index[0]];
}
}
if(count($index) == 2) {
if(array_key_exists($index[0], $this->value_errors)
and array_key_exists($index[1],
$this->value_errors[$index[0]])) {
$errors = $this->value_errors[$index[0]][$index[1]];
}
}
return $errors;
}
}Code: Select all
class TestOfArrayValidator extends UnitTestCase
{
function TestOfArrayValidator()
{
$this->UnitTestCase();
}
//
// behaviours when no specs are set
//
function testGetValidatedAlwaysReturnsNull()
{
$index_0 = array(0);
$value_0 = 'foo';
$index_1 = array(1);
$value_1 = 'alien';
$target = array($value_0, $value_1);
$validator =& new ArrayValidator;
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->getValidated($index_1), null);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), true);
$this->assertIdentical(
$validator->getAlienKeys(),
array($index_0, $index_1));
}
function testNoMissingKeyErrors()
{
// above
}
function testAllKeysAreLoggedAsAlien()
{
// above
}
//
// 1st dim keys/values
//
function testCanDetectAlienKey()
{
$index_0 = array(0);
$value_0 = 'foo';
$index_1 = array(1);
$value_1 = 'alien';
$target = array($value_0, $value_1);
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', true);
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule);
$validator->setTarget($target);
$this->assertIdentical(
$validator->getValidated($index_0),
$value_0);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), true);
$this->assertIdentical(
$validator->getAlienKeys(),
array($index_1));
}
function testCanDetectMissingKey()
{
$index_0 = array(0);
$value_0 = 'foo';
$target = array();
$rule =& new MockExtendedRule();
$rule->expectNever('isValid');
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, true);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), true);
$this->assertIdentical(
$validator->getMissingKeys(),
array($index_0));
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testNoMissingKeyErrorWithAbsentOptionalKey()
{
$index_0 = array(0);
$value_0 = 'foo';
$target = array();
$rule =& new MockExtendedRule();
$rule->expectNever('isValid');
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, false);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testCanDetectInvalidValue()
{
$index_0 = array(0);
$value_0 = 'foo';
$target = array($value_0);
$error_0 = 'aargh!';
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', false);
$rule->expectOnce('getError');
$rule->setReturnValue('getError', $error_0);
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), true);
$this->assertIdentical(
$validator->getValueErrors(),
array(array($error_0)));
$this->assertIdentical($validator->getValueErrorsAt(
$index_0),
array($error_0));
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
true);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array($index_0));
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testHaveErrorWithInvalidValueRequiredToValidate()
{
// above
}
function testNoRequiredValueErrorWithInvalidValueNotRequiredToValidate()
{
$index_0 = array(0);
$value_0 = 'foo';
$target = array($value_0);
$error_0 = 'aargh!';
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', false);
$rule->expectOnce('getError');
$rule->setReturnValue('getError', $error_0);
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, true, false);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), true);
$this->assertIdentical(
$validator->getValueErrors(),
array(array($error_0)));
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array($error_0));
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
//
// 2nd dim keys/values
//
function testCanDetect2ndDimAlienKey()
{
$index_0 = array(0, 0);
$value_0 = 'foo';
$index_1 = array(0, 1);
$value_1 = 'alien';
$target = array(array($value_0, $value_1));
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', true);
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule);
$validator->setTarget($target);
$this->assertIdentical(
$validator->getValidated($index_0),
$value_0);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), true);
$this->assertIdentical(
$validator->getAlienKeys(),
array($index_1));
}
function testCanDetect2ndDimMissingKey()
{
$index_0 = array(0, 0);
$value_0 = 'foo';
$target = array();
$rule =& new MockExtendedRule();
$rule->expectNever('isValid');
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, true);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), true);
$this->assertIdentical(
$validator->getMissingKeys(),
array($index_0));
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testNoMissingKeyErrorWithAbsentOptional2ndDimKey()
{
$index_0 = array(0, 0);
$value_0 = 'foo';
$target = array();
$rule =& new MockExtendedRule();
$rule->expectNever('isValid');
$rule->expectNever('getError');
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, false);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), false);
$this->assertIdentical($validator->getValueErrors(), array());
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array());
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testCanDetect2ndDimInvalidValue()
{
$index_0 = array(0, 0);
$value_0 = 'foo';
$target = array(array($value_0));
$error_0 = 'aargh!';
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', false);
$rule->expectOnce('getError');
$rule->setReturnValue('getError', $error_0);
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), true);
$this->assertIdentical(
$validator->getValueErrors(),
array(array(array($error_0))));
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array($error_0));
$this->assertIdentical($validator->hasRequiredValueErrors(), true);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array($index_0));
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
function testHaveErrorWithInvalid2ndDimValueRequiredToValidate()
{
// above
}
function testNoErrorWithInvalid2ndDimValueNotRequiredToValidate()
{
$index_0 = array(0, 0);
$value_0 = 'foo';
$target = array(array($value_0));
$error_0 = 'aargh!';
$rule =& new MockExtendedRule();
$rule->expectOnce('isValid', array($value_0));
$rule->setReturnValue('isValid', false);
$rule->expectOnce('getError');
$rule->setReturnValue('getError', $error_0);
$validator =& new ArrayValidator;
$validator->setRuleAt($index_0, $rule, true, false);
$validator->setTarget($target);
$this->assertIdentical($validator->getValidated($index_0), null);
$this->assertIdentical($validator->hasValueErrors(), true);
$this->assertIdentical(
$validator->getValueErrors(),
array(array(array($error_0))));
$this->assertIdentical(
$validator->getValueErrorsAt($index_0),
array($error_0));
$this->assertIdentical(
$validator->hasRequiredValueErrors(),
false);
$this->assertIdentical(
$validator->getRequiredValueErrors(),
array());
$this->assertIdentical($validator->hasMissingKeys(), false);
$this->assertIdentical($validator->getMissingKeys(), array());
$this->assertIdentical($validator->hasAlienKeys(), false);
$this->assertIdentical($validator->getAlienKeys(), array());
}
}Code: Select all
/*
return (bool)
*/
function hasValidSyntax()
{
if( !$this->_GET_validator->hasAlienKeys()
and !$this->_GET_validator->hasMissingKeys()
and !$this->_GET_validator->hasValueErrors()
and !count($_POST)
and !count($_COOKIE)
and !count($_FILES)
return true;
} else {
return false;
}
}Code: Select all
function &_getAt($property, $index)
{
$p =& $this->$property;
if(count($index) == 1) {
return $p[$index[0]];
}
if(count($index) == 2) {
return $p[$index[0]][$index[1]];
}
}Code: Select all
$suspect_array = array('foo'=>'bar');
$validator =& new ArrayValidator;
$validator->setRuleAt('foo', new FooRule);
$validator->setTarget($suspect_array);
$foo = $validator->getValidated('foo');Code: Select all
$spec =& $this->_getAt('_specs', $index);Well said. I'm going to love my unit tests even more.The comments for setRuleAt() are misleading: I've listed the parameter type as a string when it should have said "array". This is maybe a good example of tests as documentation: comments can get out of sync with the code but unit tests never lie. It's also an example of sloppiness on my part. When you're close to something it's easy to forget all the assumptions you're making.
Since one-dimensional arrays are going to be what the class will mostly be dealing with, I think the convenience is worth the cost of consistency. However, the cost may be a bit higher than you'd think since the functions that give information about failed keys and whatnot always return the index as an array, even when it's one dimensional. I have half the mind to scrap the two-dimensional support altogether (and simply chain the ArrayValidators together--not sure how that would work though).An array parameter was chosen to support two dimensional target arrays (eg checkbox options) - you can pass either one or two values in a single arg. You could either (a) always pass arrays for consistency or (b) accept arrays and strings at the cost of a line or two of extra code to deal with different types.