8768 sujets

Développement web côté serveur, CMS

Voilà, mon objectif me semblait très simple mais je me retrouve avec pas mal de problèmes
Je souhaite que les utilisateurs qui veulent recevoir un email de news puissent s'inscrire en saisissant un email dans un champ.
Une fois saisit, l'utilisateur clic sur le bouton SUBSCRIBE pour qu'ensuite son adresse email soit inscrite dans un tableau. Le tableau ayant 2 colonnes, l'une ID auto incrémenté et la seconde, l'adresse email.
J'ai le code suivant dans ma page PHP

<?php
$servername = "mysql.XXX.com";
$database = "newsletter";
$username = "username";
$password = "mdp";
$conn = mysqli_connect($servername, $username, $password, $database);
if(isset($_POST['subscribe'])){
     if(empty($_POST['email_newsletter'])){
          echo '<script>alert("empty field!")</script>';
     }else{
          if(!preg_match("#^[a-z0-9_-]+((\.[a-z0-9_-]+){1,})?@[a-z0-9_-]+((\.[a-z0-9_-]+){1,})?\.[a-z]{2,}$#i",$_POST['email_newsletter'])){
               echo '<script>alert("incorrect email address!")</script>';
         }else{
               $req = $sql->prepare('INSERT INTO newsletter(email) VALUES(:email_newsletter)');
               $req->execute([':email_newsletter' => $_POST['email_newsletter']]);
          }
     }
}
mysqli_close($conn);
?>
<form style="" method="post" action="">
<input name="email_newsletter" type="email" value="<?php if(isset($_POST['email_newsletter'])){echo $_POST['email_newsletter'];} ?>"/>
<button name="subscribe" type="submit">SUBSCRIBE!</button>
</form>


La connexion se fait bien mais l'insertion ne se fait pas dans le tableau. Et pire, le <form> étant dans mon <footer>, ce dernier disparait complètement une fois l’actualisation de la page faite après que l’utilisateur ai cliqué sur SUBSCRIBE.
De plus, si je souhaite F5 ou ctrl+F5, j'ai le message suivant:
a écrit :
Pour afficher cette page, les informations précédemment transmises par Firefox doivent être renvoyées. Ceci répétera toute action (telle qu’une recherche ou un ordre d’achat) entreprise précédemment.

Et que je fasse "Renvoyer" ou "Annuler", mon <footer> a toujours disparu.
Étant totalement noob, je n'ai aucune piste de solution :X.

Merci très grandement par avance de votre temps.
Modifié par westman (26 Jul 2024 - 21:40)
Modérateur
Bonsoir,

Sans solution à apporter, je proposerais d'effectuer une connexion à la base de donnée seulement si il y a bien une adresse mail valide passée en $_POST.

Pour éviter de soumettre à nouveau ton formulaire sur un refresh de ta page , un header('location: pageDuFormulaire.php'); au moins dans le cas où l'adresse mail est validée et enregistrée.

Dans quelles circonstances est ce que cela bugue ? uniquement avec une adresse mail valide ou n'importe qu'elle adresse ?

Si tu n'est pas certain de ta regex, tu peut faire quelques test avec https://www.php.net/manual/fr/filter.examples.validation.php pour éliminer un éventuelle problème de connexion ou d’écriture en base de donnée.

Il y aura surement d'autre réponses Smiley smile

Cdt
gcyrillus a écrit :

Dans quelles circonstances est ce que cela bugue ? uniquement avec une adresse mail valide ou n'importe qu'elle adresse ?

N'importe laquelle.
Merci de cet avis, je vais me pencher dessus également.
Modérateur
Bonjour,

Pour debugguer ton script, tu peut mettre en début :

<?php
// bebogage seulement !!	
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
?>


Cette partie est à utiliser en local et devra être retirée ou commentée une fois le script fonctionnel et en ligne. https://www.php.net/manual/fr/function.error-reporting

Pour éviter de renvoyer le formulaire au refresh:
header('Location: '.$_SERVER['PHP_SELF']);
exit;


Pour conserver momentanément l'adresse mail soumise, tu peut l'enregistrer en session. https://www.php.net/manual/fr/reserved.variables.session.php

Il ne faut pas non plus faire confiance aux variables qu'un formulaire peut t'envoyer , un strip_tags() est un minimum et trim() pour virer les espaces de début et fin de chaine.

La session peut aussi te servir à valider un formulaire seulement si le visiteur vient bien de ta page en insérant un champ caché avec une chaine unique, (mot clé:token)

Cela pourrait modifier ton formulaire comme ceci : (partie BDD commentée pour vérifier que la partie traitement n'est pas en cause).

<?php
	// debogage seulement !!	
	ini_set('display_errors', '1');
	ini_set('display_startup_errors', '1');
	error_reporting(E_ALL);
	?><?php
	$newMail='';
	session_start();
	$token  =   md5(uniqid(microtime(), true));
	if(isset($_SESSION["email"])) {
		$newMail= $_SESSION["email"];
		echo '<script>alert("Your subscription\'s email address used: '.$newMail .'")</script>';
	}
	if(	isset($_POST['subscribe']) && $_POST['token'] == $_SESSION["token"])
	{
		unset($_SESSION["token"];
		if(empty($_POST['email_newsletter'])){
			echo '<script>alert("empty field!")</script>';
			}else{
			$newMail =trim(strip_tags($_POST['email_newsletter']));
			if(!preg_match("#^[a-z0-9_-]+((\.[a-z0-9_-]+){1,})?@[a-z0-9_-]+((\.[a-z0-9_-]+){1,})?\.[a-z]{2,}$#i",$newMail)){
				echo '<script>alert("incorrect email address!" '.$newMail .')</script>';
				}else{					
				$_SESSION["email"]=$newMail;
				/*
					// connexion et ecriture BDD si adresse valide
					$servername = "mysql.XXX.com";
					$database = "newsletter";
					$username = "username";
					$password = "mdp";
					$conn = mysqli_connect($servername, $username, $password, $database);
					$req = $sql->prepare('INSERT INTO newsletter(email) VALUES(:email_newsletter)');
					$req->execute([':email_newsletter' => $newMail);
					mysqli_close($conn);
				*/
				
				header('Location: '.$_SERVER['PHP_SELF']);
				exit;
			}
		}
	}
	$_SESSION["token"]   =   $token; 
?>
<form style="" method="post" action="">
	<input name="email_newsletter" type="email" value="<?php echo $newMail; unset($_SESSION["email"]);?>"/>
	<input type="hidden" name="token" value="<?= $token ?>">
	<button name="subscribe" type="submit">SUBSCRIBE!</button>
</form>

C'est un exemple!

Une fois la partie traitement des données formulaire fonctionnelle, tu peut passer à la partie bdd, d'abord la connexion , puis l'enregistrement .

L'idée est de cloisonner chaque partie du script pour avancer patiemment en laissant les scripts afficher les erreurs , cela vaut aussi pour la bdd.

Pour finir voir aussi https://www.pierre-giraud.com/php-mysql-apprendre-coder-cours/securiser-valider-formulaire/ entre autres ressources existantes.

Cdt
Modifié par gcyrillus (27 Jul 2024 - 21:13)
Merci beaucoup de toute ces infos et de cette correction.

J'ai cette erreur ci dès le chargement dans ma page:
( ! ) Warning: session_start(): Session cannot be started after headers have already been sent in C:\wamp64\www\site\index.php on line 121
Call Stack
# Time Memory Function Location
1 0.0000 364304 {main}( ) ...\index.php:0
2 0.0000 364872 session_start( ) ...\index.php:121

Je vais être honnête, malgré avoir lu ce que vous avez envoyé et fait quelques recherches, il y a beaucoup de choses que je ne comprends pas dans votre code, même si j'ai bien saisis la logique grâce à la structure.
Du coup cette histoire de problème de session_start() et de header() me dépasse :X.

Si malgré l'erreur, je clique sur le bouton SUBSCRIBE! (avec le champ email vide ou remplit correctement, j'ai les erreurs suivantes:
( ! ) Warning: Undefined variable $_SESSION in C:\wamp64\www\karasuart\index.php on line 127
Call Stack
# Time Memory Function Location
1 0.0002 366640 {main}( ) ...\index.php:0

( ! ) Warning: Trying to access array offset on value of type null in C:\wamp64\www\site\index.php on line 127
Call Stack
# Time Memory Function Location
1 0.0002 366640 {main}( ) ...\index.php:0

Désolé Smiley sweatdrop ...
Modérateur
@gcyrillus
Tu avais laissé des petites erreurs. J'ai nettoyé ton code et amélioré celui-ci. Pas mal, ton histoire de token. Comme je ne fais plus de PHP, je ne m'en souvenais plus.


@westman
Tu testes ce fichier à part. Ensuite, tu importes au fur et à mesure. Tu lis ce code et ne pas faire du copier/coller bêtement. J'ai fait avec les moyens du bord. On peut mieux faire. Lis bien le commentaire que j'ai laissé dans le code. Il faudra améliorer le système de message dans le cas où il y a une erreur (de serveur) lors de l'insertion en base de données.


<?php
// debogage seulement !!
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

session_start();

if(isset($_POST['subscribe']) && $_POST['token'] == $_SESSION["token"]){
    unset($_SESSION['token']);
    $_POST['email_newsletter'] = trim(strip_tags($_POST['email_newsletter']));
    if(!filter_var($_POST['email_newsletter'], FILTER_VALIDATE_EMAIL)){
        $_POST['errors']['email_newsletter'] = "l'email n'est pas valide";
    }else{
        /*
            // connexion et ecriture BDD si adresse valide
            // Vérifier que le mail n'existe pas avant de faire une insertion 
            // (Sinon champs unique. Mais faut faire la vérif quand même)

            $servername = "mysql.XXX.com";
            $database = "newsletter";
            $username = "username";
            $password = "mdp";
            $conn = mysqli_connect($servername, $username, $password, $database);
            $req = $sql->prepare('INSERT INTO newsletter(email) VALUES(:email_newsletter)');
            $req->execute([':email_newsletter' => $newMail);
            mysqli_close($conn);
        */
        /*
        if insertion SUCCESS en base
        */
        $_SESSION["flash"] = "Nous avons bien pris en compte votre email. Vous recevrez prochainement notre newsletter.";
        /*
        if insertion ERROR en base
        */
        // $_SESSION["flash"] = "Une erreur est survenue. Veuillez réessayer plus tard";


        header('Location: '.$_SERVER['PHP_SELF']);
        exit;
    }
}

$_SESSION["token"] = md5(uniqid());
if(isset($_SESSION['flash'])) {
    $message = $_SESSION['flash'];
    unset($_SESSION['flash']);
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>inscription newsletter</title>
</head>
<body>
    <?php if(isset($message)): ?>
    <p style="background:#236322; color:white;padding:10px;"><?= $message ?></p>
    <?php endif; ?>
    
    <form method="post" action="<?= htmlspecialchars($_SERVER['PHP_SELF'])?>">
        <input name="email_newsletter" type="email" value="<?= isset($_POST['email_newsletter'])? $_POST['email_newsletter'] : ''  ?>"/>
        <input type="hidden" name="token" value="<?= $_SESSION['token'] ?>">
        <button name="subscribe" type="submit">SUBSCRIBE!</button>
    </form>
    <?php if(!empty($_POST['errors']['email_newsletter'])): ?>
    <script>alert("<?= $_POST['errors']['email_newsletter'] ?>")</script>
    <?php endif; ?>
</body>
</html>


Modifié par niuxe (27 Jul 2024 - 20:32)
Modérateur
@niuxe merci et merci beaucoup pour ton expertise.

@westman
a écrit :
J'ai cette erreur ci dès le chargement dans ma page:
( ! ) Warning: session_start(): Session cannot be started after headers have already been sent in ...


session_start(); devrait être appeler avant que quoique ce soit ne soit envoyer au navigateur , cela inclus un espace ou une ligne vide dans tes script , si tu construit tes pages via des includes, il faut démarrer la session le plus tôt possible :Voici le pourquoi : https://www.php.net/manual/fr/function.session-start.php#refsect1-function.session-start-notes
a écrit :
Pour utiliser des sessions basées sur les cookies, session_start() doit être appelée avant d'afficher quoi que ce soit dans le navigateur.


Pour header() , voir https://www.php.net/manual/fr/function.header.php , et tout comme session_start() :
a écrit :
N'oubliez jamais que header() doit être appelée avant que le moindre contenu ne soit envoyé, soit par des lignes HTML habituelles dans le fichier, soit par des affichages PHP. Une erreur très classique est de lire un fichier avec include ou require, et de laisser des espaces ou des lignes vides, qui produiront un affichage avant que la fonction header() ne soit appelée. Le même problème existe avec les fichiers PHP/HTML standards.

Il te faut donc traiter ton formulaire en amont de tout affichage.

Les codes proposés sont seul et à titre d'exemples . Les parties traitement sont à séparé et à inclure à tes script en amont de tout affichage . Comme @niuxe te le rappelle, il ne faut pas juste consommer une info en copier/coller, mais d'abord l’appréhender et la comprendre pour en tirer parties, quitte à faire chauffer un peu ton moteur de recherche.

Bon apprentissage, et comme le démontre en partie ce post, l'apprentissage ne s’arrête jamais. ... faire et refaire Smiley smile

Cdt
Modifié par gcyrillus (27 Jul 2024 - 21:15)