Modulo di contatto semplice con validazione dei campi

23 ago 2016, 17:16:45
Visualizzazioni: 14.8K
Voti: 2

Sto cercando di creare un modulo di contatto molto semplice che possa essere visualizzato tramite shortcode. Ho il seguente codice, ma non sembra validare correttamente e permette persino di inviare il modulo senza inserire alcun dato. Qualcuno potrebbe spiegarmi come validare il form? Vorrei anche aggiungere una trappola honeypot poiché ritengo siano migliori dei captcha, ma sono abbastanza nuovo con PHP. Qualsiasi aiuto sarebbe fantastico, grazie.

    function html_form_code() {
    echo '<form action="' . esc_url( $_SERVER['REQUEST_URI'] ) . '" method="post">';
    echo '<p>';
    echo 'Il tuo nome (obbligatorio) <br />';
    echo '<input type="text" name="cf-name" pattern="[a-zA-Z0-9 ]+" value="' . ( isset( $_POST["cf-name"] ) ? esc_attr( $_POST["cf-name"] ) : '' ) . '" size="40" />';
    echo '</p>';
    echo '<p>';
    echo 'La tua email (obbligatorio) <br />';
    echo '<input type="email" name="cf-email" value="' . ( isset( $_POST["cf-email"] ) ? esc_attr( $_POST["cf-email"] ) : '' ) . '" size="40" />';
    echo '</p>';
    echo '<p>';
    echo 'Il tuo messaggio (obbligatorio) <br />';
    echo '<textarea rows="10" cols="35" name="cf-message">' . ( isset( $_POST["cf-message"] ) ? esc_attr( $_POST["cf-message"] ) : '' ) . '</textarea>';
    echo '</p>';
    echo '<input type="text" name="content" id="content" value="" class="hpot" />';
    echo '<p><input type="submit" name="cf-submitted" value="Invia"/></p>';
    echo '</form>';
}

function deliver_mail() {

  $errors = new WP_Error();
if ( isset( $_POST[ 'content' ] ) && $_POST[ 'content' ] !== '' ) {
  $errors->add( 'cheater', 'Spiacente, questo campo non dovrebbe essere compilato. Stai cercando di imbrogliare?' );
}
if ( isset( $_POST[ 'cf-name' ] ) && $_POST[ 'cf-name' ] == '' ) {
  $errors->add('error', 'Per favore inserisci un nome valido.' );
}

if ( isset( $_POST[ 'cf-email' ] ) && $_POST[ 'cf-email' ] == '' ) {
  $errors->add('error', 'Per favore inserisci un\'email valida.' );
}

if ( isset( $_POST[ 'cf-message' ] ) && $_POST[ 'cf-message' ] == '' ) {
  $errors->add('error', 'Per favore inserisci un messaggio valido.' );
}

if ( empty( $errors->errors ) ){
  deliver_mail();
}
else {
  echo 'Per favore compila i campi obbligatori';
}


    // se viene cliccato il pulsante di invio, manda l'email
    if ( isset( $_POST['cf-submitted'] ) ) {

        // sanifica i valori del form
        $name    = sanitize_text_field( $_POST["cf-name"] );
        $email   = sanitize_email( $_POST["cf-email"] );
        $message = esc_textarea( $_POST["cf-message"] );

        // ottieni l'indirizzo email dell'amministratore
        $to = get_option( 'admin_email' );

        $headers = "From: $name <$email>" . "\r\n";

        // Se l'email è stata processata per l'invio, mostra un messaggio di successo
        if ( wp_mail( $to, $message, $headers ) ) {
            echo '<div class=cf-success>';
            echo '<p>Grazie per averci contattato '. $name .', un membro del nostro team ti risponderà al più presto.</p>';
            echo '</div>';
        } else {
            echo '<div class=cf-error>';
            echo '<p>Si è verificato un errore imprevisto</p>';
            echo '</div>';
        }
    }
}

function cf_contact_form() {
    ob_start();
    deliver_mail();
    html_form_code();

    return ob_get_clean();
}

add_shortcode( 'contact_form', 'cf_contact_form' );

Quindi sarebbe come il codice sopra? Grazie

Ricevo anche questo errore quando provo a eseguire i controlli sui campi.

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in /Applications/MAMP/htdocs/centenary-framework/wp-content/themes/cent_framework/assets/inc/core/contact-form.php on line 147

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in /Applications/MAMP/htdocs/centenary-framework/wp-includes/load.php on line 671

Non sono sicuro di cosa significhi. =(

EDIT

Questi problemi sono stati risolti. Incollo il codice funzionante qui sotto nella speranza che possa aiutare qualcuno in futuro. Un grande grazie a tutti quelli che hanno aiutato!!

// Markup del form
 function html_form_code()
 {
     ?>

 <form action="<?php esc_url($_SERVER['REQUEST_URI']);
     ?>" method="post">
   <p>Il tuo nome (obbligatorio)<br />
     <input type="text" name="cf-name" pattern="[a-zA-Z0-9 ]+" value="<?php isset($_POST['cf-name']) ? esc_attr($_POST['cf-name']) : '';
     ?>" size="40" />
   </p>
   <p>La tua email (obbligatorio)<br />
     <input type="email" name="cf-email" value="<?php isset($_POST['cf-email']) ? esc_attr($_POST['cf-email']) : '';
     ?>" size="40" />
   </p>
   <p>Il tuo messaggio (obbligatorio)<br />
     <textarea rows="10" cols="35" name="cf-message"><?php isset($_POST['cf-message']) ? esc_attr($_POST['cf-message']) : '';
     ?></textarea>
   </p>
   <p><input type="submit" name="cf-submitted" value="Invia"/></p>
 </form>

 <?php

 }

// Validazione del form
 function my_validate_form()
 {
     $errors = new WP_Error();

     if (isset($_POST[ 'content' ]) && $_POST[ 'content' ] !== '') {
         $errors->add('cheater', 'Spiacente, questo campo non dovrebbe essere compilato. Stai cercando di imbrogliare?');
     }

     if (isset($_POST[ 'cf-name' ]) && $_POST[ 'cf-name' ] == '') {
         $errors->add('name_error', 'Per favore inserisci un nome valido.');
     }

     if (isset($_POST[ 'cf-email' ]) && $_POST[ 'cf-email' ] == '') {
         $errors->add('email_error', 'Per favore inserisci un\'email valida.');
     }

     if (isset($_POST[ 'cf-message' ]) && $_POST[ 'cf-message' ] == '') {
         $errors->add('message_error', 'Per favore inserisci un messaggio valido.');
     }

     return $errors;
 }

// Invio del form
 function deliver_mail($args = array())
 {

  // Questo array $default serve per inizializzare alcuni valori predefiniti che verranno sovrascritti dal nostro array $args.
  // Potremmo aggiungere più chiavi secondo necessità ed è un buon modo per vedere quali parametri stiamo usando nella nostra funzione.
  // Verrà sovrascritto solo con i valori del nostro array $args se le chiavi sono presenti in $args.
  // Questo utilizza la funzione wp_parse_args() di WP.
  $defaults = array(
    'name' => '',
    'email' => '',
    'message' => '',
    'to' => get_option('admin_email'), // ottieni l'indirizzo email dell'amministratore
  );

     $args = wp_parse_args($args, $defaults);

     $headers = "From: {$args['name']}  <{$args['email']}>"."\r\n";

  // L'invio dell'email restituisce true in caso di successo, false altrimenti
  if (wp_mail($args['to'], $args['message'], $headers)) {
      return;
  } else {
      return false;
  }
 }

// Sanificazione del form
function my_sanitize_field($input)
{
    return trim(stripslashes(sanitize_text_field($input)));
}

// Messaggio di successo del form
function my_form_message()
{
    global $errors;
    if (is_wp_error($errors) && empty($errors->errors)) {
        echo '<div class="cf-success">';
        echo '<p>Grazie per averci contattato '.$_POST['cf-name'].', un membro del nostro team ti risponderà al più presto.</p>';
        echo '</div>';

    //Svuota $_POST perché abbiamo già inviato l'email
    $_POST = '';
    } else {
        if (is_wp_error($errors) && !empty($errors->errors)) {
            $error_messages = $errors->get_error_messages();
            foreach ($error_messages as $k => $message) {
                echo '<div class="cf-error '.$k.'">';
                echo '<p>'.$message.'</p>';
                echo '</div>';
            }
        }
    }
}

// Shortcode del form
add_shortcode('contact_form', 'cf_contact_form');
function cf_contact_form()
{
    ob_start();

    my_form_message();
    html_form_code();

    return ob_get_clean();
}

// Validazione errori
add_action('init', 'my_cf_form');
function my_cf_form()
{
    if (isset($_POST['cf-submitted'])) {
        global $errors;
        $errors = my_validate_form();
        if (empty($errors->errors)) {
            $args = array(
         'name' => my_sanitize_field($_POST['cf-name']),
         'email' => my_sanitize_field($_POST['cf-email']),
         'message' => my_sanitize_field($_POST['cf-message']),
       );
            deliver_mail($args);
        } else {
            return $errors;
        }
    }
}
0
Tutte le risposte alla domanda 2
10

Non hai alcun meccanismo di validazione.

La tua logica dovrebbe essere più o meno questa:

  • Invia il modulo
  • Controlla i campi inviati ($_POST) rispetto ai valori attesi
  • Se tutto è corretto, invia
  • Se qualcosa non è come previsto, registra l'errore (puoi usare WP_Error()) e ricostruisci il modulo mostrando il messaggio di errore (e magari ripopolando i campi con i valori "buoni" precedenti).

Tutto quello che vedo qui è che sanifichi gli input, ma non validi effettivamente se i tuoi input hanno i valori che ti aspetti (es. email valida, telefono, lunghezza del nome, ecc.).

Invi la tua email indipendentemente dal fatto che i campi abbiano i valori attesi. Il tuo else mostrerà un errore SOLO se wp_mail() fallisce, non se i tuoi campi hanno valori validati o meno.

Per aggiungere un honey pot, devi semplicemente aggiungere un campo nascosto nel tuo modulo che ti aspetti sia vuoto.

Ad esempio, nel tuo HTML

<input type="text" name="content" id="content" value="" class="hpot" />

Poi quando validi l'input del modulo, ti aspetti che quel campo sia vuoto.

Usando la classe WP_Error puoi aggiungere errori all'oggetto per poi usarli per informare l'utente o altro.

$errors = new WP_Error();
if ( isset( $_POST[ 'content' ] ) && $_POST[ 'content' ] !== '' ) {
  $errors->add( 'cheater', 'Spiacente, questo campo non dovrebbe essere compilato. Stai cercando di imbrogliare?' );
}

Quindi il controllo PHP sopra è un modo che potresti usare per validare il tuo modulo. Aggiungi semplicemente alcune istruzioni if con i valori attesi del tuo modulo (ovviamente questo può essere ampliato per validare l'input). Poi, se usi la classe WP_Error, aggiungendo all'oggetto se vengono trovati errori, devi solo fare un controllo finale prima dell'invio.

if ( empty( $errors->errors ) ){
  deliver_mail();
}
else {
  // Qui puoi usare la tua variabile $_POST per ripopolare il modulo con i valori accettati 
  // (dovresti aggiornare la tua html_form_code() per accettare un argomento) 
  // o semplicemente ricaricare la pagina di contatto mostrando un messaggio di errore.
}

MODIFICA

Ok, ecco un esempio più completo

CSS

Aggiungi questo al tuo CSS in modo che il campo non venga visualizzato nel browser

.hpot {
  display: none;
}

PHP

Ecco un altro modo di scrivere la tua funzione html, è solo più facile da leggere

function html_form_code() { ?>

<form action="<?php esc_url( $_SERVER['REQUEST_URI'] ); ?>" method="post">
  <p>Il tuo Nome (obbligatorio)<br />
    <input type="text" name="cf-name" pattern="[a-zA-Z0-9 ]+" value="<?php isset( $_POST["cf-name"] ) ? esc_attr( $_POST["cf-name"] ) : ''; ?>" size="40" />
  </p>
  <p>La tua Email (obbligatorio)<br />
    <input type="email" name="cf-email" value="<?php isset( $_POST["cf-email"] ) ? esc_attr( $_POST["cf-email"] ) : ''; ?>" size="40" />
  </p>
  <p>Il tuo Messaggio (obbligatorio)<br />
    <textarea rows="10" cols="35" name="cf-message"><?php isset( $_POST["cf-message"] ) ? esc_attr( $_POST["cf-message"] ) : ''; ?></textarea>
  </p>
  <p><input type="submit" name="cf-submitted" value="Invia"/></p>
</form>

<?php } 

La tua funzione deliver_mail non dovrebbe ascoltare $_POST e non dovrebbe sanificare. Come nota a margine, usare l'email dell'utente del modulo come intestazione from potrebbe causare problemi con alcuni ISP, perché l'email viene inviata dal tuo dominio e ora un'email viene inviata dal tuo dominio ma con un dominio non corrispondente nell'indirizzo from (potrebbe essere visto come spam). Usa un indirizzo del tuo dominio qui (come no-reply@example.com) e imposta l'email dell'utente nel corpo dell'email (nel messaggio). Potresti anche impostarlo come campo reply-to per comodità.

function deliver_mail( $args = array() ) {

  // Questo array $default è un modo per inizializzare alcuni valori predefiniti che verranno sovrascritti dal nostro array $args.
  // Potremmo aggiungere più chiavi come riteniamo opportuno ed è un buon modo per vedere quali parametri stiamo usando nella nostra funzione.
  // Verrà sovrascritto solo con i valori del nostro array $args se le chiavi sono presenti in $args.
  // Questo utilizza la funzione wp_parse_args() di WP.
  $defaults = array(
    'name'    => '',
    'email'   => '',
    'message' => '',
    'to'      => get_option( 'admin_email' ), // ottieni l'indirizzo email dell'amministratore
  );

  $args = wp_parse_args( $args, $defaults );

  $headers = "From: {$args['name']} <{$args['email']}>" . "\r\n";

  // L'invio dell'email restituisce true in caso di successo, false altrimenti
  if( wp_mail( $args['to'], $args['message'], $headers ) ) {
    return;
  }
  else {
    return false;
  }
}

La tua funzione di validazione

function my_validate_form() {

  $errors = new WP_Error();

  if ( isset( $_POST[ 'content' ] ) && $_POST[ 'content' ] !== '' ) {
    $errors->add( 'cheater', 'Spiacente, questo campo non dovrebbe essere compilato. Stai cercando di imbrogliare?' );
  }

  if ( isset( $_POST[ 'cf-name' ] ) && $_POST[ 'cf-name' ] == '' ) {
    $errors->add('name_error', 'Per favore inserisci un nome valido.' );
  }

  if ( isset( $_POST[ 'cf-email' ] ) && $_POST[ 'cf-email' ] == '' ) {
    $errors->add('email_error', 'Per favore inserisci un\'email valida.' );
  }

  if ( isset( $_POST[ 'cf-message' ] ) && $_POST[ 'cf-message' ] == '' ) {
    $errors->add('message_error', 'Per favore inserisci un messaggio valido.' );
  }

  return $errors;
}

La tua funzione di sanificazione. Ecco una funzione generale di sanificazione che elimina gli spazi bianchi e scappa l'html, ma potrebbe essere più complessa a seconda dei campi di input che hai. Ma penso che per il tuo scopo sia sufficiente

function my_sanitize_field( $input ){

  return trim( stripslashes( sanitize_text_field ( $input ) ) );

}

Mostrando il tuo messaggio di successo/errore, potresti usare questo per recuperare l'oggetto WP_Error

function my_form_message(){

  global $errors;
  if( is_wp_errors( $errors ) && empty( $errors->errors ) ){

    echo '<div class="cf-success">';
    echo '<p>Grazie per averci contattato '. $_POST['cf-name'] .', un membro del nostro team ti risponderà al più presto.</p>';
    echo '</div>';

    //Svuota $_POST perché abbiamo già inviato l'email
    $_POST = '';

  }
  else {

  if( is_wp_errors( $errors ) && ! empty( $errors->errors ) ){

    $error_messages = $errors->get_error_messages(); 
    foreach( $error_messages as $k => $message ){
        echo '<div class="cf-error ' . $k . '">';
        echo '<p>' . $message . '</p>';
        echo '</div>';

    }

  }

}

Infine la tua funzione shortcode

add_shortcode( 'contact_form', 'cf_contact_form' );
function cf_contact_form() {

  ob_start();

  my_form_message();
  html_form_code();

  return ob_get_clean();
}

E agganciandoti a init per ascoltare $_POST prima di renderizzare il nostro modulo e mostrare gli $errors se ne troviamo o inviare se tutto è ok.

add_action( 'init', 'my_cf_form');
function my_cf_form(){

  if( isset( $_POST['cf-submitted'] ) ) {

    global $errors;
    $errors = my_validate_form(); 
    if( empty( $errors->errors ) ){

       $args = array(
         'name'    => my_sanitize_field( $_POST['cf-name'] ),
         'email'   => my_sanitize_field( $_POST['cf-email'] ),
         'message' => my_sanitize_field( $_POST['cf-message'] ),
       );
       deliver_mail( $args );
    }
    else {
      return $errors;
    } 
  }
}

Ricorda sempre di prefissare le tue funzioni per non entrare in conflitto con i nomi delle funzioni di altri plugin.

23 ago 2016 17:53:34
Commenti

Scusa se faccio l'idiota, ma dove devo eseguire i controlli? Grazie

user53340 user53340
23 ago 2016 18:12:03

Se sei nuovo a PHP, non sei un idiota, devi solo imparare queste cose :P - Per quanto riguarda dove fare i controlli, devi modificare la tua funzione deliver_mail(). Non ascolti il tuo $_POST lì. In realtà, la tua funzione dovrebbe fare solo una cosa (o avere una sola logica) quindi se la tua funzione è usata per inviare email, allora non fai i controlli del contenuto lì (ha senso?) Quindi potresti controllare il tuo $_POST appena prima di validare il form. Aggiornerò la mia risposta con del codice.

bynicolas bynicolas
23 ago 2016 18:21:11

Ho aggiornato il mio codice come penso dovrebbe essere, ma immagino di no perché ancora non funziona. lol Ho anche ricevuto un errore di memoria quando ho inviato il form e poi ho ricaricato la pagina, quindi credo di non uscire correttamente dal codice o qualcosa del genere?

user53340 user53340
23 ago 2016 18:25:53

Inoltre, quando la pagina si aggiorna dopo l'invio del modulo, i campi sono ancora compilati con le informazioni, è corretto? La mia comprensione era che sanificare il modulo dovrebbe rimuovere gli input dell'utente nei campi. O sto interpretando male l'uso di sanitize?

user53340 user53340
23 ago 2016 18:30:27

Ok, questa modifica si è rivelata più lunga del previsto, ma penso possa essere un buon riferimento per te. Nota che non è stata testata, quindi se trovi bug, fammelo sapere e cercheremo di risolverli. Dacci un'occhiata e fammi sapere. Per quanto riguarda i tuoi campi che rimangono compilati dopo l'invio, è perché non pulisci la tua variabile $_POST e la usi per popolare il modulo. Il mio codice dovrebbe occuparsi di questo.

bynicolas bynicolas
23 ago 2016 19:51:32

Wow! Questa è una risposta davvero solida, se mai ne ho vista una. Penso che tu abbia ragione, sarà sicuramente molto utile per altri che la incontreranno. Grazie per tutto il tempo che hai dedicato a questo! È davvero utile. Ho una domanda: cosa fa la variabile $k? Grazie ancora!

user53340 user53340
23 ago 2016 20:59:39

$k è la chiave dell'array $errors->errors, conosciuta anche come slug per l'errore. Ricorda quando hai impostato il tuo messaggio di errore $errors->add('name_error', 'Inserisci un nome valido.' );, beh qui $k sarebbe name_error. In realtà è il codice di errore assegnato a quell'errore specifico. L'ho aggiunto come classe HTML così da poter stilizzare più facilmente ogni errore in modo diverso, se mai ce ne fosse bisogno. Sono contento che questa risposta ti sia stata utile.

bynicolas bynicolas
23 ago 2016 21:23:05

Scusa se ti disturbo ancora. Sto ricevendo un errore di sintassi per $headers, dice che si aspettava una stringa o una variabile ma ha trovato uno spazio inatteso. Ho rimosso gli spazi attorno a name e email, ma poi ottengo un errore di fine file inatteso che credo sia perché la funzione my_form_message() non è chiusa. Quindi l'ho chiusa e ho ottenuto un errore fatale dovuto a is_wp_errors e ora sono abbastanza confuso =S

user53340 user53340
24 ago 2016 12:02:49

è difficile dirlo da qui, ma vedo qualcosa che non va nella definizione della tua variabile $headers. Dato che usi il nome di una variabile (array) nella tua stringa, devi dire esplicitamente al parser PHP dove finisce la variabile usando le parentesi graffe { }. Quindi prova così e dovrebbe andare bene. $headers = "From: {$args['name']} <{$args['email']}>\r\n";

bynicolas bynicolas
24 ago 2016 17:34:33

Grazie per avermi risposto. C'erano un paio di problemi che ora sono stati risolti. Ho aggiornato il mio codice qui sopra. Grazie ancora per tutto il tuo aiuto con questo!

user53340 user53340
24 ago 2016 17:40:08
Mostra i restanti 5 commenti
5

Dato che sei relativamente nuovo a PHP, ecco un buon esempio di honeypot lato server semplice.

Quello che farai è impostare un campo di testo nascosto nel tuo form e verificare se non è vuoto per catturare lo spam.

HTML del Form

<label for="honeypot" class="bot">Lascia vuoto se sei umano</label>
<input type="text" id="honeypot" name="honeypot" class="bot" />

CSS

.bot { display:none }

PHP (semplificato)

 if ( $_POST() ) {
    $name = strip_tags(urldecode(trim($_POST['name'])));
    // OPPURE
    $name = test_input($_POST['name']; 
    $honeypot = $_POST('honeypot');
    // Verifica solo SE c'è qualcosa al suo interno.
    if ($honeypot){
      $msg = "Sei un bot";
    } else {
      $msg = "Successo";
      // Elaborazione aggiuntiva
      if ($name){
          mail(...)
      }
    }
    echo $msg;
 }

Dopo una breve ricerca ho anche trovato questa funzione che potresti usare.

function test_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}
23 ago 2016 17:40:58
Commenti

Grazie per il tuo messaggio. È sicuramente utile, il mio problema principale è la mancanza di validazione del form. Attualmente mi permette di inviare il form indipendentemente dal fatto che compili o meno i campi. E sono preoccupato che non stia verificando eventuali injection di stringhe ecc. Questa è la cosa principale che mi interessa. Sarebbe un caso di seguire lo stesso principio del honey pot? Grazie ancora.

user53340 user53340
23 ago 2016 17:44:31

Aggiornerò il PHP con ulteriori test di validazione, ma non dovresti mai fidarti dell'input utente e dovresti sempre sanificare/validare in ogni modo possibile.

Greg McMullen Greg McMullen
23 ago 2016 17:47:20

Grazie. Sto cercando di renderlo il più sicuro possibile, ma come ho detto, sono abbastanza nuovo a PHP quindi sono consapevole di queste cose, ma non ho idea di come implementare le misure di sicurezza. So che puoi interrogare i campi di input per impedire a qualcuno di provare a violare il form ecc. ma non so come si farebbe. Grazie ancora per il tuo aiuto.

user53340 user53340
23 ago 2016 17:50:07

Lo stai inserendo in un database o lo stai solo facendo inviare via email? Dal post originale, sembra che tu stia facendo solo quest'ultima cosa.

Greg McMullen Greg McMullen
23 ago 2016 17:54:27

Sì, voglio solo che venga inviato via email. Non dovrebbe andare nel database, ma non voglio nemmeno che renda il mio sito vulnerabile. È successo con Gravity Forms un po' di tempo fa. Grazie per l'aggiornamento!

user53340 user53340
23 ago 2016 17:59:29