Статус online у соціальній мережі

02.09.2011

Суть проблеми така, що сервер по http протоколу ніяким чином не може відстежувати, чи користувач ще на сайті або він вже давно з нього пішов, якщо користувач не виконує жодних активних дій на сайті. Отже, інформувати сервер про те, що користувач ще знаходиться в мережі, повинен клієнт користувача (браузер). І ця проблема вирішується в такий спосіб.

  1. Браузер AJAX-ом при оновленні сторінки та після оновлення у певний інтервал часу відсилає "сміття" на сервер, наприклад через POST передає змінну online зі значенням true кожні 45 секунд після оновлення сторінки, при оновленні якої він відсилає його відразу. Це звичайно погано, що ми змушені подібним чином засмічувати сервер, змушуючи його постійно з'єднуватися з БД, але подітися нікуди, і при грамотному виборі часу, я вибрав 45 секунд, і спосіб оновлення, а вибрав через AJAX, з цим можна жити :) Чому саме AJAX? А ви уявіть, дивіться ви на сайті якісь сторінки, сайт досить громіздкий з фотографіями і тут вам браузер, примусово оновлює кожні 45 с сторінку з усім вмістом, і далеко не факт що після оновлення ви перейдете на ту саму сторінку на якій знаходилися. Дурно чи не так? Тому тут варіант 1 – Використовувати AJAX.
  2. Сервер, точніше, у моделі mvc, - контролер, при виявленні існування сесії, коректного id користувача в цій сесії та ключа суперглобалного масиву $_POST['online'] рівному логічному true, посилає його id $_SESSION['id'] та поточний час на сервері time() модель для перезапису в БД поле `status` цим часом у користувача з цим id.
  3. При виведенні списку користувачів, або друзів чи черги на додавання в друзі або просто при перегляді чогось профілю в поданні повз дані користувачів, виводитися з БД і час його останньої присутності і віднімається з поточного часу сервера. І якщо ця різниця менше 60 (секунд), то користувач вважається в мережі і виводитиметься відповідний напис або іконка, якщо більш або рівно, то користувач вважається недоступним і виводитиметься відповідний напис або іконка.

Одним із втрачених мною моментів, що список користувачів, а точніше статуси цих користувачів в ідеалі, потрібно б те ж саме оновлювати AJAX-ом хоча б кожну хвилину, щоб користувач не здійснюючи довгий час активних дій отримував актуальну інформацію про активність інших користувачів, а так він бачить стан лише до останнього оновлення сторінки.

Наприкінці привиду код AJAX-а першої частини, оскільки код інших елементів примітивний і повинен викликати жодних труднощів.

Бібліотека роботи з AJAX-ом

ajax = {
	obj: false,
	url: '/',
	init: function(){
			try {
			        this.obj = новий XMLHttpRequest();
			}
			catch (trymicrosoft) {
				try {
				        this.obj = новий ActiveXObject("Msxml2.XMLHTTP");
				}
				catch (othermicrosoft) {
				        try {
					        this.obj = новий ActiveXObject("Microsoft.XMLHTTP");
					}
					catch (failed) {
					        ajax.obj = false;
					}
				}
			}
		},
	get: function(url_u){
		       if(!this.obj){
		                alert('Error initializing XMLHttpRequest!');
			        return false;
		        }
		        url = ajax.url;
		        this.obj.open('GET',url,true);
		        this.obj.onreadystatechange = this.answer;
		        this.obj.send(null);
	},
	post: function(_form){
		       if(!this.obj){
		                alert('Error initializing XMLHttpRequest!');
			        return false;
		        }
		        data = this.splitForm(_form);
		        this.obj.open('POST',this.url,true);
		        this.obj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		        this.obj.onreadystatechange = this.answer;
		        this.obj.send(data);
	},
	answer: function()
				{
					if (ajax.obj.readyState == 4)
					{
						if (ajax.obj.status == 200)
						{
							if(ajax.obj.getResponseHeader('Content-Type') == 'text/xml')
							{
								ajax.parseXML();
							}
							//else{ajax.parseText();}
						}
						else if (ajax.obj.status == 404){}
						else
						{
							alert("Error: status code is" + ajax.obj.status);
						}
					}
				},
	parseXML: function(){
			    if(this.obj.responseXML == null || this.obj.responseXML.documentElement == null){
			        try {
				        this.obj.responseXML.loadXML(this.obj.responseText)
				}
				catch(e){alert("Can't load");}
			    }
			    _xml = this.obj.responseXML.documentElement;
		            node_list = _xml.getElementsByTagName('error');
		            for(i=0; i<node_list.length; i++){
		                node = node_list.item(i);
				_text=node.text || node.textContent;
				id = node.getAttribute('id');
				try{
				document.getElementById(id).innerHTML = _text;
				} catch (e) {}
			    }
			    _eval_list = _xml.getElementsByTagName('eval');
			    if(_eval_list.length > 0){
			        script = _eval_list.item(0).text || _eval_list.item(0).textContent;
			        try{eval(script);}catch(error){alert('AJAX: сталася помилка eval');}
			    }
	},
	parseText: function(){
		             text = this.obj.responseText;
			     alert(text);
	},
	splitForm: function(_form){
	                var _param = новий Array();
	                _input = _form.getElementsByTagName('input');
	                _textarea = _form.getElementsByTagName('textarea');
	                _select = _form.getElementsByTagName('select');
	                _li = _input.length;
	                _lt = _textarea.length;
	                _ls = _select.length;
	                for(i=0; i<_li; i++){
	                        if(_input.item(i).type != 'checkbox'){
	                                try{_el = encodeURIComponent(_input.item(i).name)+'='+encodeURIComponent(_input.item(i).value);}
	                                catch(e){}
	                                _param.push(_el);
	                        }
	                        else if(_input.item(i).type == 'checkbox' && _input.item(i).checked){
	                                try{_el = (_input.item(i).name)+'='+encodeURIComponent(_input.item(i).value);}
	                                catch(e){}
	                                _param.push(_el);
	                        }
	                }
	                for(i=0; i<_lt; i++){
	                        if(_textarea.item(i).value.length > 0){
	                                try{_el = encodeURIComponent(_textarea.item(i).name)+'='+encodeURIComponent(_textarea.item(i).value);}
	                                catch(_e){}
	                                _param.push(_el);
	                        }
	                }
	                for(i=0; i<_ls; i++){
	                        if(_select.item(i).value.length > 0){
	                                try{_el = encodeURIComponent(_select.item(i).name)+'='+encodeURIComponent(_select.item(i).value);}
	                                catch(_e){}
	                                _param.push(_el);
	                        }
	                }
	                return _param.join('&');

	}
}
ajax.init();

Код надсилання статусу на сервер

<?php
	echo '<script type="text/javascript" src="/media/ajax.js"></script>
			<script type="text/javascript">
				function online()
				{
					ajax.post(document.onlineForm);
				}
				setInterval(function(){online();},45000);
				function addHandler(object,event,handler)
				{
					if (typeof object.addEventListener != \'undefined\')
						object.addEventListener(event, handler, false);
					else if (typeof object.attachEvent != \'undefined\')
						object.attachEvent(\'on\' + event, handler);
					else
						throw \'Incompatible browser\';
				}
				addHandler(window, 'load', online);
			</script>';
?>

У цьому коді ми підключаємо раніше наведену бібліотеку, яка у нас знаходиться в папці /media/ajax.js і визначаємо функцію online(), яка викликатиме метод post класу ajax передаючи йому як параметр форму з ім'ям onlineForm, яка надсилатиме на сервер статус.

Потім ми встановлюємо циклічний виклик цієї функції кожні 45 секунд, і визначаємо крос-браузерний обробник події DOM рівня 2, який при завантаженні документа так само викликає функцію update()

Ну і власне наша форма з ім'ям onlineForm з єдиним прихованим полем з ім'ям online і постійним параметром логічного true, яка передають його на сервер методом post який оброблятиметься головним контролером сайту.

Єдине хочу додати, що цей код потрібно розміщувати на місці, яке постійно буде присутнє у зареєстрованого та захищеного на сайт користувача. Я його розмістив у модулі меню користувача, тому що цей модуль виводитиметься у мене скрізь на сайті і тільки тоді, коли користувач закрив на сайт .

Останнє в нашому блозі

Інтернет маркетинг
04.11.2019