статья обновлена февраль 2021г. (внесены изменения, с учетом накопленных знаний)
В процессе создания и эксплуатации плагина WatchMan-Site7 мне пришла в голову идея создать такой механизм, который мог бы сам, без ручного вмешательства со стороны админа – обновлял основной экран посещений всех типов посетителей (людей, роботов) по мере их появления на сайте. Потому что – надоело: сидишь в админке и как дятел стучишь по клавише “обновить” экран браузера, что бы увидеть новых посетителей сайта. Пусть бы сам сайт посылал мне в браузер информацию о новых посещениях по мере их поступления!
Пришлось углубиться в теорию. И вот, что я накопал… Существуют три технологии динамической связи “клиент-сервер”:
1. AJAX (набор ранее существующих технологий – JS, PHP, HTML)
2. WebSocket (мощная вещь, но достаточно сложная в реализации)
3. SSE (Server-Sent Events)
Пришлось познакомиться с перечисленными технологиями, причем зная, что базовый класс WP_LIST_TABLE в WordPress адаптирован к технологии AJAX. И поэтому, подспудно, думая на какой технологии остановиться, я склонялся к AJAX. Однако, спустя некоторое время, я все-таки остановился на технологии SSE ( спецификация SSE ). И вот, как это удалось реализовать.
На стороне сервера:
(строка 6) – подключается файл wms7-sse-backend.php в котором находится функционал по сбору и передаче одних данных на сторону клиента.
(строка 7) – активируется функция “wms7_sse_backend” при срабатывании хука “wp_ajax_backend” для того, кто залогинился.
(строка 11) – подключается файл wms7-sse-frontend.php в котором находится функционал по сбору и передаче других данных на сторону клиента.
(строка 12) – активируется функция “wms7_sse_frontend” при срабатывании хука “wp_ajax_frontend” для того, кто залогинился.
(строка 13) – активируется та же функция “wms7_sse_frontend” при срабатывании хука “wp_ajax_nopriv_frontend” для того, кто не залогинился.
<?php ... /** * Used to send a count of records of visitor, number of unseen emails. */ require_once __DIR__ . "/includes/wms7-sse-backend.php"; add_action( "wp_ajax_backend", "wms7_sse_backend" ); /** * Used to transfer data about site visits to a widget - counter of visits. */ require_once __DIR__ . "/includes/wms7-sse-frontend.php"; add_action( "wp_ajax_frontend", "wms7_sse_frontend" ); add_action( "wp_ajax_nopriv_frontend", "wms7_sse_frontend" ); ...Теперь посмотрим на код в файле “/includes/wms7-sse-backend.php”, который был подключен выше. Тут находим в строке 40 функцию “wms7_sse_backend”, указанную выше в строке 7 основного файла плагина watchman-site7.php
<?php /** * Description: Used to send a count of records of visitor, number of unseen emails. * * PHP version 7.4 * * @category Wms7-sse-backend.php * @package WatchMan-Site7 * @author Oleg Klenitskiy <klenitskiy.oleg@mail.ru> * @version 4.0.0 * @license GPLv2 or later */ /** * Used for Serves to send information to the client browser (backend of site). * * @param string $data Number of visits records and mails inbox unseen. */ function wms7_send_backend( $data ) { if ( ! headers_sent() ) { header( 'Content-Type: text/event-stream' ); header( 'Cache-Control: no-cache' ); header( 'Connection: keep-alive' ); header( 'Access-Control-Expose-Headers: *' ); echo 'data: ' . json_encode( $data ); echo ';' . json_encode( current_time( 'mysql' ) ); echo "\n\n"; } // check for output_buffering activation. if ( 0 !== count( ob_get_status() ) ) { ob_flush(); } flush(); } /** * Used for Serves to send information to the client browser (backend of site). */ function wms7_sse_backend() { $_wms7_nonce = filter_input( INPUT_GET, 'wms7_nonce', FILTER_SANITIZE_STRING ); if ( !isset($_wms7_nonce) ) { wp_die( 'wms7_nonce: nonce is empty.' ); } if ( $_wms7_nonce !== wp_create_nonce( 'wms7_nonce' ) ) { wp_die( 'wms7_nonce: nonce is incorrect.' ); } wms7_send_backend( get_option('backend', '0,0') ); wp_die(); } /** * Used for to obtain the number of visits records. * * @return number. */ function wms7_count_rows() { global $wpdb; $results = $wpdb->get_var( $wpdb->prepare( " SELECT count(%s) FROM {$wpdb->prefix}watchman_site ", '*' ) );// db call ok; no cache ok. return $results; }На стороне клиента:
/** * Description: Used to manage the plug-in on the client side. * * @category Wms7_backend.js * @package WatchMan-Site7 * @author Oleg Klenitskiy <klenitskiy.oleg@mail.ru> * @version 4.0.0 * @license GPLv2 or later */ /** * Process Control Server Sent Events on the client side (backend). */ function wms7_sse_backend() { let sse = document.getElementById( "sse" ); if (sse.checked) { wms7_ctrl_sound("sse_on"); // start SSE backend. localStorage.setItem("wms7_sse_backend", "on" ); if ( window.EventSource ) { let wms7_source = new EventSource( wms7_ajax_url + "?action=backend" + "&wms7_nonce=" + wms7_nonce); wms7_source.onmessage = function(e) { let arr = e.data.replace(/"/g, '').split( ";" ); let arr_add = arr[0].split( "," ); console.log( "All visits=" + arr_add[0] + " New letters=" + arr_add[1] + " Server time=" + arr[1] + " Origin=" + e.origin ); if (localStorage.getItem( "wms7_records_count" ) !== arr_add[0] || localStorage.getItem( "wms7_unseen_count" ) !== arr_add[1] ) { localStorage.setItem("wms7_records_count", arr_add[0] ); localStorage.setItem("wms7_unseen_count", arr_add[1] ); wms7_beep(); location.replace( window.location.href ); } } } else { alert( "Your browser does not support Server-Sent Events. Execution stopped." ); return; } } else { wms7_ctrl_sound("sse_off") // stop SSE backend. localStorage.setItem("wms7_sse_backend", "off" ); location.replace( window.location.href ); } wms7_ctrl_btn_href(); }
Прокомментирую:
В JS коде, в строке 23 создается событие EventSource с названием action=backend, которое совпадает с название хука “wp_ajax_backend” в основном файле плагина watchman-site7.php в строке 7. Кроме этого, wms7_ajax_url = “https://you-site/wp-admin/admin-ajax.php”;
В JS коде, в строке 28 происходит сравнение ранее сохраненных данных в localStorage.getItem( “wms7_records_count” ) с вновь пришедшими данными и сохраняются в localStorage.setItem(“wms7_records_count”, arr_add[0] ) для последующего сравнения (когда придут с сервера новые данные). Затем, в строке 33 – звуковой сигнал, и в строке 34 – обновление экрана. Все действие можно наблюдать в консоли, нажав F12.
😉Совет:
Такой подход мог бы быть полезен при проведении каких-либо коммерческих акций, опросов, политических компаний, голосования…