I need a php validator class that validates user inputs.
I want it to be able to accept an assoc array of fields => values like:
array(
"username" => "Alex",
"email_address" => "###3423£alex#my.mail.com"
);
and then return an array of errors like this:
array(
"username" => "",
"email_address" => "Invalid Email Address"
);
But I'm really struggling on HOW the hell I'm going to do this!
I've read countless pages on PHP validators and read that the best way to do this is with the strategy pattern. But i dont know how??
Like... This is what I've got so far:
class Validator {
private
$_errors,
$_fields,
static private $_map = array (
"firstname" => "name",
"surname" => "name",
"agency_name" => "name",
"agency_office" => "name",
"username" => "username",
"email_address" => "email_address",
);
public function __construct( array $fields ) {
$this->_fields = $fields;
}
public function validate() {
foreach ( $this->_fields as $field => $value ) {
if ( method_exists( __CLASS__, self::$_map[$field] ) ) {
if ( in_array( $field, self::$_map ) ) {
$this->{self::$_map[$field]}( $field, $value );
}
}
else {
die( " Unable to validate field $field" );
}
}
}
public function get_errors() {
return $this->_errors;
}
private function name( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z]{2,50}$/", $value ) ) {
$this->errors[$field] = "Invalid. Must be 2 to 50 alphanumerical characters";
}
}
private function username( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z0-9_\-]{10,50}$/", $value ) ) {
$this->errors[$field] = "Invalid. Must be 10 to 50 characters. Can contain digits, characters, _ (underscore) and - (hyphen)";
}
}
private function password( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z0-9\.\-]{8,30}$/", $value ) ) {
$this->_errors[$field] = "Invalid. Must be 8 to 30 characters. Can contain digits, characters, . (full stop) and - (hyphen)";
}
}
private function email_address( $field, $value ) {
if ( !filter_var( $value, FILTER_VALIDATE_EMAIL ) ) {
$this->_errors[$field] = "Invalid Email Address";
}
}
}
The problems with this is, it doesn't even consider database connections for like, already registered usernames,
Also is doesn't match passwords
I've just got coders block at the moment and its destroying me on the inside :(
Can anybody give a an explaination of the classes required and functions each class will need to do?
I really need the inputs and outputs to be in the format already explained though!
Thankyou Very Much Internet People!
As a part of the my MVC I have solved the same problem. I could give you a listing, but in a few lines try to describe how.
I got 3 base classes Form, Validator, Field, each of object of this classes configuring through one YAML file, structured somehow like this:
name: // field name
i18n: [ ru, en ] // is the field i18n
field:
class: Base // class to use for field
options: { specific_save: true } // options from available (defined in class)
attributes: { } // attributes, for HTML rendering
validator:
class: String // Class to validate with
options: { required: true, max: 100 } // options for validator
So, lets start with Form, when object is constructing the form takes the YAML file described above, and due to that configuration creates fields. Something like this:
// Imlement this function to configure form;
foreach ($this->_config as $f => $c)
{
$class = '\\Lighty\\Form\\Field\\' . (isset($c['field']['class']) && $c['field']['class'] ? $c['field']['class'] : 'Base');
$o = isset($c['field']['options']) && is_array($c['field']['options']) ? $c['field']['options'] : array();
$a = isset($c['field']['attributes']) && is_array($c['field']['attributes']) ? $c['field']['attributes'] : array();
$field = new $class($this, $o, $a);
$field->setName($f);
$class = '\\Lighty\\Form\\Validator\\' . (isset($c['validator']['class']) && $c['validator']['class'] ? $c['validator']['class'] : 'Base');
$o = isset($c['validator']['options']) && is_array($c['validator']['options']) ? $c['validator']['options'] : array();
$m = isset($c['validator']['messages']) && is_array($c['validator']['messages']) ? $c['validator']['messages'] : array();
$field->setValidator($validator = new $class($field, $o, $m));
if (isset($this->_options['default'][$f]))
{
$field->setValue($this->_options['default'][$f]);
}
if (isset($c['i18n']))
{
if (is_array($c['i18n']))
{
$field->setCultures($c['i18n']);
}
$field->setI18n((bool) $c['i18n']);
}
$this->addField($field);
So, now we have form with fields and validator for each field, then to validate I use this mechanism:
Form goes through each field, calling validate() method,
Field (got the binded value) call validate($value) method of binded Validator, passing the stored value. Inside this method Validator calls the validateOption() method, in which there is a simple switch for each options, for example:
switch ($o)
{
case 'required':
$valid = $state && trim($value) != '' || !$state;
break;
default:
return \warning(sprintf('Undefined validator option "%s" in %s validator class', $o, get_class($this->getField()->getValidator())), null);
}
Here you can see validating on required option. If I need more validators, I extend class of the Base validator, defined few more options, and redefine validateOption(), where in default statement of the option's switch put parent::validateOption(). So specified options validates in new class, and old one in base validator Class.
If there any questions... You're welcome.
Related
This is an extension of a question I asked earlier that was deemed to be unsafe practise, due to the use of eval(). So I went for another approach but I have run into a problem. I do not know how to convert it to a class. My attempt ends with an error when I try to use call_user_func_array. it can't find the function in the class. Can you give me some hint so I get going? Thanks!
The error message I get when I try to run my code is Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'testlength' not found or invalid function name but on all validation methods. This is what I don't understand. This is what I want help to understand why it does not work.
class ruleValidator
{
protected $postData = array();
protected $ruleSet = array();
var $exceptions = 'Å,Ä,Þ,å,ä,þ,Ø,Ö,Ð,ø,ö,ð,Æ,Ü,æ,ü,á,é,í,ñ,ó,ú,ü,Á,É,Í,Ñ,Ó,Ê,Ú,Ü,ß';
function __construct(){
$this->exceptions = explode(',',$exceptions);
}
function testlength($string,$threshold)
{
return strlen($string)<$threshold?
'Your %s is too short.': // TRUE
''; // FALSE
}
function testnumeric($string,$offset,$length,$switch=true)
{
if(is_numeric(substr($string,$offset,$length))===$switch)
{
return $switch?
'Your %s has to begin with a character.': // Truely TRUE
'Your %s is containing non numeric characters. Please enter only digits.'; // Falsely TRUE
}
}
function testemail($string)
{
return filter_var($string, FILTER_VALIDATE_EMAIL)?
'': // TRUE
'Your email is not in a valid form.'; // FALSE
}
function testpattern($string,$pattern='/^[0-9]{8,10}$/')
{
return preg_match($pattern, $string)?
'': // TRUE
'Your %s is entered incorrect. Please use the correct format when entering data.'; // FALSE
}
function testequalto($string1,$string2)
{
return $string1==$string2?
'': // TRUE
'Your %s fields do not match eachother.'; // FALSE
}
function testchecked($bool)
{
return $bool===true?
'': // TRUE
'You are required to check this %s to continue.'; // FALSE
}
function testspecchar($string,$excludes=array())
{
if(is_array($excludes)&&!empty($excludes))
{
foreach($excludes as $exclude)
{
$string=str_replace($exclude,'',$string);
}
}
if(preg_match('/[^a-z0-9 ]+/i',$string))
{
return 'Your %s contains illegal characters.'; // TRUE
}
return; // FALSE
}
}
This is an array with how the POST data is recieved in the validator and the rules I use for the different fields in the form.
$exceptions = explode(',','Å,Ä,Þ,å,ä,þ,Ø,Ö,Ð,ø,ö,ð,Æ,Ü,æ,ü,á,é,í,ñ,ó,ú,ü,Á,É,Í,Ñ,Ó,Ê,Ú,Ü,ß');
$postData = array
(
'name' => 'Mikael',
'familyname' => 'Eriksson`',
'username' => 'Mik',
'password' => 'testtest',
'password-confirm' => 'testtesty',
'email' => 'try.to#guess.it,se',
'phone' => '0000000000a',
'policy' => 0
);
$ruleSet = array
(
'name'=>array
(
'testlength'=>2,
'testnumeric'=>array(0,1),
'testspecchar'=>array($exceptions)
),
'familyname'=>array
(
'testlength'=>2,
'testnumeric'=>array(0,1),
'testspecchar'=>array($exceptions)
),
'username'=>array
(
'testlength'=>4,
'testnumeric'=>array(0,1),
'testspecchar'=>array()
),
'email'=>array
(
'testemail'=>array()
),
'phone'=>array
(
'testnumeric'=>array(0,strlen($postData['phone']),false),
'testpattern'=>'/^[0-9]{8,10}$/'
),
'password'=>array
(
'testlength'=>8
),
'password-confirm'=>array
(
'testequalto'=>$postData['password-confirm']
),
'policy'=>array
(
'testchecked'=>array()
)
);
Here is how I validated the data up until now. It works, but I want to make this to a class to streamline the code in my project.
foreach($postData as $key => $value)
{
if(!array_key_exists($key,$ruleSet))
{
$errors[] = "The field `$key` is not part of the form. Only send actual form data.";
break;
}
$slice = array($key=>$ruleSet[$key]);
foreach($slice as $rules => $rule)
{
foreach($rule as $rls => $r)
{
$r = array_merge((array)$value,(array)$r);
$errors[] = sprintf(call_user_func_array($rls,$r),$key);
}
}
}
if(count($errors)>0) return implode(';;',array_filter($errors,'strlen'));
When you want to call a method of a class, you have to make an instance (using new) or call them statically when the methods are declared static.
In both ways, you have to tell call_user_func_array() that you are not calling a function in the global scope, but from within a class.
call_user_func_array(array('ruleValidator', $rls), $r)
Then declare the functions static:
public static function testlength($string,$threshold) {
}
Or with new:
$slice = array($key=>$ruleSet[$key]);
$callbackClass = new ruleValidator();
foreach($slice as $rules => $rule)
/** ... */
call_user_func_array(array($callbackClass, $rls), $r)
Thanks #Deadooshka for providing me with the solution.
call_user_func_array("ruleValidator::$rls", $r)
I need to validate just one field (called 'Instance') to accept lowercase ASCII letters and numbers only, the first character also has to be a letter not a number. It will accept uppercase characters but we will need it to lowercase them on input. So if someone uses the instance name McDonalds it will be lowercased to mcdonalds (not just with CSS). Spaces are not allowed either.
Is this possible with CF7? If so please explain how.
I've already tried this custom validation method but even with the preset custom validation in the file it was just displaying the field shortcode rather than the field itself.
Thanks
From contactform7.com on Custom Validation → Validation as a Filter:
In Contact Form 7, a user-input validation is implemented as a filter
function. The filter hook used for the validation varies depending on
the type of form-tag and is determined as: wpcf7_validate_ + {type of
the form-tag}. So, for text form-tags, the filter hook
wpcf7_validate_text is used. Likewise, wpcf7_validate_email* is used
for email* form-tags.
Let’s say you have the following email fields in a form:
Email: [email* your-email]
Confirm email: [email* your-email-confirm]
The following listing shows code that verifies whether the two fields
have identical values.
add_filter('wpcf7_validate_email*', 'custom_email_confirmation_validation_filter', 20, 2);
function custom_email_confirmation_validation_filter($result, $tag) {
$tag = new WPCF7_FormTag($tag);
if ('your-email-confirm' == $tag->name) {
$your_email = isset($_POST['your-email']) ? trim($_POST['your-email']) : '';
$your_email_confirm = isset($_POST['your-email-confirm']) ? trim($_POST['your-email-confirm']) : '';
if ($your_email != $your_email_confirm) {
$result->invalidate($tag, "Are you sure this is the correct address?");
}
}
return $result;
}
Two parameters will be passed to the filter function: $result and
$tag. $result is an instance of WPCF7_Validation class that manages a
sequence of validation processes. $tag is an associative array
composed of given form-tag components; as you saw in the previous
recipe, you can use WPCF7_FormTag class to handle this type of data.
Look through the inside of the filter function. First, check the name
of the form-tag to ensure the validation is applied only to the
specific field (your-email-confirm).
The two email field values are then compared, and if they don’t match,
$result->invalidate() will be called. You need to pass two parameters
to the invalidate() method: the first parameter should be the $tag
variable, and the second parameter is the validation error message
that you want the field to display.
Lastly, don’t forget to return the $result.
// Add custom validation for CF7 form fields
function is_company_email($email){ // Check against list of common public email providers & return true if the email provided *doesn't* match one of them
if(
preg_match('/#gmail.com/i', $email) ||
preg_match('/#hotmail.com/i', $email) ||
preg_match('/#live.com/i', $email) ||
preg_match('/#msn.com/i', $email) ||
preg_match('/#aol.com/i', $email) ||
preg_match('/#yahoo.com/i', $email) ||
preg_match('/#inbox.com/i', $email) ||
preg_match('/#gmx.com/i', $email) ||
preg_match('/#me.com/i', $email)
){
return false; // It's a publicly available email address
}else{
return true; // It's probably a company email address
}
}
function your_validation_filter_func($result,$tag){
$type = $tag['type'];
$name = $tag['name'];
if('yourid' == $value){ // Only apply to fields with the form field name of "company-email"
$the_value = $_POST[$name];
if(!is_company_email($the_value)){ // Isn't a company email address (it matched the list of free email providers)
$result['valid'] = false;
$result->invalidate( $tag, wpcf7_get_message( 'invalid_email' ));
}
}
return $result;
}
add_filter( 'wpcf7_validate_email', 'your_validation_filter_func', 10, 2 );
// Email field or contact number field
add_filter( 'wpcf7_validate_email*', 'your_validation_filter_func', 10, 2 ); // Req. Email field or contact number
I had a similar issue for validating name fields, I added the following code in my functions.php, you could customize it by changing the regex
function my_wpcf7_validate_text( $result, $tag ) {
$type = $tag['type'];
$name = $tag['name'];
$value = $_POST[$name] ;
if ( strpos( $name , 'name' ) !== false ){
$regex = '/^[a-zA-Z]+$/';
$Valid = preg_match($regex, $value, $matches );
if ( $Valid > 0 ) {
} else {
$result->invalidate( $tag, wpcf7_get_message( 'invalid_name' ) );
}
}
return $result;
}
add_filter( 'wpcf7_validate_text*', 'my_wpcf7_validate_text' , 10, 2 );
add_filter( 'wpcf7_messages', 'mywpcf7_text_messages' );
function mywpcf7_text_messages( $messages ) {
return array_merge( $messages, array(
'invalid_name' => array(
'description' => __( "Name is invalid", 'contact-form-7' ),
'default' => __( 'Name seems invalid.', 'contact-form-7' )
)
));
}
Please use this wordpress plugin
Jquery Validation For Contact Form 7
https://wordpress.org/plugins/jquery-validation-for-contact-form-7/
You can add your own custom validation for a form field input by using the add_filter function.
For adding a custom validation for a textarea field you can add the following inside functions.php file in the root directory of your theme.
add_filter( 'wpcf7_validate_textarea*', 'custom_textarea_validation_filter', 1, 2 );
function custom_textarea_validation_filter( $result, $tag ) {
$tag = new WPCF7_Shortcode($tag);
$result = (object)$result;
$name = 'project-synopsis';
if ( $name == $tag->name ) {
$project_synopsis = isset( $_POST[$name] ) ? trim( wp_unslash( (string) $_POST[$name] ) ) : '';
if ( empty( $project_synopsis ) ) {
$result->invalidate( $tag, "Please write a quick project synopsis." );
}
}
return $result;
}
For me the trick was to cast the $result parameter to an object, because the invalidate method that is used to add the error message didn't work before casting.
Try this plugin. It's allow to set custom validation message for each field in free version.
URL : https://wordpress.org/plugins/cf7-custom-validation-message/
In Laravel 4, I have a class extending Eloquent and I need to record changes (to keep the history) at the time of saving.
Saving event in boot function is called as expected. The question is: how do I know which fields were changed and are about to be saved? Also, can I access existing values without loading the record again?
I know, one way could be to load the record again and compare all fields one by one. Is there any better optimized way to do that?
class Record extends Eloquent {
protected static function boot()
{
parent::boot();
static::saving(
function($record)
{
// It runs properly. This is where changes should be compared
return true;
}
);
}
}
Thank you.
Eloquent's getDirty() and getOriginal() did the trick:
static::saving(
function($record)
{
$dirty = $record->getDirty();
foreach ($dirty as $field => $newdata)
{
$olddata = $record->getOriginal($field);
if ($olddata != $newdata)
{
// save changes from $olddata to $newdata
}
}
return true;
}
);
I don't think accepted answer is the best way to do that. Why would you loop through all changed fields to figure out what has changed? Laravel has this two Model methods that can help you better,
on save function/route/controller whichever you use
$model::find(X);
...
'''
$model->fill(Input::all());
if( !empty($model->getDirty() )
// Get status of model. if nothing has changed there is no need to get in here
{
if($model->isDirty( { mixed data (array,string,null) } ))
// isDirty() will help you to find out if the field is different than original
{
.....
enter code here
....
}
}
You can use events:
Event::listen('saving', function($model)
{
// Do whatever you need to do with your $model before saving or not:
return true;
});
You can also hook that saving method to any controller action:
Event::listen('saving', 'LogThings#saving');
I have something like this in my project.
Record::updating(function($record){
$original = $record->getOriginal();
foreach($original as $index => $value){
if($index != 'updated_at' AND $index != 'created_at'){
if($value != $record[$index]){
RecordUpdate::create(array('record_id' => $record->id, 'fieldname' => $index, 'old_value' => $value, 'new_value' => $record[$index], 'created_at' => date('Y-m-d H:i:s')));
}
}
}
});
The Eloquent class that you're extending has getOriginal() and getAttributes() methods to get the originally-hydrated properties and the currently set properties, respectively.
So, in your saving event you can do something like...
if ( ! empty( array_diff( $record->getOriginal(), $record->getAttributes() ) ) )
{
//log changes
}
If you want to do this for all/most of your Models, I suggest abstracting this out to a generic Model Observer:
////
/* Subclassable, generic Model Observer */
////
class ModelObserver {
public function saving($model)
{
if ( ! empty( array_diff( $record->getOriginal(), $record->getAttributes() ) ) )
{
//log changes
}
}
}
////
/* Set up observers, for example in start/global.php */
////
$modelObserver = new ModelObserver;
Comment::observe($modelObserver);
Post::observe($modelObserver);
User::observe($modelObserver);
I have this class
class Validator implements iValidation{
protected
$_fields,
$_errors;
public function __construct($fields){
$this->_errors = array();
$this->_fields = $fields;
}
public function validate(){
$map = unserialize( iValidation::map );
foreach ($this->_fields as $field_type => $data){
if ( array_key_exists( $field_type, $map ) ){
$class = "Validate_{$map["$field_type"]}" ;
$object = new $class($data);
$object->validate();
unset($object);
}
}
}
public function getErrors(){
return $this->_errors;
}
}
Now this class loops through an array given in the format
$admin_test_data = array(
"firstname" => "Alex1",
"surname" => "Morley-Finch",
"username" => "alex123",
"password" => "cheese",
"re_password" => "cheese",
"email_address" => "alex54353hotmail.co.uk",
"user_type" => "ADMIN",
"add_admin" => "Add Admin",
);
I have a map that describes the type of validation on each field that is declared like so:
define(
"VALIDATION_MAP",
serialize(
array(
// FIELD => VALIDATION TYPE
"firstname" => "Name",
"surname" => "Name",
"agency_name" => "Agency_Name",
"agency_office" => "Name",
"username" => "Username",
"email_address" => "Email_Address",
"password" => "Password",
)
)
);
interface iValidation{
public function __construct($data);
public function validate();
const map = VALIDATION_MAP;
}
And I have sub classes One of which is like this:
class Validate_Name extends Validator implements iValidation{
private $_data;
public function __construct($data){
$this->_data = $data;
}
public function validate(){
$data = $this->_data;
$length = strlen($data);
if ( $length > 40 ){
$this->_errors[] = "Cannot be more than 40 characters";
}
if ( $length < 3 ){
$this->_errors[] = "Cannot be less than 3 characters";
}
if ( !preg_match("/^[a-zA-Z]+$/", $data) ){
$this->_errors[] = "A-Z characters only";
}
}
}
And the whole framework is used like so:
$validator = new Validator($admin_test_data);
$validator->validate(); // ^ defined earlier ^^^
$errors = $validator->getErrors();
Now after all that background info... lets get to the problem.
I'm probably doing some EXTREMELY stupid but I can't figure it out and Google isn't doing a good job of enlightening me ( or I'm not doing a good job of asking google )
The sub classes reference $this->_errors[] = "This is the error"; and when using die( var_dump( $this->_errors ) ); inside the sub class, the errors for that class appear as they should.
However if I call die( var_dump( $validator->getErrors() ) ); outside the classes, the array is empty.
Asif the parent has forgotten all the values the children set?
Whats going on?
For each field you validate in Validator, you create an instance of the required implementation of iValidation.
Now the child validator validates, and it finds errors. It adds those errors to its _errors instance attribute.
When it's done validating, the child instance is discarded. And that's where the problem lies, since the errors found were assigned to the child instance's _errors attribute, not that of the parent instance.
Nothing ever gets assigned to Validator::_errors, because those properties are in different objects.
I'm parsing the HTTP_ACCEPT_LANGUAGE header to get users' language and I'm building a class to do that.
Actually I build an associative array ("$this->user_lang") where keys are the language (such as "en-us", "it-it", "it-ch" etc) and the value is the quality factor (so I can order languages).
Then I have another associative array named "$this->installed_langs" where I declare supported language and locales (in the form "en" => "en_US", "it" => "it_IT").
All I want to do is try to match one of the key of "$this->user_lang" with one of the "$this->installed_langs" (without care of the local zone after the "-") and return the first occurrence (with no care for other matching case).
I ended up with this method but it seems a bit too complex...
public function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
foreach($this->installed_langs as $valid => $locale) {
if (strpos($lang, $valid) !== false) {
if ($g_locale === null) $g_locale = $locale;
}
}
}
// debug:
echo $g_locale;
}
I hope I have explained it well, btw if you need more informations, please, ask me.
Try this
public function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
if ( array_key_exists( $lang, $this->installed_langs ) ) {
$g_locale = $this->installed_langs[$lang];
}
}
}
function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
$_key=explode($lang, '-'); // 'en-us' => 'array('en', 'us')
$key=$_key[0]; // 'en'
if ( array_key_exists( $key, $this->installed_langs ) ) {
$g_locale = $this->installed_langs[$key];
}
}
}