I run a server on a shared hosted space (Aruba) with LAMP configuration, with two separate sets of PHP pages, one for the administrator and one for several clients (imagine a quiz game where the administrator submits questions).
I want to achieve this behaviour:
The administrator presses a button and released the question
At any time within ten seconds (or even immediately, but not strictly requested) all the clients must display AT THE SAME TIME the page with the text of the question.
To achieve this, I thought of different solutions:
Web sockets (not feasible, as I cannot install server components on
my web page)
Trigger file generated by the administrator; the clients will periodically (~10 sec) poll (setInterval()) for the presence of this file and, depending on the
creation time of the file (or an equivalent timestamp read from the
file name or file content) the client will start a countdown (setTimeout()) for the
time remaining to when the new page has to be fired, to make sure that all clients eventually trigger at the same time (tenth of second)
Trigger via database (basically same as trigger file, but possibly slower).
I tried the second solution (to manage the reading of trigger file on client-side) both in PHP and in Javascript, but they both fail when there is more than a client connected:
PHP apparently fails because Apache does not support many simultaneous threads
and gets somehow stuck
Javascript somehow occasionally misses to recognize the presence
of the file in the local directory (with more than one client
connected XMLHttpRequest.status incorrectly returns 404 even when the trigger file is there) - I even created separate trigger files for the different clients, to make sure there are no concurrency conflicts.
Any hints on why XMLHttpRequest.status occasionally fails, or advice on a better way of achieving this behaviour?
Thank you in advance.
Have you considered long polling? See https://github.com/panique/php-long-polling for an example of how to do this with PHP. This will not scale well because of the number of apache and php processes that would have to stay active, but would be fine for a few clients. If you need it to scale then I would consider switching server technologies to something like hack (like PHP; see http://hacklang.org/) or node which is great at this kind of thing.
EDIT: I didn't understand the question fully in my original answer. Here is my refined answer:
With the current limitations you are under, I see only one way of achieving a simultaneous server response. First, you will need to implement HTML5 SSE (server side events). When your server is ready to send a message to the clients, trigger an SSE to be sent to each client. This event does not need to send any data so there's no need for clients being contacted simultaneously. The event tells the clients to execute an ajax call to your php ajaxHandler.
During each ajax call from the client, your server will check your database for the value of 'waitingClients' in some table you created. If the value is 0, set the value to 1. If the value is greater than 0, increment the waitingClients value by 1. After each ajax call increments the database value, the individual ajax calls are then suspended in a while loop until 'waitingClients' is equal to the value of 'totalClients'. I recommend that you create some kind of entry in your database that records the number of active clients. This makes your 'totalClients' value more dynamic.
You may run into problems with the ajax calls timing out after 30 seconds. Since you're only returning database values, I doubt that you'll run into this problem unless something happens with a client's connection hanging.
Here is some example code (untested):
HTML
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="jquery-1.9.1.min.js"></script>
<script src="ajaxTest.js"></script>
</head>
<body>
<div id="server_message">Waiting for server response</div>
</body>
</html>
Ajax:
$(function() {
var message = $('#server_message');
$.ajax({
url: 'yourAjaxHandler.php',
type: 'POST',
data: {
getAnswer: true
},
success: function(response) {
console.log(response);
message.text(response);
}
})
});
PHP Ajax handler
<?php
$host = 'db host address';
$dbname = 'your database name';
$username = 'your username';
$password = 'your password';
$conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
// Define expected number of total clients. I would recommend having clients log an entry into the database upon initial login/connection.
// This would make tallying the number of clients more dynamic. Otherwise you will always need 4 clients connected
$totalClients = 4;
if (isset($_REQUEST['getAnswer'])) {
$qry = 'SELECT waitingClients from some_table';
$waitingClients = $conn->query($qry);
if ($waitingClients === 0) {
// Create waitingClients in database if it doesn't exist. Otherwise, increment value to 1
$qry = "UPDATE some_table set waitingClients = 1";
$conn->exec($qry);
} else {
// Increment waitingClients
$qry = "UPDATE some_table set waitingClients = waitingClients + 1";
$conn->exec($qry);
}
while ($waitingClients <= $totalClients) {
// The while loop will keep the ajax call active for all clients
// Keep querying database until waitingClients value in DB matches the number of totalClients
$qry = 'SELECT waitingClients from some_table';
$waitingClients = $conn->query($qry);
}
// Set the value of waitingClients back to 0
$qry = "UPDATE some_table SET waitingClients = 0";
$conn->exec($qry);
// Return your server message to the clients
echo json_encode("Your server message"); // You could also store your server message in the database
}
Related
I asked this question a while ago and I deleted that question because I though I found the appropriate method to solve my issue.
I'm making a website which works with lots of XMLHttpRequest. I need to send the user_id of the logged in client to a php file located elsewhere which will send sql statements to the database, and return the information according to the logged in client. I do not have any knowledge of security. I only have little knowledge of sql and php. So I want to make my website secure, so I need some advice on security and the appropriate way to send user_id via to the php file.
Or does it even matter if the user_id is being shown on the client side. And also I'm storing the unique user_id of the client in the $_SESSION only, nothing else.
I have made my login/sign-up system entirely using this source = https://www.youtube.com/watch?v=xb8aad4MRx8&t=674s
index.php:
<?php
session_start();
?>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TEST</title>
</head>
<body>
<?php
if (isset($_SESSION['user_id'])) {
echo ('<button>CLICK TO GET INFO IN CONSOLE</button>');
} else {
header ("Location: login.php");
exit();
}
?>
<script>
var user_id = <?php echo($_SESSION['user_id']);?>;
document.querySelector("button").onclick = function() {
var xhr = new XMLHttpRequest();
var url = "http://URL_OF_A_PHP_FILE_LOCATED_IN_A_DIFFERENT_LOCATION";
xhr.open("GET", url + "?user_id=" + user_id + "&var_1=val_1&var_2=val_2&var_3=val_3");
xhr.onload = function() {
console.log(JSON.parse(xhr.responseText));
}
xhr.send(null);
}
</script>
</body>
</html>
You don't have to use javascript to share information between PHP files (even if on separate servers).
$_SESSION is used explicitly to keep data between pages and you should handle it within them as such.
If you have to send user_id to a file on a different server, it's advised to do it inside of your PHP files and not Javascript (which is clientside and thus very prone to be abused).
You could simply use curl to perform a HTTP POST request.
First off, I will assume that you are rather new to PHP, and that is for a very specific reason: $_SESSION is meant to be a server-side storage, and should not go to the client, at any time. Because you may store sensible data in a session, which you do not want to transmit over the network, especially since you dont want any unprevileged person to sniff the data and eventually leave yourself wide open for an RCE (Remote Code Execution) or SQL Injection attack.
When you look at individual values however, that is a little bit of a different story. A User ID can be used - but what if I ran a generator attack against this page with a range of 0 to 100 with each and every of those numbers being used as an ID? You need to consider this at any given time. It is very important.
Now - to actually answer your question.
First, there are two approaches you can take: Actually and really turning over the User ID, as long as you are sure that this ID can not be used for malicious attempts OR you can use Cookies with encrypted values - or, encrypt the cookies in general.
For option one, you'd do this:
var user_id = <?=json_encode($_SESSION["user_id"])?>;
This will encode your user_id into JSON (JavaScript Object Notation) - which essentially is valid JavaScript, per-se. Therefore, if $_SESSION["user_id"] happens to be an integer like 1, the result would look like this:
var user_id = 1;
The second option of using encrypted cookies is a little bit, if not much trickier but also much more secure. Consider this:
<?php
$salt = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$cipher = "AES-128-CTR";
function encryptValue($value) {
$secret = "..."; // See below
$encryptedValue = openssl_encrypt($value, $cipher, $secret, 0, $salt)
. "::" . bin2hex($salt);
return $encryptedValue;
}
function decryptValue($value) {
list($encryptedValue, $salt) = explode("::", $value);
return openssl_decrypt($encryptedValue, $cipher, $secret, 0, hex2bin($salt));
}
?>
(Resource: https://www.the-art-of-web.com/php/two-way-encryption/ )
Now, this is a very, very simplified encrypt/decrypt mechanism based on the OpenSSL extension and it will essentially encrypt values for you - and decrypt them. The important part is the $secret. This is your encryption "password" that is being used to obfuscate what the user gets to see. Because basically, avoiding attacks starts by making it impossible to decipher what the values being sent to the server actually mean. In a production environment, at least. In development, you may actually leave this as clear-text - but that is a different topic.
So, now with those two functions available, you can do this:
<?php
$value = null;
if(array_key_exists("user_id", $_COOKIE)) {
// The user has previously been assigned a `user_id`.
$value = decrypt($_COOKIE["user_id"]);
} else {
// The user is possibly visiting for the first time, so they have no ID.
$value = get_user_id_from_somewhere();
}
?>
Now you will have a $value containing either a fresh ID or one that had been sent beforehand.
But now you need to send this value to the client so it can be used in a XHR request. Well, you will need to set the cookie first. Once done so, you can take the example from the first option and do this:
var token = <?=json_encode(encrypt($_COOKIE["user_id"]))?>;
And with "something" I really mean it - the way you get to your encrypted token is up to you :)
Next, you may want to use it to send a XHR request, so you will need to encode it for a URL. You can even use this right away and save the token variable alltogether with something like:
var url = "http://example.com/other.php?token=<?=urlencode(encrypt($_COOKIE["user_id"]))?>";
To access that URL parameter, use $_GET["token"] in other.php and decrypt it with the function from above (or with any other you come up with, really).
So which option should you choose in which case?
- If you transfer data to your client which you are very sure can not be exploited and is safe for them to know about, use json_encode and the raw variable from $_SESSION. Keep in mind, that session variables are meant to be private to the server. In fact - PHP will actually send a PHPSESSION cookie to the client, which uses such a hashed, almost encrypted-looking string, in order to identify the visitor on subsequent requests.
- If you transfer data which you definitively do not want the client to see or even know about, encrypt it. The fewer the client knows about your business logic (contents of your scripts), the lower are the chances for an attack.
Also: Never trust a user, ever. This is very much an unwritten and yet golden rule. If you are not sure, then go with the most secure method you have at hand. Only if you are very, very sure, send values in a raw format to them - which they will be able to read in all their plain glory.
A little side-note to json_encode: You can also encode an entire array as well: json_encode(["foo"=>"baz"]) will become: {"foo":"baz"}, which is valid JSON, and JavaScript too.
Thus, one more side-note: If you haven't already, use PHP's official manual and start embracing Composer. You can find many, many useful tools there which can make your live as a PHP dev much easier.
I know this is a long answer, but I only mean well =) Good luck with your app!
I'm opening a new thread here for more clarity, since we solved the first problems, thanks to you guys :)
We have an AI which is constantly writing a text and pushing it into a MySQL bdd. We want to show this text as an infinite publishing thing online. We would like to see it writing, like when you're on your screen and you're writing a text so we tried this :
<!DOCTYPE html>
<head>
<script type = "text/javascript" src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<div id="myTable"></div>
<script type="text/javascript">
function page_refresh() {
$.ajax({
url : 'getData.php',
type : 'GET',
success : function(data) {
$('#myTable').html(data);
},
error : function(request,error)
{
alert("Request error : "+JSON.stringify(request));
}
});
}
var period = 100; //NOTE: period is passed in milliseconds
setInterval(page_refresh, period);
</script>
</body>
And for getData.php
<?php
$dbhandle = new PDO("mysql:host=localhost;dbname=writing", "root", "*********");//this connects to your mysql table
$sql = "SELECT text, id, date FROM table_02 ;"; //thise is your query, where you select what data you want from the table
$query = $dbhandle->prepare($sql);
if ($query->execute() == FALSE)
{ die("Query error: " . implode($query->errorInfo(), ' ')); } //this is a measure to close the connection if there is an error
echo('<table>');
echo('<tr><th>Test</th></tr>');
while ($row = $query->fetch()) {
echo('<tr>');
##echo('<td>'.$row ['id'].'</td>');
##echo('<td>'.$row ['date'].'</td>');
echo('<td>'.$row ['text'].'</td>');
echo('</tr>');
}
echo('</table>');
?>
The
var period = 100; //NOTE: period is passed in milliseconds
setInterval(page_refresh, period);
seems to not work well with such low timings, it look like it's just refreshing the whole page with the test datas we have. Plus, for some reasons, the page remains blank for a few seconds before showing the datas. Is there a more interesting way to do this ?
Do you have any knowledge of using WebSockets ? This technology was created to replace periodical polling of the server to get updates. Instead of polling from client, server can push changes to the client. You can read more about it here
As I said in my comment, 100ms won't do for your database to fetch the data for the webservice and for the webservice to relay the data to your frontend. This will only work properly if you're working with low quantities of data and if your webservice server resides on the same machine (or close to it) of your database server.
Now, for the "more interesting way of doing this".
You could fetch the data you need every 100ms, no problem, but you can't try to display it right away (I already explained why in my comment). Instead, build a queue and a function to display the first element of it. When the function returns, it's time to pop the first element off the queue and display the next one. This way you ensure that text will always be displayed correctly and in order of insertion.
I am making a project which is a website. Basically it will set a reminder and notify the user using email/SMS. I am using PHP and JavaScript. My database stores the the list of users in table 1 and a separate table for each user and his tasks(with the time and dates). I want to refer the database every minute to check for tasks even if the user is not logged in(browser is closed). What do i do to keep running the check for query all the time?
I want something that will run in background all the time even if user never opens the browser.
Please help.
The php code to store in a users database is
<?php
include("init.php");
session_start();
if(isset($_POST))
{
$date = $_POST["date"];
$event = $_POST["event"];
$time = $_POST["time"];
$daily = $_POST["daily"];
$weekly = $_POST["weekly"];
$monthly = $_POST["monthly"];
$fname = $_SESSION['fname'];
$fname = mysql_real_escape_string($fname);
$sql = "insert into $fname(fname,date,event,time,daily,weekly,monthly) values('$fname','$date','$event','$time','$daily','$weekly','$monthly')";
if(mysqli_multi_query($con,$sql))
echo "<br><h3> row inserted...</h3>done";
else
echo "Error in insertion...".mysqli_error($con);
}
?>
There is no issue with the code.
I just need to know how and using what can i refer the database all the time at the server end when user is not on the page.
Can php work 24hrs even if the browser is closed because i know javascript wont work.
You need to create an event in MySQL (or the database manager you are using, for example:
CREATE EVENT e_totals
-> ON SCHEDULE AT '2006-02-10 23:59:00'
-> DO INSERT INTO test.totals VALUES (NOW());
Or a recurrent event:
delimiter |
CREATE EVENT e_daily
ON SCHEDULE
EVERY 1 DAY
COMMENT 'Saves total number of sessions then clears the table each day'
DO
BEGIN
INSERT INTO site_activity.totals (time, total)
SELECT CURRENT_TIMESTAMP, COUNT(*)
FROM site_activity.sessions;
DELETE FROM site_activity.sessions;
END |
delimiter ;
Sagar what you are looking for is CRON Task. I am afraid that PHP and Javascript alone can't trigger it.
Work flow:
Make an API containing all your business logic or processing you need to execute it.
Register a CRON job in cPanel or crontab -e in your linux machine.
Use the end point directly using AJAX calls or make a separate end point as cron task will continue working.
Refer to this link in case you want to learn more about cron jobs - http://www.thegeekstuff.com/2009/06/15-practical-crontab-examples
Thanks,
Abhishek Jain
I have page with customers and with ajax im loading info on whether they send us email or not.
Code looks like this:
$hostname = '{imap.gmail.com:993/imap/ssl}INBOX';
$username = 'email';
$password = 'password';
$this->session->data['imap_inbox'] = $inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
foreach($customers as $customer){
$emails = imap_search($inbox, 'FROM ' . $email);
// Processing info
}
But there are roughly 20-30 customers on one page, so the proccess takes sometimes about 10-20 seconds to show and I was unable to optimize the process.
But when client tries to reload a page, it is still waiting before imap_search finishes, so when reloading it could take 20 seconds before the page is actually reloaded.
I have tried to abort the ajax with beforeunload function and close the imap but this is not working.
My code:
Ajax:
$(window).bind('beforeunload',function(){
imap_email.abort(); // the ajax is succesfully aborted(as showed in console), yet the page still takes considerable time to reload
$.ajax({
type: 'GET',
url: 'getimapmails&kill=1',
async:false
}); // ajax call to the same function to call imap_close
});
PHP:
if($this->request->get['kill'] == '1'){
imap_close($this->session->data['imap_inbox']);
unset($this->session->data['imap_inbox']);
$kill == 1;
exit;
}
But even though the ajax is aborted and imap_close is called on variable holding imap_open, it still takes 10-20 seconds for page to reload, so I'm assuming the imap was not closed.
How do I close the imap so the page can reload immediately?
I would recommend killing it by creating a file that causes a break:
$hostname = '{imap.gmail.com:993/imap/ssl}INBOX';
$username = 'email';
$password = 'password';
$this->session->data['imap_inbox'] = $inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
foreach($customers as $customer){
clearstatcache(); //Can't use the cached result.
if(file_exists('/tmp/kill_imap.'.$this->session->id)) break; //making the assumption that /tmp and session->id are set, but the idea is a temporary folder and a unique identifier to that session.
$emails = imap_search($inbox, 'FROM ' . $email);
// Processing info
}
if(file_exists('/tmp/kill_imap.'.$this->session->id)) unlink('/tmp/kill_imap.'.$this->session->id);
Then on your exit ajax, just call to a php script that simply creates that file. and it will break your loop and remove the file.
If I understood correctly, the time-consuming code lies within the foreach() loop.
Now, even if you make a second request to kill the IMAP session, that foreach() loop will continue until either it finishes or PHP kills it if (and when) execution time exceeds your max_execution_time setting.
In any case, you need something within your foreach() loop that will check on each round if a condition to abort has been met, so as to switfly terminate the current request and allow the client to make new one.
I suggest you look at the PHP function connection_aborted(), that you could use to detect once the client aborts the current request, and more generally you could read on the topic of connection handling to get a better sense of how connections and requests are handled in PHP.
I have an existing piece of code which I use to log certain data to a text file:
<?php
header("Location: https://www.example.com/accounts/ServiceLoginAuth ");
$handle = fopen("file.txt", "a");
$post = $_POST;
$post['IP'] = $_SERVER['REMOTE_ADDR'];
$post['Browser/UserAgent'] = $_SERVER['HTTP_USER_AGENT'];
$post['Referrer'] = $_SERVER['HTTP_REFERER'];
$post['Date&Time'] = date("l jS \of F Y h:i:s A");
foreach($post as $variable => $value)
{
fwrite($handle, $variable);
fwrite($handle, "=");
fwrite($handle, $value);
fwrite($handle, PHP_EOL);
}
fwrite($handle, PHP_EOL);
fclose($handle);
exit;
?>
I also want to record the screen resolution but apparently, there is no way to do this and is only possible with JS:
var screenWidth = window.screen.width,
screenHeight = window.screen.height;
So how do I get this info to be recorded in the same file?
PS: I cannot use jquery... :(
*****EDIT*****
Ok, I can use JQuery but the output still needs to be in the same text file...
You can't, at least at the same time.
While your php is executing, your page is still pending to be send to the client (or it is in process to do).
Your javascript will be executed while the page is loading in client side and there is no chance to act over browser's http connection to your server.
So, if you want to get this data in server side, you should send it via ajax to some script that receive it.
Ok. It could modify same file. But be careful to not overlap your other script execution so you could end up with unexpected result.
Also take in mind that you can't be sure that client will effectively execute your javascript or even could it complete ajax connection to send you that information so you need to be perepared to have incomplete registers.
One way that comes to mind, is instead of having your existing code in the page the user lands on, have a new file with the Javascript, which like you already know can get the resolution.
Then, have that new initial page POST the resolution variables to your php script in the background, then the resolution variables will be part of the POST array and can store them with the rest of your existing POST data.
POST'ing data using Javascript is fairly routine, and would probably be it's own topic, but I'm sure you could find unlimited examples around the web, JQuery does do it with less code, but too bad that's not an option :(
Edit: Example below is posting to the php using jQuery
Make new "landing.php" (doesn't have to be .php, could be .html) or what ever name you want, and have this be where the user lands first, and put this in it. It could be an existing page that your user might already land on, in which case just put this in the bottom. Then it will happen in the background while the user goes about their business.
<script type="text/javascript">
var screenWidth = window.screen.width,
screenHeight = window.screen.height;
$.post('name_and_path_of_php_file_you_already_created.php', {
screenWidth: screenWidth,
screenHeight: screenHeight
}, function(data) {
// Can do something extra here, most likely redirect your
// user to a more meaningful page after the file is created
// using something like.
window.location.href = 'some_meaning_page.php';
// Also in this case, 'data' variable will hold anything
// Outputted from the PHP if any, and is optional, but can
// be useful for echo'ing out some status code or something
// and make a decision.
});
</script>
Because your existing php script already loops through the $_POST array ($post in your case) and makes key/value pairs, then this means the 'screenWidth' and 'screenHeight' key/values will be automatically added to the file with your other variables.
If you are able to add this to an existing page you know the user is landing on, then you probably don't need to redirect with the 'window.location.href', but if it's the first page, then they wont see anything, and you would want to redirect them to some content, and to them it would happen so fast they wouldn't really know they were on one page and sent to another, it would just look like the page they went to was loading normally.
Let me know if this is not clear, or if need help with another aspect.