Real time data updates with comet and PHP? - php

I'm looking to implement real time notification updates on my social networking website. I have done some research on comet and i'm really fascinated by it.
From what I understand, this is the basic flow of what happens on a comet server.
Webpage:
Sends an ajax request to server when the document is ready.
Server:
Queries the database every x amount of seconds and returns a json string containing results if any are found.
Webpage:
Receives the result of the json string from the server and sends out another ajax request to do the above process again.
By understanding the flow of how comet works, I've written some PHP and Javascript code.
The JavaScript code uses the jQuery library and sends an ajax request out to the server with the current time in a unix timestamp format as a GET parameter.
$(document).ready(function(){
var timestamp = Math.round(new Date().getTime() / 1000);
function comet2(){
$.ajax({
type : 'GET',
url : 'comet.activities.php?timestamp=' + timestamp,
async : true,
cache : false,
success : function(data) {
alert("current timestamp "+timestamp)
var json = JSON.parse(data);
if(json !== null){
alert(data);
}
timestamp = json[0].timestamp;
setTimeout('comet2()', 1000);
},
error : function(XMLHttpRequest, textstatus, error) {
setTimeout('comet2()', 15000);
}
});
}
//call the comet function because the page has loaded.
comet2();
});
The PHP code will query for new activities by searching the database for new rows by using a timestamp paramater (in this case, a unix timestamp in a query). For this example, I have limited the amount of results to 1.
<?php
set_time_limit(0);
include("models/config.php");
global $mysqli,$db_table_prefix;
$last = isset($_GET['timestamp']) ? $_GET['timestamp'] : 0;
$results = null;
$flag=true;
$stmt = $mysqli->prepare("SELECT id,timestamp FROM uc_user_activity WHERE timestamp > ? ORDER BY timestamp DESC LIMIT 0,1");
$stmt->bind_param("i", $last);
$stmt->bind_result($id,$timestamp);
while($flag){
$stmt -> execute();
while ($row = $stmt->fetch()){
$flag = false;
$results[] = array(
"id" => $id,
"timestamp" => $timestamp
);
}
$stmt -> close();
usleep(100000);
clearstatcache();
}
echo json_encode($results);
?>
The code above doesn't actually 'work' The problem is that if a user posts a new comment, it will fail to add to the database when the comet script is running. This means that the comet script will never return any json result because the statement in the sql query is never met (no new activities are added with a newer timestamp). My ajax code for posting new comments is working 100%, so I know that isn't the problem. Simply 'nothing happens', that is - nothing (no errors) are alerted or outputted to the browser console.
Edit number 3:
I'm seriously struggling to explain what I mean by 'nothing is happening', so I have uploaded an image showing that the database insert fails when the comet script is being called from jquery (notice how the textbox is disabled whilst the comment is being posted via ajax).
What can I do about this? I've spent hours searching the internet trying to fix this/find a similar working example with no avail.
If I change the query in my PHP code to be:
$stmt = $mysqli->prepare("SELECT id,timestamp FROM uc_user_activity WHERE timestamp **<** ? ORDER BY timestamp DESC LIMIT 0,1");
instead of:
$stmt = $mysqli->prepare("SELECT id,timestamp FROM uc_user_activity WHERE timestamp > ? ORDER BY timestamp DESC LIMIT 0,1");
results are instantly alerted to the browser window, comments can be posted again and the script is called again and new posts are displayed. This shows that my code 'is working' fine afterall and it looks like the query is causing the problem...
Can anyone see what is going on here? I have edited this question 7 times now and any guidance would be great as I'm just getting nowhere.
Just so this doesn't get closed, here is my question to round up what I have discussed above:
Are there any better ways of implementing a comet server? I'm not the
most experienced guy ever, but I would really like to learn how to do
this. It seems StackOverflow has this functionality and it works
perfectly - how are they doing it?
I can't possibly write my post in any further detail than this and I would REALLY appreciate some guidance from you awesome people. A suggestion as to why my code 'isn't working' or links to any tutorials explaining how to implement this would be amazing! Thanks in advance and apologies for this monster of a question and all of the edits!

My hunch is that the timestamp value which you are passing returns no results. You get the current time through Javascript. The query queries for all posts after this timestamp.
Can you try to print the query and run the same query manually to ensure that it retrieves data from the DB?

So, for the best available tutorial for Comet with PHP is here.
http://www.zeitoun.net/articles/comet_and_php/start
Like it, if it helps :)
For those who want to use the simple chat solution above in the link with jQuery here is the solution.
<script type="text/javascript">
var Comet = {};
Comet.jquery = {
timestamp: 0,
url: './backend.php',
noerror: true,
initialize: function () {
},
connect: function ()
{
this.ajax = $.ajax({
type: "get",
url: this.url,
data: {timestamp: this.timestamp},
success: function (data) {
// handle the server response
var response = JSON.parse(data);
console.log(response);
//alert(response.timestamp);
Comet.jquery.timestamp = response.timestamp;
Comet.jquery.handleResponse(response);
Comet.jquery.noerror = true;
},
complete: function (data) {
// send a new ajax request when this request is finished
if (!Comet.jquery.noerror) {
// if a connection problem occurs, try to reconnect each 5 seconds
setTimeout(function () {
Comet.jquery.connect()
}, 5000);
}
else {
Comet.jquery.connect();
}
Comet.jquery.noerror = false;
}
});
},
disconnect: function ()
{
},
handleResponse: function (response)
{
$('#content').append('<div>' + response.msg + '</div>');
},
doRequest: function (request)
{
$.ajax({
type: "get",
url: this.url,
data: {'msg': request}
});
}
}
</script>

Related

Ajax/PHP long-polling results in 503 error

I'm playing around with AJAX long-polling and trying to read/update a simple counter (number) value in an MySQL cell by clicking on a button.
The PHP creates an infinite while loop and is checking if the value in the according cell has been modified (MySQL "current_timestamp", UNIX). If it has and the current_timestamp value is bigger then the timestamp the AJAX call was made, it's breaking the loop and sending the updated value and the updated current_timestamp to the client. The AJAX processes the data.
The Problem: It works but after a while I get a 503 error. I guess it's obviously the while loop or other open connections through multiple windows in other browsers (for testing).
PHP-File text.php:
// Connect to database
$con = mysql_connect('XX', 'XX', 'XX');
if (!$con)
{
die('Error' . mysql_error());
}
mysql_select_db('X', $con);
// Get data
$query = mysqli_query("SELECT counter_value, last_modified FROM content WHERE X = 'X' ORDER BY X DESC");
// Start infinite loop
set_time_limit(0);
while (true)
{
// The timestamp of the last ajax call = the last modified timestamp
$last_ajax_call = $_GET['timestamp'];
clearstatcache();
// Get the value of the counter and the last modified timestamp
while($row = mysql_fetch_array($query))
{
$counter_value = $row['counter_value'];
$last_modified= strtotime($row['last_modified']);
}
// If the time of the last modified timestamp is bigger/later than the last ajax call
if ($last_modified > $last_ajax_call)
{
$result = array(
'counter_value' => $counter_value,
'timestamp' => $last_modified
);
$json = json_encode($result);
echo $json;
break;
// If not, try again in 3 seconds
} else
{
sleep(3);
continue;
}
}
// Close database
mysql_close($con);
AJAX Part in js-File:
function getContent()
{
// get timestamp of last modified stored in attribute. The initial/first timestamp attribute is set beforehand.
var timestamp = $('#timestamp').attr('data-timestamp');
$.ajax(
{
type: 'GET',
url: 'test.php',
async: true,
cache: false,
data: {timestamp:timestamp},
success: function(data){
var obj = jQuery.parseJSON(data);
$("#counter").text(obj.counter_value);
$("#timestamp").attr("data-timestamp", obj.timestamp);
getContent();
}
}
);
}
getContent();
So the result is a 503 error which goes away after ca. 10 Minutes and it's working again.
(Any typos/formatting might be the result of cleaning up the code.)
I just started learning PHP and JS, so there might be a few newbie mistakes or weird lines in there, please be nice. Any advice on optimising the code is very appreciated!
It dies because PHP doesn't work the way you think it does.
You have intentionally put an infinite loop into your php in the assumption it will make your code keep looping around and rechecking the next GET request on each loop.
The reality is that the code is executed once for each request, and until execution of the code completes the server doesn't respond.
AJAX long polling requires no special handling in php, it's just an AJAX request in a loop. You might want to include a slight delay in your AJAX code otherwise your server will be hammered with requests.
To be honest this isn't what long polling is for, the idea of it is to update pages without any user interaction to display unread message notifications etc.
If you want to monitor user events like button clicks then bind your AJAX function to the clicking of the button.

jQuery AJAX call in jTable

I am trying to implement paging in jTable.
Can anyone help me understand the code below? I have read the jQuery Ajax documentation and understand all but the URL, specifically what is the url referring to? Why is the url field formatted the way it is? e.g. why /demo/studentlist?jtStartIndex what does this mean?
listAction: function (postData, jtParams) {
return $.Deferred(function ($dfd) {
$.ajax({
url: '/Demo/StudentList?jtStartIndex=' + jtParams.jtStartIndex + '&jtPageSize=' + jtParams.jtPageSize + '&jtSorting=' + jtParams.jtSorting,
type: 'POST',
dataType: 'json',
data: postData,
success: function (data) {
$dfd.resolve(data);
},
error: function () {
$dfd.reject();
}
});
});
}
Currently I have a php script that is calling stored procedures in a mysql database. Is AJAX the only way to implement paging in this case or could I implement the above for paging using jTable by doing something along the lines of:
(Redudant code not included)
//open database etc
$offset = $_POST['jtStartIndex'];
$amount = $_POST['jtPageSize'];
$result = $mysqli -> query ("CALL Proc($offset, $amount);
//while loop using fetch_assoc() to assign row objects to rows array
$jtableResults['Result'] = "OK";
$jtableResults['Records'] = $rows;
//close
echo json_encode($jtableResults);
MySQL Procedure:
CREATE DEFINER='user''#'%' PROCEDURE 'Proc' (offset INTEGER, amount INTEGER)
BEGIN
SELECT * FROM reports Order By idReports ASC LIMIT offset, amount;
END
I believe I cannot do above because jTable expects the jtParams object back in a AJAX call?
What's not to understand, URL is most important part of $.ajax call, basically it is a end point you need to call to get some data. This URL can be absolute like in this case:
var url = 'http://api.themoviedb.org/3/',
mode = 'search/movie?query=',
movieName = '&query='+encodeURI($('#movie-title').val()),
key = '&api_key=470fd2ec8853e25d2f8d86f685d2270e';
$.ajax({
url: url + mode + key + movieName ,
dataType: "jsonp",
async: true,
success: function (result) {
ajax.parseJSONP(result);
},
error: function (request,error) {
alert('Network error has occurred please try again!');
}
});
Or it can be relative like in your case. In your case, url destination file is just some server side class (ASP.NET, PHP would usually have .php extension) that accepts certain parameters and returns a JSON response, something like this:
{
"Result":"OK",
"Record":{"PersonId":5,"Name":"Dan Brown","Age":55,"RecordDate":"\/Date(1320262185197)\/"}
}
Because you are a PHP developer, your destination URL would look like:
demo/StudentList.php?jtStartIndex=' + jtParams.jtStartIndex + '&jtPageSize=' + jtParams.jtPageSize + '&jtSorting=' + jtParams.jtSorting
I forgot one last thing. When working with $.ajax calls, absolute URL is used when client side works independently from server side (like in case of hybrid mobile applications) or in case you want to call some public API (like IMDB, Yahoo weather etc.) . Relative URL is used when you work on a classic web site where destination URL is your web server, basically same source like your site.

Counting clicks changing link href

I asked this question but did not explain it thoroughly. I have a regular link:
Click Me
I want the change the href after the link is clicked 10 times not by the individual use but clicked 10 total times by all users.My jquery is obviously flawed but here is what i have:
var count = 0;
$(document).ready(function(){
$('a').click(function(){
count++;
if(count > 10){
$('a').attr("href","https://www.yahoo.com");
}
});
});
I am new to jQuery but from what ive read cookies and local storage store individual users information not the total websites information. So how could i use ajax with a database to do this? maybe even php?
You have a huge fundamental misunderstanding of how JavaScript works.
Firstly, when someone clicks that link, they're going to be navigated away from your page unless you do something to prevent that (e.preventDefault or return false in jQuery). Once they're navigated away, your counter is lost because is stored locally, in memory, for the life of the page.
Secondly, even if the counter wasn't cleared, or you stored the counter in a cookie, or localStorage, it will only count for a single user. If you want to count the clicks by all users, you're going to have to do that server side. i.e., in PHP.
So... how do we do that? Well, as I said before, when a user clicks that link, they're going to be sent to Google. Your site will have no knowledge of what has occurred.
We have two options to deal with this. We can intercept the click, and use AJAX (more appropriately "XHR") to send a request back to your server, where you can log the click, before forwarding them off to Google.
Or, you re-write the URL to something like /log_click.php?forward=http://google.com. Now when the user clicks the link, they will actually be sent to your log_click.php script, where you can log the click to your database, and then use $_GET['forward'] in combination with header('location: ...') to forward them off to their destination. This is the easiest solution. Through some JavaScript hackery, you can hide the link so that when they mouse over it, they won't even know they're being sent to your site (Google does this).
Once you've accumulated your 10 clicks, you again use PHP to write out a different HTML link the next time someone views that page.
HTML
<a href='http://www.google.com' data-ref='99'>Click Me</a>
Javascript
$("a").click(function() {
var _this = $(this);
var ref = $(this).data('ref');
$.ajax({
url: '/click_url.php',
type: 'POST',
data: {id:ref}
success: function(href) {
if(href != '')
_this.attr("href",href);
}
});
}
PHP (click_url.php)
if($_POST['id'] > 0){
$id = $_POST['id'];
//count increment
$sql = "UPDATE table SET count = count + 1 WHERE id = '$id'";
mysql_query($sql);
//get row count
$sql = "SELECT * FROM table WHERE id = '$id' LIMIT 1";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
//if count > 10 , return new url
if($row['count'] > 10){
die($row['href']);
}
}
While clicking the link you can call an ajax request and increment the count in the server. So that u should remove link from href and call manually by using javascript window.location.href each time. Hope that helps
var count = 0;
$(document).ready(function(){
$('a').click(function(e){
e.preventDefault();
count++;
if(count > 10){
$('a').attr("href","https://www.yahoo.com");
}
});
});
and use ajax like below
//send set state request
$.ajax({
type: "POST",
contentType: "text/xml; charset=utf-8",
datatype: "xml",// you can set json and etc
url:"your php file url",
data: {test:test1},// your data which you want to get and post
beforeSend: function (XMLHttpRequest) {
// your action
},
success: function (data, textStatus, XmlHttpRequest) {
// your action },
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});
for more deatils see Ajax
Mark's answer is more useful, even you want to implement for the sake of some constraints then try below with jQuery 1.9
I have implemented for 3 clicks, AFAIU you need to change the URL on every 3rd successive click
var c=0;
$(document).on('click', 'a#ten', function(e){
c++;
alert('clicked ' + c + ' times');
if(c%3 == 0) {
$('a').attr("href","https://www.yahoo.com");
alert('changed');
c = 0;
}
e.preventDefault();
})
working DEMO
You must save no of times that link has been clicked in the database with php. when you render the link(with php) check the no of times it has been called before and decide what link to render.
Click Me
write this javascript in the page wher you place your link
$(function()
{
$('.mylink').click(function()
{
$.ajax({
type: "POST",
url: "listening/end/point", // enter your counting url here
async: false
);
});
});
And in server on the listening end point write php script to store no of times that link has been called.

Refresh div, but only if there is new content from php file

Background Info
I'm fiddling around with some PHP and AJAX at the moment, to try and get the code working for an auto refreshing div (every 10 seconds), that contains comments.
Here is javascript code I am using to refresh the div..
<script type="text/javascript">// <![CDATA[
$(document).ready(function() {
$.ajaxSetup({ cache: false });
setInterval(function() {
$('#content_main').load('/feed_main.php');
}, 5000);
});
// ]]></script>
The code that will populate the div called "content_main", which is in feed_main.php, essentially accesses the database and echo's out the latest comments ...
Question
Is it possible, to only load the div "content_main" if the data inside of it, hasn't changed since the last time it was loaded?
My logic
Because I'm relatively new to javascript and AJAX I don't quite know how to do this, but my logic is:
For the first time it is run..
load data from feed_main.php file
Create a unique value (perhaps a hash value? ) to identify say 3 unique comments
Every other time it is run...
load the data from feed_main.php file
create a NEW unique value
check this value with the previous one
if they're the same, don't refresh the div, just leave things as they are, but if they're different then refresh..
The reason why I want to do this is because the comments usually have pictures attached, and it is quite annoying to see the image reload every time.
Any help with this would be greatly appreciated.
I've faced similar problem not too long ago, i assume that you using mysql or something for your comments storage serverside ?
I solved my problem by first adding timestamp integer column to my mysql table, then when i added a new row, i'd just simply use time() to save the current time.
mysql row insert example:
$query = "INSERT INTO comments (name, text, timestamp) VALUES ('". $name ."', '". $text ."',". time() .");";
step two would be to json_encode the data you sending from serverside:
$output = array();
if ($html && $html !== '') { // do we have any script output ?
$output['payload'] = $html; // your current script output would go in this variable
}
$output['time'] = time(); // so we know when did we last check for payload update
$json = json_encode($output, ((int)JSON_NUMERIC_CHECK)); // jsonify the array
echo $json; // send it to the client
So, now instead of pure html, your serverside script returns something like this:
{
"payload":"<div class=\"name\">Derpin<\/div><div class=\"msg\">Foo Bar!<\/div>",
"time":1354167493
}
You can grab the data in javascript simply enough:
<script type="text/javascript"> // <![CDATA[
var lastcheck;
var content_main = $('#content_main');
pollTimer = setInterval(function() {
updateJson();
}, 10000);
function updateJson() {
var request = '/feed_main.php?timestamp='+ (lastcheck ? lastcheck : 0);
$.ajax({
url: request,
dataType: 'json',
async: false,
cache: false,
success: function(result) {
if (result.payload) { // new data
lastcheck = result.time; // update stored timestamp
content_main.html(result.payload + content_main.html()); // update html element
} else { // no new data, update only timestamp
lastcheck = result.time;
}
}
});
}
// ]]> </script>
that pretty much takes care of communication between server and client, now you just query your database something like this:
$timestamp = 0;
$where = '';
if (isset($_GET['timestamp'])) {
$timestamp = your_arg_sanitizer($_GET['timestamp']);
}
if ($timestamp) {
$where = ' WHERE timestamp >= '.$timestamp;
}
$query = 'SELECT * FROM comments'. $where .' ORDER BY timestamp DESC;';
The timestamps get passed back and forth, client always sending the timestamp returned by the server in previous query.
Your server only sends comments that were submitted since you checked last time, and you can prepend them to the end of the html like i did. (warning: i have not added any kind of sanity control to that, your comments could get extremely long)
Since you poll for new data every 10 seconds you might want to consider sending pure data across the ajax call to save substantial chunk bandwidth (json string with just timestamp in it, is only around 20 bytes).
You can then use javascript to generate the html, it also has the advantage of offloading lot of the work from your server to the client :). You will also get much finer control over how many comments you want to display at once.
I've made some fairly large assumptions, you will have to modify the code to suit your needs. If you use my code, and your cat|computer|house happens to explode, you get to keep all the pieces :)
How about this:
<script type="text/javascript">
// <![CDATA[
$(function () {
function reload (elem, interval) {
var $elem = $(elem);
// grab the original html
var $original = $elem.html();
$.ajax({
cache : false,
url : '/feed_main.php',
type : 'get',
success : function (data) {
// compare the result to the original
if ($original == data) {
// just start the timer if the data is the same
setTimeout(function () {
reload(elem, interval)
}, interval);
return;
}
// or update the html with new data
$elem.html(data);
// and start the timer
setTimeout(function () {
reload(elem, interval)
}, interval);
}
});
}
// call it the first time
reload('#content_main', 10000);
});
// ]]>
</script>
This is just an idea to get you going it doesn't deal with errors or timeouts.
Best And Easy Code
setInterval(function()
{
$.ajax({
type:"post",
url:"uourpage.php",
datatype:"html",
success:function(data)
{
$("#div").html(data);
}
});
}, 5000);//time in milliseconds

Putting mySQL Database Information into a JavaScript Array

What I'm trying to do is create a slideshow by grabbing database information and putting it into a javascript array. I am currently using the jquery ajax function to call information from a separate php file. Here is my php code:
mysql_connect('x', 'x', 'x') or die('Not Connecting');
mysql_select_db('x') or die ('No Database Selected');
$i = 0;
$sql = mysql_query("SELECT comicname FROM comics ORDER BY upldate ASC");
while($row = mysql_fetch_array($sql, MYSQL_ASSOC)) {
echo "comics[" .$i. "]='comics/" .$row['comicname']. "';";
$i++;
}
What I want is to create the array in php from the mysql query and then be able to reference it with javascript in order to build a simple slideshow script. Please let me know if you have any questions or suggestions.
Ok have your .php echo json_encode('name of your php array');
Then on the javascript side your ajax should look something like this:
$.ajax({
data: "query string to send to your php file if you need it",
url: "youphpfile.php",
datatype: "json",
success: function(data, textStatus, xhr) {
data = JSON.parse(xhr.responseText);
for (i=0; i<data.length; i++) {
alert(data[i]); //this should tell you if your pulling the right information in
}
});
maybe replace data.length by 3 or something if you have alot of data...if your getting the right data use a yourJSArray.push(data[i]); I'm sure there's a more direct way actually...
You may want to fetch all rows into a large array and then encode it as a JSON:
$ret = array();
while($row = mysql_fetch_array($sql, MYSQL_ASSOC))
$ret[] = $row
echo json_encode($ret);
Then, on the client side, call something like this:
function mycallback(data)
{
console.log(data[0].comicname); // outputs the first returned comicname
}
$.ajax
(
{
url: 'myscript.php',
dataType: 'json',
success: mycallback
}
);
Upon successful request completion, mycallback will be called and passed an array of maps, each map representing a record from your resultset.
It's a little hard to tell from your question, but it sounds like:
You are making an AJAX call to your server in JS
Your server (using PHP) responds with the results
When the results come back jQuery invokes the callback you passed to it ...
And you're lost at this point? ie. the point of putting those AJAX results in to an array so that your slideshow can use them?
If so, the solution is very simple: inside your AJAX callback, put the results in to a global variable (eg. window.myResults = resultsFromAjax;) and then reference that variable when you want to start the slideshow.
However, since timing will probably be an issue, what might make more sense is to actually start your slideshow from within your callback instead. As an added bonus, that approach doesn't require a global variable.
If this isn't where you are stuck, please post more info.

Categories