I'm just wondering if anyones got 5 minutes to take a look over some code I've written today.

I spent a couple of hours over the weekend reading about OO development and wrote the beginnings of a validation class for user submitted forms.

At the moment there are two classes one to validate the content passed from the form and one to generate error messages based on the ValidateForm.

I think it could be tidied up a helluva alot more but would really welcome another viewpoint of how I could strengthen this.

Please be gentle though, this is the first bit of OO dev I've done and I know in bits it will be fairly wide of the mark.

    I only had a look at one of the files. The class itself is ok. However I am a bit concerned with how things are happening. This class doesnt seem very adaptable for other situations or big forms.

    What I saw was the constructor was actually making sure the object vars were set to a predefined set e.g. max characters. I guess for different form inputs this would change, and if so wouldnt it be best to make that an argument for the checking function?

    I didnt really see much in the error validation class, but at a quick glance it wasnt too bad.

      At the moment the ValidateForm class has 4 methods - NotEmpty, MaxLength, MinLength and RegEx. These were initially created as these are the basic things I need to test against. There are a few other methods I will add to this at a later date.

      As such I don't think there is anything fundamentally amiss with the class itself.

      What I do have a problem with at the moment is the way I am initiating the objects in the req_acc.php which is contained within the zip.

      Also how could I make the class more adaptable for instance if I then wanted to add a login class which used the ValidateForm class to ensure the user was submitting expect values.

      Any illustrated examples would be great!

        firstname = new ValidateForm('firstname', $_POST['firstname'], '100', '', 'aZ');
        	$surname = new ValidateForm('surname', $_POST['surname'], '100', '', 'aZ');
        	$company = new ValidateForm('company', $_POST['company'], '100', '', 'text');
        	$address1 = new ValidateForm('address1', $_POST['address1'], '100', '', 'text');
        	$address2 = new ValidateForm('address2', $_POST['address2'], '100', '', 'text');
        	$address3 = new ValidateForm('address3', $_POST['address3'],  '100', '', 'text');
        	$town = new ValidateForm('town', $_POST['town'], '100', '', 'text');
        	$postcode = new ValidateForm('postcode', $_POST['postcode'], '100', '6', 'postcode');
        	$tel = new ValidateForm('tel', $_POST['tel'], '12', '', 'phone');
        	$email = new ValidateForm('email', $_POST['email'], '100', '', 'email');
        

        Hmm Creating a new object all the time doesnt really seem adaptable to me and also the data maybe different as well.

        Say for example you may require Number Data only. Although the regex is there i dont think its making it extremely useful.

        But like i said creating all these objects to me is just useless. With that also you'll find some data will need different checking than other data. However with this setup its currently all getting checked.

        
        class formValidation {
        
        /* this is just a basic example */
        
          function checkEmpty($string,$length) {
              if (empty($string)) {
                 return false;
              } else if (strlen($string)<$length) {
                 return false;
              } else {
                 return true;
              }
          }
        
           function numeric($string) {
              return (is_numeric($string))? true : false;
           }
        
        }
        
        

        As i said basic example its not probably what id use for production.

          Ok had a rethink about how things were occuring.

          Would appreciate any more comments on how I could tidy things up more and make them flexible.

          The class now looks like this:

          <?php
          
          class formValidation {
          
          // Set Default Variables
          var $errors; # Error Reporting
          
            // Check for an empty string
            function isEmpty($field, $string) 
            {
            	if(empty($string)) {
          		$this->errors[] = 'The '.$field.' is empty.';
          	} else {
          		return true;
          	}
            }
          
            // Check for a string is a string
            function isString($field, $string) 
            {
            	if(is_string($string)) {
          		return true;
          	} else {
          		$this->errors[] = 'The '.$field.' is not a string.';
          	}
            }
          
            // Check a string is numeric
            function isNumeric($field, $string) 
            {
          	 if(is_numeric($string)) {
          	 	return true;
          	 } else {
          		$this->errors[] = 'The '.$field.' is not numeric.';
          	 }
            }
          
            // Compare 2 strings
            function strCompare($field, $string1, $string2) 
            {
          	if((trim($string1)) != (trim($string2))) {
          		$this->errors[] = 'The '.$field.'s do not match.';
          	} else {
          		return true;
          	}
            }
          
            // Check a string against a maximum length
            function maxLength($field, $string, $length)
            {
            	if(strlen($string) > (int)$length) {
          		$this->errors[] = 'The '.$field.' is greater than '.(int)$length.' character maximum limit.';
          	} else { 
          		return true;
          	}
            }
          
            // Check a string against a minimum length
            function minLength($field, $string, $length)
            {
            	if(strlen($string) < (int)$length){
          		$msg = "string is greater than minimum length allowed";
          		$this->errors[] = 'The '.$field.' is has fewer characters than the '.(int)$length.' charcter minimum limit';
          	} else {
          		return true;
          	}
            }
          
            // Compare string against a regular expression
            function regEx($field, $string, $regex) 
            {
          
            	if(!is_string($string)){
          		return false; 
          	} else {
          
          		switch($regex){
          
          			case 'az': # Basic az lowercase character class
          			$rule = '^[a-z]*$';
          			$error = 'The '.$field.' can only contain lowercase letters'; 
          			break;
          
          			case 'aZ': # Lower & uppercase character class
          			$rule = '^[A-Za-zÀ-ÖØ-öø-ÿ]*$';
          			$error = 'The '.$field. ' can only contain letters'; 
          			break;
          
          			case 'num': # Numbers 0-9
          			$rule = '^[0-9]*$';
          			$error = 'The '.$field. ' can only contain letters and numbers'; 
          			break;
          
          			case 'alphanum': # Alpha-numeric all basic characters and letters
          			$rule = '^[A-Za-zÀ-ÖØ-öø-ÿ0-9]*$';
          			$error = 'The ' . $field. ' can only contain letters and numbers'; 
          			break;       
          
          			case 'files': # Filename
          			$rule = '^[a-zA-Z_-]{1}[a-zA-Z0-9_-]*\.[a-zA-Z0-9]{1, 3}$';
          			$error = 'The '.$field.' has to start with a letter and end with a full stop and the file type extention (e.g. .txt), as well as only contain letters, numbers and the following characters: ., _, -'; 
          			break;
          
          			case 'name': # Name
          			$rule = '^[A-Za-zÀ-ÖØ-öø-ÿ0-9 \'`’_-]*$';
          			$error = 'The '.$field." can only contain letters, numbers and the following characters: ' ', ', `, ’, _, -"; 
          			break;
          
          			case 'text': # Basic text
          			$rule = "^[A-Za-zÀ-ÖØ-öø-ÿ0-9\r\n \.,\?!:\(\)/'`’&_-]*$";
          			$error = 'The '.$field. " can only contain letters, numbers and the following characters: ' ', ., ',', ?, !, :, (, ), /, ', `, ’, &, _, -"; 
          			break;
          
          			case 'phone': # Telephone Numbers
          			$rule = '^[0-9 \+\(\)]*$';
          			$error = 'The '.$field." can only contain numbers and the following characters: ' ', +, (, )"; 
          			break;
          
          			case 'postcode' :
          			$rule = '^[a-zA-Z0-9A-Za-zÀ-ÖØ-öø-ÿ _-]*$';
          			$error = 'The '.$field." can only contain letters, numbers and the following characters: ' ', _, -"; 
          			break;
          
          			case 'email': # Email address
          			$rule = '^[A-Za-z0-9\._-]+@[A-Za-z0-9\.-]+\.[A-Za-z]{2,4}$';
          			$error = 'The '.$field.' does not match the standard email format'; 
          			break;
          
          			default:
          			return false;
          
          		}
          
          		if(eregi($rule, $string)){
          			return true;
          		} else {
          			$this->errors[] = $error;
          		}
          	}
            }
          
          
          }
          

          I'm now calling the object like so. My only concern now is the methods array I have which calls the methods required. Any ideas of how to deal with this in a more effective manner??

          <?php
          
          // Include validation class
          include 'includes/validate2.php';
          
          // Instantiate a new formValidation object
          $validate = new formValidation();
          
          // Test Array - $_POST can be used 
          $input = array('firstname' => 'a', 'surname' => 'rrr', 'postcode' => 'ls12 7rt');
          
          // Define array for form elements to be tested against
          $methods = array('firstname' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'surname' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'company' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'address1' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'address2' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'town' => array('isEmpty' => '', 'regEx' => 'aZ'),
          			  'county' => array('isEmpty' => ''),
          			  'postcode' => array('isEmpty' => '', 'regEx' => 'postcode'),
          			  'tel' => array('isNumeric' => '', 'regEx' => 'phone')
          			 );
          
          // Loop through the values being passed from the $input array
          // Test to see if the $input array key matches the key in the methods arrays.
          //
          // IF this evaluates to true then loop through that specific dimension and 
          // use the key to call the method using the switch statement, passing in 
          // the values from the $input array and also the values to test against.
          //
          
          foreach($input as $key => $value){
          	if(array_key_exists($key, $methods)){
          		foreach($methods[$key] as $v_key => $v_value){
          
          		switch($v_key){
          
          		case isEmpty:
          		$validate->isEmpty($key, $value);
          		break;
          
          		case isString:
          		$validate->isString($key, $value);
          		break;
          
          		case isNumeric:
          		$validate->isNumeric($key, $value);
          		break;
          
          		case maxLength: 
          		$validate->maxLength($key, $value, $v_value);
          		break;
          
          		case minLength: 
          		$validate->minLength($key, $value, $v_value);
          		break;
          
          		case regEx:
          		$validate->regEx($key, $value, $v_value);
          		break;
          
          		default:
          		break;
          
          		}
          
          	}
          }
          }	
          
          // Finally access the errors variable within the object and output any errors 
          // which have occured whilst looping through the values.
          if(count($validate->errors) > 0){
          	foreach($validate->errors as $error){
          		echo '<li>'.$error.'</li>';
          	}
          }
          

            This article on the Strategy pattern looks at dealing with form validation in a pattern and object oriented fashion, and I found it excellent and well worth using.

            In your class(es) you find yourself having to make all kinds of allowances for the different types of field or validity check which could happen. The Strategy pattern provides a way around that. It also allows you to use only the functionality that you need, keeping the client code nice and clean.

              Thanks for that link Shrike, I found the phppatterns site over the weekend hadn't got round to getting to that article.

              From a quick glance what that article is suggesting is that in effect you create an overall class and then extend this class for each of the requirements such as validating passwords, users etc

                Another useful way to handle form validation is to pass in a generated hidden variable that denotes what type of validation a form variable should be using.

                i.e.:

                <input type="text" name="first" value="Geoffrey" />
                <input type="hidden" name="first_v" value="name" />

                Then when you process on your php page you can look for any variable with a hidden field of the type:

                $POST[$form_var . "v"]

                or even better

                $_POST[$form_var . $validation_delinator]

                and process accordingly.

                IMHO there is no way when dealing with forms to completely get away from loops and selection. Creating an extensible abstract class is a good idea, but still requires the creation of the child classes and then also a way to determine which variable is of which class.

                It is some what more managable to do it the OOP way since you have a chunk of code (a class) for each variant (or validation type) you run across. Using the OOP approach you can also further extend validations be deriving children from children.

                I.E. you might have a text field that needs to be a valid integer, and then you might further extend that to a zip code (numeric representation no dash) to be a integer of 7 digits.

                I am not suggesting this is a particularly valid way to do this, in fact I would keep my validation dependancies at one level only looking to the super class (abstract class) as you will probably want to change them without having to change to code base at two levels.

                Anyway if you used a hidden variable you could automate the instantiation of the appropriate class in the reception side, but you still have to input additional form variables on the form creation level.

                HTH

                Geoffrey

                  Write a Reply...