Using PHP filter functions like filter_var_array () there is a way to check if the length of the input string does not exceed some value
I have been playing around with the PHP filter library. I liked it, but I can't seem to execute a simple filter function. I essentially want to invalidate those values ββin my input array that are strings and are longer than a certain value. Is there a way to do this, for example
$data = array('input_string_array' => array('aaa', 'abaa', 'abaca'));
$args = array(
'component' => array('filter' => FILTER_DEFAULT,
'flags' => FILTER_REQUIRE_ARRAY,
'options' => array('min_length' => 1, 'max_length' => 10)
)
);
var_dump(filter_var_array($data, $args));
I tried this and it gave me an error. because presumably there is no min_length / max_length option. But then how to implement it? There is also a place that mentions all the parameters like max_range, min_range, regexp.
Also I had another doubt about the filters in the FILTER_CALLBACK filter. I wanted to know if there is a way to pass a parameter other than data to the called function? something like that,
echo filter_var($string, FILTER_CALLBACK, array("options"=> array("lengthChecker", "5")));
Many thanks for the help.
source to share
If there is no better, more direct filter +, you can use FILTER_VALIDATE_REGEXP
$data = array('input_string_array' => array('', 'aaa', 'abaa', 'abaca'));
$args = array(
'input_string_array' => array(
'filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_ARRAY|FILTER_NULL_ON_FAILURE,
'options' => array('regexp'=>'/^.{1,3}$/')
)
);
var_dump(filter_var_array($data, $args));
prints
array(1) {
["input_string_array"]=>
array(4) {
[0]=>
NULL
[1]=>
string(3) "aaa"
[2]=>
NULL
[3]=>
NULL
}
}
To get rid of NULL elements, you can use eg. array_filter () .
source to share
Introduction
You will need at least PHP 5.4+ for this answer (due to at least array syntax).
If the goal is to do something like this:
$filteredArray = filter_var_array($arrayToFilter, $filterInstuctionsArray);
... and use the return results of the main PHP functions (along with the decision logic) to check the length of the string ( mb_strlen()
, strlen()
) of the string, the key must have different Array filter commands. To pass arguments to your callback function (for reuse, encapsulation and generalization purposes, etc.), I believe there are at least two scenarios.
Scripts
A ) Forms of class / object.
'options' => [$this, 'callbackMethod']
'options' => [$this->object, 'callbackMethod']
'options' => [$object, 'callbackMethod']
B ) Procedural form.
'options' => 'callbackFunction'
Decision
1 ) Replace either in script A with an instance of an object right then and there, passing its constructor in any arguments. This seems unlikely as it attacks tightly coupled code. $this
$object
new
Alternatively, one would be able to inject the prepopulated object into a class of a Validator
specific type and run filter_input_array()
from that class Validator
. Thus, passing arguments to callbackMethod
is unnecessary.
or
2 ) Replace in script B with PHP Anonymous Function and implement syntax for passing class arguments / constraints . 'callbackFunction'
use
Validator
function ($value) use ($min, $max) {
$length = mb_strlen($value, 'UTF-8');
return ($length >= $min) && ($length <= $max);
}
Attempt # 1: filter command with anonymous functions
Here is a step-by-step example of working with scalar values.
$filterInstructionsArray[
'fName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => function ($value) use ($min, $max) {
$length = mb_strlen($value, 'UTF-8');
return ($length >= $min) && ($length <= $max);}],
'lName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => function ($value) use ($min, $max) {
$length = mb_strlen($value, 'UTF-8');
return ($length >= $min) && ($length <= $max);}]
];
Of course, this violates DRY principles . So you can define an anonymous function as a property of a class Validator
(or just assign it to a variable), making it a named instance of an object Closure
.
private $checkNameLength = function ($value) use ($this->nameMin, $this->nameMax) {
$length = mb_strlen($value, 'UTF-8');
return ($length >= $this->nameMin) && ($length <= $this->nameMax);
};
or
$checkNameLength = function ($value) use ($min, $max) {
$length = mb_strlen($value, 'UTF-8');
return ($length >= $min) && ($length <= $max);
};
So, I hope one of two things will work.
Attempt # 2: Array filter command with named anonymous functions
$filterInstructionsArray[
'fName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => [$this, 'checkNameLength']]
];
or
$filterInstructionsArray[
'fName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => 'checkNameLength']
];
Potential complications
Closure
instances $this->checkNameLength
and $checkNameLength
are objects, not regular "methods / functions". But, if PHP is not complaining, I will not tear my hair. I am guessing one can try to define a function inside an anonymous function.
$checkName = function ($value) use ($min, $max) {
function lengthTest($string, $min, $max){
$length = mb_strlen($string, 'UTF-8');
return ($length >= $min) && ($length <= $max);
}
};
Then your array of filter commands will look like this.
$filterInstructionsArray[
'fName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => [$checkName, 'lengthTest']]
];
Or perhaps it is ...
$filterInstructionsArray[
'fName' = ['filter' => FILTER_CALLBACK,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => 'lengthTest']
];
Conclusion
There is a better way to create a generic string length checker than using PHP filter_var_array()
and anonymous functions. Usage 'filter' => FILTER_VALIDATE_REGEXP
may make it easier to check the length of a single string, but this does not replace DRY principles. You end up with many operators in your array of filter instructions, just to handle the lengths of the strings in the input array.
//If you just want to test only the lengths first, this is
//very inefficient. Assume each $regex is only checking string length.
$filterLengthInstructions = [
'fName' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $fNameRegex]],
'lName' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $lNameRegex]],
'company' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $comanyRegex]],
'address1' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $address1Regex]],
'address2' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $address2Regex]],
'zip' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $zipRegex]],
'website' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $urlRegex]],
'email' => ['filter' => FILTER_VALIDATE_REGEXP,
'flags' => FILTER_REQUIRE_SCALAR,
'options' => ['regexp' => $emailRegex]]
];
This can be done if you are trying to create a pure class filter_input_array()
Validator
or subroutine. But you'll have to do multiple passes filter_var_array()
, with multiple arrays of filter commands (because the length of a string isn't the only thing that makes it valid or invalid).
For website
and email
you will want to use 'filter' => FILTER_VALIDATE_URL
and 'filter' => FILTER_VALIDATE_EMAIL.
. In other cases, you will want to take advantage of 'filter' => FILTER_VALIDATE_IP
. Also, especially in case email
, you can restrict valid email addresses to a subset of the official regex RFCs. To keep it clean, you would not put this regex in $filterLengthInstructions
.
The underlying causes in order to change min_length
and max_length
the ability to write business logic once and use it everywhere.
At a minimum, my advice is to create an abstract class Validator
and define one (1) method that checks the length of strings.
Now you can split this method or delegate this task to the injected method of the object StringTester
.
It doesn't matter, but by defining concrete child classes Validator
, all you have to do is define your test parameters in an array, create a loop, and call:
$this->testString($string, $min, $max, $pattern, $errorMessage);
or
$this->stringTester->testString($string, $min, $max, $pattern, $errorMessage);
... inside the loop. Be sure to consider $ errorMessage.
abstract Class Tester
{
}
class StringTester extends Tester
{
private function testString($string, $min, $max, $pattern, &$errorMessage)
{
$length = mb_strlen($string, 'UTF-8');
if($length < $min) //Test string against minimum length.
{
$errorMessage = 'Too small! ('.$min.' min, ' .$length. ' given.)';
}
elseif($length > $max) //Test string against maximum length.
{
$errorMessage = 'Too large! ('.$max.' max, ' .$length. ' given.)';
}
elseif(preg_match($pattern, $string) === 0) //Test string pattern.
{
$errorMessage = 'Invalid string format!';
}
else
{
$errorMessage = ''; //The error message is the empty string.
}
return;
}
}
abstract Class Validator
{
//Arrays
protected $inputArray;
protected $errorMessagesArray = [];
protected $stringTestRulesArray; //I know. I know. :-)
//Objects
protected $stringTester;
//Abstract functions
abstract public function validate();
public function __construct(Tester $stringTester, array $inputArray, array $stringTestRutlesArray)
{
$this->stringTester = $stringTester;
$this->inputArray = $inputArray;
$this->stringTestRulesArray = $stringTestRulesArray
}
public function getInput()
{
return $this->inputArray;
}
public function getErrorMessages()
{
return $this->errorMessagesArray();
}
protected function validateStrings()
{
//Notice how input values correspond to error message elements via $key.
foreach($this->stringTestRulesArray as $key = $valuesArr)
{
$this->stringTester->testString($this->inputArray[$key], $valuesArr['min'], $valuesArr['max'], $valuesArr['pattern'], $this->errorMessagesArray[$key]);
}
return;
}
}
class ContactValidator extends Validator
{
public function __construct(Tester $stringTester, Sanitizer $sanitizer)
{
$stringTestRulesArray = [
'fName' => ['min' => 1, 'max' => 25, 'pattern' => '/[A-Za-z\' -]/'],
'lName' => ['min' => 1, 'max' => 40, 'pattern' => '/[A-Za-z\' -]/']
];
parent::__construct($stringTester, $sanitizer->getInput(), $stringTestRulesArray);
}
public function validate()
{
$this->validateStrings();
//Other, contact form specific validation stuff.
}
}
class RegisterValidator extends Validator
{
public function __construct(Tester $stringTester, Sanitizer $sanitizer)
{
$stringTestRulesArray = [
'fName' => ['min' => 1, 'max' => 30, 'pattern' => '/[A-Za-z\' -]/'],
'lName' => ['min' => 1, 'max' => 45, 'pattern' => '/[A-Za-z\' -]/']
];
parent::__construct($stringTester, $sanitizer->getInput(), $stringTestRulesArray);
}
public function validate()
{
$this->validateStrings();
//Other, register form specific validation stuff.
}
}
source to share