10

I am developing an IOS social application that is written in SWIFT.

The backend is PHP, MySQL (for event handling), + a bit of NodeJS, Socket.io (for realtime chat and notifications)


I have made the chat successfully:

When the user sends a message the Socket.io server handles it the following way:

  • it inserts the datas to the database
  • if successful then emits the message to all the participant users

/ so for this the backend is only the Socket.io server, which handles the database aswell


Works fine.

But then there are events that are not meant to be real time, but still I want to send a notification to the given user with Socket.io

for example: if a post has been liked, then send a noti to the posts owner

I have already written the PHP files for saving the like in the database, but

How should I do the notification part, safe?


I have came up with 3 ideas:

  1. The app sends a web request to my PHP+MySQL backend, it handles the data there, then after returning back "success", the application (SWIFT) sends a notification to the post owner (via Socket.io XCode pod)
func likePost(postId : Int, completion: @escaping (ActionResult?)->()){

        let connectUrl = URL(string: appSettings.url + "/src/main/like.php")
        var request = URLRequest(url: connectUrl!)
        request.httpMethod = "POST"
        let postString = "userId=\(userId)&session=\(session)&pId=\(postId)"
        request.httpBody = postString.data(using: String.Encoding.utf8)


        let task = URLSession.shared.dataTask(with: request) {
            (data: Data?, response: URLResponse?, error: Error?) in

            if error != nil {
                return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
            }
            do {

                let responseJson = try JSONSerialization.jsonObject(with: data!, options: [])
                if let responseArray = responseJson as? [String: Any] {

                    let responseStatus = responseArray["status"] as? String
                    let responseTitle = responseArray["title"] as? String
                    let responseMessage = responseArray["message"] as? String


                    if responseStatus != "1" {
                        return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
                    }

                    // SUCCESS, SEND NOTI WITH SOCKET.IO

                    socket.emit("notification_likedPost", ["postId": postId)

                    return completion(ActionResult(type: 1, title: "success", message: "yay"))

                }
            } catch {
                return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
            }
        }
        task.resume()
    }
  1. same, but after returning back "success" from the PHP, itself (the PHP file) handles the Socket.IO notification emitting as well (I think this is not possible, I haven't found any PHP->Socket.io plugins..)

-

  1. The app does not send anything to my web PHP+MySQL file, instead it sends the whole "like" process to my NodeJs, Socket.IO server, it handles it there, saves it to the database, then emits the notifications (Just like the real time chat part, but this would be a lot work because I have already written all the other code in PHP files)

The first case is the most ideal for me, but I am scared that it would be hackable..

Because if I do it the first way, the backend NodeJs+Socket.io server won't check if the liking process was successful (because it was checked client-sided)

so it is likely that anyone could send fake "post like" notifications, like a billion times.


Then maybe the second option would be great as well, so that back-end handles both checking, and notification sending, but sadly there's no Socket.io plugin for PHP

Kárpáti András
  • 1,221
  • 1
  • 16
  • 35
  • Using Socket.io with PHP wouldn't be the most efficient way of handling inter process communication... (even dangereous if not set up correctly). I suggest that you take a look at Redis as a message broker between PHP & NodeJS. But still NodeJS is much more powerfull without PHP, hope you get enough motivation to move on from PHP. – EMX Oct 04 '18 at 19:23

3 Answers3

7

It would be much more simpler to ...

Forget PHP, Go full Nodejs:

Express (you can also combine it with handlebars & i18n for multi-language purpose)

With express you can build a router for incoming requests (GET,PUT,POST,...)

This means that you can use it to render pages with server-side dynamic data

const express = require('express');
const exphbs = require('express-handlebars');
const app = express();

// Register Handlebars view engine
app.engine('handlebars', exphbs());
// Use Handlebars view engine
app.set('view engine', 'handlebars');

var visit_counter = 0; 
app.get('/', (req, res) => {
  var time_stamp = Date.now(); visit_counter++
  res.render('index',{"timestamp":time_stamp,"visits":visit_counter});
});

app.listen(3000, () => {
  console.log('Example app is running → PORT 3000');
});

The views/index.hbs file would look like this :

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example App</title>
</head>
<body>

 <p> Current Time : {{timestamp}} </p>
 <p> Total Visits : {{visits}} </p>

</body>
</html>

This above part is an example of server-side data being rendered in the final html.


Socket.io (if you want more than 1 instance of the server running, no problem, lookup socket.io-redis)

You can combine express with socket.io in different ways, you could even use cookie-based authentication for your socket protocol. so when an event is coming in you could actually tell 100% if its a legit user and its user-id.


To prevent the spam of likes... you have to control them somehow. You should store the action of the like, so it cant be repeated more than once for the same post (so user-id & post-id seem to be the important variables here)



Here comes the update :

Since you made quite clear that you want a php & nodejs combo :

Redis is an in-memory data structure store which can be used as a database, a cache and a message broker.

PHPRedis @Github

Redis PubSub with PHP and Node.JS

A quick example of Node.js reading PHP session on Redis

Using Redis, you can easily listen to php events from your nodejs instance.

I suggest that you also think about the future scaling of your system and give a try at learning more nodejs to be able to move on from php.

Community
  • 1
  • 1
EMX
  • 6,066
  • 1
  • 25
  • 32
  • Thank you very much for this level of detailed answer, and suggestions! I appreciate it and am on the same conclusion, switching fully to NodeJS would be the best option, and would save me a lot of time in the future: But rewriting all the files would be just too much work for now + I am more familiar with PHP yet: therefore I try to combine where I can. What do you think, If I have a running NodeJs->Socket.io server, and a PHP server: is it okay to emit the notifications from the PHP? – Kárpáti András Sep 30 '18 at 10:05
  • @AndrasKarpati , If you have to go 100% php you have to choose an option that doesn't become a future bottleneck... I will update my answer with more details – EMX Oct 04 '18 at 13:36
3

I understand your concern as your whole project has more concentration of PHP code as compared to other frameworks/languages. In order to rectify your problem, here is the Socket.io implementation for PHP v5.3 and above https://github.com/walkor/phpsocket.io.

With the help of this, you can use socket.io library in your PHP code. Below you can see an example of using Socket.io library in PHP.

use Workerman\Worker;
use PHPSocketIO\SocketIO;

// listen port 2020 for socket.io client
$io = new SocketIO(2020);
$io->on('connection', function($socket){
    $socket->addedUser = false;
    // when the client emits 'new message', this listens and executes
    $socket->on('new message', function ($data)use($socket){
        // we tell the client to execute 'new message'
        $socket->broadcast->emit('new message', array(
            'username'=> $socket->username,
            'message'=> $data
        ));
    });
    // when the client emits 'add user', this listens and executes
    $socket->on('add user', function ($username) use($socket){
        global $usernames, $numUsers;
        // we store the username in the socket session for this client
        $socket->username = $username;
        // add the client's username to the global list
        $usernames[$username] = $username;
        ++$numUsers;
        $socket->addedUser = true;
        $socket->emit('login', array( 
            'numUsers' => $numUsers
        ));
        // echo globally (all clients) that a person has connected
        $socket->broadcast->emit('user joined', array(
            'username' => $socket->username,
            'numUsers' => $numUsers
        ));
    });
    // when the client emits 'typing', we broadcast it to others
    $socket->on('typing', function () use($socket) {
        $socket->broadcast->emit('typing', array(
            'username' => $socket->username
        ));
    });
    // when the client emits 'stop typing', we broadcast it to others
    $socket->on('stop typing', function () use($socket) {
        $socket->broadcast->emit('stop typing', array(
            'username' => $socket->username
        ));
    });
    // when the user disconnects.. perform this
    $socket->on('disconnect', function () use($socket) {
        global $usernames, $numUsers;
        // remove the username from global usernames list
        if($socket->addedUser) {
            unset($usernames[$socket->username]);
            --$numUsers;
           // echo globally that this client has left
           $socket->broadcast->emit('user left', array(
               'username' => $socket->username,
               'numUsers' => $numUsers
            ));
        }
   });
});

Worker::runAll();
Abhishek Singh
  • 2,642
  • 15
  • 24
1

You can create multiple web sockets channel. In your case, you have added one using socket.io in NodeJS. You can add another channel through php way.

You can listen to that channel the same way your are listening from NodeJS.

Few handy links 1. http://php.net/manual/en/book.sockets.php 2. How to create websockets server in PHP

Nishant
  • 109
  • 1
  • 6