Satori Docs

Your definitive guide to subscribing and publishing live data

PHP SDK Quickstart Direct link

This tutorial shows you how to create a publish-subscribe application written in PHP, using the PHP SDK for Satori RTM. 

The application does the following:

  • Connects to RTM
  • Publishes messages to a channel
  • Subscribes to the same channel
  • Receives messages from the subscribed channel
  • Provides callbacks that respond to RTM confirmations and error messages

This tutorial is designed for new users of the Satori platform. It describes a single application that publishes to a channel, subscribes to the same channel, and receives messages it has published. While RTM applications are allowed to do this, in most actual situations the publisher and subscriber are different.

Prerequisites Direct link

  • PHP. The version of PHP must be 5.4 or later. If possible, use PHP 5.6 or later.
  • Composer PHP package manager (optional)

Setup Direct link

Set up Satori

To set up Satori for the tutorial:

  1. If you haven't already, register for a developer account in the Dev Portal.
  2. After you log in, to create a project select Projects from the top menu, then select Create a project.
  3. Enter a project name such as "php_tutorial", then click Add. The Dev Portal responds with connection information for your project.
  4. Store the value of Appkey and Endpoint. You use these in a subsequent step in the tutorial.

Get the SDK source

To get the tutorial source code, clone the PHP RTM SDK from GitHub.

$ git clone https://github.com/satori-com/satori-rtm-sdk-php.git
$ cd satori-rtm-sdk-php

The code for the tutorial is in the cloned repo, in satori-rtm-sdk-php/examples/quickstart.php.

Tutorial summary Direct link

Channels

  • The tutorial application connects to RTM using your Satori project credentials, then subscribes and publishes to "animals" channel 
  • You don't need to create this channel or associate it with your project. RTM creates channels on demand; when it receives the first request containing a channel name, RTM creates a channel instance with default channel permissions and other settings. 
  • You only need to use the Dev Portal if you want set special channel permissions and settings for a channel.

RTM client life cycle

An application that uses Satori RTM to publish and subscribe is an RTM client.

Typically, RTM clients have the following steps:

  1. Specify RTM credentials
  2. Connect to RTM
  3. Publish messages to a channel
  4. Subscribe to a channel
  5. Receive messages from the channel, and receive responses and errors from RTM

Each of these steps is covered in the tutorial.

Tutorial steps Direct link

Specify RTM credentials

  1. Navigate to the location where you put the clone of the PHP RTM SDK repository.
  2. Edit the file examples/quickstart.php.
  3. Locate the RTM credential placeholders YOUR_APPKEY and YOUR_ENDPOINT.
  4. Replace the RTM credential placeholders with the values you obtained in the section Setup > Set up Satori.

Note: For the moment, you don't need to update role and role_secret. If, later on, you want to use this code as a model, and you want to authenticate a client, you can get role credentials from the Dev Portal.

const ENDPOINT = 'YOUR_ENDPOINT';
const APP_KEY = 'YOUR_APPKEY';
const ROLE = 'YOUR_ROLE';
const ROLE_SECRET_KEY = 'YOUR_SECRET';

Connect to RTM

The PHP RTM SDK RtmClient class specifies the main methods for working with RTM. It includes the RtmClient.connect() method that establishes a connection with RTM.

The SDK detects when the connection to RTM drops, but it doesn't automatically reconnect. If your client loses the connection, the SDK invokes the RtmClient.onDisconnect() callback method. In this method, add code to reconnect to RTM or report the problem, or both.

The following code creates a new RTM client instance and creates event listeners that respond to successful and unsuccessful connections:

$options = array();

// Authenticate if necessary
if (ROLE_SECRET_KEY != 'YOUR_SECRET') {
   $options['auth'] = new RoleAuth(ROLE, ROLE_SECRET_KEY);
}

echo 'RTM client config:' . PHP_EOL;
echo '  endpoint = ' . ENDPOINT . PHP_EOL;
echo '  appkey = ' . APP_KEY . PHP_EOL;
echo '  authenticate? = ' . json_encode(!empty($options['auth'])) . PHP_EOL;

$client = new RtmClient(ENDPOINT, APP_KEY, $options);

// The SDK invokes onConnected() when the connection is successful
$client->onConnected(function () {
   echo 'Connected to Satori RTM and authenticated as ' . ROLE . PHP_EOL;
});

// The SDK invokes onDisconnected when the connection is unsuccessful,
// or when an existing connection drops
$client->onDisconnected(function () {
   echo 'Disconnected' . PHP_EOL;
});

// The SDK invokes onError when it receives an error message from RTM
$client->onError(function ($type, $error) {
   echo "Type: $type; Error: $error[message] ($error[code])" . PHP_EOL;
});

$client->connect() or die;

Subscribe to channel and receive messages

The tutorial app specifies a function that the SDK invokes whenever it receives data from RTM. The data can be a message from the subscribed channel, a success response to a subscription request, or an error message. The function tests the $type parameter to determine what it should do with the message.

The following code example demonstrates these tasks:

  1. Defines the callback function $callback that handles subscription events
  2. Calls RtmClient.subscribe() to send a subscribe request to RTM and set the callback function
  3. Calls RtmClient.waitAllReplies() to read synchronously from the socket and wait for an ack response, indicating that the read request was received.. After the SDK returns from the read, the client proceeds. 

Although this section of code subscribes to a channel, it doesn't read from the socket to get new messages. The client does that in the publishing loop, which is described in the next section.

// Callback function that handles subscription data and RTM responses and errors
$callback = function ($ctx, $type, $data) {
   switch ($type) {
       // The SDK received a channel message
       case Events::DATA:
           // Iterates over all the messages in the incoming message object
           foreach ($data['messages'] as $message) {
               if (isset($message['who']) && isset($message['where'])) {
                   echo 'Got animal ' . $message['who'] . ': ' . json_encode($message['where']) . PHP_EOL;
               } else {
                   echo 'Got message: ' . json_encode($message) . PHP_EOL;
               }
           }
           break;
       // The SDK received confirmation that the subscription succeeded
       case Events::SUBSCRIBED:
           echo 'Subscribed to: ' . $data['subscription_id'] . PHP_EOL;
           break;
       // The SDK received an error for the subscription
       case Events::ERROR:
           echo 'Subscription error! Error: ' . $err['error'] . '; Reason: ' . $err['reason'] . PHP_EOL;
           break;
   }
};
// Sends a subscription request for the 'animals' channel to RTM
// waitAllReplies() waits for RTM to confirm the subscription.
$client->subscribe('animals', $callback);

$client->waitAllReplies();

// To receive messages, the client still has to read from the socket. It does
// this in the publishing loop, after it has finished writing to the socket, by
// calling sockReadFor()

Because PHP is single-threaded by default, the PHP RTM SDK uses a single-threaded model. The client must do explicit reads from the WebSocket connection and alternate read and write operations.

Publish

To publish a message to the channel, the tutorial application calls RTMClient.publish() with the following arguments:

  • Channel name
  • Message object
  • Callback function that the SDK invokes after it receives a response from RTM

The SDK converts the message object to JSON format before publishing it.

The application publishes the location of an animal every 2 seconds, using coordinates chosen at random.

Because the client is now publishing messages to the channel, there are messages it can retrieve from the channel. The last line of the code does a synchronous read of as many messages as possible for two seconds.

If the read succeeds, the SDK invokes the callback function that the subscription specified. Because the message from RTM has the event type DATA, the callback function prints out the contents of the message.

while (true) {
   $lat = 34.134358 + rand(0, 100)/10000;
   $lon = -118.321506 + rand(0, 100)/10000;

   $animal = array(
       'who' => 'zebra',
       'where' => array(
           'lat' => $lat,
           'lon' => $lon,
       ),
   );

   $client->publish("animals", $animal, function ($code, $response) use ($animal) {
       if ($code == RtmClient::CODE_OK) {
           echo 'Animal is published ' . json_encode($animal) . PHP_EOL;
       } else {
           echo 'Publish request failed. ';
           echo 'Error: ' . $response['error'] . '; Reason: ' . $response['reason'] . PHP_EOL;
       }
   });

   // You must read from the incoming buffer to process incoming messages and avoid buffer overflow
   // Let's read all incoming messages for 2 seconds
   $client->sockReadFor(2);
}

Run the tutorial

To run the tutorial, open a console window and enter the following:

$ php examples/quickstart.php

The application displays text in the console window. The following is an example; your own values of endpointappkey, and where may be different:

$ php examples/quickstart.php
RTM client config:
	endpoint = YOUR_ENDPOINT
	appkey = YOUR_APPKEY
	authenticate? = true
[info] 2017/08/31 13:57:16.120400 Client: Connecting to endpoint: YOUR_ENDPOINT
[info] 2017/08/31 13:57:16.662300 Auth: Starting authentication
[info] 2017/08/31 13:57:16.925200 Auth: Successfully authenticated
Connected to Satori RTM and authenticated as ROLE
[info] 2017/08/31 13:57:17.053900 Subscribed (animals)
Subscribed to: animals
Got animal zebra: {"lat":34.136158,"lon":-118.318506}
Animal is published {"who":"zebra","where":{"lat":34.136158,"lon":-118.318506}}
Got animal zebra: {"lat":34.134658,"lon":-118.312706}
Animal is published {"who":"zebra","where":{"lat":34.134658,"lon":-118.312706}}

You can also see messages in the Dev Portal Web Console:

  1. Log in to the Dev Portal
  2. Select Projects > My projects from the top menu bar.
  3. Click the Console tab
  4. Enter 'animals' in the Enter Channel text box, then click outside the box.
  5. Messages from the tutorial application appear in the console window.

Troubleshooting Direct link

Unable to Connect to a Secure Endpoint

Symptom:

$ php examples/quickstart.php
[info] 2017/08/03 15:28:22.512700 Client: Connecting to endpoint: wss://<endpoint>.api.satori.com/v2
[erro] 2017/08/03 15:28:22.535000 Failed establish connection to <endpoint>.api.satori.com:443:  (0)

Solution:

  1. Try to connect to this endpoint:
    $ telnet <endpoint>.api.satori.com 443
    Trying 123.123.123.123...
    Connected to <endpoint>.api.satori.com.
    Escape character is '^]'.
    If you can't connect, check your firewall settings.
  2. Check the SSL settings for PHP:
    $ php -r "print_r(openssl_get_cert_locations());";
    Array
    (    
          [default_cert_file] => /usr/local/etc/openssl/cert.pem    
          [default_cert_file_env] => SSL_CERT_FILE    
          [default_cert_dir] => /usr/local/etc/openssl/certs
          [default_cert_dir_env] => SSL_CERT_DIR
          [default_private_dir] => /usr/local/etc/openssl/private
          [default_default_cert_area] => /usr/local/etc/openssl
          [ini_cafile] =>
          [ini_capath] =>
    )
    Make sure that the default_cert_file points to an existing cert file and default_cert_area points to the existing directory. If your certs are in another location, set environment variables that point to the certs by entering the following in a console window:
    $ export SSL_CA_FILE=/new/cert/dir/cert.pem 
    $ export SSL_CA_PATH=/new/cert/dir/
    $ php your_app/index.php