Websocket
In this example Ratched
Install via Composer: composer require cboden/ratchet
socket.php
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
class Socket implements MessageComponentInterface {
protected $clients;
private $state = 0;
private $date;
public function __construct() {
$this->clients = new \SplObjectStorage;
$this->date = date("G");//Saves the date the day the socket was started
}
public function dateListener(){
$d = date("G");
if($d < $this->date){//check if day has changed
$this->state = 0;
$this->date = $d;
foreach($this->clients as $client){//resetting the state to default(0)
$client->send($this->state);
}
}
}
public function onOpen(ConnectionInterface $conn) {
// Store the new connection to send messages to later
$this->clients->attach($conn);
foreach($this->clients as $client){
//if a new client connects it gets the current state
$client->send($this->state);
}
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client) {
if ($from !== $client) {
// The sender is not the receiver, send to each client connected
if($msg == 1){//changing state and sending it to all clients
$this->state = 1;
$client->send($this->state);
}else if($msg == 0){
$this->state = 0;
$client->send($this->state);
}
}
}
}
public function onClose(ConnectionInterface $conn) {
// The connection is closed, remove it, as we can no longer send it messages
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
// Run the server application through the WebSocket protocol on port 8080
$sckt = new Socket();
$server = IoServer::factory(
new HttpServer(
new WsServer(
$sckt
)
),
8080
);
//to check continuously if the date has changed
$server->loop->addPeriodicTimer(5, function () use ($sckt) {
echo $sckt->dateListener();
});
$server->run();
index.html
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div id="text"></div>
</body>
<script>
var conn = new WebSocket('ws://localhost:8080');//connect
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {//receive current state
var div = document.getElementById("text");
if (e.data == 1){
div.innerHTML = "text";//your text
}else if (e.data == 0){
div.innerHTML = "";
}
};
</script>
</html>
switch.html
<!DOCTYPE html>
<html>
<head>
<title>switch</title>
</head>
<body>
<button onclick="sendToggle()">Toggle</button>
</body>
<script>
var conn = new WebSocket('ws://localhost:8080');//connect
var toggle = 1;
conn.onopen = function (e) {
console.log("Connection established!");
};
conn.onmessage = function (e) {//receive current state
if(e.data == 1){
toggle = 1;
}else if(e.data == 0){
toggle = 0
}
};
this.send = function (message, callback) {
this.waitForConnection(function () {
conn.send(message);
if (typeof callback !== 'undefined') {
callback();
}
}, 1000);
};
this.waitForConnection = function (callback, interval) {
if (conn.readyState === 1) {
callback();
} else {
var that = this;
// optional: implement backoff for interval here
setTimeout(function () {
that.waitForConnection(callback, interval);
}, interval);
}
};
function sendToggle() {//send new state by pressing the button
if (toggle == 1) {
this.send("0",function(){
console.log("sent");
toggle = 0;
});
} else {
this.send("1",function(){
console.log("sent");
toggle = 1;
});
}
}
</script>
</html>
To start the websocket simply run the following command
php socket.php
To deploy the this version you need ssh access to your sever to run socket.php. So that the execution of the file does not stop when you close the terminal window you need to create a screen session.
To install screen on a linux server do the following:
On Ubuntu or Debian
sudo apt-get install screen
On CentOS and Fedora
sudo yum install screen
To start a screen session type screen. After starting the session navigate to your websocket.php and run it. To detach from a session simply type Ctrl+a d
Edit
Using long pulling is also an option.
this removes the need of ssh access.
I am using ajax to get and change the current state by making requests to the server
Ajax and php docs
Create the following file structure:
-client.js
-index.html
-switch.html
-switch.js
--/server
-changeState.php
-data.txt
-server.php
Extends this
index.html
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="client.js"></script>
</head>
<body>
<h1>Response from server:</h1>
<div id="response"></div>
</body>
</html>
switch.html
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="switch.js"></script>
</head>
<body>
<button id="toggler" state="" onclick="toggleState()">Toggle</button>
<script>
toggleState();//to get current state
</script>
</body>
</html>
Change the url parameter in both ajax requests to deploy it.
client.js
/**
* AJAX long-polling
*
* 1. sends a request to the server (without a timestamp parameter)
* 2. waits for an answer from server.php (which can take forever)
* 3. if server.php responds (whenever), put data_from_file into #response
* 4. and call the function again
*
* @param timestamp
*/
function getContent(timestamp)
{
var queryString = {'timestamp' : timestamp};
$.ajax(
{
type: 'GET',
url: 'http://localhost:80/longPulling/server/server.php',
data: queryString,
success: function(data){
// put result data into "obj"
console.log(data);
var obj = jQuery.parseJSON(data);
// put the data_from_file into #response
$('#response').html(obj.data_from_file);
// call the function again, this time with the timestamp we just got from server.php
getContent(obj.timestamp);
}
}
);
}
// initialize jQuery
$(function() {
getContent();
});
switch.js
function toggleState()
{
var currentState = $("#toggler").attr("state")
console.log("currentState: "+currentState);
var queryString = {};
if(currentState == ""){
queryString = {'state' : 0};//default state
}else{
queryString = {'state' : currentState}
}
$.ajax(
{
type: 'GET',
url: 'http://localhost:80/longPulling/server/changeState.php',
data: queryString,
success: function(data){
// put result data into "obj"
var obj = jQuery.parseJSON(data);
$("#toggler").attr("state",obj.state);
}
}
);
}
// initialize jQuery
$(function() {
toggleState();
});
provides the current state
server/server.php
<?php
/**
* Server-side file.
* This file is an infinitive loop. Seriously.
* It gets the file data.txt's last-changed timestamp, checks if this is larger than the timestamp of the
* AJAX-submitted timestamp (time of last ajax request), and if so, it sends back a JSON with the data from
* data.txt (and a timestamp). If not, it waits for one seconds and then start the next while step.
*
* Note: This returns a JSON, containing the content of data.txt and the timestamp of the last data.txt change.
* This timestamp is used by the client's JavaScript for the next request, so THIS server-side script here only
* serves new content after the last file change. Sounds weird, but try it out, you'll get into it really fast!
*/
// set php runtime to unlimited
set_time_limit(0);
// where does the data come from ? In real world this would be a SQL query or something
$data_source_file = 'data.txt';
// If your using a database your table needs to following columns
// ID state createdAt
//
// $sql = "SELECT * FROM mytable ORDER BY ID DESC LIMIT 1"
// $servername = "localhost";
// $username = "username";
// $password = "password";
// $dbName = "myDataBase"
// // Create connection
// $conn = new mysqli($servername, $username, $password, $dbName);
// // Check connection
// if ($conn->connect_error) {
// die("Connection failed: " . $conn->connect_error);
// }
//
// $result = $conn->query($sql);
// $row = $result->fetch_assoc();
// $lastChanged = $row['created_at'];
// $currentState = $row['state'];
// main loop
while (true) {
// if ajax request has send a timestamp, then $last_ajax_call = timestamp, else $last_ajax_call = null
$last_ajax_call = isset($_GET['timestamp']) ? (int)$_GET['timestamp'] : null;
// PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache
clearstatcache();
// get timestamp of when file has been changed the last time
// use $lastChanged if using database
$last_change_in_data_file = filemtime($data_source_file);
// if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp
if ($last_ajax_call == null || $last_change_in_data_file > $last_ajax_call) {
// get content of data.txt
// use $state if using database
$data = file_get_contents($data_source_file);
// put data.txt's content and timestamp of last data.txt change into array
$result = array(
'data_from_file' => $data,
'timestamp' => $last_change_in_data_file
);
// encode to JSON, render the result (for AJAX)
$json = json_encode($result);
echo $json;
// leave this loop step
break;
} else {
// wait for 1 sec (not very sexy as this blocks the PHP/Apache process, but that's how it goes)
sleep( 1 );
continue;
}
}
changes the current state
server/changeState.php
<?php
//copy database connection from server.php
if(isset($_GET['state']) && ($_GET['state'] == 1 || $_GET['state'] == 0)){
$newState = 0;
if($_GET['state'] == 0){
$newState = 1;
}
// $sql = "INSERT INTO mytable (state) VALUES ($newState);"
// if ($conn->query($sql) === TRUE) {
// echo "New record created successfully";
// } else {
// echo "Error: " . $sql . "<br>" . $conn->error;
// }
file_put_contents("data.txt", $newState);
$response = array(
'state' => $newState
);
$json = json_encode($response);
echo $json;
}else{
//look into server.php how to get data out of database
$content = trim(file_get_contents("data.txt"));
if($content == 1 || $content == 0){
$response = array(
'state' => $content
);
$json = json_encode($response);
echo $json;
}else{
//copy content insertion from above
file_put_contents("data.txt", 0);
$response = array(
'state' => 0
);
$json = json_encode($response);
echo $json;
}
}
stores the current state
server/data.txt
0
You should create the file structure locally and then upload the folder to your server.
It's not very sexy but it works.