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.
Related
I had succeeded earlier on sending a push notification to my iPhone using raywenderlich's excellent tutorial here http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1 and running the php script using the mac terminal, however, now I am trying to achieve the same thing only now I have the push.php script uploaded to my server and run the script every 5 min via Cron Job but i get this error now:
stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection timed out)
Like I said before it was working fine earlier when I was running the script through the terminal. I think the problem is that the php script from the server cannot access my push certificates. How do I give the script access to them? Please help!
Heres the php script if it helps:
<?php
// Assign data into variables
$deviceToken = "e39ffc6b98f649f127d07d2d881bc9faa621a3c5d59f9647e64f6452fc37af6c";
$passphrase = "9q3n6k80";
$sound = '';
$blank = "";
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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' => "mic check",
'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 succesfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
Thank You!
EDIT: Im using the File Manager in Bluehost to upload files
Im in a situation where i have one server that needs to send push notification to two different client apps without knowing which app is it.
I have two different iOS apps (2 different bundle identifiers) and i have 2 different sets of all the necessary certifications, one for each app.
I have a PHP code that receives the deviceToken and a the message to be pushed.
the code is based on reywenderlich's SimplePush that can be found here: http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1
The only part that need to change is the ck.pem file that will be different for each app.
One solution i can think of would be to try the two different ck.pem files, if one fails try the other one.
Can any one help me with implementing that in this PHP code ? or if there are any better solution suggestions ?
<?php
// Put your device token here (without spaces):
//$deviceToken = 'a6a543b5b19ef7b997b2328';
$deviceToken = $_GET["device_token"];
$message = $_GET["message"];
$elementID = $_GET["element_ID"];
// Put your private key's passphrase here:
$passphrase = '123456';
// Put your alert message here:
//$message = 'My first push notification! yay';
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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'
);
// Create the extra data
$body['extra'] = array(
'element_id' => $elementID
);
// 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);
UPDATE:
the solution is to add another piece of code at the end, to send the same payload to the second server:
//connecting to second server
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'SecondCk.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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 to note server: $err $errstr" . PHP_EOL);
// connected to server sending note msg
$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);
I don't know how to do this in PHP, but you should simply create the payload body + binary notification before opening the first connection and then create 2 connections(or loop a connection, if possible) to Apple's Push Server and send the same binary notification to both of them.
Best regards,
Gabriel Tomitsuka
I can send notification manually as following code.
<?php
// Device token:
$deviceToken = 'xxxxxx';
$passphrase = 'xxxxx';
$badge = 1;
// Displays alert message here:
$message = 'Match Found!';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '/Users/Documents/iOS_Application_Developement/new/APNSPHP/ApnsPHP-master/ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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,
'badge' => $badge,
'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);
?>
I also had a APNS PHP integration.but in that there are so many files.For my purpose only one file is useful.
Now i want to run this php script when certain occurrence take place in iOS through my API. So i can write this code under some function and call that function when certain occurrence take place. So what is the best way to do it? and how can i achieve notification cron that run after every 5 minutes?
Any help will be appreciated.
create a cron entry in /etc/cron.d/
*/5 * * * * root cd /path_to_your_script/ && php your_script.php >> /var/some.log &2>&1
I have the following php code for ios push notification.Here i code for 2 devices using loop in fwrite() section . the current code is working properly . My doubt is , can i pass the array of device tokens directly without using the for loop?.
<?php
// Put your device token here (without spaces):
$deviceToken[0] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$deviceToken[1] = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy';
// Put your private key's passphrase here:
$passphrase = '123456';
// Put your alert message here:
$message = 'multiple device push notification...!';
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'abc.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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',
'badge' => '+1'
);
// Encode the payload as JSON
$payload = json_encode($body);
for($i=0;$i<2;$i++)
{
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken[$i]) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
echo "msg may be delivered";
}
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
Its design by default, yet there is no option for passing array of device tokens. You have to iterate through the loop.
Alternate to this approach will be using third party like amazon SNS service. Here you can publish to topic(one request) and all devices that are subscribed to this topic will receive the notification.
I am working on a PHP website + iPhone application and API for iPhone application, has a messaging system for students and doctors, when any one sends message (from website or iPhone) the other user should get push notification on his iphone. For example if student adds a new question for teacher, a push notification on teachers iPhone/iPad will be send to teacher and when teacher replies to student's answer, student will get a push notification.
Since there is no restriction on number of teachers and student registering to website, my question is how to send push messages to registered user's iPhone? I want to send push message as soon as someone replies or adds a question. Please provide me PHP code for sending multiple push messages.
I am saving device token for each user while registration.
When teacher reply to question I am sending mail to student, I want to send a push notification too to student and vice versa so please specify code able to manage error conditions.
Simple way to do it without use any file. You can call it multiple times with different tokeid.
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ckipad.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 amarnew: $err $errstr" . PHP_EOL);
//echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'badge' => +1,
'alert' => $message,
'sound' => 'default'
);
$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 amar'.$message. PHP_EOL;
// Close the connection to the server
fclose($fp);
You should better use APNS library for PHP. You can find it here. Look through samples that developers provide.
I also had problems with certificates. My actions were:
locate file ApnsPHP/Abstract.php
make some changes to _connect() method, paste this lines
$streamContext = stream_context_create(
array(
'ssl' => array(
'local_cert' => $this->_sProviderCertificateFile,
'passphrase' => ''
)
)
);
$this->_hSocket = #stream_socket_client(
$sURL,
$nError,
$sError,
$this->_nConnectTimeout,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$streamContext);
instead of original listed there
now you can use *.pem certificates without need of entrust_root_certification_authority.
This worked fine for me.
This is the way I have done it finally
Downloaded apns-php
PHP Code
set_time_limit(0);
$root_path = "add your root path here";
require_once($root_path."webroot\cron\library\config.php");
require_once($root_path."Vendor\ApnsPHP\Autoload.php");
global $obj_basic;
// Basic settings
$timezone = new DateTimeZone('America/New_York');
$date = new DateTime();
$date->setTimezone($timezone);
$time = $date->format('H:i:s');
//Get notifications data to send push notifications
$queueQuery = " SELECT `notifications`.*, `messages`.`mes_message`, `messages`.`user_id`, `messages`.`mes_originated_from` FROM `notifications`
INNER JOIN `messages`
ON `notifications`.`message_id` = `messages`.`mes_id`
WHERE `notifications`.`created` <= NOW()";
$queueData = $obj_basic->get_query_data($queueQuery);
if(!empty($queueData)) {
// Put your private key's passphrase here:
$passphrase = 'Push';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'server_certificates_bundle_sandbox.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$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 '<br>'.date("Y-m-d H:i:s").' Connected to APNS' . PHP_EOL;
foreach($queueData as $val) {
// Put your device token here (without spaces):
$deviceToken = $val['device_token'];
// Create message
// Get senders name
$sql = "SELECT `name` FROM `users` WHERE id =".$val['user_id'];
$name = $obj_basic->get_query_data($sql);
$name = $name[0]['name'];
$message = $name." : ";
// Get total unread messaged for receiver
$query = "SELECT COUNT(*) as count FROM `messages` WHERE mes_parent = 0 AND user_id = ".$val['user_id']." AND mes_readstatus_doc != 0 AND mes_status = 1";
$totalUnread = $obj_basic->get_query_data($query);
$totalUnread = $totalUnread[0]['count'];
$message .= " This is a test message.";
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'badge' => $totalUnread,
'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 '<br>'.date("Y-m-d H:i:s").' Message not delivered' . PHP_EOL;
} else {
$sqlDelete = "DELETE FROM `notifications` WHERE id = ".$val['id'];
$query_delete = $obj_basic->run_query($sqlDelete,'DELETE');
echo '<br>'.date("Y-m-d H:i:s").' Message successfully delivered' . PHP_EOL;
}
}
// Close the connection to the server
fclose($fp);
echo '<br>'.date("Y-m-d H:i:s").' Connection closed to APNS' . PHP_EOL;
} else {
echo '<br>'.date("Y-m-d H:i:s").' Queue is empty!';
}