How to make a button execute a php script?
Basic scenario:
- Store currently authenticated user's unique id and time in a
.txt
file in the following format (as JSON):
json_encode(['user_id' => 1, 'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s')]);
- Check the stored
user_id
andcreated_at
fields in the file when a user attempts to sign in. - If the file is empty, log the user in and write user's unique id and time to the file.
- If the file is not empty and
user_id
field is same as theid
of the user who attempts to log in andcreated_at
field is not older than 12 hours ago (or your custom logic), just updatecreated_at
field in the file and log the user in. - If the file is not empty and
user_id
field is same as theid
of the user who attempts to log in, but passed more than 12 hours (or your custom logic), ask the user if he/she want to take over another user. - If the file is not empty and
user_id
field is not same as theid
of the user who attempts to log in ask the user if he/she want to take over another user.
Basic implementation:
- Create a
.txt
file in your project directory. - Add these helper functions to your project (
helpers.php
in my case):
if (! function_exists('check_auth')) {
function check_auth(): bool
{
if (! isset($_SESSION['user_id'])) {
return false;
}
if (! file_exists('current_user.txt') || filesize('current_user.txt') === 0) {
return true;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// You can write your own logic here.
return (int) $trace->user_id === $_SESSION['user_id'] && (new DateTime($trace->created_at))->modify('+12 hours') > new Datetime('now');
}
}
if (! function_exists('logout'))
{
function logout()
{
if (isset($_SESSION['user_id'])) {
$trace = json_decode(file_get_contents('current_user.txt'));
if ((int) $trace->user_id === $_SESSION['user_id']) {
file_put_contents('current_user.txt', '');
}
unset($_SESSION['user_id']);
}
}
}
if (! function_exists('redirect')) {
function redirect(string $url, int $status_code = 303): void
{
header('Location: ' . $url, true, $status_code);
die();
}
}
- Create a login page (
login.php
in my case):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
// Redirect user to homepage/dashboard if authenticated.
if (check_auth()) {
redirect('index.php');
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = new PDO('mysql:host=[DB_HOST];dbname=[DB_NAME];charset=utf8mb4', '[DB_USERNAME]', '[DB_PASSWORD]', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();
if (! ($user && password_verify($_POST['password'], $user->password))) {
echo json_encode([
'success' => false,
'message' => 'These credentials don\'t match our records.',
]);
return;
}
// Log user in if another is not authenticated.
if (filesize('current_user.txt') === 0) {
file_put_contents('current_user.txt', json_encode([
'user_id' => $user->id,
'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s'),
]));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// Log user in if the last authenticated user is himself/herself.
if ((int) $trace->user_id === $user->id) {
$trace->created_at = (new DateTime('now'))->format('Y-m-d H:i:s');
file_put_contents('current_user.txt', json_encode($trace));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
// Ask user if he/she wants to take over.
echo json_encode([
'success' => false,
'takeover' => true,
'message' => 'Another user is logged in. Do you want to take over?',
]);
return;
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
</head>
<body>
<form method="post" id="form">
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Email" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" placeholder="Password">
</div>
<div>
<span id="message" style="color: red;"></span>
</div>
<button>Log in</button>
</form>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
$('#form').submit(function (e) {
e.preventDefault();
$('#message').text('');
$.post('login.php', $(this).serialize(), function (response) {
const res = JSON.parse(response);
if (res.takeover) {
// Ask user if he/she wants to take over. If user confirms, run `confirmed()` function.
confirm(res.message) && confirmed();
return;
}
if (res.success) {
// Login is successful. Reload or redirect user to another page.
location.reload();
} else {
// Login failed. Incorrect email or password entered.
$('#message').text(res.message || '');
}
});
});
function confirmed() {
$.post('confirmed.php', function (response) {
const res = JSON.parse(response);
console.log(res.data);
});
}
});
</script>
</body>
</html>
- Check if another user took over currently authenticated user in your pages (
index.php
in my case):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home</title>
</head>
<body>
<?php if (check_auth()): ?>
Welcome friend.
<?php else: ?>
<a href="/login.php">Log in</a>
<?php endif; ?>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
// Check if another user took over currently authenticated user in every 3 seconds.
setInterval(function () {
$.post('check.php', function (response) {
let res = JSON.parse(response);
if (! res.auth && res.redirect) {
location.replace(res.redirect);
}
});
}, 3000);
});
</script>
</body>
</html>
- Implement your check logic (
check.php
in my case):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
if (! check_auth()) {
logout();
echo json_encode([
'auth' => false,
'redirect' => 'login.php',
]);
return;
}
echo json_encode([
'auth' => true,
]);
- Finally, create your action/function for the case when user confirms to take over (
confirmed.php
in my case):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
/**
* Run your action here if user confirms to take over another user.
*/
echo json_encode([
'data' => 'User confirmed to take over another user.',
]);
Tested and works fine. But you should customize it for your needs.
Here the basic concept.
- You need a central storage where the current active user
active_user_id
andlast_updated
is stored (database/file/memcache/redis whatever you want) - You need to poll the current active user for each session - if the current user is not the active user he will logged out and notified, so he knows someone else took over.
Flow
- User loggs in
$_SESSION['user_id'] = user_id
- IF:
$_SESSION['user_id']
!==active_user_id
(central storage - see above) ANDlast_updated
< 10s- TRUE: log in, update
active_user_id
to$_SESSION['user_id']
; updatelast_updated
to now; redirect to main-page - FALSE: show popup "Take over?" -> ok: same as TRUE from above, abort: close popup
- TRUE: log in, update
- While logged-in call
check-active.php
every 5 seconds
check-active.php
(pseudocode):
- IF: user_id from
$_SESSION['user_id']
===active_user_id
fromstorage
- TRUE: update
last_updated
instorage
; return 'ok'; - FALSE: call
session_destroy();
return 'failed';
- TRUE: update
frontend
pseudocode (logged in):
- call
check-active.php
every 5 seconds - handle result
- IF: result === 'ok'
- TRUE: do nothing
- FALSE: redirect to main page like 'main.php?message=' . urlencode('You got replaced')
Why check for
last_updated
> 10s?
Because someone logged in could just close the browser, so we dont know if the session is still active or not. So we check last_updated
(which will be updated every 5 seconds as long as the browser is still open). If last_updated
is > 10s we consider it as "noone is currently active" so we dont have to ask to take over.
Working example
Open this - hit "run" (if not running).
Then open this twice. One in a incognito tab, to get a secondary session to kick yourself out.
Disclaimer: This is absolutely experimental.