Here we are going to build a chat app using:

Ratchet, for WebSockets abstraction. Siler, for Ratchet abstraction. React, for UI/DOM (HTML) abstraction.

Since neither Siler or Ratchet require us to have a predefined folder structure, we can start working with create-react-app then nicely mix our back-end code within.

$ create-react-app $(codename --slug)

If you have naming problems like me, check out Intel Codenames Picker.

Here I got downey (DowneyChat looks good), so let’s cd to it:

$ cd downey

Here we magically got a running React app with its dependencies and front-end development facilities already loaded. Now we are going to get out back-end folks:

$ composer require leocavalcante/siler dev-master

$ composer require cboden/ratchet

Don’t worry, package.json , node_modules/ , composer.json and vendor/ can all play together seamlessly.

Here comes the code

You don’t need any previous knowledge on Ratchet, despite I really encourage you to have look on it.

This is our back-end, I’m naming it server.php :

<?php require_once __DIR__.'/vendor/autoload.php'; use Siler\Ratchet; Ratchet\connected(function ($conn) {

print("New connection

"); Ratchet\broadcast(json_encode([

'event' => 'message',

'payload' => 'New connection!',

]));

}); Ratchet\inbox(function ($from, $message) {

printf("New message: %s

", $message); $data = json_decode($message, true); if ($data['event'] == 'message') {

Ratchet\broadcast($message);

}

}); print("Running at 3001

");

Ratchet\init(3001);

Pretty simple, right? Yeah, that’s Siler doing it’s thing. We are just declaring a connected callback to handle new clients and an inbox callback to handle incoming messages then Siler\Ratchet\broadcast will send the given message to all connected clients. You don’t have to worry about the connected clients, Siler already does it for you.

In fact, now we have much more front-end code than PHP to build a chat app have you ever imagined that before? Don’t worry, not that much:

import React, { Component } from 'react' class App extends Component {

constructor() {

super() this.state = {

messages: []

}

} componentDidMount() {

this.conn = new WebSocket('ws://localhost:3001')

this.conn.onmessage = this.onMessage.bind(this)

} onMessage(event) {

const data = JSON.parse(event.data) if (data.event === 'message') {

const messages = this.state.messages

messages.push(data.payload)

this.setState({messages})

}

} onSubmit(event) {

event.preventDefault() const message = this.refs.message.value.trim()

this.refs.message.value = '' this.conn.send(JSON.stringify({

event: 'message',

payload: message,

}))

} render() {

return (

<div className="App">

<ul className="MessageList">

{this.state.messages.map((message, i) => (

<li className="MessageItem" key={i}>{message}</li>

))}

</ul> <form className="Form" onSubmit={this.onSubmit.bind(this)}>

<input ref="message" />

</form>

</div>

)

}

} export default App

Is it working?

To bring up our chat server, run:

$ php server.php

You should see the printed message: Listening on 3001 .

And to bring up our client code, run:

$ yarn[npm] start

You should see:

Compiled successfully! The app is running at: http://localhost:3000/ Note that the development build is not optimized.

To create a production build, use npm run build.

It will already starts a new tab in your default browser and you should be seeing a “New connection” message, yeah, that is you. Type something in the input box and hit enter, the message should be listed right after. Open a new browser window, put them side-by-side and talk to yourself a little.

Feel free to ask any questions.

Thank you.