Sử dụng SSE trong PHP

Sử dụng SSE trong PHP là sử dụng kỹ thuật SSE để đưa dữ liệu từ server về user 1 cách tự động mỗi khi có dữ liệu mới từ phía server. Bài hướng dẫn này  hướng dẫn code ở phía server với công nghệ PHP.

Tổng quát về sử dụng SSE trong PHP

Để thực hiện bài hướng dẫn này, bạn nên  đọc bài này trước: Kỹ thuật Server Sent Events – truyền dữ liệu tự động từ server để có khái niệm cơ bản nhé.

Khi sử dụng sse, thường bạn sẽ code ở trong hai file. File thứ nhất là code ở phía server để cung cấp dữ liệu về cho client và file thứ hai sẽ code javasscript để đón dữ liệu ở phía client. Code file nào trước cũng được, đủ hai file thì mới chạy.

a. Code phía client

Code phía client (browser) là code javascript để đón dữ liệu từ server và hiện ra trong nội dung trang.  Mỗi lần dữ liệu đổ về, có thể lấy dữ liệu từ server bằng cú pháp event.data, và lấy id của sự kiện theo cú pháp event.lastEventId . Trong 3 ví dụ ứng dụng bên dưới , bạn sẽ thấy cách sử dụng các code này.

b. Code phía server

Khi code cho phía server, kết quả bạn đổ về cho client phải là 1 khối text gồm nhiều dòng chữ. Dòng chữ nào bắt đầu bằng id:  tức là trả về giá trị mã của sự kiện. Còn dòng chữ bắt đầu bằng chữ retry: là thời khoản (miligiây) cho browser biết để lên lại server lấy tiếp thông tin. Những dòng chữ bắt đầu bằng data: là dữ liệu chính đổ về cho browser.  Mỗi dòng chữ kết thúc bằng phím ký tự  xuống hàng enter (\n)  riêng dòng cuối kết thúc bằng 2 dấu xuống hàng.

Sau đây là ba ví dụ về cách sử dụng sse trong PHP

Hiện tự động giá trị biến từ server sử dụng sử dụng sse trong php

Gìờ thì bắt đầu dùng SSE với ứng dụng thật đơn giản: đổ giá trị tự động  1 biến trên server về phía client, code đơn giản thôi.

Chuẩn bị:  Bạn tạo folder sse trong htdocs để thực tập thử. Chút nữa các file tạo ra sẽ đặt trong đây.

a. Code phía server

Tạo file sse/dem_server.php và code

<?php //dem-server.php
  header('Cache-Control: no-cache');
  header('Content-Type: text/event-stream');
  session_start();
  if (isset($_SESSION['dem'])==false) $_SESSION['dem']=0;
  $_SESSION['dem']++;
?>
id: <?php echo $_SESSION['dem'];?>
retry: 5000
data: <div>Thử nghiệm SSE </div>
data: Lần yêu cầu thứ  <b> <?php echo $_SESSION['dem'];?> </b>
data: <hr>

b. Code phía client

Tạo file sse/dem_client.html và code

<div id="kq"></div> 
<script>
  var source = new EventSource("http://localhost/sse/dem_server.php");
  source.onmessage = function(event) {
     document.getElementById("kq").innerHTML += event.data;
  };
</script>

Test: Mở trình duyệt và gõ http://localhost/sse/dem_client.html sẽ thấy dữ liệu tự động định kỳ đổ về client

Ứng dụng xem giờ trên server bằng cách sử dụng SSE trong PHP

Ví dụ thứ hai: đổ giờ tự động từ server về và hiện ra trong trong trang web. Mục đích của thực hành này để phía client luôn có thời gian chính xác do server cung cấp.

a. Code phía server

Tạo file sse\gio_server.php

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$time = gmdate('d/m/Y H:i:s', time() + 3600*7 );
session_start();
if (isset($_SESSION['dem'])==false) $_SESSION['dem']=0;
$_SESSION['dem']++;
echo "id: ", $_SESSION['dem'], "\n";
echo "retry: 500\n";
echo "data: {$time}\n\n";
?>

Test: http://localhost/sse/gio_server.php

b. Code phía client

Code phía client để đón giờ đổ về từ server.  Tạo file sse\gio_client.html

<meta charset="utf-8">
<body>
<h2>Bây giờ là <span id="kq"></span> </h2>
<script>
if(typeof(EventSource)!== "undefined") {
  var source = new EventSource("http://localhost/sse/gio_server.php");
  source.onmessage = function(e) {
     gioserver = e.data; 
    document.getElementById("kq").innerHTML = gioserver;
  };
} else document.getElementById("kq").innerHTML = "Browser không hỗ trợ";
</script>
</body>

Test: http://localhost/sse/gio_client.html , bạn sẽ thấy giờ liên tục thay đổi vì cứ 500 mili giây thì trình duyệt request lại server để lấy dữ liệu.

Bạn có thể phát triển thêm:  thay vì lấy giờ thì lấy các thông tin hệ thống như tình trạng cpu, mem … để theo dõi trạng thái server

Sử dụng sse trong php để tường thuật bóng đá

Ví dụ 3 về sử dụng sse trong php, chúng ta sẽ thử ứng dụng tường thuật bóng đá: Admin vừa chèn dữ liệu vào database, dữ liệu vừa chèn trong database sẽ hiện tự động trên các client. Người xem chẳng cần làm gì

a. Tạo table

Vào phpMyadmin tạo database có tên là sse. Trong database  vừa tạo, nhắp link SQL và chạy lệnh sau để tạo table tuongthuat 

CREATE TABLE `tuongthuat` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT ,
  `phut` varchar(10) NOT NULL,
  `noidung` varchar(2000) NOT NULL,
  `thoidiem` timestamp NOT NULL DEFAULT current_timestamp(),
   PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

b. Code phía server

– Tạo sse\bongda_server.php và code:

<?php
$db = new mysqli("localhost","root","", "sse");
$db->set_charset("utf8");
$kq = $db->query( "select id, phut, noidung from tuongthuat order by id DESC LIMIT 0,1");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
if ($kq->num_rows > 0) { 
    $row = $kq->fetch_assoc(); 
    echo "id: ", $row['id'], "\n";
    echo "retry: 5000\n";
    echo "data: <p>Phút ", $row['phut'], "</p> \n";
    echo "data: <div>",nl2br($row['noidung'])."</div><hr>\n\n";
}
?>

– Test : chạy file http://localhost/sse/bongda_server.php  nếu thấy trang trắng là tốt , vì chưa có dữ liệu.  Chúng ta tiếp tục code ở client

c. Code phía client

Code phía cluent là code javascript để đón dữ liệu từ server. Tạo file sse\bongda_client.html và code

<meta charset="utf-8">
<body>
<h1>Tường thuật bóng đá</h1>
<div id="kq"></div>
<script>
if(typeof(EventSource) !== "undefined") {
  var id=0; //biến này để lưu id của event
  var source = new EventSource("http://localhost/sse/bongda_server.php");
  source.onmessage = function(event) {
    if (event.lastEventId!=id){  //nếu id lần này và lần trước khác nhau thì mới hiện
      id = event.lastEventId; //mỗi lần dữ liệu về là lưu vào biến id
      document.getElementById("kq").innerHTML +=  event.data + "<br>";      
    } //if
  };
} else document.getElementById("kq").innerHTML = "Browser không hỗ trợ";
</script>
</body>

Test: http://localhost/sse/bongda_client.html, không thấy lỗi là ok, vì chưa có dữ liệu.

d. Chèn dữ liệu vào db

Giờ thì chèn dữ liệu vào database, ngay lập tực sẽ hiện trên client. Tạo file sse\bongda_tuongthuat.php và code:

<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">
<form class="col-8 mt-2 mx-auto p-2 border border-primary" method="post">
    <div class="mb-3">
      <label>Phút:</label>
      <input class="form-control shadow-none" name="phut" autofocus>
    </div>
    <div class="mb-3">
      <label>Nội dung:</label>
      <textarea  class="form-control shadow-none"  name="nd" rows="3"></textarea>
    </div>
    <button type="submit" class="btn btn-primary px-5 py-2"> LƯU </button>
</form>

Test: Mở trình duyệt gõ  http://localhost/sse/bognda_tuongthuat.php sẽ thấy form như hình.

Như vậy, chúng ta mới tạo xong form để chèn dữ liệu, giờ thì code tiếp để chèn dữ liệu vào table. Code tiếp ở đầu trang:

<?php
session_start();
if ($_POST){
    $phut= trim($_POST['phut']);
    $noidung= trim($_POST['nd']);
    if ($phut !="" && $noidung!=""){
      $db= new mysqli ("localhost","root","","sse");   
      $db->set_charset("utf8");
      $sql="INSERT INTO tuongthuat SET phut='$phut', noidung='$noidung'";
      $kq = $db->query($sql);
      $_SESSION['thongbao']= "Đã lưu. <br>Phút=$phut <br>ND: $noidung "; 
      header("location:chentuongthuat.php"); 
      exit();
    }
}
?>

Code trên giúp lưu dữ liệu vào db rồi đó, cần hiện thêm thông báo rồi test nhé. Đoạn code sau viết ngay sau mở đầu của tag form, mục đích là để hiện thông báo đã lưu thông tin vào db xong.

<?php if (isset($_SESSION['thongbao'])) {
echo "<div class='alert alert-success mt=3'>", $_SESSION['thongbao'], "</div>";
unset($_SESSION['thongbao']);
} ?>

Test thử nhé

Mở vài cửa sổ trình duyệt sắp xếp cho thấy hết nhé. Như trong hình dưới, trình duyệt 1 đóng vai admin (bongda_tuongthuat.php đề chèn dữ liệu vào db) . Cửa sổ 2 , 3, 4 đóng vai khách (trang bongda_client.html)

Mỗi lần bạn nhập thông tin vào form ở cửa sổ 1 (bongda_tuongthuat.php) rồi nhắp nút Lưu, bạn sẽ thấy thông tin mới trên các màn hình client một cách tự động.

Thử nhắp tiếp vài thông tin khác trong form (phút 6, 12, 16 như bên dưới) , mỗi lần Lưu sẽ thấy hiện tự động trên các client

Dữ liệu tường thuật bóng đá nhập liệu

1 Trận đấu bắt đầu.
6 Rabo Ali thực hiện đường chuyền từ sân nhà. Wander Luiz bật cao đánh đầu nhưng bóng đi không trúng đích. Chủ nhà Bình Dương chủ động chơi bóng dài trong điều kiện mặt sân trơn trượt.
12 Từ chấm đá phạt bên cánh trái, Minh Vương thực hiện cú đá đưa bóng hướng vào góc xa khung thành Bình Dương nhưng thủ môn Tấn Trường đã chơi cảnh giác.
16 NGUY HIỂM! Từ pha căng ngang của Châu Ngọc Quang, Shumeiko lóng ngóng chạm bóng nhưng Tấn Trường đã kịp phản xạ cứu thua cho chủ nhà Bình Dương.
26 CƠ HỘI! Nhận đường chuyền của Ngọc Quang, V.Toàn tung cú sút xa nhưng không gây khó khăn cho Tấn Trường
35 KHÔNG VÀO!! Trọng Huy tung cú sút xa đưa bóng đi chệch khung thành đội khách trong gang tấc.
45 45 phút đầu tiên trôi qua mà không có bàn thắng nào được ghi. Cơ hội của 2 đội hầu hết đến từ những tình huống treo bóng vào vùng cấm hoặc sút xa
55 VÀO!! Từ pha đá phạt bên cánh trái, Tiến Linh thoải mái băng vào đánh đầu cận thành không cho Văn Trường cơ hội cản phá.
68 VÀO!! Nhận đường chuyền của Tô Văn Vũ, Sỹ Giáp xoay người dứt điểm hạ gục Văn Trường. Xem lại pha quay chậm, số 27 của chủ nhà đã việt vị nhưng bàn thắng vẫn được công nhận
75 KHÔNG VÀO!! Tấn Trường có tình huống cứu thua xuất sắc cú sút của Văn Toàn. Thủ môn đội chủ nhà đã bị đau
90 Trận đấu khép lại với thắng lợi 3-0 cho chủ nhà Bình Dương