PHP: 504 Gateway Time-out Error? - php

I know this question has been asked several times but I'm looking for another answer to my problem.
Basically, I have a MYSQL database with over 10000 records of iPhone's device ID for push notifications in it.
Now, I need to run a PHP file that will connect to the Apple APNS server to send push notification to these 10000 records in my MYSQL database.
When I run my php page, after a while i get a timed out error...
in my research I came across a few solutions BUT most require to edit some stuff in etc folder on the server.
My question is:
is there any way to select lets say 30 records from MYSQL database, then stop and then select another 30 and then stop and then another 30 and so on and so forth until the who records have been sent the PN's?
This is my current code:
$message = "Welcome";
$sqld = "SELECT * FROM pushUsers ORDER BY id DESC";
$queryd = mysqli_query($db_conx, $sqld);
$productCountd = mysqli_num_rows($queryd); // count the output amount
if ($productCountd > 0) {
//while($rowd = mysqli_fetch_array($queryd, MYSQLI_ASSOC)){
while($rowd = mysqli_fetch_assoc($queryd)){
$deviceID = $rowd["deviceNo"];
$deviceType = $rowd["deviceType"];
if($deviceType == 'iPhone' || $deviceType == 'iPad'){
///////////////SEND PUSH NOTU8FOCATION FOR iOS//////////////////////
// Put your device token here (without spaces):
$deviceToken = ''. $deviceID.'';
// Put your private key's passphrase here:
$passphrase = '123456';
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apple_push_notification_production.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
//****IMPORTANT**** LIVE APNS URL:
//ssl://gateway.sandbox.push.apple.com:2195
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => ''.$message.'',
'sound' => 'default',
'link_url' => 'http://xxxx.xom',
'category' => 'APP',
'badge' => '1',
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
///////////////END SEND PUSH NOTU8FOCATION FOR iOS//////////////////////
}
}
} else {
}
ANY HELP WOULD BE APPRECIATED.
Thanks in advance.

There are a few ways to solve your problem.
I can't help you writing code, instead I'll write some conceptual tips.
Change maximum execution time in PHP
Use set_time_limit() to increase execution time limit.
But, if your PHP is running in safe_mode, this function has no effect.
Split your job
The way to do it depends on how you call this PHP file, if it's a cron job, you can persist the last index that you pushed the notification, then call the cron job multiple times.
Improve the performance
The push service should have a bulk method, where you send many notifications in a single http request. The HTTP request is the bottleneck here, not the database.
Every request have it own delay, if you have 10k rows in database, there will be 10k requests. If the request takes 10ms (a very low time), you will take 100 seconds to execute your script.
If you send your notifications in a bulk method that accepts 100 notifications per request and take 50ms to run, you will need only 100 requests, and will take only 5 seconds to execute it.
Doing this way, you can split your mysql query with LIMIT and OFFSET, so you only load in the memory what you need before sending the request.

Yes, you need to send push notification in 100-100 or 500-500 at once because may be your server not too much strong for 10,000 record process at once and other reason is that may be your execution time is 30 sec or something like that so a script run for particular time and after that its give timeout.
$message = "Welcome";
for($i=10000 ;$i>0;$i-$limit){
//your code here
sleep(2);
}

Related

apns discarded the old push notification while device offline

I am working on push notifications and I want to send chat push notifications to IOS using php. But when I send 5 push notifications to apns (Apple Push Notification Server) then apns discard the old push notification and send only the most recent push notification to device when device get online.
I search the for solution on internet and one solution that I found is to set the notification expiry time. So I implemented this solution by my actual problem not solved.
Is there any way that solve my problem. Suggest any usefull solution or reference site.
IOS push notification with PHP
Below is my sample code
public function sendIOSNotification($tokens, $data, $envoirement = 'production') {
try {
$payload = json_encode($this->setIosNotificationDataParameters($data));
$deviceTokens = str_replace(array(' ', '<', '>'), '', $tokens['ios']);
// FUNCTION NOTIFICATIONS   
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', config('push-notification.appNameIOS.certificate_' . $envoirement));
stream_context_set_option($ctx, 'ssl', 'passphrase', 'push');
//send notification
$fp = stream_socket_client(
config('push-notification.appNameIOS.ios_push_notification_' . $envoirement), $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx
);
$res = [];
foreach ($deviceTokens as $deviceToken) {
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken)
. pack('n', strlen($payload)) . $payload
.pack('N', time()).pack('N', time() + 86400);
$res = json_encode($result);
}
fclose($fp);
\Log::info("=== IOS Notification Send Successfully ===");
return true;
} catch (\Exception $ex) {
$messages = $ex->getMessage() . '::' . $ex->getFile() . '( ' . $ex->getLine() . ' )';
\Log::ifno("===Push Notificaion Exception===");
\Log::ifno($messages);
return true;
}
}
You cannot do what you intend according to the documentation. It is the documented behavior:
Quality of Service, Store-and-Forward, and Coalesced Notifications
Apple Push Notification service includes a Quality of Service (QoS)
component that performs a store-and-forward function. If APNs attempts
to deliver a notification and the destination device is offline, APNs
stores the notification for a limited period of time and delivers it
when the device becomes available again. This component stores only
the most recent notification per device and per app. If a device is
offline, sending a notification request targeting that device causes
the previous request to be discarded. If a device remains offline for
a long time, all its stored notifications in APNs are discarded.
source: apple push notifications documentation
This means just an offline notification per user per app.
You should architecture your application in a different way. First of all, for a chat application you cannot expect sending thousand push notifications when the device is back online. You will have to implement some extra mechanism for your app to retrieve the old messages if you want to show them in your app

How to improve the success rate for Apple Push Notifications?

I'm sending Apple Push Notifications (APN) from my server with a PHP script. First step is to populate a database table with the device tokens and the message to send. Then my script iterates over these table rows and sends the notifications one by one. The script is invoked by a cronjob every minute and if there are messages in my table it sends at most 50 of them and then it exits. The most important parts of my script are shown below.
I can use this script to send messages to five or ten test devices and all the messages get delivered successfully. But if I use the very same script to send the message to all 300 registered devices, only 50% get delivered. The same device sometimes gets the notifications and sometimes it doesn't. So, I know my script works and I know my devices can receive the notifications, but if I want to send a message to all devices only about a half of the messages are received.
I know that APN does not guarantee to successfully deliver the messages, but a success rate of 50% seems very low to me. Thus, these are my questions:
What is the typical success rate of messages sent via APN?
Should I better send all 300 push notifications at once?
Are there any ways to get error messages from the APN servers?
Here's the code from my script:
// create stream context
$ctx = stream_context_create();
stream_context_set_option(
$ctx,
'ssl',
'local_cert',
'/path/to/push.pem'
);
stream_context_set_option(
$ctx,
'ssl',
'passphrase',
$passphrase
);
// connect with Apple server
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195',
$err,
$errstr,
60,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$ctx
);
// get next 50 rows from database table
// ...
// for every row in my table
foreach( $rows as $row ) {
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// send a message
$result = fwrite($fp, $msg, strlen($msg));
if( $result ) {
// delete row from database
} else {
// log error
break;
}
}

How to send 1000 push notification at same time to ios devices without using the loop in php

How to implpement on below code for send 1000 push notification at same time server to ios deivce using php.
function push_iphone($deviceTokenid,$message){
$passphrase = '12345';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
return false;
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'message' => $message,
'sound' => 'default',
'flag' => '0'
);
// Encode the payload as JSON
$payload = json_encode($body);
//echo $device_token_id;
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceTokenid) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if(!$result){
echo "tst";
return true;
}else{ echo "test ";
return false;
}
fclose($fp);
}
Can any body help me about it.
Apple has an answer to this in their documentation:
You may establish multiple connections to the same gateway or to
multiple gateway instances. If you need to send a large number of
remote notifications, spread them out over connections to several
different gateways. This improves performance compared to using a
single connection: it lets you send the remote notifications faster,
and it lets APNs deliver them faster.
Keep your connections with APNs open across multiple notifications;
don’t repeatedly open and close connections. APNs treats rapid
connection and disconnection as a denial-of-service attack. You should
leave a connection open unless you know it will be idle for an
extended period of time—for example, if you only send notifications to
your users once a day it is ok to use a new connection each day.
By threading your php application you can achieve this at (almost) the same time. Take a look at Patterns for PHP multi processes? for a direction which to take to setup forks.

APNS not sending response nor sending push notification

Apple's docs say that:
"If you send a notification and APNs finds the notification malformed or otherwise unintelligible, it returns an error-response packet prior to disconnecting. (If there is no error, APNs doesn’t return anything.) Figure 5-3 depicts the format of the error-response packet."
This leads me to believe that the only reason that APNS would not send something back is if what I sent them was in the correct format. However when I try to fread for their response I get a string of 0 length and when unpacked becomes null, which I assume means nothing has been written back to me.
I the stream was opened by stream_socket_client() and did not return false or throw an exception. I know that my fwrite successfully wrote 154 bytes to that stream as well. Why would there be no response from Apple?
Here is the code for connecting to APNS:
function openConnection() {
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $this->APNS_CERT);
stream_context_set_option($streamContext, 'ssl', 'passphrase', $this->Password);
$apns = stream_socket_client('ssl://' . $this -> APNS_HOST . ':' . $this -> APNS_PORT, $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);
return $apns;
}
Then, in the next method after calling openConnection:
//open the connection to use with apple.
$apns = $this->openConnection();
$alert = $message['alert'];
$badge = $message['badge'];
$deviceToken = $message['deviceToken'];
$payload['aps'] = array(
'alert' => $message['alert'],
'badge' => $message['badge'],
'sound' => $message['sound']
);
if ($message['extraPayload'] != null) {
$payload['acme'] = $message['extraPayload'];
}
$encodedString = json_encode($payload);
//create message
$apnsMessage = chr(1) . pack("N", $message['identifier']) . pack("N", $message['expire']) . pack("n", 32) . pack('H*', str_replace(' ', '', $message['deviceToken'])) . pack("n",strlen($encodedString)) . $encodedString;
$write = fwrite($apns, $apnsMessage);
echo $write;
//the echo was just to see if it wrote.
if (!$apns) {
socket_close($apns);
fclose($apns);
echo "connection to APNS was lost.";
}
//look for changes. $null=null because some bug doesn't just let you pass null.
$null = null;
$changedStreams = stream_select($streamArray, $null, $null, 0, 1000000);
//check if it is actually false
if ($changedStreams === false) {
//close stream when done.
socket_close($apns);
fclose($apns);
echo "No response from APNs";
} elseif ($changedStreams > 0) {
//then check if what they sent back is an error and grab the error packet
$responseBinary = fread($apns, 6);
var_dump($responseBinary);
//check that it's the right thing
if ($responseBinary != false || strlen($responseBinary) == 6) {
//convert it from it's binary stream state and print.
$response = unpack('Ccommand/Cstatus_code/Nidentifier', $responseBinary);
var_dump($response);
//close stream when done.
socket_close($apns);
fclose($apns);
}
} else {
echo "Apple failed to respond, message was not sent.";
}
The var_dump at the end is NULL.
Edit:
Turns out it was an error with conflicting credentials. It was solved by creating a new pem file.
From that page:
You should regularly connect with the feedback web server and fetch the current list of those devices that have repeatedly reported failed-delivery attempts. Then you should cease sending notifications to the devices associated with those applications. See “The Feedback Service” for more information.
Feedback Service
Access to the feedback service takes place through a binary interface similar to that used for sending push notifications. You access the production feedback service via feedback.push.apple.com, port 2196; you access the sandbox feedback service via feedback.sandbox.push.apple.com, port 2196. As with the binary interface for push notifications, you must use TLS (or SSL) to establish a secured communications channel. The SSL certificate required for these connections is the same one that is provisioned for sending notifications. To establish a trusted provider identity, you should present this certificate to APNs at connection time using peer-to-peer authentication.

PHP IMAP push email notification to iPhone

I'm working on a PHP script that periodically checks the user's inbox for new messages via IMAP. The script leaves an open connection to the IMAP server, and grabs the UID of the most recent message every 5 seconds. If the UID is greater than the initially recorded comparison UID, the script sends a push notification to the user's iPhone notifying him/her that there is a new message available, records the new UID as the comparison UID, and continues to check for new messages in this fashion. Here is the script:
<?php
$server = '{imap.gmail.com:993/ssl}';
$login = 'email_address#gmail.com';
$password = 'my_email_password';
$connection = imap_open($server, $login, $password) OR die ("can't connect: " . imap_last_error());
$imap_obj = imap_check($connection);
$number = $imap_obj->Nmsgs;
$uid = imap_uid($connection, $number);
//infinite loop, need to add some sort of escape condition...
for(;;){
$imap_obj = imap_check($connection);
$number = $imap_obj->Nmsgs;
//if there is a new message send push notification
if(imap_uid($connection, $number) > $uid){
$uid = imap_uid($connection, $number);
$result = imap_fetch_overview($connection,$number,0);
$message = $result[0]->subject;
$deviceToken = 'xxxxxxxxxxxxxxxxxx';
$passphrase = 'my_secret_password';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
//echo 'Message not delivered' . PHP_EOL;
else
//echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
}
sleep(5);
}
imap_close($connection);
?>
This works. But it seems terribly inefficient to me. Each additional user maintains an indefinite connection with the IMAP server, and checks for new messages every couple seconds, which seems silly.
Is there a better way to do this?
Couldnt you use something like CRON to schedule a script to run every 30 seconds, which keeps a db record of the last UID for every user, and then searches each users mailbox locally (via local shell not imapd connection), if a particular user has a new message, push the notification and update the latest UID for that user in the DB...
This is assuming the PHP script is living on the mail server and you have root access.

Categories