26

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.

JJJ
  • 32,902
  • 20
  • 89
  • 102
Subodh Ghulaxe
  • 18,333
  • 14
  • 83
  • 102
  • 6
    **PSA:** be careful with the approaches listed below as your SSL is wholly insecure without peer verification. PHP's SSL stream wrappers *DO NOT* verify peers by default even if you specify a pem cert and passphrase. This provides a false sense of security and makes you vulnerable to man-in-the-middle attacks. Your data will still be encrypted, but there's no guarantee that the people on the other end are who they say they are. If you go this route it's *important* to add the appropriate context option `array('ssl'=>array('verify_peer' => TRUE));` in addition to the relevant certificate keys. –  Jun 12 '13 at 16:05
  • You are revealing your app's concept here..which is not correct.. –  Apr 09 '15 at 11:27

3 Answers3

35

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);
vikmalhotra
  • 9,981
  • 20
  • 97
  • 137
Amar Banerjee
  • 4,992
  • 5
  • 34
  • 51
  • 7
    Isn't opening and closing the connection to APNS in a loop a very expansive operation? – vikmalhotra Jul 05 '13 at 05:34
  • 2
    Yes, that is quite slow. Also you should take into account rejected messages and handle them and read Apples feedback service. Implementing pushnotifications from scratch is not easy, you should really look into a library like: https://github.com/mac-cain13/notificato – Mac_Cain13 Jan 12 '15 at 21:23
  • @Mac_Cain13 does this library support sending one push message to Multiple devices? I can't find it in example – Muhammad Umar Mar 27 '15 at 12:21
  • Not directly @MuhammadUmar, but it's easy to do by creating a MessageBuilder, setting all properties, then in a loop do the `$messageBuilder->setDeviceToken()->build()` to create all messages with different tokens and send them to Notificato. Feel free to open an issue on the Github repo if you want help using Notificato. – Mac_Cain13 Mar 27 '15 at 14:49
  • @Mac_Cain13 thanks, i get that. However i am struggling to implement the lib without installing composer. Is that possible? – Muhammad Umar Mar 27 '15 at 14:51
  • Should be possible. Even though I would very much advise to use composer. But this is quite off topic here, so please open an issue on the GitHub repo if you need assistance or open another question here on SO. – Mac_Cain13 Mar 27 '15 at 20:38
  • How can i send push message to multiple devices with array? i dont want to use for loop or foreach etc – Paresh Gami Sep 07 '15 at 06:10
  • Push notifications use a .p8 file now, is there a tutorial for this? – Joseph Astrahan Mar 04 '17 at 13:12
15

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:

  1. locate file ApnsPHP/Abstract.php
  2. 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

  3. now you can use *.pem certificates without need of entrust_root_certification_authority.

This worked fine for me.

Oliver Salzburg
  • 21,652
  • 20
  • 93
  • 138
Gino Pane
  • 4,740
  • 5
  • 31
  • 46
12

This is the way I have done it finally

  1. Downloaded apns-php
  2. 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!';
        }
Subodh Ghulaxe
  • 18,333
  • 14
  • 83
  • 102
  • 9
    this doesn't seem using apns-php classes. – Egemenk May 30 '14 at 22:53
  • 5
    This code has several flaws: 1.) $result is always true not mathers if sending was successful or not 2.) first invalid token encountered will break the stream to the APN server and the loop will fail to send messages to further recipients – cybercow Apr 20 '15 at 09:28
  • Above code will return true even if the token is not valid – rishad2m8 Dec 12 '16 at 11:05