This question is a little complex so I will first sum up the context :
My app needs to deliver a important amount of push notificaton at a very precise time. FCM (Firebase Cloud Messaging, remote service that delivers the push notification to the devices), has a maximum of 500 push notification per HTTP request.
I couldn't simply loop over pending push notifications in database and send them to FCM without threads , because I would have to wait the response of each request before sending the following.
Therefore, I created a PHP script in the aim of sending N payloads of 500 push notifications to FCM in N http request sent parellely. I used "parallel" PHP librairy, which is the new standard to have PHP with multithreading, as I understood.
The script does that :
// THIS IS THE THREAD CALLBACK :
$sendNotificationBatch = static function(
string $poolId,
int $batchIndex,
array $firebaseCredentialArray,
array $rawFirebaseNotifs)
{
require_once // all necessary libs ...
require_once __DIR__ . "open_sql_co.php" ; // open a new mysql connection here
$factory = (new Factory)->withServiceAccount($firebaseCredentialArray);
$messagingService = $factory->createMessaging();
$firebaseMessages = [];
foreach ($rawFirebaseNotifs as $k => $rawFirebaseNotif)
{
$message = [....] // preparing FCM payload here ..
$firebaseMessages[] = $message ;
}
DebugLog::info("[Pool ID : ".$poolId.", batch $batchIndex] : Sending ".count($firebaseMessages)." notifications..");
$sendReport = $messagingService->sendAll($firebaseMessages);
DebugLog::info("[Pool ID : ".$poolId.", batch $batchIndex] : Sent.");
// **** mark all the notification we've sent as "sent" status in database ****
$fromId = $rawFirebaseNotifs[0]['id'];
$toId = $rawFirebaseNotifs[count($rawFirebaseNotifs) - 1]['id'];
OSQL::set("update pushnotif__queue set sta = 'sent', time_sent = now()
where sending_pool_id = '$poolId' and ID >= $fromId and ID <= $toId");
foreach($sendReport->getItems() as $k => $sendReportItem)
{
// processing report sent back by FCM...
}
};
// MAIN SCRIPT :
while(1)
{
OSQL::set("lock tables pushnotif__queue write");
// poll database continuously
$notifs = OSQL::get("select * from pushnotif__queue where sta = 'pending' order by ID asc");
if($notifs !== false)
{
$poolId = uniqid("", false);
// mark them as 'sending' state and put the poolId
OSQL::set("update pushnotif__queue set
sta = 'sending',
sending_pool_id = '$poolId',
time_inqueue = now()
where sta = 'pending' limit " . MAX_WINDOW_PER_POOL );
// lock and unlock prevent new "pending" notification to be inserted by other part of the server while we set them to "sending" status
OSQL::set("unlock tables");
$pnotifs = [] ;
//now create one thread per group of 500 notifications
$countBatch = ceil(count($pnotifs) / 500) ;
$firebaseRawNotifs = [];
foreach ($pnotifs as $k => $pnotif)
{
$firebaseRawNotifs[] = [
// compute all variable the thread callback will need as primitives values/arrays
//(can't send objects to thread callback)
];
}
DebugLog::info("[Pool ID : ".$poolId."] Pool of " . count($pnotifs) . " notifications found, creating " . $countBatch . " threads.. " );
// create N thread and send them the right part ([0;500], [500-1000], ..) of the full notification array
for($b = 0; $b <= $countBatch - 1; $b++)
{
$len = $b !== $countBatch - 1 ? 500 : $countBatch % 500;
$batch = array_slice($firebaseRawNotifs, $b * 500, $len);
// create the thread and send the payload of push notifs
parallel\run($sendNotificationBatch, [$poolId, $b,$firebaseCredentialArray, $batch] );
}
}
else
{
OSQL::set("unlock tables");
DebugLog::log("no new notification found" );
}
usleep(DELAY_POLL_MS * 1000);
}
Now comes the problem :
Sometime, I have no idea when and why, the MySQL connection gets closed in the thread callback only. For example, the app adds a new pending notification, I have this :
MySQL server has gone away[Pool ID : 5f6525d14bd02] Pool of 1 notifications found, creating 1 threads..
[Pool ID : 5f6525d14bd02, batch 0] : Sending 1 notifications..
[Pool ID : 5f6525d14bd02, batch 0] : Sent.
The push notification is correctly sent, with the right messages and variables so the SQL requests in the main script work well. Though, the SQL request in the thread callback doesn't run. I find my push notifications still in "sending" state.
This problem occurs after a long time. When I first execute the script, everything works well. After several hours, I have the problem.
I saw that MySQL closes TCP connection after 8 hours of inactivity, and it can output this "MySQL server has gone away". But here, a new connection is created in the thread callback, which is created at the time the notification is retrieved from main script. So I don't understand why there would be any "8 hours inactivity" here ..
Thanks for reading
Found myself : I just forgot to close mysql connections at each thread :
mysqli_close($mysqlRes);
As it was only the thread that ended (and not the entiere script), mysql cons weren't closed, eventually accumulating until MySQL decided not to receive any more request.
I want to send a mail using PHP Mailer, but first I need to validate 3 tables
This is the code I have,
//First I query the 2 tables to validate today's data and 3rd table to see if mail already sent
$verifyJobData = $dbconnection->query("SELECT id FROM [Jobs] WHERE date=CAST(GETDATE() AS DATE)");
$verifyJobData->fetchAll(PDO::FETCH_OBJ);
$verifyWIPData = $dbconnection->query("SELECT id FROM [WIP] WHERE date=CAST(GETDATE() AS DATE)");
$verifyWIPData->fetchAll(PDO::FETCH_OBJ);
$verifyDuplicateMail = $dbconnection->query("SELECT status FROM [Mail_Log] WHERE mailtype = 'DailyStatus' AND datesent = CAST(GETDATE() AS DATE) AND status = 1;");
$mailStatus = $verifyDuplicateMail->fetchAll(PDO::FETCH_OBJ);
$mailStatus=$mailStatus->status;
//On the first IF I want to validate that IF $mailStatus->status is different than 1 pass to the next IF to validate the other data
if ($mailStatus != 1) //Enter next IF
{
if ($verifyJobData->rowCount() && $verifyWIPData->rowCount())//The select had result continue to send code
{
echo "Code to send mail already working";
}
} else {
echo "Do not send mail and execute Code to insert failure status to DB also working";
}
The first IF is not validating the $mailStatus IF the value is 0, null or different than 1 also passes to the next IF.
This is what I tried, I did a lot of research but I am not finding what I need I might be searching wrong, also if you can share the URL to read the examples
Try this:
switch($mailStatus){
case 1:
echo "Do not send mail and execute Code to insert failure status to DB also working";
break;
default:
if ($verifyJobData->rowCount() && $verifyWIPData->rowCount()) {
echo "Code to send mail already working";
}
}
I am writing a custom Moodle (2.6) web service for taking up the moodle quiz, that requires to create an attempt or start an attempt. I written the API to start an attempt as well as get the questions for a particular quiz. However I m not able to figure out how to save the User responses for the question attempt. (Question attempt step data). Will appreciate if anyone can help me.
//Fetch all the user attempts
$attempts = quiz_get_user_attempts($quizObj->get_quizid(), 3, 'all', true);
//Get the last attempt
$lastattempt = end($attempts);
// Delete any previous preview attempts belonging to this user.
quiz_delete_previews($quizObj->get_quiz(), $USER->id);
//fetch the quiz usage object
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizObj->get_context());
$quba->set_preferred_behaviour($quizObj->get_quiz()->preferredbehaviour);
// Create the new attempt and initialize the question sessions
$timenow = time(); // Update time now, in case the server is running really slowly.
$attempt = quiz_create_attempt($quizObj, $attemptnumber, $lastattempt, $timenow, $quizObj->is_preview_user());
if ($lastAttemptStatus == quiz_attempt::FINISHED) {
$attemptnumber = $lastattempt->attempt + 1;
$attempt = quiz_start_new_attempt($quizObj, $quba, $attempt, $attemptnumber, $timenow);
} elseif ($lastAttemptStatus == quiz_attempt::IN_PROGRESS) {
$attempt = quiz_start_attempt_built_on_last($quba, $attempt, $lastattempt);
}
// It is here Lets say I would like to hardcode (get param) the responses from the user for a quiz for a specific question must go.. and I have add these responses as attempt step data..
$transaction = $DB->start_delegated_transaction();
$attempt = quiz_attempt_save_started($quizObj, $quba, $attempt);
quiz_fire_attempt_started_event($attempt, $quizObj);
$transaction->allow_commit();
$lastattempt = end($attempts);
For saving quiz attempts you need to call mod_quiz_save_attempt API
with question and answers response in parameters.
For eg:
http://**yourdomain.com**/webservice/rest/server.php?wstoken=**yourToken**&wsfunction=mod_quiz_save_attempt&moodlewsrestformat=json&attemptid=4545&data[0][name]=slots&data[0][value]=2&data[1][name]=q87124:2_:sequencecheck&data[1][value]=1&data[2][name]=q87124:2_answer&data[2][value]=2
For more details refer this link
I've been working on a quick and simple jQuery/PHP chat to put in my website for the visitors to communicate. I've extimated peaks of 200 simultaneous website users (connected users) with at most 10-20 people actually chatting.
Here's the quirk:
As I experienced already twice (thought it seems to be rather an unlikely event more than something happening after you perform something specific) the chat happens to load multiple messages which have already been red and display them.
Trying to keep the chat system as simple as possibile I came up with this code:
HTML CODE:
<div class="chat">
<ul class="chat">
<li class="chat" >
<h5 class="chat">Date</h5>
<h6 class="chat">Time</h6>
<h4 class="chat">User</h4>
<br/>
<q class="chat">Message</q>
</li>
</ul>
<input class="chat" placeholder="write something..."/>
</div>
As you can see I put a placeholder li element for the jQuery to take and use as a snippet to create new li elements with the actual messages and prepend them inside the ul element.
jQuery CODE:
Sending messages:
$(document).ready(function(){
chatSnippet = $('ul.chat').html(); // here chatSnippet is a global variable
$('ul.chat').html('');
$('input.chat').change(function(event){// Send your message
message = $(this).attr('value');
// first thing I perform an asynchronous POST to the receiving php script
$.post(
'php/chatRec.php',
{
user : currentUser,
message: message,
}
);
// meanwhile I add a new li element to the chat html with the content just submitted
date.setTime(event.timeStamp);
hours = ''+date.getHours();
if(hours.length < 2) hours = '0'+hours;
minutes = ''+date.getMinutes();
if(minutes.length < 2) minutes = '0'+minutes;
day = ''+date.getDate();
if(day.length < 2) day = '0'+day;
newChatMessage = chatSnippet.replace('Date', ''+day+' '+months[date.getMonth()]);
// here months is an array with the months names (in italian)
newChatMessage = newChatMessage.replace('Time', ''+hours+':'+minutes);
newChatMessage = newChatMessage.replace('User', connectedUser);
newChatMessage = newChatMessage.replace('Message', message);
$mess = $(newChatMessage);
$mess.hide().prependTo('ul.chat').fadeIn(500);
$(this).attr('value','');
});
refreshChat(''); // this function retrives new messages from the DB
// Here I perform a void refreshChat call so I'll get all the messages in the DB regardless from the currentUser (at page refresh)
});
Receiving messages:
// This code is placed outside (before) the .ready function
function refreshChat(user){// Receiving messages
$.post(
'php/chatInv.php',
{
user : user,
token: lastMessage // this variable contains the token of the last red message
},
function(data){
receivedMessages = jQuery.parseJSON(data);
for(message in receivedMessages){
message = receivedMessages[message].Message;
date = receivedMessages[message].Day.split('-');
time = receivedMessages[message].Time.split(':');
newChatMessage = chatSnippet.replace('Date', ''+date[2]+' '+months[parseInt(date[1])-1]);
newChatMessage = newChatMessage.replace('Time', ''+time[0]+':'+time[1]);
newChatMessage = newChatMessage.replace('User', receivedMessages[message].Sender);
newChatMessage = newChatMessage.replace('Message', message);
$mess = $(newChatMessage);
$mess.hide().prependTo('ul.chat').fadeIn(500);
lastMessage = receivedMessages[messages].token;
}
nextRefresh = setTimeout("refreshChat('"+currentUser+"')",2000);
// When I'm done I set a timeout of 2 secs and then perform another refresh
}
);
}
PHP CODE:
Receive a new message (I think the issue is in here):
mysql_connect("localhost", "root", "root") or die(mysql_error());
mysql_select_db("chat") or die(mysql_error());
$characters = array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
$token = $characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)];
$all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");
$prev_Msg = array('ID' => 1 , 'Sender' => $_POST['user'], 'Message' => $_POST['message'], 'Day' => date("Y-m-d"), 'Time' => date("H:i:s"), 'token' => $token);
while($Msg = mysql_fetch_array($all_Msgs)){
$update_success = mysql_query("UPDATE Messages SET Sender='".$prev_Msg['Sender']."', Message='".$prev_Msg['Message']."', Day='".$prev_Msg['Day']."', Time='".$prev_Msg['Time']."', token = '".$prev_Msg['token']."' WHERE ID=".$Msg['ID']);
$prev_Msg = $Msg;
}
Basically what I do here is receive the new post message, generate a token and an array element (which is itself an array) containing the new entered datas, done this I perform a seuqence of UPDATE statements on a fixed size SQL table overriding the new datas on the first record and then overriding each record with the previous one (so that the last record will be finally lost).
Sending messages:
mysql_connect("localhost", "root", "root") or die(mysql_error());
mysql_select_db("chat") or die(mysql_error());
$receiver = $_POST['user'];
$token = $_POST['token'];
$all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");
$newMessages = array();
while($Msg = mysql_fetch_array($all_Msgs)){
if($Msg['token'] == $token) break;
if($Msg['Sender'] != $receiver) array_unshift($newMessages,$Msg);
}
echo json_encode($newMessages);
So I send the client the JSON encode of an array of all the records in the DB inserted after the last known message and whose author was not the querying client.
My suspects:
I came to the conclusion that when the message reception (server side) is being performed there is a time span when each message is taken from the DB, if a refresh is being performed in the meanwhile the message is not found and if that message was the one we were looking for as the last red message then the server will just select all the messages in the table and send them back.
The result is you see a bunch of messages you already red without your messages in between (cuz they were added to the view client side and the server script doesn't send you back your own messages)
Stated that:
I don't care if the messages aren't exactly in the actual insertion order: let's say A and B are chatting, the actual real messages order is BAB, but A may se the order ABB for his view is immediatly updated at input time (this helps me keep a 'fast-realtime' feel)
I don't care if some message is lost (like if it falls over the fixed DB table edge before someone can read it)
At this time I don't care much about actual efficency, speed and optimization
I know I should probalby handle the message insertion differently adding the new record and then updating the IDs only and delete the last record out. But if possible I'd like to keep this UPDATE-only fashion.
do you think my interpretation of the problem is right?
If not: what would then be the cause? / how can I fix that?
If yes: how can I fix that easily?
If the actual fix is rather complex: how actually likely to happen would be this quirk in a 10-20 users chat?
Thanks
I noticed this when I worked on a chat code too, the solution is to store the last message ID (set as an Auto Increment field in MySQL) in a session and search the database for messages where the ID is higher than that, rather than use the time() function.
if (!$_SESSION['message_id']]) {
// if there isn't a message_id, select the last seven entries in the message list
$sql = "SELECT messages.message_id, messages.message, users.username FROM (SELECT * FROM messages, users user.user_id = messages.user_id ORDER BY message_id DESC LIMIT 7) as new_tbl ORDER BY message_id ASC";
} else {
// if there is a message_id, select the messages sent since the last entry
$sql = sprintf("SELECT messages.message_id, messages.message, users.username FROM messages, users WHERE user.user_id = messages.user_id message_id > '%d'", $_SESSION['message_id']);
}
$data = array();
$query = mysql_query($sql);
while ($row = mysql_fetch_array($query)) {
// build the data array from the mysql result and set the message_id session to the id of the last message
$data[$i] = array('user' => $row['username'], 'message' => $row['message']);
$_SESSION['message_id'] = $row['message_id'] > $_SESSION['message_id'] ? $row['message_id'] : $_SESSION['message_id'];
$i++;
}
Obviously you'd need to escape the session!
If there isn't a message_id session, it loads the last 7 messages from the table (ordered descending, then orders those messages in ascending order). If there is a message_id session, it loads new messages.
In the while loop, it builds a data array (I send it to my script as JSON) and sets the message_id session as the message_id row, with a failsafe check to make sure the message_id session doesn't end up being lowered.
The SQL implies that you have a table of users with the user_id and username, and a table of messages with a user_id, message_id and message.
I'm building an iPhone push server, and trying to get the push to work. I have a message.php file which put new message in the database, and then add the message to a push_queue table in the database.
To send the push, I manually have to go to the browser and call the push file (../push/push.php) which will send out the push.
Is there any way I can call the push.php file from the message.php file automatically?
I tried require_one, include, exec and file_get_contents without any luck.
It works if I use:
header('Location: ../push/push.php');
However, the push.php file takes a couple of seconds to execute and finish, so there's a delay for the user when trying to send a message.
I guess I could use a cron job to call the push.php file, but I'd rather not.
Here is the core function in push.php (based on http://www.raywenderlich.com/3525/apple-push-notification-services-tutorial-part-2):
function start()
{
//writeToLog('Connecting to ' . $this->server);
if (!$this->connectToAPNS())
exit;
while (true)
{
// Do at most 20 messages at a time. Note: we send each message in
// a separate packet to APNS. It would be more efficient if we
// combined several messages into one packet, but this script isn't
// smart enough to do that. ;-)
$stmt = $this->pdo->prepare('SELECT * FROM push_queue WHERE time_sent IS NULL LIMIT 20');
$stmt->execute();
$messages = $stmt->fetchAll(PDO::FETCH_OBJ);
$deletedIds = array();
foreach ($messages as $message)
{
if ($this->sendNotification($message->message_id, $message->device_token, $message->payload))
{
//$stmt = $this->pdo->prepare('UPDATE push_queue SET time_sent = NOW() WHERE message_id = ?');
//$stmt->execute(array($message->message_id));
$deletedIds[] = $message->message_id;
//$stmt = $this->pdo->prepare('DELETE FROM push_queue WHERE message_id = ?');
//$stmt->execute(array($message->message_id));
}
else // failed to deliver
{
$this->reconnectToAPNS();
}
}
//Delete the chunk of messages.
$this->pdo->query('DELETE FROM push_queue WHERE message_id IN ('.implode(',', $deletedIds).')');
unset($messages);
}
}
Create a function or class that does everything that your push.php does and call it when a new message is received or when the iPhone app queries for new messages. In this case you will not need to call other PHP in message.php.
This is a concept of MVC i.e. having your business logic separated from your controllers. In this case pushing is a business logic and message.php and push.php are your controllers.