I am trying to send a push notification to IOS using php and Yii framework. I created a function as follows to get data and send notification. $data is an array which contains a message and an array inside to keep registration IDs (device tokens). I ran it on my localhost but notification is not sent to my device. By the way, the output of my function is an integer value and I cannot recognize if the message has been sent or what!?
Is there anything wrong in my code?
private function _pushIOS($data)
{
// Create a connection
$apnsHost = $this->_notificationUrl;
$apnsPort = 2195;
$apnsCert = Yii::app()->controller->createUrl('/').'/my_certificate_dev.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
if (!$apns)
exit("Failed to connect: $error $errorString" . PHP_EOL);
foreach($data['registration_ids'] as $deviceToken) {
$body = array();
$body['aps'] = array(
'alert' => $data['data']['message'],
'badge' => 1,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($apns, $apnsMessage, strlen($apnsMessage));
}
// Close the socket and connection to the server
#socket_close($apns);
fclose($apns);
return json_decode($result);
}
Finally, I got the answer. The problem was with the pem file path. Absolute path must be passed to the function to work. The following line should be replaced:
$apnsCert = Yii::app()->controller->createUrl('/').'/my_certificate_dev.pem';
Correct path:
$apnsCert = dirname(Yii::app()->request->scriptFile).'/my_certificate_dev.pem';
Related
I have written a function to send push notifications to iOS devices. It sends pushes to more than 100 devices for now. However sometimes it is not sending push notifications. When I debug through the process I identified based on push notification the whole process fails. but the issues is all push tokens are in valid format. So my initial code was like this.
$certificatePath = CERTIFICATE_PATH . "/".APN_CERTIFICATE_FILE;
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $certificatePath);
stream_context_set_option($streamContext, 'ssl', 'passphrase', APN_PASSWORD);
$apns = stream_socket_client(APN_HOST . ':' . APN_PORT , $error, $errorString, 2, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $streamContext);
if (!$apns) {
echo "Failed to connect $err $errstrn";
return false;
}
$payload = array();
$payload['aps'] = array('alert' =>$message,'title'=>$title, 'sound' => 'default','data'=>array());
$payload = json_encode($payload);
foreach ($pushTokens as $pushToken)
{
$pushToken = str_replace(' ', '', $pushToken);
if(!ctype_xdigit($pushToken))
continue;
$apnsMessage = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $pushToken)) . pack ("n",strlen($payload)) . $payload;
$rst = fwrite($apns, $apnsMessage);
}
fclose($apns);
As a fix I changed the code to different way. I know this code is expensive since it open and close connection for every push notification.
$certificatePath = CERTIFICATE_PATH . "/".APN_CERTIFICATE_FILE;
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $certificatePath);
stream_context_set_option($streamContext, 'ssl', 'passphrase', APN_PASSWORD);
$payload = array();
$payload['aps'] = array('alert' =>$message,'title'=>$title, 'sound' => 'default','data'=>array());
$payload = json_encode($payload);
foreach ($pushTokens as $pushToken)
{
$apns = stream_socket_client(APN_HOST . ':' . APN_PORT , $error, $errorString, 2, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $streamContext);
if (!$apns) {
echo "Failed to connect $err $errstrn";
return false;
}
$pushToken = str_replace(' ', '', $pushToken);
if(!ctype_xdigit($pushToken))
continue;
$apnsMessage = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $pushToken)) . pack ("n",strlen($payload)) . $payload;
$rst = fwrite($apns, $apnsMessage);
fclose($apns);
}
I am wondering if it is an issue with fwrite or somewhere else. Appreciate if someone can help me with my first method as it is speed, and run with minimum resources.
I have generated the .cer file, provision file with the correct device ID, generated the .pem file by combining the .cer and private key files, uploaded it to the server. The app id matches. I have provided the passphrase too, which is correct.
I tested the port and connection using telnet from the server, it connects fine.
I have tested the certificate by openssl command, and it returned 0 - no errors.
The certificates and the application is in development/debug mode, the iPhone is set up to receive notifications, the token is received and is delivered to the server correctly and in the same length - 64.
When sending the message from the server, the error code is 0 - which means no errors.
Here is the code sample from the server:
$options = array('ssl' => array(
'local_cert' => 'cert.pem',
'passphrase' => 'pass'
));
$streamContext = stream_context_create();
stream_context_set_option($streamContext, $options);
$apns = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);
if ($apns)
{
$payload['aps'] = array('alert' => 'push test', 'badge' => 1, 'sound' => 'default');
$payload = json_encode($payload);
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $token)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
fclose($apns);
}
else
{
echo "Connection failed";
echo $errorString."<br />";
echo $error."<br />";
}
What else can I possibly try?
The code that worked in the end is the following:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'pushcert.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', 'pass');
// Create the payload body
$body['aps'] = array(
'alert' => array('body' => 'Message', 'action-loc-key' => 'Show'),
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Open a connection to the APNS server
$apns = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$apns)
{
echo "Failed to connect: $err $errstr" . PHP_EOL;
}
echo 'Connected to APNS' . PHP_EOL;
$imsg = chr(0) . pack('n', 32) . pack('H*', $message) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$res = fwrite($apns, $imsg, strlen($imsg));
if (!$res)
{
echo 'Message not delivered' . PHP_EOL;
}
else
{
echo 'Message successfully delivered' . PHP_EOL;
}
fclose($apns);
i am working on a project in PHP which requires me to push an alert notification on APNS server. I have used enhanced push notification format. but I am not receiving response as specified by the APNS docs. I am getting response in three digits usually 133, 132, 154, 138, etc. Which I concluded to be Status signs, eg. 133 is 1, 3, 3. but now I have also received 139. so I doubt that my interpretation of response is wrong. But I am not getting where it is wrong. And important thing is though I am receiving these responses Alert is getting pushed and I am receiving notification on my iPhone as well as on iPad.
My code is as follows:
$payload['aps'] = array('alert' => $message, 'badge' => 1, 'sound' => 'default');
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195; // default port
$apnsCert = 'apns-dev.pem'; // APNS crtificate.
$passPhrase = '';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
stream_context_set_option($streamContext, 'ssl', 'passphrase', $passPhrase);
try{
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);
if (!$apns) {
print "Failed to connect {$error} {$errorString}\n";
}
else {
// Sending the payload
$apnsMessage = chr(0) . pack('n', 1) . pack('n', $nid) . pack('n', time() + 604800) . pack('n', 32) . pack('H*', str_replace(' ', '', $alert_device_token)) . pack('n', strlen($payload)) . $payload;
echo 'APNS Message: ' . $apnsMessage;
$fwrite = fwrite($apns, $apnsMessage);
echo 'APNS response: ' . $fwrite;
And when this get executed i got the following response printed on the browser:
APNS Message: ��=ŸÂ� òc6–U:õŸŠ ¸Þ ÷ćÚ0ßqšÊzÂífÕnZ�`{"aps":{"alert":"Your EUR\/USD SELL alert price has been reached!","badge":1,"sound":"default"}}APNS response: 139
Can anyone please tell me what does this 139 means here. am doing anything wrong here.
The echoed "139" is the return value of fwrite(). It's the number of bytes written by fwrite()
See: PHP: fwrite
I successfully manage to implement an iPhone push notification server (PHP) last year; I had to change the server, and was thinking that moving files was sufficient... I was wrong, since the modification notifications are not sent anymore. There's no error, everything seems ok, but notification aren't received.
Below is my server code; anyone can think of a cause, or a way to find the problem ? (notes: the $deviceTokens var is correct, contains the device tokens, and I've successfully tested my .pem certificate with an openssl command).
$payload['aps'] = array('alert' => 'notification!!', 'sound' => 'push.aif');
$payload = json_encode($payload);
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', 'libraries/ck_prod.pem');
$apns = stream_socket_client('ssl://gateway.sandbox.push.apple.com:' . 2195, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
if($error) {
log_message('error', $errorString);
return;
}
log_message('debug', 'sending push notification...');
if($apns) {
foreach($deviceTokens as $deviceToken) {
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
}
fclose($apns);
} else {
log_message('error', 'error while sending push notification');
}
Well well... Maybe I should have notice that I was contacting the test server (gateway.sandbox.push.apple.com)... Some days are just difficult...
I'm trying so send push notifications ton my iPhone (APNS). I read this post and try to implement it. So all my certificates are good (normaly).
Now I have this php script :
$device = '4f30e047 c8c05db9 3fa87e7d ca5325f7 738cb2c0 0b4a02d4 d4329a42 a7128173'; // My iphone deviceToken
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);
$apnsCert = 'apple_push_notification_production.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $device)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
//socket_close($apns); seems to be wrong here ...
fclose($apns);
When I run this script nothing happen. no error just an empty page but my iPhone doesn't receive the push notification ...
Have you got an idea?
Thanks a lot !
no space in device token, strip them
For development certificate you need url
'ssl://gateway.sandbox.push.apple.com:2195'
but for production
'ssl://gateway.push.apple.com:2195'
Strip the spaces from token and even then it's not work then
$apnsHost = 'gateway.sandbox.push.apple.com';
Remove "sandbox" from this url and it will run.
If you have more than one device and you loop through them I found that you have to create a new stream_context_create for each fwrite to prevent apple from closing the connection for a bad token.
FYI
To send notifications success fully you have to do 1 more thing.
Replace $output = json_encode($payload);
to $payload = json_encode($payload);
You are missing to provice certificate private key. Add this row:
stream_context_set_option($streamContext, 'ssl', 'passphrase', $certificatePrivateKey);