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:
/ 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:
- 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()
}
- 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..)
-
- 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
It would be much more simpler to ...
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)
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.
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();
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With