Forums

The forums ran from 2008-2020 and are now closed and viewable here as an archive.

Home Forums Other Can someone recommend a customizable contact form, please?

  • This topic is empty.
Viewing 15 posts - 16 through 30 (of 225 total)
  • Author
    Posts
  • #168544
    __
    Participant

    Alright, cool. How is this for the basic form? Is there anything else (besides error messages, thank-yous, etc.) you need on the form itself?

    You mentioned multiple recipients, right? do you mean a list of recipients to choose from, or one email sending to several recipients at once? does the user choose who gets the email, or is it pre-set?

    I’ll start writing up a couple functions at a time and we can put together a good, basic contact form.

    #168557
    __
    Participant

    Start with an empty class for your form:

    class contactMichael1961{
    }
    

    We’re going to need the HTML markup for the form. In addition, we’ll put “%placeholders%” where any dynamic values will go. Mostly, this means for error messages and the value attributes on <input>s.

    The HTML markup will go in a class _property_¹, which we will make protected so it cannot be changed or deleted accidentally.

    protected $_htmlMarkup = <<< 'HTML'
    <form class=michael1961 action=? method=POST>
        <fieldset>
            <legend>Contact Us</legend>
            <label class=required>
                Your Name: <input name=name required value=%name_value%>
                %name_error%
            </label>
            <label class=required>
                Email Address: <input name=email type=email required value=%email_value%>
                %email_error%
            </label>
            <label>
                Message: <textarea name=message>%message_value%</textarea>
                %message_error%
            </label>
            <fieldset class=antispam>
                <legend>
                    Anti-Spam Question
                    <a href=# title="Please answer the question below. This is used to help prevent spam.">(why?)</a>
                </legend>
                <label class=required>
                    %antispam_question% <input name=spam required>
                    %antispam_error%
                </label>
                <label class=blank>
                    (leave this field blank) <input name=required>
                    <input name=token type=hidden value=%token%>
                </label>
            </fieldset>
            <label><input type=submit value=Submit></label>
        </fieldset>
    </form>
    HTML;
    

    When we print the form, all of those placeholders will need values to be replaced with. There are mainly two types of dynamic values —values and errors— so we’ll create arrays that define each.

    The values and errors are all empty to start, since a new form will not be filled out yet, and will also have no mistakes. If we ever need these values or error messages, they will be created when the form is validated.

    protected $_values = array(
        "name"    => "",
        "email"   => "",
        "message" => ""
    );
        
    protected $_errors = array(
        "name"     => "",
        "email"    => "",
        "message"  => "",
        "antispam" => ""
    );
    

    And, finally, a _method_² that prepares the HTML markup, fills in the placeholders, and gives us something suitable for printing:

    public function htmlMarkup(){
    
        // for each value {name}:
        //    create a placeholder key {name}_value;
        //    escape special html chars in the value and "quote" it
        //    (the $v? allows us to skip the htmlspecialchars() function if the value is empty)
        foreach( $this->_values as $k=>$v ){
            $pr["%{$k}_value%"] = $v?
                '"'.htmlspecialchars( $v ).'"':
                '""';
        }
    
        // same as above, but the value goes in an error message
        foreach( $this->_errors as $k=>$v ){
            $pr["%{$k}_error%"] = $v?
                '<p class=error>'.htmlspecialchars( $v ).'</p>':
                '';
        }
    
        // add a randomly selected antispam question
        $pr["antispam_question"] = htmlspecialchars( $this->_generateAntispam() );
    
        // add a security token
        $pr["token"] = $this->_generateToken();
    
        // the keys are the placeholders; the values are the replacements
        $placeholders = array_keys( $pr );
        $replacements = array_values( $pr );
    
        // replace placeholders in the html with their replacement values
        return str_replace( $placeholders,$replacements,$this->_htmlMarkup );
    }
    

    Most of this code is building the placeholders and replacement values, by looping³ through each key=>value pair in the $_values and $_errors properties. Notice there are two additional placeholder=>replacement pairs: the “antispam_question” and the security “token”. These replacement values come from other class methods —_generateAntispam and generateToken, respectively— which we will be writing next.

    When they are ready, we use the PHP function str_replace to replace each placeholder in the _htmlMarkup with its replacement.

    Here is the code so far.

    ¹ a property is like a normal PHP variable, but it “belongs” to a class.

    I’m using a NOWDOC to define this property: you can use double and single quotes in a nowdoc string, so it’s very convenient for large blocks of HTML markup.

    ² a method is like a normal PHP function, but it “belongs” to a class.

    ³ loops are cool. this is a foreach loop, which does something “for each” value (or key and value) in an array.

    the variable $this is special: it always means “this object.” it is used to access class properties and methods, like $this->propertyName or $this->methodName().

    #168558
    __
    Participant

    I should have known better than to create a huge post, and then edit it to add a hyperlink. Hopefully the Mods can recover it from anti-spam-land quickly.

    In the meantime, here’s the link. Not done yet. More to follow.

    #168567
    Anonymous
    Inactive

    @traq

    Wow, I deeply appreciate this!!!

    do you mean a list of recipients to choose from, or one email sending to several recipients at once? does the user choose who gets the email, or is it pre-set?

    Yes, I want a dropdown list (I assume this is the way to go) containing different people/departments that an individual can choose from depending on who/what department they are wishing to contact. Selecting the person/department will automatically send the message to that specific person/department when the form is submitted.

    Here’s the last version of the form I worked on. All of the JS and HTML is viewable in the source code if that helps. Getting it to look like I want was easy but the coding for getting it to work is something I haven’t worked with in many, many years. I am very aware just how critical it is to get it to work, both in function but also for security purposes. All of the changes and features since the days of forms in the late ‘90’s leave me in the dark and I am excited by this opportunity to learn how to do it.

    The dropdown and a strong anti-spam/trolling feature are my main requirements/concerns. I also do not want attachments to be included in any way.

    I would like the form to be “embedded” in a page like this concept image but be able to be resized across the entire page if desired by the user. I mention this only as a “heads up” for anything going into the coding that might need to be considered given this.

    Is Captcha” something that should be utilized here or is the Q&A approach sufficient?

    It’s early a.m. here and I’m just getting back to work after Easter Holiday and will be away again this weekend, so please bear with me on this. I’m so far behind on many projects that I’ll be a bit rattled on this.

    Best Regards.

    #168634
    __
    Participant

    Here’s the last version of the form I worked on

    That’s what I based the HTML and CSS on. I didn’t look at the javascript. I’m not worried, though: validation can be accomplished without JS in most modern browsers, and anything you need/want to add can be without any trouble.

    I would like the form to be “embedded” in a page like this concept image…

    no problem. By putting all the code in its own class, away from any functional code, you’ll be able to print the form wherever you like. Likewise, changing the appearance is simply a matter of CSS.

    strong anti-spam/trolling feature are my main requirements/concerns. I also do not want attachments to be included in any way.

    …Is Captcha” something that should be utilized here or is the Q&A approach sufficient?

    I would say Q&A is mostly sufficient. In my view, captchas are more of a liability than a benefit: legitimate users are frustrated (or even defeated) by them, and spammers simply outsource to cheap third-world labor.

    Other anti-spam features I always use include:

    • nonce tokens uniquely identify every form you print, preventing attackers from creating their own forms, and ensuring each real form can only be used once.
    • timers reject form submissions that are too old (expired) or very fast (automated).
    • honeypot fields are not displayed to regular users, but bots will “see” them and fill them in.
    • multiple emails or additional email headers are automatically filtered, which prevents a popular contact form exploit.

    There will always be live, human spammers that you cannot distinguish from legit users, but all things considered, combined with the Q&A, these tactics work very well.

    Yes, I want a dropdown list (I assume this is the way to go) containing different people/departments that an individual can choose

    I’ll update the code-so-far to allow for this. (edit: I also noticed I forgot to make a placeholder for the form action attribute.) I might have more for you today, or tomorrow if not.

    Do you have any questions? We’re kind of “jumping in the deep end,” here. I’ll try to keep it straightforward and understandable, but I do want you to be able to understand what’s going on.

    #168645
    Anonymous
    Inactive

    Greetings traq,

    It all sounds great!

    I did a comparison last night of your coding to the original and to the coding Chris posted here. I can see patterns which is a big help in understanding what is what and what does what.

    I’ll not have a real opportunity to digest this until next Tuesday, so there’s no rush. I’ll be away this weekend and won’t be online.

    I do have questions, but want to be more clear on things before asking. One thing I do want to ask now is what do you mean by “print”?

    Seeing the coding being made is like a demo for me. I can learn much faster by being shown than I can by reading which in most cases is more fill than useful information.

    Best Regards.

    #168701
    __
    Participant

    One thing I do want to ask now is what do you mean by “print”?

    print (the PHP command) is very similar to echo. The biggest difference is that echo can take multiple parameters, while print cannot. In most cases, you will probably never notice any difference between the two.

    “print” (the verb used to describe something being printed) is me referring to the moment when your PHP script actually sends output to the client. This is important because it’s a kind of “point of no return” —you can’t change or make corrections to HTML that you’ve already printed, you can’t add more headers or cookies or start a session once you’ve already sent output, and so forth.

    It is best practice to put all of your program’s logic first, and not print anything until the very end. Take this as an example:

    <table>
        <tr><th>column 1</th><th>column 2</th></tr>
        <?php
            $result = $DB->query( "select col1,col2 from my_awesome_database" );
            while( $row = $result->fetch() ){
                print "<tr><td>{$row['col1']}</td><td>{$row['col2']}</td></tr>"
            }
        ?>
    </table>
    

    If something goes wrong running or fetching this query, you’re stuck with a broken HTML table on your page. Compare this to:

    <?php
    $result = $DB->query( "select col1,col2 from my_awesome_database" );
    while( $row = $result->fetch() ){
        $trs[] = "<tr><td>{$row['col1']}</td><td>{$row['col2']}</td></tr>"
    }
    $table = "<table>
        <tr><th>column 1</th><th>column 2</th></tr>"
        .implode( "\n",$trs )
    </table>";
    
    print $table;
    

    Even if something catastrophic happens, your user is not looking at a broken page. Better yet, if you can catch the problem in time, you can fix it, or at least replace it with a helpful “please try again later” message.

    #168838
    Anonymous
    Inactive

    Greetings traq,

    I’ve had some time to look into the print and echo statements and understand them a bit more, but as you said it’s “jumping into the deep end” for me. I’m certain once the coding is in place and I can see the “interface” of the form and all the fields and compare that to the coding, I’ll comprehend more. There are sure to be questions about things, just so you’ll be aware.

    Best Regards and Many Thanks.

    #168890
    __
    Participant

    No problem.

    Here’s some of the functions we use in htmlMarkup, but haven’t defined yet.

    First, we need to be able to pick random antispam question: answer pairs. We’ll define a map of questions to choose from. (This is just an example list; add more as desired. The more to choose from, the better.) The question will be the array key, and the answer will be the array value*:

    protected $_antispam = array(
        "What color is the grass?" => "green",
        "What is 1+1?            " => "2"
        //  etc. ...
    );
    

    Then, our method will simply choose one of the questions:

    /**
     * selects a random anti-spam question for use as an antispam challenge.
     *
     * @return string                   a challenge question
     */
    protected function _generateAntispam(){
        return array_rand( $this->_antispam );
    }
    

    Next, we need a _generateToken method. This gives us a (reasonably) unique/random string to identify the form by.

    /**
     * generates a random hash for use as a nonce token.
     *
     * @return string                   32-char hexadecimal hash
     */
    protected function _generateToken(){
        // generate a unique nonce using a random bytestring + the current time
        // then hash it
        $token = md5( openssl_random_psuedo_bytes( 32 ).$this->_time );
        // save to the session: use the token as the key, and the current time as the value
        //  (so we know when the form was requested)
        $_SESSION[__CLASS__][$token] = $this->_time;
        return $token;
    }
    

    You might have noticed we’re using the “current time” in the code above. We need to add that property to our class:

    protected $_time;
    

    …and we’ll give it the current time in the class constructor (not written yet) using the microtime function.

    Updated gist.

    #169082
    Anonymous
    Inactive

    Greetings traq,

    Thus far I understand most of what’s being done and what I don’t I’ve googled such as “generateToken”. I’m certain there will be more questions in the future.

    Many thanks and looking forward to the next installment of the “tutorial”.

    Best Regards.

    #169094
    __
    Participant

    Thus far I understand most of what’s being done and what I don’t I’ve googled…

    Great!
    (BTW, I haven’t forgotten you; I’ve just been waiting for your responses/questions before moving on to each new part.)

    important: I have fixed some typos in the code so far. (Among other things, I missed a few %s in our html template, and misspelled openssl_random_pseudo_bytes). Make sure you test with a fresh copy.

    (gist updated.)

    You’re ready for a quick test, now. include (or copy+paste) the class definition into your php script, and run the following code:

    $contact = new contactMichael1961;
    print "<pre>".htmlspecialchars( $contact->htmlMarkup() );
    

    This should show you some nice HTML form markup, like so:

    <form class=michael1961 action=? method=POST>
        <fieldset>
            <legend>Contact Us</legend>
            <label class=required>
                Your Name: <input name=name required value="">
            </label>
            <label class=required>
                Email Address: <input name=email type=email required value="">
            </label>
            <label>
                Send To:
                <select required>
                    <option value>please select…</option>
                    <option>display name</option>
                </select>
            </label>
            <label>
                Message: <textarea name=message>""</textarea>
            </label>
            <fieldset class=antispam>
                <legend>
                    Anti-Spam Question
                    <a href=# title="Please answer the question below. This is used to help prevent spam.">(why?)</a>
                </legend>
                <label class=required>
                    Are you a Robot? <input name=spam required>
                </label>
                <label class=blank>
                    (leave this field blank) <input name=required>
                    <input name=token type=hidden value=e2c6ae19913cec1b60d7d36ea7a0561c>
                </label>
            </fieldset>
            <label><input type=submit value=Submit></label>
        </fieldset>
    </form>
    

    If you want to see the actual form, remove the <pre> tag and the call to htmlspecialchars. We can also write a quick method that gets the form’s css for us:

    public function cssStyle(){
        return "<style>{$this->_cssRules}</style>";
    }
    

    Now, try:

    <?php
    
    include "path/to/contactMichael1961.php";
    $contact = new contactMichael1961;
    print $contact->cssStyle();
    print $contact->htmlMarkup();
    

    Should give you something like:

    example output

    #169176
    __
    Participant

    Had some free time this evening. I assume you’ve had a chance to test the code so far.
    (If not, shame, shame!)

    So, once we print the form, the user will fill it out, and then click [Submit].

    While I know your original form had javascript validation (and this one can too, but hear me out), remember that javascript runs client-side, and users have complete control over it. They can change it to do whatever they like, or even simply turn it off if it bothers them.

    Javascript is a convenience —a nicety, for nice users—, not a security feature.

    Everything must be checked server-side.

    So, we write our validate method. First, we’ll add a property named $_option, which will be used for —you guessed it— options on how to handle validating the form submission.

    protected $_option = array(
        "token.max"  => 1800,
        "token.min"  => 2,
        "value.trim" => true
    );
    
    • token.max is how long (in seconds) it takes our security token to expire. 1800 is 60 (seconds) × 30 (minutes).
    • token.min is how long it takes our security tokens to become valid. (See me post above regarding “timers.”)
    • value.trim tells the validator whether or not to trim whitespace from submitted values.

    We also need instructions on how each field is supposed to be validated. We can use an array, where the keys are the field names, and the values are (lists of) validation keywords that the validate method can use to decide how to validate each field.

    Using this property as a validation “map” will also allow us to make sure we only accept fields that we expect: no one can “sneak in” extra form fields.

    protected $_validation = array(
        "name"    => "required",
        "email"   => array("required","email"),
        "message" => null
    );
    

    Here’s the validate method itself, with lots of comments:

    public function validate( array $_post ){
        // if the token is missing, or the honeypot is full,
        //  we conclude the submitter is either a spambot or an attacker
        if( ! isset( $_post["token"] ) || isset( $_post["required"] ) ){
            $this->_spamTrap();
            return false;
        }
        // check if the provided token is valid
        if( ! $this->_validateToken( $_post["token"] ) ){
            return false;
        }
        // validate each form field
        foreach( $this->_validation as $field=>$validation ){
            // missing values might simply be optional
            $value = isset( $_post[$field] )?
                $_post[$field]:
                "";
            // trim whitespace?
            if( $this->_option["value.trim"] ){
                $value = trim( $value );
            }
            // try any validation methods
            foreach( (array)$validation as $method ){
                $methodName = "_validate{$method}";
                if( ! $this->$methodName( $value,$m ) ){
                    // $m is a _reference_ {@see http://php.net/references},
                    //  which validation methods will use to provide our error messages.
                    $this->_errors[$field] = $m;
                    // discard the invalid value
                    $value = "";
                    // stop checking if any validation fails
                    break;
                }
            }
            // if the submitted value was valid, we'll keep it.
            //  if it was invalid, then it will have been replaced with an empty string.
            $this->_values[$field] = $value;
        }
        // save antispam for last, since legitimate users will sometimes miss it
        //  (give them a chance; we don't want to discard an otherwise valid form submission)
        if( isset( $_post["antispam"] ) && $this->_validateAntispam( $_post["antispam"] ) ){
            // submission was good!
            return true;
        }
        // submission was bad.
        return false;
    }
    

    This method is something of a controller, basically managing which other methods should be called to validate each part of the submission. It’ll return true (if the submission was valid) or false (if something was wrong).

    Two things to take special note of:

    This method does not access the $_POST superglobal directly.
    You have to pass $_POST in as a argument, i.e.,

    $formIsValid = $contactForm->validate( $_POST );
    

    The reason behind this is twofold. The first thing you might think of is that, at some point, you might want to change the form to use the GET method. You shouldn’t, but good thinking. There are other changes you might make, however. Someday you might want to do some pre-validation processing (or something). If the method used $_POST directly, it would be difficult to do so (and if you modify the $_POST values “in place,” you’re just making things confusing and easy to break). Using this approach, however, it’s easy:

    $coolValues = do_something_cool_with( $_POST );
    $formIsValid = $cntactFrom->validate( $coolValues );
    

    This is called dependency injection. If a method needs something, you provide it —you never leave the method to “go find it” on its own. The second reason this is good is less obvious, but more important: it makes your code testable.

    This method can be tested without actually having to set up a test domain and fill out and submit the form. You can make sure it works just by creating an array of “good” values and making sure it returns true, then an array of various “bad” values and making sure it returns false.

    Another consequence of this is, if something does go wrong, it ends up being much easier to track down the problem; and much, much easier to fix.

    Next,
    This method simply reports whether the form is valid.

    It doesn’t “do” anything. If there are errors, it finds them and provides error messages, but it doesn’t print them. If spam is suspected, it says so, but doesn’t grab control of the entire script, ban IPs, and die. This is good because, as above, it makes things much more flexible and robust.

    I’ll leave you with that to think about. Let me know if you have any questions; next, we’ll look at all the methods that validate uses to do its work.

    updated gist

    #169188
    James Burton
    Participant

    @traq

    Hello

    Would this contact us form stop hackers from sending message like <script > alert(); </ script> in the message box and would validating the name ,email and message to only allow letters, numbers and ! @ – in php. Would this help?

    From
    James

    #169189
    __
    Participant

    Would this contact us form stop hackers* from sending message like <script > alert(); </ script>

    Not necessarily. But that’s not a problem: we’re planning on sending plain text emails. If there comes a time when you want html emails, or anytime you want to print something to the webpage, you need to sanitize the user input—but not before then.

    edit:
    * also, “hacking” is not bad. hacking is awesome, and can be a very positive, productive thing. attacking is bad. : )

    validating the name ,email and message to only allow letters, numbers and ! @ – in php. Would this help?

    Help with what?

    You can have any string you like in PHP. It only becomes a problem if you use it somewhere (e.g., echo, eval, or in an SQL query) that changes its meaning into something unexpected.

    The problem with trying to come up with a list of “unsafe” characters, and then assuming that you’re “safe” if you don’t have them, is that it’s a false sense of security. This is why PHP’s “magic quotes” are a horrible non-solution, just like the dozens of “make_safe_string” functions that amateur programmers come up with to shove into their contact form scripts. “Unsafe” characters in HTML are different than “unsafe” characters in PHP or SQL (or even different SQL brands/engines).

    The only way to really solve this problem is to “read up” so you know what to do, when and where, to keep yourself safe. Most programming languages, PHP included, believe it or not, have existing functions to use in each of these cases (e.g., you can see we’re using htmlspecialchars on text data that we print, even with text that didn’t necessarily come from the user).

    As to those specific characters:

    • what about names that have apostrophes? or forms like this one, where we ask for the full name in only one field (we need spaces)?
    • what about common punctuation (,.?'”:/&%$()) in messages?
    • as for emails, the best way to validate it is by using PHP’s filter_var. It’s a much better check than you can build yourself, and (as a native function) much faster, too. In addition, by validating the email field as a single address, you can stop header injection attacks (where multiple addresses or other email headers are added to the field).

    Hope that helps. Please ask if you have any other questions!

    #169194
    James Burton
    Participant

    @traq

    Thank you for your help.

    Please could you show me how you would sanitize the user input for sql query, contact form and for echoing comments in a function like Validation($UserInput) { // Some Code }?

    From
    James Burton

Viewing 15 posts - 16 through 30 (of 225 total)
  • The forum ‘Other’ is closed to new topics and replies.