главная

Теги:


AJAX веб чат с использованием PHP, MySQL и jQuery (Часть 1 из 2-х)

Если рассматривать тему коммуникаций в реальном времени, то найдется мало решений, которые смогут соперничать по силе с простым веб чатом. Особенно хорошо такое решение тем, что все что нужно, чтобы его реализовать - это веб браузер.

В данном уроке из двух частей мы создадим AJAX веб чат с использованием PHP, MySQL и jQuery. В первой части урока мы обсудим использование PHP и MySQL, а в следующей - клиентскую часть jQuery и CSS.

HTML

Как обычно первый шаг посвящен разметке HTML. Наш документ строится в соответствии с HTML5, что позволяет использовать новый, более короткий синтаксис DOCTYPE, и опускать атрибут type в тегах script.

index.html





Делаем AJAX веб чат с использованием PHP, MySQL и jQuery | Демонстрация для сайта RUSELLER.COM









Для оптимизации загрузки, стили включены в секции head, а файлы JavaScript подключаются внизу документа, перед закрывающим тегом body.

Для организации прокручиваемой области со строками чата мы используем плагин jScrollPane. Данный плагин имеет свои собственные стили, который включаются в секции head.

Разметка чата состоит из четырех основных элементов div – верхней панели, контейнера чата, контейнера пользователей и нижней панели. Последний div содержит формы для регистрации пользователя и отправки сообщения. Форма отправки сообщения по умолчанию скрыта и выводится, только если пользователь успешно вошел в систему чата.

Затем мы включаем файлы JavaScript: библиотеку jQuery, плагин mousewheel (используется в jScrollPane), плагин jScrollPane и наш файл

script.js.

listing

Для оптимизации загрузки, стили включены в секции head, а файлы JavaScript подключаются внизу документа, перед закрывающим тегом body. Для организации прокручиваемой области со строками чата мы используем плагин jScrollPane. Данный плагин имеет свои собственные стили, который включаются в секции head. Разметка чата состоит из четырех основных элементов div – верхней панели, контейнера чата, контейнера пользователей и нижней панели. Последний div содержит формы для регистрации пользователя и отправки сообщения. Форма отправки сообщения по умолчанию скрыта и выводится, только если пользователь успешно вошел в систему чата. Затем мы включаем файлы JavaScript: библиотеку jQuery, плагин mousewheel (используется в jScrollPane), плагин jScrollPane и наш файл script.js.


Схема базы данных

Прежде чем перейти к части PHP, нужно сначала взглянуть на организацию данных чата в базе данных MySQL.

Для нашего скрипта мы используем две таблицы. В таблице webchat_users хранится информация об участниках чата. Таблица имеет поля id, name, gravatar и last_activity. Поле name определено как уникальное, таким образом предотвращается использование дублирующихся имен в чате.

Другим полезным свойством поля с уникальным индексом является то, что запрос на вставку данных завершится с ошибкой и свойство inserted_rows объекта MySQLi будет установлено в значение 0, если попытаться вставить дублирующиеся строки. В классе PHP Chat данное свойство будет активно использоваться.

Поле last_activity содержит значение времени. Значение обновляется каждые 15 секунд для каждого пользователя. Поле также определено как индекс, что позволяет быстро удалять неактивных пользователей (значение в поле last_activity более 15 означает, что пользователь более не просматривает окно чата).

Таблица webchat_lines содержит записи в чате. Заметьте, что мы храним имя автора и gravatar здесь тоже. Такое дублирование позволяет нам отказаться от использования затратной директивы join при запросе последних записей – наиболее часто используемых в нашем приложении.

Определения таблиц имеются в файле tables.sql в исходниках. Вы можете использовать текст запросов для создания таблиц. Также, при установке чата на свой хост, нужно поменять установки в ajax.php на ваши данные для соединения с базой MySQL.

PHP

Теперь у нас есть база данных, давайте обсудим скрипт PHP, который управляет чатом.

Первый файл, который мы рассмотрим, ajax.php. Он обрабатывает запросы AJAX от клиентской части из jQuery и выводит данные в формате JSON.

ajax.php

 

/* Конфигурация базы данных. Добавьте свои данные */

$dbOptions = array(
	'db_host' => '',
	'db_user' => '',
	'db_pass' => '',
	'db_name' => ''
);

/* Конец секции конфигурации базы данных */

error_reporting(E_ALL ^ E_NOTICE);

require "classes/DB.class.php";
require "classes/Chat.class.php";
require "classes/ChatBase.class.php";
require "classes/ChatLine.class.php";
require "classes/ChatUser.class.php";

session_name('webchat');
session_start();

if(get_magic_quotes_gpc()){
	
	// Удаляем лишние слэши
	array_walk_recursive($_GET,create_function('&$v,$k','$v = stripslashes($v);'));
	array_walk_recursive($_POST,create_function('&$v,$k','$v = stripslashes($v);'));
}

try{
	
	// Соединение с базой данных
	DB::init($dbOptions);
	
	$response = array();
	
	// Обработка поддерживаемых действий:
	
	switch($_GET['action']){
		
		case 'login':
			$response = Chat::login($_POST['name'],$_POST['email']);
		break;
		
		case 'checkLogged':
			$response = Chat::checkLogged();
		break;
		
		case 'logout':
			$response = Chat::logout();
		break;
		
		case 'submitChat':
			$response = Chat::submitChat($_POST['chatText']);
		break;
		
		case 'getUsers':
			$response = Chat::getUsers();
		break;
		
		case 'getChats':
			$response = Chat::getChats($_GET['lastID']);
		break;
		
		default:
			throw new Exception('Wrong action');
	}
	
	echo json_encode($response);
}
catch(Exception $e){
	die(json_encode(array('error' => $e->getMessage())));
}

Для удобства используется оператор switch для определения действий, которые обрабатывает скрипт. Здесь реализованы подсистемы чата, функциональность входа/выхода и действия по запросу списка реплик и пользователей в режиме онлайн.

Вывод осуществляется в форме сообщений JSON (которые удобно обрабатывать с помощью jQuery), ошибки генерируют исключения. Оператор switch распределяет все запросы соответствующим статическим методам класса Chat, который будет обсуждаться позже в данном разделе.

DB.class.php

class DB {
	private static $instance;
	private $MySQLi;
	
	private function __construct(array $dbOptions){

		$this->MySQLi = @ new mysqli(	$dbOptions['db_host'],
						$dbOptions['db_user'],
						$dbOptions['db_pass'],
						$dbOptions['db_name'] );

		if (mysqli_connect_errno()) {
			throw new Exception('Ошибка базы данных.');
		}

		$this->MySQLi->set_charset("utf8");
	}
	
	public static function init(array $dbOptions){
		if(self::$instance instanceof self){
			return false;
		}
		
		self::$instance = new self($dbOptions);
	}
	
	public static function getMySQLiObject(){
		return self::$instance->MySQLi;
	}
	
	public static function query($q){
		return self::$instance->MySQLi->query($q);
	}
	
	public static function esc($str){
		return self::$instance->MySQLi->real_escape_string(htmlspecialchars($str));
	}
}

Класс DB - менеджер базы данных. Конструктор объявлен как private, таким образом, объект не может быть создан вне пределов класса, и инициализация возможна только из статического метода init(). Он берет массив с параметрами соединения с MySQL и создает экземпляр класса, который содержится в статической переменной self::$instance. Таким образом, обеспечивается существование единственного соединения с базой данных в конкретный момент времени.

Остальная часть класса реализует коммуникацию с базой данных, в основе которой лежит статический метод query().

ChatBase.class.php

 

/* Базовый класс, который используется классами ChatLine и ChatUser */

class ChatBase{

	// Данный конструктор используется всеми класса чата:

	public function __construct(array $options){
		
		foreach($options as $k=>$v){
			if(isset($this->$k)){
				$this->$k = $v;
			}
		}
	}
}

Это простой базовый класс. Его основное назначение - определить конструктор, который получает массив параметров, но сохраняет только те, которые определены в классе.

ChatLine.class.php

/* Строка чата */

class ChatLine extends ChatBase{
	
	protected $text = '', $author = '', $gravatar = '';
	
	public function save(){
		DB::query("
			INSERT INTO webchat_lines (author, gravatar, text)
			VALUES (
				'".DB::esc($this->author)."',
				'".DB::esc($this->gravatar)."',
				'".DB::esc($this->text)."'
		)");
		
		// Возвращаем объект MySQLi класса DB
		
		return DB::getMySQLiObject();
	}
}

Класс ChatLine является производным классом от ChatBase. Объект данного класса может быть легко создан с помощью передачи конструктору массива с текстом, именем автора и элементом gravatar. Свойство класса gravatar содержит хэш md5 email адреса. Оно нужно для получения пользовательского аватара, соответствующего email адресу, с сайта gravatar.com.

Данный класс также определяет метод save, который сохраняет объект в базе данных. Так как метод возвращает объект MySQLi, содержащийся в классе DB, вы можете проверить успешность завершения операции с помощью свойства affected_rows.

ChatUser.class.php

class ChatUser extends ChatBase{
	
	protected $name = '', $gravatar = '';
	
	public function save(){
		
		DB::query("
			INSERT INTO webchat_users (name, gravatar)
			VALUES (
				'".DB::esc($this->name)."',
				'".DB::esc($this->gravatar)."'
		)");
		
		return DB::getMySQLiObject();
	}
	
	public function update(){
		DB::query("
			INSERT INTO webchat_users (name, gravatar)
			VALUES (
				'".DB::esc($this->name)."',
				'".DB::esc($this->gravatar)."'
			) ON DUPLICATE KEY UPDATE last_activity = NOW()");
	}
}

Класс имеет свойства name и gravatar (обратите внимание на модификатор доступа protected – свойства доступны в классе ChatBase, и мы можем устанавливать их значения в конструкторе).

В классе определен метод update(), который обновляет поле last_activity значением текущего времени. Таким образом показывается, что пользователь держит окно с чатом отрытым и его надо учитывать как автора в режиме онлайн.

Chat.class.php – Часть 1

 

/* Класс Chat содержит публичные статические методы, которые используются в ajax.php */

class Chat{
	
	public static function login($name,$email){
		if(!$name || !$email){
			throw new Exception('Заполните все необходимые поля.');
		}
		
		if(!filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)){
			throw new Exception('Неправильный адрес email.');
		}
		
		// Подготовка кэша gravatar:
		$gravatar = md5(strtolower(trim($email)));
		
		$user = new ChatUser(array(
			'name'		=> $name,
			'gravatar'	=> $gravatar
		));
		
		// Метод save возвращает объект MySQLi
		if($user->save()->affected_rows != 1){
			throw new Exception('Данное имя используется.');
		}
		
		$_SESSION['user']	= array(
			'name'		=> $name,
			'gravatar'	=> $gravatar
		);
		
		return array(
			'status'	=> 1,
			'name'		=> $name,
			'gravatar'	=> Chat::gravatarFromHash($gravatar)
		);
	}
	
	public static function checkLogged(){
		$response = array('logged' => false);
			
		if($_SESSION['user']['name']){
			$response['logged'] = true;
			$response['loggedAs'] = array(
				'name'		=> $_SESSION['user']['name'],
				'gravatar'	=> Chat::gravatarFromHash($_SESSION['user']['gravatar'])
			);
		}
		
		return $response;
	}
	
	public static function logout(){
		DB::query("DELETE FROM webchat_users WHERE name = '".DB::esc($_SESSION['user']['name'])."'");
		
		$_SESSION = array();
		unset($_SESSION);

		return array('status' => 1);
	}

Этот код выполняет всю работу. В операторе switch в файле ajax.php выбирались действия, которые соответствовали методам данного класса. Каждый из этих методов возвращает массив, который затем конвертируется в объект JSON с помощью функции json_encode() (это происходит внизу в файле ajax.php).

Когда пользователь входит в систему, его имя и gravatar сохраняются как элементы массива $_SESSION и становятся доступны в последующих запросах.

Chat.class.php – Часть 2

 

	public static function submitChat($chatText){
		if(!$_SESSION['user']){
			throw new Exception('Вы вышли из чата');
		}
		
		if(!$chatText){
			throw new Exception('Вы не ввели сообщение.');
		}
	
		$chat = new ChatLine(array(
			'author'	=> $_SESSION['user']['name'],
			'gravatar'	=> $_SESSION['user']['gravatar'],
			'text'		=> $chatText
		));
	
		// Метод save возвращает объект MySQLi
		$insertID = $chat->save()->insert_id;
	
		return array(
			'status'	=> 1,
			'insertID'	=> $insertID
		);
	}
	
	public static function getUsers(){
		if($_SESSION['user']['name']){
			$user = new ChatUser(array('name' => $_SESSION['user']['name']));
			$user->update();
		}
		
		// Удаляем записи чата старше 5 минут и пользователей, неактивных в течении 30 секунд
		
		DB::query("DELETE FROM webchat_lines WHERE ts < SUBTIME(NOW(),'0:5:0')");
		DB::query("DELETE FROM webchat_users WHERE last_activity < SUBTIME(NOW(),'0:0:30')");
		
		$result = DB::query('SELECT * FROM webchat_users ORDER BY name ASC LIMIT 18');
		
		$users = array();
		while($user = $result->fetch_object()){
			$user->gravatar = Chat::gravatarFromHash($user->gravatar,30);
			$users[] = $user;
		}
	
		return array(
			'users' => $users,
			'total' => DB::query('SELECT COUNT(*) as cnt FROM webchat_users')->fetch_object()->cnt
		);
	}
	
	public static function getChats($lastID){
		$lastID = (int)$lastID;
	
		$result = DB::query('SELECT * FROM webchat_lines WHERE id > '.$lastID.' ORDER BY id ASC');
	
		$chats = array();
		while($chat = $result->fetch_object()){
			
			// Возвращаем время создания сообщения в формате GMT (UTC):
			
			$chat->time = array(
				'hours'		=> gmdate('H',strtotime($chat->ts)),
				'minutes'	=> gmdate('i',strtotime($chat->ts))
			);
			
			$chat->gravatar = Chat::gravatarFromHash($chat->gravatar);
			
			$chats[] = $chat;
		}
	
		return array('chats' => $chats);
	}
	
	public static function gravatarFromHash($hash, $size=23){
		return 'http://www.gravatar.com/avatar/'.$hash.'?size='.$size.'&default='.
				urlencode('http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size='.$size);
	}
}

jQuery отправляет запросы getUsers() каждые 15 секунд. Мы используем данный факт, чтобы удалить реплики, которые старше 5 минут и неактивных пользователей из базы данных. Потенциально можно было бы удалять данные записи в getChats, но этот запрос поступает каждую секунду и дополнительная нагрузка может повлиять на производительность приложения.

В методе getChats() используется функция gmdate вывода времени в формате GMT. В клиентской части мы используем значения часов и минут для установки в объекте JavaScript, а в результате время отображается в соответствии с часовым поясом пользователя.

Продолжение во второй части!

Источник урока: tutorialzine.com/2010/10/ajax-web-chat-php-mysql/

Урок Создан:2011-02-10 Просмотров:22898

Добавить комментарий:


Copyright© 2009 Hosted by Zhost