/*---------------------------------------------------------------*/
|
/*
|
Titre : Calculatrice en PHP + HTML + CSS avec 6 opérateurs
|
|
URL : https://phpsources.net/code_s.php?id=1189
|
Date édition : 03 Fev 2026
|
Date mise a jour : 03 Fev 2026
|
|
Rapport de la maj:
|
- fonctionnement du code vérifié
|
Date mise a jour : 17 Fev 2026
|
|
Rapport de la maj:
|
- amélioration du code
|
- ajout d'une démo
|
- refactoring du code en PHP 8
|
- modification de la description
|
*/
|
/*---------------------------------------------------------------*/
|
|
declare(strict_types=1);
|
|
session_start();
|
|
// Génération token CSRF
|
if (!isset($_SESSION['csrf_token'])) {
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
}
|
|
// Initialisation de l'historique
|
if (!isset($_SESSION['history'])) {
|
$_SESSION['history'] = [];
|
}
|
|
$result = null;
|
$error = null;
|
$num1 = '';
|
$num2 = '';
|
$operation = '';
|
|
// Traitement du formulaire
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
// Vérification CSRF
|
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION[
|
'csrf_token']) {
|
$error = "Erreur de sécurité";
|
} else {
|
|
// Récupération et validation des données
|
$num1 = $_POST['num1'] ?? '';
|
$num2 = $_POST['num2'] ?? '';
|
$operation = $_POST['operation'] ?? '';
|
|
// Conversion en nombres
|
if (is_numeric($num1) && is_numeric($num2) && !empty($operation)) {
|
|
$n1 = floatval($num1);
|
$n2 = floatval($num2);
|
|
try {
|
$result = match($operation) {
|
'+' => $n1 + $n2,
|
'-' => $n1 - $n2,
|
'*' => $n1 * $n2,
|
'/' => $n2 != 0 ? $n1 / $n2 : throw new DivisionByZeroError(
|
),
|
'%' => $n2 != 0 ? $n1 % $n2 : throw new DivisionByZeroError(
|
),
|
'^' => $n1 ** $n2,
|
default => throw new InvalidArgumentException(
|
"Opération invalide")
|
};
|
|
// Arrondi à 8 décimales max
|
$result = round($result, 8);
|
|
// Ajoute à l'historique
|
$_SESSION['history'][] = [
|
'expression' => "$num1 $operation $num2",
|
'result' => $result,
|
'time' => date('H:i:s')
|
];
|
|
// Limite l'historique à 10 entrées
|
if (count($_SESSION['history']) > 10) {
|
array_shift($_SESSION['history']);
|
}
|
|
} catch (DivisionByZeroError) {
|
$error = "Division par zero impossible !";
|
} catch (Exception $e) {
|
$error = "Erreur : " . $e->getMessage();
|
}
|
|
} else {
|
$error = "Veuillez entrer des nombres valides";
|
}
|
}
|
}
|
|
// Effacer l'historique
|
if (isset($_GET['clear_history'])) {
|
$_SESSION['history'] = [];
|
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
|
exit;
|
}
|
|
?>
|
<!DOCTYPE html>
|
<html lang="fr">
|
<head>
|
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<title>Calculatrice PHP</title>
|
<style>
|
* {
|
margin: 0;
|
padding: 0;
|
box-sizing: border-box;
|
}
|
|
body {
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
min-height: 100vh;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
padding: 20px;
|
}
|
|
.container {
|
background: white;
|
border-radius: 20px;
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
padding: 40px;
|
max-width: 500px;
|
width: 100%;
|
}
|
|
h1 {
|
text-align: center;
|
color: #667eea;
|
margin-bottom: 30px;
|
font-size: 2em;
|
}
|
|
.calculator {
|
background: #f8f9fa;
|
padding: 30px;
|
border-radius: 15px;
|
margin-bottom: 20px;
|
}
|
|
.input-group {
|
margin-bottom: 20px;
|
}
|
|
label {
|
display: block;
|
margin-bottom: 8px;
|
color: #495057;
|
font-weight: 600;
|
font-size: 14px;
|
}
|
|
input[type="number"] {
|
width: 100%;
|
padding: 15px;
|
border: 2px solid #e9ecef;
|
border-radius: 10px;
|
font-size: 18px;
|
transition: all 0.3s;
|
font-family: 'Courier New', monospace;
|
}
|
|
input[type="number"]:focus {
|
outline: none;
|
border-color: #667eea;
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
}
|
|
.operations {
|
display: grid;
|
grid-template-columns: repeat(3, 1fr);
|
gap: 10px;
|
margin: 20px 0;
|
}
|
|
.operation-btn {
|
display: none;
|
}
|
|
.operation-label {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 15px;
|
background: white;
|
border: 2px solid #e9ecef;
|
border-radius: 10px;
|
cursor: pointer;
|
transition: all 0.3s;
|
font-size: 20px;
|
font-weight: bold;
|
color: #495057;
|
}
|
|
.operation-btn:checked + .operation-label {
|
background: #667eea;
|
color: white;
|
border-color: #667eea;
|
transform: scale(1.05);
|
}
|
|
.operation-label:hover {
|
border-color: #667eea;
|
transform: translateY(-2px);
|
}
|
|
.submit-btn {
|
width: 100%;
|
padding: 18px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
border: none;
|
border-radius: 10px;
|
font-size: 18px;
|
font-weight: bold;
|
cursor: pointer;
|
transition: all 0.3s;
|
margin-top: 10px;
|
}
|
|
.submit-btn:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
}
|
|
.submit-btn:active {
|
transform: translateY(0);
|
}
|
|
.result {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
padding: 25px;
|
border-radius: 15px;
|
text-align: center;
|
font-size: 24px;
|
margin-bottom: 20px;
|
animation: slideIn 0.3s ease;
|
}
|
|
.error {
|
background: #f8d7da;
|
color: #721c24;
|
padding: 15px;
|
border-radius: 10px;
|
border-left: 4px solid #dc3545;
|
margin-bottom: 20px;
|
animation: shake 0.5s;
|
}
|
|
@keyframes slideIn {
|
from {
|
opacity: 0;
|
transform: translateY(-20px);
|
}
|
to {
|
opacity: 1;
|
transform: translateY(0);
|
}
|
}
|
|
@keyframes shake {
|
0%, 100% { transform: translateX(0); }
|
25% { transform: translateX(-10px); }
|
75% { transform: translateX(10px); }
|
}
|
|
.history {
|
background: #f8f9fa;
|
padding: 20px;
|
border-radius: 15px;
|
margin-top: 20px;
|
}
|
|
.history-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 15px;
|
}
|
|
.history h3 {
|
color: #495057;
|
font-size: 18px;
|
}
|
|
.clear-btn {
|
padding: 8px 15px;
|
background: #dc3545;
|
color: white;
|
border: none;
|
border-radius: 6px;
|
cursor: pointer;
|
font-size: 12px;
|
transition: all 0.3s;
|
}
|
|
.clear-btn:hover {
|
background: #c82333;
|
}
|
|
.history-item {
|
background: white;
|
padding: 12px;
|
border-radius: 8px;
|
margin-bottom: 8px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
font-family: 'Courier New', monospace;
|
}
|
|
.history-time {
|
font-size: 11px;
|
color: #6c757d;
|
}
|
|
.history-empty {
|
text-align: center;
|
color: #6c757d;
|
padding: 20px;
|
font-style: italic;
|
}
|
|
.features {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 15px;
|
margin-top: 20px;
|
}
|
|
.feature {
|
background: #e7f3ff;
|
padding: 15px;
|
border-radius: 10px;
|
text-align: center;
|
font-size: 14px;
|
color: #495057;
|
}
|
|
.feature strong {
|
display: block;
|
color: #667eea;
|
margin-bottom: 5px;
|
}
|
|
@media (max-width: 600px) {
|
.container {
|
padding: 20px;
|
}
|
|
.operations {
|
grid-template-columns: repeat(2, 1fr);
|
}
|
|
.features {
|
grid-template-columns: 1fr;
|
}
|
}
|
</style>
|
</head>
|
<body>
|
<div class="container">
|
<h1>Calculatrice</h1>
|
|
<!-- Résultat ou Erreur -->
|
<?php if ($error): ?>
|
<div class="error"><?php echo htmlspecialchars($error) ?></div>
|
<?php elseif ($result !== null): ?>
|
<div class="result">
|
<?php echo htmlspecialchars((string)$num1) ?>
|
<?php echo htmlspecialchars($operation) ?>
|
<?php echo htmlspecialchars((string)$num2) ?>
|
=
|
<strong><?php echo htmlspecialchars((string)$result) ?></strong>
|
</div>
|
<?php endif; ?>
|
|
<!-- Formulaire -->
|
<form method="POST" class="calculator">
|
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION[
|
'csrf_token'] ?>">
|
|
<div class="input-group">
|
<label for="num1">Premier nombre</label>
|
<input type="number"
|
id="num1"
|
name="num1"
|
step="any"
|
value="<?php echo htmlspecialchars((string)$num1) ?>"
|
placeholder="Ex: 42"
|
required
|
autofocus>
|
</div>
|
|
<div class="input-group">
|
<label>Operation</label>
|
<div class="operations">
|
<input type="radio" id="add" name="operation" value="+"
|
class="operation-btn" <?php echo $operation === '+' ? 'checked' : '' ?>
|
required>
|
<label for="add" class="operation-label">+</label>
|
|
<input type="radio" id="sub" name="operation" value="-"
|
class="operation-btn" <?php echo $operation === '-' ? 'checked' : '' ?>>
|
<label for="sub" class="operation-label">-</label>
|
|
<input type="radio" id="mul" name="operation" value="*"
|
class="operation-btn" <?php echo $operation === '*' ? 'checked' : '' ?>>
|
<label for="mul" class="operation-label">x</label>
|
|
<input type="radio" id="div" name="operation" value="/"
|
class="operation-btn" <?php echo $operation === '/' ? 'checked' : '' ?>>
|
<label for="div" class="operation-label">/</label>
|
|
<input type="radio" id="mod" name="operation" value="%"
|
class="operation-btn" <?php echo $operation === '%' ? 'checked' : '' ?>>
|
<label for="mod" class="operation-label">%</label>
|
|
<input type="radio" id="pow" name="operation" value="^"
|
class="operation-btn" <?php echo $operation === '^' ? 'checked' : '' ?>>
|
<label for="pow" class="operation-label">^</label>
|
</div>
|
</div>
|
|
<div class="input-group">
|
<label for="num2">Deuxieme nombre</label>
|
<input type="number"
|
id="num2"
|
name="num2"
|
step="any"
|
value="<?php echo htmlspecialchars($num2) ?>"
|
placeholder="Ex: 8"
|
required>
|
</div>
|
|
<button type="submit" class="submit-btn">
|
Calculer
|
</button>
|
</form>
|
|
<!-- Historique -->
|
<?php if (!empty($_SESSION['history'])): ?>
|
<div class="history">
|
<div class="history-header">
|
<h3>Historique</h3>
|
<a href="?clear_history=1" class="clear-btn">Effacer</a>
|
</div>
|
<?php foreach (array_reverse($_SESSION['history']) as $item): ?>
|
<div class="history-item">
|
<span><?php echo htmlspecialchars($item['expression'])
|
?> = <strong><?php echo htmlspecialchars((string)$item['result']) ?>
|
</strong></span>
|
<span class="history-time"><?php echo htmlspecialchars(
|
$item['time']) ?></span>
|
</div>
|
<?php endforeach; ?>
|
</div>
|
<?php endif; ?>
|
|
<!-- Fonctionnalites -->
|
<div class="features">
|
<div class="feature">
|
<strong>Moderne</strong>
|
Design responsive
|
</div>
|
<div class="feature">
|
<strong>Securise</strong>
|
Protection CSRF
|
</div>
|
<div class="feature">
|
<strong>Decimales</strong>
|
Support total
|
</div>
|
<div class="feature">
|
<strong>Historique</strong>
|
10 derniers calculs
|
</div>
|
</div>
|
</div>
|
|
<script>
|
// Auto-focus sur le premier champ après soumission
|
document.querySelector('input[name="num1"]').focus();
|
|
// Raccourcis clavier
|
document.addEventListener('keydown', (e) => {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
// Enter sans Shift = soumettre
|
} else if (e.key === '+' || e.key === '-' || e.key === '*' || e.key
|
=== '/' || e.key === '%') {
|
// Sélectionne l'opération avec le clavier
|
const operation = e.key === '*' ? '*' : e.key === '/' ? '/' :
|
e.key;
|
const radio =
|
document.querySelector(`input[value="${operation}"]`);
|
if (radio) {
|
radio.checked = true;
|
e.preventDefault();
|
}
|
}
|
});
|
</script>
|
</body>
|
| </html> |