SitePoint Sponsor

User Tag List

Results 1 to 8 of 8

Hybrid View

  1. #1
    SitePoint Addict AdRock952's Avatar
    Join Date
    Aug 2006
    Posts
    243
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Form validation help needed

    I have found a great article on creating form validation http://simonwillison.net/2003/Jun/17/theHolyGrail/ but it is several years old and the author doesn't support it anymore.

    This is exactly what I want and have got stuck ttrying to add a textarea validation.

    I have added a textarea and I have got it validate but what i want it to be able to do is set the colour the same as the other fields with errors.

    Does anybody know how i can do this?

    This is the page with the form
    PHP Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
    <head>
      <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
      <title>FormProcessor Demo</title>
      <style type="text/css">
    body {
      font-family: georgia;
      margin: 2em;    
    }
    div label {
      display: block;
      font-size: 0.8em;
    }
    div {
      margin-bottom: 0.5em;    
    }
    input.invalid {
      background-color: pink;
    }
    strong.error {
      color: red;
    }
      </style>
    </head>
    <body>
    <h1>FormProcessor Demo</h1>
    <?php

    include('FormProcessor.php');

    $form = <<<EOD
    <form action="test.php" method="post">
    <errorlist>
    <ul>
    <erroritem> <li><message /></li>
    </erroritem></ul>
    </errorlist>
    <div>
     <label for="name">Name: </label>
     <input type="text" name="name" id="name" compulsory="yes" validate="alpha" callback="uniqueName" size="20" /><error for="name"> <strong class="error">!</strong></error>
    </div>
    <errormsg field="name" test="compulsory">You did not enter your name</errormsg>
    <errormsg field="name" test="alpha">Your name must consist <em>only</em> of letters</errormsg>
    <div>
     <label for="email">Email: </label>
     <input type="text" name="email" id="email" compulsory="yes" validate="email" size="20" /><error for="email"> <strong class="error">!</strong></error>
    </div>
    <errormsg field="email" test="compulsory">You must provide an email address</errormsg>
    <errormsg field="email" test="validate">Your email address is invalid</errormsg>
    <div>
     <label for="pass1">Password: </label>
     <input type="password" name="pass1" id="pass1" compulsory="yes" validate="alphanumeric" size="10" /><error for="pass1"> <strong class="error">!</strong></error>
    </div>
    <errormsg field="pass1" test="compulsory">You did not provide a password</errormsg>
    <errormsg field="pass1" test="validate">Your password must contain only letters and numbers</errormsg>
    <div>
     <label for="pass2">Repeat password: </label>
     <input type="password" name="pass2" id="pass2" validate="alphanumeric" mustmatch="pass1" size="10" /><error for="pass2"> <strong class="error">!</strong></error>
    </div>
    <errormsg field="pass2" test="mustmatch">The two passwords did not match</errormsg>
    <div>
     <label for="message">Message: </label>
     <textarea name="message" id="message" compulsory="yes" validate="alphanumeric" rows="20" cols="45"></textarea><error for="message"> <strong class="error">!</strong></error>
    </div>
    <errormsg field="message" test="compulsory">You forgot to write your message</errormsg>
    <div><input type="submit" value="Create Account" /></div>
    </form>
    EOD;

    function 
    uniqueName($name) {
        return 
    true;
    }

    $processor =& new FormProcessor($form);
    if (
    $processor->validate()) {
        echo 
    '<p>Form data is OK.</p><pre>';
        
    print_r($_POST);
        echo 
    '</pre>';
    } else {
        
    $processor->display();
    }

    ?>
    </body>
    </html>
    This is the validation functions
    PHP Code:
    <?php

    /* FormProcessor 0.3
       Simon Willison, 17th June 2003
       A work in progress
    */

    class FormProcessor {
        var 
    $formML;
        var 
    $rules = array();
        var 
    $errormsgs = array();
        var 
    $errors = array();
        var 
    $output '';
        
        var 
    $errorClass 'invalid';
        
    // Properties used during XML parsing for the form() method
        
    var $_parser;
        var 
    $_copy true// Flag: should formML contents be copied to output?
        
    var $_saveForTemplate false// Flag: are we saving this for an erroritem template?
        
    var $_errorTemplate '';
        var 
    $_inTextArea false;
        function 
    FormProcessor($formML) {
            
    $this->formML $formML;
            
    // Extract the validation rules
            
    $extractor =& new FormRuleExtractor($formML);
            
    $this->rules =& $extractor->rules;
            
    $this->errormsgs =& $extractor->errormsgs;
        }
        function 
    validate() {
            if (!
    $_POST) {
                return 
    false;
            }
            
    $this->errors = array();
            foreach (
    $this->rules as $name => $rules) {
                
    // Check if compulsory field has not been filled
                
    if ($rules['compulsory'] && (!isset($_POST[$name]) || trim($_POST[$name]) == '')) {
                    
    $this->errors[$name] = $this->getErrorMsg($name'compulsory');
                    continue;
                }
                if (!isset(
    $_POST[$name])) {
                    
    // Field not set, and it's not compulsory
                    
    continue;
                }
                
    $value $_POST[$name];
                
    // If a regular expression is specified, check using that
                
    if (isset($rules['regexp']) && !preg_match($rules['regexp'], $value)) {
                    
    $this->errors[$name] = $this->getErrorMsg($name'regexp');
                    continue;
                }
                
    // If there is a mustmatch rule, check using that
                
    if (isset($rules['mustmatch']) && $value != $_POST[$rules['mustmatch']]) {
                    
    $this->errors[$name] = $this->getErrorMsg($name'mustmatch');
                    continue;
                }
                
    // If there is a calback rule, run that function
                
    if (isset($rules['callback'])) {
                    
    $callback $rules['callback'];
                    if (
    substr($callback05) == 'this:') {
                        
    // It's actually a method on this class
                        
    $method substr($callback5);
                        if (!
    $this->$method($value)) {
                            
    $this->errors[$name] = $this->getErrorMsg($name'callback');
                            continue;
                        }
                    } else {
                        
    // It's just a normal function
                        
    if (!$callback($value)) {
                            
    $this->errors[$name] = $this->getErrorMsg($name'callback');
                            continue;
                        }
                    }
                }
            }
            
    // All rules should now have been processed
            
    return count($this->errors) == 0;
        }
        function 
    display() {
            echo 
    $this->form();
        }
        function 
    form() {
            
    // Returns the XHTML for the form, after processing
            
    $this->output '';
            
    $this->_parser xml_parser_create();
            
    // Set XML parser to take the case of tags in to account
            
    xml_parser_set_option($this->_parserXML_OPTION_CASE_FOLDINGfalse);
            
    // Set XML parser callback functions
            
    xml_set_object($this->_parser$this);
            
    xml_set_element_handler($this->_parser'tag_open''tag_close');
            
    xml_set_character_data_handler($this->_parser'cdata');
            if (!
    xml_parse($this->_parser$this->formML)) {
                die(
    sprintf('XML error: %s at line %d',
                    
    xml_error_string(xml_get_error_code($this->_parser)),
                    
    xml_get_current_line_number($this->_parser)));
            }
            
    xml_parser_free($this->_parser);
            return 
    $this->output;
        }
        function 
    tag_open($parser$tag$attr) {
            if (!
    $this->_copy) {
                return;
            }
            if (
    $this->_saveForTemplate) {
                if (
    $tag == 'message') {
                    
    $this->_errorTemplate .= '%%%MESSAGE_HERE%%%';
                } else {
                    
    $this->_errorTemplate .= '<'.$tag.$this->_makeAttr($attr);
                    if (
    in_array($tag, array('br''img'))) {
                        
    $this->_errorTemplate .= ' />';
                    } else {
                        
    $this->_errorTemplate .= '>';
                    }
                }
                return;
            }
            
    $killattrs = array('compulsory''validate''regexp''callback''mustmatch''errormsg');
            
    // Kill unrequired attributes
            
    foreach ($killattrs as $a) {
                unset(
    $attr[$a]);
            }
            switch (
    $tag) {
                case 
    'error':
                    if (!
    $_POST || !isset($this->errors[$attr['for']])) {
                        
    $this->_copy false;
                    }
                    return;
                case 
    'errormsg':
                    
    $this->_copy false;
                    return;
                case 
    'errorlist':
                    if (!
    $_POST) {
                        
    // Stop copying
                        
    $this->_copy false;
                        return;
                    } else {
                        
    // We are going to be redisplaying, so keep copying
                        
    return;
                    }
                case 
    'erroritem':
                    
    $this->_saveForTemplate true;
                    return;
                case 
    'img':
                case 
    'br':
                    
    // Empty tags
                    
    $this->output .= '<'.$tag.$this->_makeAttr($attr).' />';
                    return;
                case 
    'input':
                    
    // Add the value attribute, if redisplaying
                    
    if (isset($_POST[$attr['name']]) && $attr['type'] != 'password') {
                        
    $attr['value'] = htmlentities(stripslashes($_POST[$attr['name']]));
                    }
                    
    // Add en error class if an error occured
                    
    if (isset($this->errors[$attr['name']])) {
                        
    $attr['class'] = isset($attr['class']) ? $attr['class'].' '.$this->errorClass $this->errorClass;
                    }
                    
    $this->output .= '<'.$tag.$this->_makeAttr($attr).' />';
                    return;
                case 
    'textarea':
                    
    $this->_inTextArea $attr['name'];
            }
            
    // Add tag to the output
            
    $this->output .= '<'.$tag.$this->_makeAttr($attr).'>';
        }
        function 
    cdata($parser$data) {
            if (
    $this->_saveForTemplate) {
                
    $this->_errorTemplate .= $data;
                return;
            }
            if (
    $this->_copy) {
                
    $this->output.= $data;
            }
        }
        function 
    tag_close($parser$tag) {
            if (
    $this->_saveForTemplate && $tag != 'erroritem' && !in_array($tag, array('br''img''message'))) {
                
    $this->_errorTemplate .= '</'.$tag.'>';
                return;
            }
            switch (
    $tag) {
                case 
    'error':
                case 
    'errormsg':
                case 
    'errorlist':
                    
    $this->_copy true;
                    break;
                case 
    'erroritem':
                    
    // Stop saving this in the template
                    
    $this->_saveForTemplate false;
                    
    // Now output all error messages using that template
                    
    foreach ($this->errors as $name => $error) {
                        
    $this->output .= str_replace('%%%MESSAGE_HERE%%%'$error$this->_errorTemplate);
                    }
                    return;
                case 
    'textarea':
                    if (isset(
    $_POST[$this->_inTextArea])) {
                        
    $this->output .= htmlentities(stripslashes($_POST[$this->_inTextArea]));
                    }
                    
    $this->output .= '</textarea>';
                    
    $this->_inTextArea false;
                    break;
                case 
    'img':
                case 
    'br':
                case 
    'input':
                case 
    'message':
                    
    // Empty tags
                    
    break;
                default:
                    if (
    $this->_copy) {
                        
    $this->output.= '</'.$tag.'>';
                    }
            }
        }
        function 
    _makeAttr($attr) {
            
    $html ' ';
            foreach (
    $attr as $name => $value) {
                
    $html .= $name.'="'.$value.'" ';
            }
            return 
    substr($html0, -1); // Remove trailing space
        
    }
        function 
    getErrorMsg($field$test) {
            if (isset(
    $this->errormsgs["$field:$test"])) {
                return 
    $this->errormsgs["$field:$test"];
            }
            
    // No error message has been specified - generate one based on the $test
            
    switch ($test) {
                case 
    'regexp':
                    return 
    "Field '$field' contained invalid data";
                case 
    'compulsory':
                    return 
    "Field '$field' must be filled in ";
                case 
    'mustmatch':
                    return 
    "'$field' must match '{$this->rules[$field]['mustmatch']}'";
                case 
    'callback':
                default:
                    return 
    "Field '$field' was not valid";
            }
        }
        function 
    checkEmail($email) {
            
    // Used as callback or validate="email" shortcut
            
    return preg_match(
                
    '#^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$#',
                
    $email
            
    );
            
    // Regexp from http://www.regexplib.com/REDetails.aspx?regexp_id=26
        
    }
    }

    class 
    FormRuleExtractor {
        
    // Extracts the validation rules from the formML.
        // Implementation note: the 'validate' attribute is a convenience, it is converted in 
        // to either a regular expression rule or a callback
        
    var $rules = array();
        var 
    $errormsgs;
        
    // Following variables used during XML parsing
        
    var $_parser;
        var 
    $_errorMsgName;
        var 
    $_errorMsgValue;
        var 
    $_collectErrorMsg false;
        function 
    FormRuleExtractor($formML) {
            
    $this->_parser xml_parser_create();
            
    // Set XML parser to take the case of tags in to account
            
    xml_parser_set_option($this->_parserXML_OPTION_CASE_FOLDINGfalse);
            
    // Set XML parser callback functions
            
    xml_set_object($this->_parser$this);
            
    xml_set_element_handler($this->_parser'tag_open''tag_close');
            
    xml_set_character_data_handler($this->_parser'cdata');
            if (!
    xml_parse($this->_parser$formML)) {
                die(
    sprintf('XML error: %s at line %d',
                    
    xml_error_string(xml_get_error_code($this->_parser)),
                    
    xml_get_current_line_number($this->_parser)));
            }
            
    xml_parser_free($this->_parser);
        }
        function 
    tag_open($parser$tag$attr) {
            
    // First, the stuff to deal with the errormsg tag and contents
            
    if ($tag == 'errormsg') {
                if (!isset(
    $attr['field']) || !isset($attr['test'])) {
                    
    // Die noisily
                    
    die('FormML error: errormsg tag needs field and test attributes');
                }
                
    $this->_errorMsgName $attr['field'].':'.$attr['test'];
                
    $this->_errorMsgValue '';
                
    $this->_collectErrorMsg true;
                return;
            }
            if (
    $this->_collectErrorMsg) {
                
    $this->_errorMsgValue .= '<'.$tag.$this->_makeAttr($attr);
                if (
    in_array($tag, array('br''img'))) {
                    
    $this->_errorMsgValue .= ' />';
                } else {
                    
    $this->_errorMsgValue .= '>';
                }
            }
            
    // Now the stuff to deal with everything else
            
    if (!in_array($tag, array('input''select''textarea'))) {
                return;
            }
            
    // Skip submit, image and reset fields
            
    if (isset($attr['type']) && in_array($attr['type'], array('submit''reset''image'))) {
                return;
            }
            
    $rules = array();
            if (isset(
    $attr['type'])) {
                
    $rules['type'] = $attr['type'];
            } else {
                
    $rules['type'] = $tag;
            }
            
    $name $attr['name'];
            
    // compulsory="yes"
            
    if (isset($attr['compulsory']) && $attr['compulsory'] == 'yes') {
                
    $rules['compulsory'] = true;
            }
            
    // validate="something"
            
    if (isset($attr['validate'])) {
                switch (
    $attr['validate']) {
                    case 
    'alpha':
                        
    $rules['regexp'] = '|^[a-zA-Z\s]*$|';
                        break;
                    case 
    'alphanumeric':
                        
    $rules['regexp'] = '|^[a-zA-Z0-9]*$|';
                        break;
                    case 
    'numeric':
                        
    $rules['regexp'] = '|^[0-9]*$|';
                        break;
                    case 
    'email':
                        
    $rules['callback'] = 'this:checkEmail';
                        break;
                }
            }
            
    // callback="someFunction"
            
    if (isset($attr['callback'])) {
                
    $rules['callback'] = $attr['callback'];
            }
            
    // regexp="someregexp"
            
    if (isset($attr['regexp'])) {
                
    $rules['regexp'] = $attr['regexp'];
            }
            
    // mustmatch="something"
            
    if (isset($attr['mustmatch'])) {
                
    $rules['mustmatch'] = $attr['mustmatch'];
            }
            
    // Save the rules to $this->rules
            
    $this->rules[$name] = $rules;
        }
        function 
    cdata($parser$data) {
            if (
    $this->_collectErrorMsg) {
                
    $this->_errorMsgValue .= $data;
            }
        }
        function 
    tag_close($parser$tag) {
            if (
    $tag == 'errormsg') {
                
    $this->errormsgs[$this->_errorMsgName] = $this->_errorMsgValue;
                
    $this->_collectErrorMsg false;
            }
            if (
    $this->_collectErrorMsg && !in_array($tag, array('br''img'))) {
                
    $this->_errorMsgValue .= '</'.$tag.'>';
            }
        }
        function 
    _makeAttr($attr) {
            
    $html ' ';
            foreach (
    $attr as $name => $value) {
                
    $html .= $name.'="'.$value.'" ';
            }
            return 
    substr($html0, -1); // Remove trailing space
        
    }
    }

    ?>

  2. #2
    ✯✯✯ silver trophybronze trophy php_daemon's Avatar
    Join Date
    Mar 2006
    Posts
    5,284
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Since the CSS defines only the style for input elements and textearea is not one, and you're probably following the same example; and since I'm too lazy to read all that code, try modifying the CSS code:
    Code css:
    input.invalid, textearea.invalid {
      background-color: pink;
    }
    Saul

  3. #3
    SitePoint Addict AdRock952's Avatar
    Join Date
    Aug 2006
    Posts
    243
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That didn't work. It still doesn't highlight pink.

    I think it's got something to do with FormProcessor.php

    Maybe something to do with this code

    PHP Code:
    case 'textarea':
         
    $this->_inTextArea $attr['name']; 
    I don't know very much about OOP in php to understand this code

  4. #4
    SitePoint Member
    Join Date
    Jul 2007
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm much too lazy for all of that code. I just found a program, yesterday, that offers Javascript validation and automated form creation. I won't post a link but you can do a search for the program: Simfatic Forms

    Yes...I know that not everyone has java enabled and I know that PHP is probably the RIGHT way to go, but...

  5. #5
    SitePoint Addict AdRock952's Avatar
    Join Date
    Aug 2006
    Posts
    243
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Javascript validation is the reason I want to go for php because the user cannot bypass that.

    Here is a link so you can see what I am trying to do http://www.jackgodfrey.org.uk/adam/test.php

    I have looked through the code more and more and have tried differerent things but nothing works and without being able to contact the author I have come to a halt.

    The only bit I can think of it being is do do with this part of the function. I have looked at the case input and noticed there is different code than the textarea and the input fields highlight on errors

    PHP Code:
    switch ($tag) {
                case 
    'error':
                    if (!
    $_POST || !isset($this->errors[$attr['for']])) {
                        
    $this->_copy false;
                    }
                    return;
                case 
    'errormsg':
                    
    $this->_copy false;
                    return;
                case 
    'errorlist':
                    if (!
    $_POST) {
                        
    // Stop copying
                        
    $this->_copy false;
                        return;
                    } else {
                        
    // We are going to be redisplaying, so keep copying
                        
    return;
                    }
                case 
    'erroritem':
                    
    $this->_saveForTemplate true;
                    return;
                case 
    'img':
                case 
    'br':
                    
    // Empty tags
                    
    $this->output .= '<'.$tag.$this->_makeAttr($attr).' />';
                    return;
                case 
    'input':
                    
    // Add the value attribute, if redisplaying
                    
    if (isset($_POST[$attr['name']]) && $attr['type'] != 'password') {
                        
    $attr['value'] = htmlentities(stripslashes($_POST[$attr['name']]));
                    }
                    
    // Add en error class if an error occured
                    
    if (isset($this->errors[$attr['name']])) {
                        
    $attr['class'] = isset($attr['class']) ? $attr['class'].' '.$this->errorClass $this->errorClass;
                    }
                    
    $this->output .= '<'.$tag.$this->_makeAttr($attr).' />';
                    return;
                case 
    'textarea':
                    
    $this->_inTextArea $attr['name'];
            } 
    If no-one knows what I need to do I'll have to rethink my validation

  6. #6
    SitePoint Member
    Join Date
    Jul 2007
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Not that I've found an answer, yet, but did you notice that if you don't repeat your password, the "Repeat Password" field is highlighted, but the "Password" field is not?

  7. #7
    SitePoint Addict AdRock952's Avatar
    Join Date
    Aug 2006
    Posts
    243
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh yeah....i never noticed that before. The blog i got this source code from said it was full of bugs and he was supposed to be improving it but that was 4 years ago and I guess it never happened

  8. #8
    ✯✯✯ silver trophybronze trophy php_daemon's Avatar
    Join Date
    Mar 2006
    Posts
    5,284
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    When you tried my suggestion, I hope you fixed the typo I made?
    Saul


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •