Execute imap_close not working - javascript

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.

Related

Php script is automatically being invoked multipletimes

I am facing a strange issue here.
I am using javascript ajax(I used jquery). Now the scenario is;
One ajax call is invoking a php script which is basically a long running process and it sets some session variables.
Later in some intervals(lets say in each 2 sec) I am running another ajax calls to check the session variables to know when the process(first php script execution) is completed.
First php script is fetching data from database and wring it into a file. In each fetching I am counting the loop number and storing it into a session variable to keep some kind of tracking record. Like;
$i=0;
$_SESSION['time']=date('m-d-Y H:i:s');
while(...)
{
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);
session_start();
$_SESSION['tracksatus']="loop number : ".$i." time is :"$_SESSION['time'];
session_write_close();
$i++;
......
......
}
Another php script which I am invoking via setInterval ajax is just doing like;
echo $_SESSION['trackstatus']
The set interval ajax is returning me like;
loop number 1 time is m-d-Y H:i:s
loop number 5 time is m-d-Y H:i:s
loop number 8 time is m-d-Y H:i:s
......
Then after few call again;
loop number 1 time is m-d-Y H1:i1:s1
.....
Notice the change of H:i:s to H1:i1:s1
So as per my understanding the php script is invoking twice. And for your information same code was working just before 12 hrs may be. And I faced this issue before and somehow solved it(trial and error so I don't know how or may be automatically....ok actually I have no clue).
Can you please give me an insight what I am doing wrong?
Please mention if you need more information.
And the funny thing is that it is working as expected just after asking this question without even changing a single line of code. But I want to know the reason.
I think that I know what the reason, PHP writes session variables to file, but it do it only on end of script execution, so you can`t see the changes of session in another script before end of long one.
You can fix it by adding session_write_close(); session_start(); after each change of session data.
session_write_close will write changes to HD, so another script can read it.
session_start will load session from HD, but make sure that your another script make no changes for a session, these changes will be overwritten by your long script.
And one more thing if you are using separate domains:
Before actual AJAX call happen your browser sends OPTIONS request to the same URL for checking CORS headers. So on start of your script check the HTTP METHOD and if it HEAD or OPTIONS make die();
Instead of using sessions, try using a temp file to keep count with a dynamic ID
Javascript
var time = Date.now();
$.get('/firstURL?time='+time);
setInterval(function(){
$.get('/secondURL?time='+time, function(response){
console.log(response);
}
}, 1000);
PHP 1st URL
<?php
$id = $_GET['time'];
$count = 0;
while(...) {
// Do your stuff
$count++;
file_put_contents("/tmp/{$id}", $count);
}
?>
PHP 2nd URL
<?php
$id = $_GET['time'];
$count = 0;
try {
$count = file_get_contents("/tmp/{$id}");
} catch(Exception $e) {}
echo $count;
?>
As other have said PHP does not write the session until execution has finished. you better off creating a php function that you call that writes a file with the progress and then your second ajax call just reads the file.
function updateCreateProgress($jobStartTime, $progress){
file_put_contents('/tmp/'.$jobStartTime.'.txt', $progress);
}
function completeProgress($jobStartTime){
unlink('/tmp/'.$jobStartTime.'.txt')
}
now your second script can check for '/tmp/'.$jobStartTime.'.txt' if it's there read it using file_get_contents if its not there report back it has finished.
Try adjusting to something like this:
$i=0;
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);
session_start();
$_SESSION['time']=date('m-d-Y H:i:s');
while(...)
{
$_SESSION['tracksatus']="loop number : ".$i." time is :"$_SESSION['time'];
session_write_close();
session_start();
$i++;
......
......
}
You started talking about $_SESSION before calling session_start();
If you call ajax with GET method - you must set "cache:false" option.
Yes, you must protect your php script from other requests. With unique key (GET parameter) or session.
php lock session data for single call and release it only when this call end. Using session_write_close() when script still working - bad practice. Maybe you want save into session something more after loop but before using this data from other requests.
Flexible and clear solution:
1) script1.php - invoke from ajax for start long job.
2) script2.php (long job here) - run directly from script1.php as background without wait, or add new cron job (insert into table) and run script2.php from cron (check jobs every second or other time).
3) script3.php - check job status (ajax).
For "communication" between script2.php and script3.php can be use database or special file with flock(), clearstatcache() and flush().

allow PHP script with long execution time to send updates back to the browser

I looked over a few of the questions, namely
Show progress for long running PHP script
How do you run a long PHP script and keep sending updates to the browser via HTTP?
and neither one seems to answer my question, part of which seems to be "how do I do this?" and the other half is "Hey, the way I'm doing this right now - is it the best way? Could I code this better?"
I have a simple ajax script that sends some data over to a PHP script:
$.ajax({
type: 'POST',
url: 'analysis.php',
data: { reportID:reportID, type:type, value:value, filter_type:filter_type, filter_value:filter_value, year:year },
success:function(dataReturn){
analysis_data = JSON.parse(dataReturn);
/* do stuff with analysis_data... */
});
This PHP script takes about 3 minutes to run, as it loops through a database and runs some pretty complex queries:
<?php
session_start();
ob_start();
ini_set('max_execution_time', 180);
$breaks = [ 1000, 2000, 4000, 6000, 8000, 10000, 20000, 50000, 99999999 ];
$breaks_length = count($breaks);
$p = 0;
foreach ( $breaks as $b ) {
$p++;
$percentage_complete = number_format($p / $breaks_length,2) . "%";
$sql = "query that takes about 20 seconds to run each loop of $b....";
$query = odbc_exec($conn, $sql);
while(odbc_fetch_row($query)){
$count = odbc_result($query, 'count');
}
$w[] = $count;
/* tried this... doesn't work as it screws up the AJAX handler success which expects JSON
echo $percentage_complete;
ob_end_flush();
*/
}
echo json_encode($w);
?>
All of this works - but what I'd really like to do is find a way after each foreach loop, to output $percentage_complete back to the user so they can see it working, instead of just sitting there for 2 minutes with a FontAwesome icon spinning in front of them. I tried using ob_start();, but not only does it not output anything until the page is done running, it echoes the value, which is then part of what is sent back to my AJAX success handler, causing it to screw up. (I need the output in a JSON_encoded format as I use it for something else later.)
So far in threads I've read, my only thought is to start the $breaks array loop on the previous page, so instead of looping 6 times on the same page, I loop once, return an answer, then call analysis.php again using the second element of the $breaks array, but I'm not sure this is the best way to go about things.
Also - during the 3 minutes that the user is waiting for this script to execute, they cannot do anything else on the page, so they just have to sit and wait. I'm sure there's a way to get this script to execute in such a way it doesn't "lock down" the rest of the server for the user, but everything I've searched for in Google doesn't give me a good answer for this as I'm not sure exactly what to search for...
You are encountering what is know as Session Locking. So basically PHP will not accept another request with session_start() until the first request has finished.
The immediate fix to your issue is to remove session_start(); from line #1 completely because I can see that you do not need it.
Now, for your question about showing a percentage on-screen:
analysis.php (modified)
<?php
ob_start();
ini_set('max_execution_time', 180);
$breaks = [ 1000, 2000, 4000, 6000, 8000, 10000, 20000, 50000, 99999999 ];
$breaks_length = count($breaks);
$p = 0;
foreach ( $breaks as $b ) {
$p++;
session_start();
$_SESSION['percentage_complete'] = number_format($p / $breaks_length,2) . "%";
session_write_close();
$sql = "query that takes about 20 seconds to run each loop of $b....";
$query = odbc_exec($conn, $sql);
while(odbc_fetch_row($query)){
$count = odbc_result($query, 'count');
}
$w[] = $count;
/* tried this... doesn't work as it screws up the AJAX handler success which expects JSON
echo $percentage_complete;
ob_end_flush();
*/
}
echo json_encode($w);
check_analysis_status.php get your percentage with this file
<?php
session_start();
echo (isset($_SESSION['percentage_complete']) ? $_SESSION['percentage_complete'] : '0%');
session_write_close();
Once your AJAX makes a call to analysis.php then just call this piece of JS:
// every half second call check_analysis_status.php and get the percentage
var percentage_checker = setInterval(function(){
$.ajax({
url: 'check_analysis_status.php',
success:function(percentage){
$('#percentage_div').html(percentage);
// Once we've hit 100% then we don't need this no more
if(percentage === '100%'){
clearInterval(percentage_checker);
}
}
});
}, 500);
I have done this a couple different ways, but the pattern I like the best is to have three scripts (or one controller to handle all of this), analysis_create.php, analysis.php, and analysis_status.php. The key is to create a DB object that you reference in your status checks (analysis_status.php). analysis_create.php will store all the data in the post into a DB table that will also have a column for percent_complete. The analysis_create.php function should return an ID/Token for the analysis. Once the front-end has the ID, it would post to analysis.php and then after a delay (250ms) kill the request, because you don't want to wait for it to finish. analysis.php should read the data out of the DB and start doing the work. You will need to make sure ignore_user_abort is set properly in your analysis.php script. Once the request to analysis.php is killed, you will start long polling to analysis_status.php with that ID. As analysis.php is working through the query, it should be updating the corresponding DB record with the percentage complete. analysis_status.php should look up this record and return the percentage complete to the front end.
I ran into the same issue. What caused it is different to what people are suggesting here.
Reason was gzip was enabled. Leading to a type of session locking even without an actual session.
Several ways to disable for one specific file:
How to disable mod_deflate in apache2?
Put this in httpd.conf
SetEnvIfNoCase Request_URI getMyFile\.php$ no-gzip dont-vary

Merge JavaScript output with 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.

What is causing a very slow ajax response?

I wrote some PHP code to help me connect to a REST API for a telephone system (ie. ICWS.php.)
Then to make my life easier, I wrote a small script (ie. interations.php) that accepts two parameters: a method and an ID. This script will basically call a public method in my PHP connector.
In addition, I have another script (ie. poll.php). This script will ping the API once every half a second to see if there is a new message available. I am using server-side polling to handle this. The code below will show how poll.php
while(1){
//process Messages
$icws->processMessages();
//show the Calls Queue
$result = $icws->getCallsQueue();
//$current = $icws->getCurrentUserStatusQueue();
echo 'event: getMessagingQueue' . "\n";
echo 'data: ' . json_encode( array('calls' => $result));
echo "\n\n"; //required
ob_flush();
flush();
putToSleep($sleepTime);
}
function putToSleep($val = 1){
if( is_int($val) ){
sleep($val);
} else {
usleep($val * 1000000);
}
}
From my site (ie. phonesystem.html) I start server-side polling "which pings the API once every 1/2 seconds." From the same page, I can also make other direct calls (ie. Dial 7204536695); all requests are done via Ajax.
Here is my code that generates the server-side polling
//Server Side Message Polling
function startPolling(){
var evtSource = new EventSource("poll.php");
evtSource.addEventListener("getMessagingQueue", function(e) {
var obj = JSON.parse(e.data);
if(!obj.calls || obj.calls.length === 0){
console.log('no messages');
phoneKeyPad(false);
return;
}
processMessages(obj.calls);
}, false);
}
$(function(){
startPolling();
});
The problem that I am facing is that when making an ajax call, the response takes way too long (+1 minute).
It seems that the Apache server slows down as using other application becomes a little slower.
What can I check and how can I trouble shoot this problem?

Syncing multiple clients triggered by a server in PHP and Javascript

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
}

Categories