Web socket is a bidirectional communication protocol based on client-server communication. The client and server perform a handshake in this protocol to form a TCP connection. There are callbacks to handle the occurring events such as “onClose” and “onMessage” between the server and client,  and they can share data continuously without any data loss. Once they are done sharing the data, either of them can close the connection to end the communication. Moreover, as the HTTP starts with “http://” or “https://”, it starts with “ws://:or “wss://”.

Below is the tutorial to write a client-server chat program using web sockets in PHP:

  1. XAMPP Package
  2. Creating Chat UI
  3. UI Script
  4. PHP Socket
  5. Chat Handler
  6. Send Function
  7. Seal Function
  8. Unseal Function
  9. Handshake Function
  10. New Connection Acknowledge Function
  11. Connection Disconnect Acknowledge Function
  12. Create ChatboxMessage Function
  13. Starting the Connection

 

XAMPP Package

The users can download and install XAMPP on their computers to use it as the local web server. It contains the interpreters for various scripting languages such as PHP. Moreover, the users can also use it to integrate MySQL into the chatting program. Basically, in this scenario, it downloads the required packages and software to set up the PHP environment.

Downloading and Installation Process

The users can visit XAMPP to download the XAMPP installer. Then, they can run the installer to open the installation window, where they can specify the languages and the package’s root directory. After installation, they can open the XAMPP package and start the Apache by giving them firewall access. Also, the users need to place the scripting file, such as php files, in the “C:/XAMPP//htdocs/” directory of the XAMPP package.

Note: The users must go to “C://XAMPP/php/” and find the “php.ini” file. Then, they must find the line “;extension=intl” and replace it with “extension=intl” to run the web sockets properly. Otherwise, they may encounter the error “Call to undefined function socket_create()“.

Creating Chat UI

The users can create a folder inside the htdocs director with any name of their choice, for instance, a chat application. In this folder, they need to create a file named “index.php” where they can include all the style sheets and HTML for their chat application. Following is an example of CSS and HTML code for the chat UI:

            body{width:600px;font-family:calibri;}
            .error {color:#FF0000;}
            .chat-connection-ack{color: #26af26;}
            .chat-message {border-bottom-left-radius: 4px;border-bottom-right-radius: 4px;
            }
            #btnSend {background: #26af26;border: #26af26 1px solid; border-radius: 4px;color: #FFF;display: block;margin: 15px 0px;padding: 10px 50px;cursor: pointer;            
            #chat-box {background: #fff8f8;border: 1px solid #ffdddd;border-radius: 4px;border-bottom-left-radius:0px;border-bottom-right-radius: 0px;min-height: 300px;padding: 10px;overflow: auto;
            }
            .chat-box-html{color: #09F;margin: 10px 0px;font-size:0.8em;}
            .chat-box-message{color: #09F;padding: 5px 10px; background-color: #fff;border: 1px solid #ffdddd;border-radius:4px;display:inline-block;}
            .chat-input{border: 1px solid #ffdddd;border-top: 0px;width: 100%;box-sizing: border-box;padding: 10px 8px;color: #191919;
            }
            </style>          
<body>

                        <form name="frmChat" id="frmChat">
                                    <div id="chat-box"></div>
                                    <input type="text" name="chat-user" id="chat-user" placeholder="Name" class="chat-input" required />
                                    <input type="text" name="chat-message" id="chat-message" placeholder="Message"  class="chat-input chat-message" required />
                                    <input type="submit" id="btnSend" name="send-chat-message" value="Send" >
                        </form>
</body>

The above example code creates a form “frmChat” containing the two input fields, i.e., user name, user message and the submit button to send messages, with the unique classes and ids. The CSS styling uses the classes and ids of these fields to style the form for the user’s screen.

Note: The users can choose to write the HTML and CSS codes in separate files, which is a good programming practice.

UI Script

In the same index.php file, the users can include the “jquery” and use it inside the script tag. It tries to connect with the file named “php-socket.php“ hosted using web sockets. Then, if the socket is open, it creates a “div” to display the “connection established message” to the users, and if any error occurs or the connection gets closed, it again creates a div to display the respective updates on the user’s screen. Additionally, whenever it receives a new message, it again creates an HTML div with message type in its class and displays the user’s message on the user’s screen. Moreover, when the user presses the submit button, the action listener takes the values of both input fields, the user’s message and name in the variables, makes a JSON object, and sends it to the web server.

Below is the code to execute the above functionality:

<script> 
function showMessage(messageHTML) {
            $('#chat-box').append(messageHTML);
}

$(document).ready(function(){
            var websocket = new WebSocket("ws://localhost:8090/demo/php-socket.php");
            websocket.onopen = function(event) {
                        showMessage("<div class='chat-connection-ack'>Connection is established!</div>");              
            }
            websocket.onmessage = function(event) {
                        var Data = JSON.parse(event.data);
                        showMessage("<div class='"+Data.message_type+"'>"+Data.message+"</div>");
                        $('#chat-message').val('');
            };
                     
            websocket.onerror = function(event){
            showMessage("<div class='error'>Problem due to some Error</div>");
            };
            websocket.onclose = function(event){
                        showMessage("<div class='chat-connection-ack'>Connection Closed</div>");
            };
                     
            $('#frmChat').on("submit",function(event){
                        event.preventDefault();
                        $('#chat-user').attr("type","hidden");               
                        var messageJSON = {
                                    chat_user: $('#chat-user').val(),
                                    chat_message: $('#chat-message').val()
                        };

                        websocket.send(JSON.stringify(messageJSON));
            });
});
</script>

PHP Socket

The users now need to create a new file for the web socket, named, php-socket.php. This file contains the code to create the PHP socket that runs infinitely to wait for any upcoming connection request. Whenever a new request comes, it reads the client’s IP address and handshakes with the client to form a connection. Moreover, it acknowledges the client’s request by sending a sealed acknowledgment message.

Furthermore, this code receives all the socket messages, unseals them, decodes the JSON data, and sends them to the client. When the data stops coming, it acknowledges the disconnection and disconnects the client.

Below is the code to write the web socket in PHP:

<?php
define('HOST_NAME',"localhost");
define('PORT',"8090");
$null = NULL;

require_once("class.chathandler.php");
$chatHandler = new ChatHandler();

$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);

$clientSocketArray = array($socketResource);
while (true) {

$newSocketArray = $clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);

if (in_array($socketResource, $newSocketArray)) {
$newSocket = socket_accept($socketResource);
$clientSocketArray[] = $newSocket;

$header = socket_read($newSocket, 1024);
$chatHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);

socket_getpeername($newSocket, $client_ip_address);
$connectionACK = $chatHandler->newConnectionACK($client_ip_address);

$chatHandler->send($connectionACK);
$newSocketIndex = array_search($socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}

foreach ($newSocketArray as $newSocketArrayResource) {
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
$socketMessage = $chatHandler->unseal($socketData);
$messageObj = json_decode($socketMessage);

$chat_box_message = $chatHandler->createChatBoxMessage($messageObj->chat_user, $messageObj->chat_message);
$chatHandler->send($chat_box_message);
break 2;
}

$socketData = @socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {

socket_getpeername($newSocketArrayResource, $client_ip_address);
$connectionACK = $chatHandler->connectionDisconnectACK($client_ip_address);
$chatHandler->send($connectionACK);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($socketResource);

Chat Handler

In the next step, the users have to create a new PHP file, named, “class.chathandler.php.” This file contains all the handlers for seal, unseal, send, and handshake functionalities.

This code has a single PHP class named “ChatHandler ”, which contains the functions to perform the above functionality.

<?php
class ChatHandler {
}
?>

Send Function

The send function takes the message string as its parameter, calculates its length, writes it on the web socket with the message’s length, and returns the boolean ”true”.

function send($message) { 
global $clientSocketArray; 
$messageLength = strlen($message); 
foreach($clientSocketArray as $clientSocket) 
{ 

@socket_write($clientSocket,$message,$messageLength); 
} 

return true; 
}

Seal Function

The seal function takes the socket data in its argument and gets the header string using a pre-defined method. It calculates the data’s length and uses various packing methods, converting the numbers to binary string and storing them in the header variable. Then it concatenates the header string with the socket data and returns the resulting string.

            function seal($socketData) {
$b1 = 0x81;
$length = strlen($socketData);

if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$socketData;
}

Unseal Function

The unseal function takes the already sealed socket data and unseals it using the prior calculations, and returns the original socket data. There is no recommended or strict rule in writing these functions, and the users can write their seal and unseal functions depending on their need, with their calculations and techniques to seal the data.

           function unseal($socketData) {
$length = ord($socketData[1]) & 127;
if($length == 126) {
$masks = substr($socketData, 4, 4);
$data = substr($socketData, 8);
}

elseif($length == 127) {
$masks = substr($socketData, 10, 4);
$data = substr($socketData, 14);
}

else {
$masks = substr($socketData, 2, 4);
$data = substr($socketData, 6);
}

$socketData = "";
for ($i = 0; $i < strlen($data); ++$i) {
$socketData .= $data[$i] ^ $masks[$i%4];
}

return $socketData;
}

Handshake Function

The handshake function receives the hostname, port number, header, and the client socket resource in its argument to perform the handshake. It does its calculations and sends the connection and web socket details to the client.

            function doHandshake($received_header,$client_socket_resource, $host_name, $port) {
$headers = array();
$lines = preg_split("/\r\n/", $received_header);
foreach($lines as $line)
{

$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{

$headers[$matches[1]] = $matches[2];
}
}

$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$buffer  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host_name\r\n" .
"WebSocket-Location: ws://$host_name:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_socket_resource,$buffer,strlen($buffer));
}

New Connection Acknowledge Function

The new connection acknowledge function takes the client’s IP address in its argument, writes the acknowledgment message, encodes the message, seals the message by calling the sealed function, and returns the resulting string.

            function newConnectionACK($client_ip_address) {
$message = 'New client ' . $client_ip_address.' joined';
$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}

Connection Disconnect Acknowledge Function

The connection disconnect acknowledge function takes the client’s IP address and acknowledges the disconnection in a similar manner as the new connection acknowledge function.

            function connectionDisconnectACK($client_ip_address) {
$message = 'Client ' . $client_ip_address.' disconnected';
$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}

Create ChatboxMessage Function

The create chat box function takes the user’s name and the user’s message in its argument creates a new div to display the message, encodes and seals the message, and returns the sealed result.

function createChatBoxMessage($chat_user,$chat_box_message) {
$message = $chat_user . ": <div class='chat-box-message'>" . $chat_box_message . "</div>";
$messageArray = array('message'=>$message,'message_type'=>'chat-box-html');
$chatMessage = $this->seal(json_encode($messageArray));
return $chatMessage;
}

Starting the Connection

After writing the code files and placing them in the htdocs directory, the users can either use the command line to start the connection and the chat, or they can go to their browser and open the php-socket.php file using the localhost.