Пишем свой чат на PHP

Задался я мыслью, написать подобие чата. как и все великое, начнется это с простых вещей....

  1. чат будет на фреймах -
    • главный, с сообщениями
    • ввод, где будет вводится текст
    • список ников, понятно
    • и..фрейм "драйвер" всего этого дела )
  2. будет юзать mySQL, потому как очень просто все тогда получится
  3. в чате не будет регистрации, а следовательно админов и т.д., для простоты реализации и понятности...

UML диаграмма последовательностиUML диаграмма последовательности

Итак, начнем.

заглав пага у нас будет index.html куда и будет попадать юзер при заходе на chat.test1.ru допустим ):

index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd">
<html>
    <frameset rows="0,50,*" onunload='alert( "выход" );'>
        <frame noresize frameborder="0" name="dummy" src="dummy.php" >
        <frame noresize frameborder="0" name="chat" src="input.php" >
        <frameset cols="200,*" onunload='alert( "выход" );'>
            <frame noresize frameborder="0"  name="nicklist" src="users.php" >
            <frame noresize frameborder="0"  name="main" src="main.php" >
        </frameset>
    </frameset>
</html>

объясняю - это страница фреймсет - набор фреймов,

  1. первый фрейм, с нулевой высотой - "драйвер"...он будет перегружатся каждые...ну скажем 10 секунд, и проверять новые сообщения...пользователей убирать по тайм ауту и т.д.
  2. второй фрейм ссылается на input.php..простенький скрипт, который будет принимать мессаги от юзера и пиндюрить их в БД нашу (кстати...)
  3. третий - список ников...с js ссылками на вставку в input поле
  4. ну и четвертый - где будут выводится все сообщения....


для чата нам понадобятся всего две таблицы (да, да ))):

CREATE TABLE `chat` (
  `userid`          int(10)         unsigned    NOT NULL default '0',
  `message`         varchar(255)                NOT NULL default '0',
  `date`            int(10)         unsigned    NOT NULL default '0',
  KEY `userid` (`userid`)
) TYPE=MyISAM;

CREATE TABLE `user` (
  `username`        varchar(50) default     NULL,
  `userid`          int(10)     unsigned    NOT NULL auto_increment,
  `lastactivity`    int(10)     unsigned    NOT NULL default '0',
  `lastupdate`      int(10)     unsigned    NOT NULL default '0',
  PRIMARY KEY  (`userid`)
) TYPE=MyISAM;

тут кажется должно быть просто:
chat - три поля:

  1. номер юзера (хотя надо было бы заносить имя а не ссылку на таблицу юзеров, так как при выходе стирается запись из user на которую будет ссылатся chat, ну да не страшно....
  2. сам текст сообщения
  3. и время его...

user

  • имя,
  • номер,
  • последняя активность (для определения диссконнекта)
  • последний апдейт сообщений (чтобы проверять на наличие новых)

ну еще подготовимся...чтобы уж быть во всеоружие. Напишем...functions.php всего с парой функций )
functions.php

<?php
/** 
 * (c) Vaulter 2003 )))))
 */
    
/**
 * Sets the location of browser window
 * used for jumping to other location
 * or for refreshing page
 *
 * Устанавливает адрес окна броузера
 * используется для перехода к другому адресу
 * или для обновления страницы
 * @param string $window name of window (frame)
 * @param $time delay before refresh
 * @param $url location to go
 */
function setlocation( $window, $time, $url ) {
    
    if( $time ) {
        echo "";
    
    } else {
        echo "";
    }
}
    
/**
 * Возвращает строку даты
 * @param unsigned int date timestamp
 * @return string representation of date
 */
function getdatestring( $datest ) {
    
    global $GMT;
    
    if( 0 == $datest ) {
        return "никогда";
    }
    
    $now = mktime() + $GMT;
    $datest += $GMT;
    $strdate = strftime( "%H:%M:%S", $datest );
    
    return $strdate;
}
?>

первая просто выводит в страничку тег META REFRESH для перезагрузки какого либо фрейма, через указанный тайм, на указанный урл, вторая возвращает читаемую строку из UNIX TIMESTAMP....
поехали дальше?

далее...начнем наш нелегкий труд... Напишем скриптик, который будет вызыватся для каждого скриптика... в нем мы будет

  1. коннектится к БД
  2. начинать сессию
  3. ставить текущему юзеру нашего чата, что он еще тут
  4. и брать его статистику
  5. переводить фокус на окошко ввода....

фух...
global.php

<?php
/**
 * Script that will include to all others
 * common header file
 *
 * Скрипт, который будет включатся во все остальные
 * общий заголовочный файл
 */
error_reporting( E_ALL ^ E_NOTICE );
session_start();
/** DB settings - CHANGE IT TO YOUR
 *
 * БД настройки - ИЗМЕНИТЬ НА СВОИ
 */
$dbhost = "localhost";
$dblogin = "root";
$dbpass = "";
$dbname = "chat";//имя БД где наши таблицы
    
$dbconnect = mysql_connect( $dbhost, $dblogin, $dbpass ) or 
    myexit( __FILE__." (".__LINE__."): cannot connect to $dblogin@$dbhost/$dbname" );
    
mysql_select_db( $dbname );
    
/**
 * тут нет никаких проверок на ошибки, поэтому лучше чтобы
 * 1. mySQL user с логином root был
 * 2. chat БД была )
 */
//set activity if user loginned
if( $_SESSION['userid'] ) {
    $query = "UPDATE `user` SET `lastactivity` = '".mktime()."' WHERE `userid` = '$_SESSION[userid]'";
    @mysql_query( $query );//апдейтим lastactivity и глушим вывод об ошибках
    
    $query = "SELECT * FROM `user` WHERE `userid` = '$_SESSION[userid]'";
    global $user;
    $user = mysql_fetch_assoc( $res = mysql_query( $query ) );//берез данные о юзере
    
    mysql_free_result( $res );
}
?>

далее, а очереди dummy.php, чуть ли не самый хлавный тут скрипт, в его задачу входит

  1. перегружать себя через каждые 10 секунд
  2. удалять из таблицы чата старые сообщения
  3. удалять юзеров по таймауту
  4. перегружать соответсвующие фреймы

все просто:
dummy.php

<?php
/**
 * engine to check new messages and reload chat window if need
 * this script will contantly refreshed by self or others
 * 
 * Скрипт, который будет постоянно обновлятся и при 
 * наличии обновлений обновлять другие фреймы
 */
set_time_limit( 0 );
    
if( connection_aborted() ) {//we have to delete user from DB
    $query="INSERT INTO `chat`
            ( `userid`, `message`, `date` )
        VALUES
            ( '$user[userid]', 'я ушел!!!!', '".mktime()."' )";
    
    @mysql_query( $query );
    exit;
}
    
include 'functions.php';
include 'global.php';
    
echo setlocation( 'dummy', 10, 'dummy.php' );
    
/**
 * We will check for need of update frames
 */
    $r = 0;
    $now = mktime() - 5 * 60;//ten minutes
    $query = "DELETE FROM chat WHERE `date` < $now";
    mysql_query( $query );
    
    $query = "DELETE FROM user WHERE `lastactivity` < $now";
    mysql_query( $query );
    
    if( mysql_affected_rows() ) {
        echo setlocation( 'nicklist', 0, 'users.php' );//если есть удаленные записи то обновить ник лист
    }
    
    $query="SELECT count(*) FROM `chat`,`user`
        WHERE `chat`.`date` > `user`.`lastupdate` AND `user`.`userid` = $_SESSION[userid]";
    
    list( $row ) = mysql_fetch_array( $res = mysql_query( $query ) );
    mysql_free_result( $res );
    
    if( $row ) {
        echo setlocation( 'main', 0, 'main.php' );
    }
?>

следующий на очереди input.php скрипт подхватывающий сообщения юзера и заносящий их в БД...
input.php

<?php
/**
 * Script that load to input window
 * 
 * Скрипт выводящий в окно ввода
 */
include 'functions.php';
include 'global.php';
    
if( $_SESSION['userid'] ) {

    if( "POST" == $_SERVER['REQUEST_METHOD'] && !empty( $_POST['message'] ) ) {//parse form
        $query = "INSERT INTO `chat` VALUES ( '$user[userid]', '".addslashes( $_POST['message'] )."', '".mktime()."' )";
        @mysql_query( $query );
        echo setlocation( 'main', 0, 'main.php' );
    }
    
    echo "
"; //выводим js скрипт переводящий фокус куды надо echo ""; } else { echo "
Введи ник
"; } ?>

далее users.php
здесь мы просто выводим кто сейчас у нас есть!
если кого нет, то я не виноват )

users.php

<?php
/**
 * Script that just output online users of chat
 * 
 * Скрипт выводящий пользователей онлайн
 */
include 'functions.php';
include 'global.php';
    
$query = "SELECT * FROM `user`";
$result = mysql_query ( $query ) or exit( __FILE__." (".__LINE__.") ".$query );
    
print "
    ".PHP_EOL; while( $line = mysql_fetch_assoc( $result ) ) { echo "
  • $line[username]
  • "; } print "
".PHP_EOL; echo setlocation( "nicklist", 60, $_SERVER['PHP_SELF'] );//60 - second for refresh mysql_free_result( $result ); ?>

было бы логично тут же проверять старых юзеров, но я как то забыл об этом ))))

ну и наконец! main.php!!! который:

  1. логинит только что пришедшего
  2. выводит сообщения для залогиненых
  3. разлогинивает
  4. ставит lastupdate для юзера (для проверки в dummy.php)

итак:
main.php

<?php
/** 
 * Main script that will logon and logout users
 *
 * Скрипт, который будет логинить и разлогинивать 
 * пользователей
 */
error_reporting( E_ALL ^ E_NOTICE );
session_start();
session_register( "userid" );
    
@$action = $_GET['action'];

include 'functions.php';
include 'global.php';
    
global $user;
echo "";
    
if( empty( $_SESSION['userid'] ) ) {
    
    if( 'POST' == $_SERVER['REQUEST_METHOD'] ) {//catch incoming nick
    
        if( !empty( $_POST['username'] ) ) {//check for existence
            $query = "SELECT `userid` FROM `user` WHERE `username` = '".htmlspecialchars( $_POST['username'] )."'";
            $r = mysql_query( $query ) or exit( __FILE__." (".__LINE__.") ".$query );
    
            if( mysql_num_rows( $r ) ) {//if exists
                echo "
Уже есть
"; } else {//reg $query = "INSERT INTO `user` ( `username`, `lastactivity` ) VALUES ( '".htmlspecialchars( $_POST['username'] )."',".mktime()." )"; mysql_query( $query ) or exit( __FILE__." (".__LINE__.") ".$query ); $_SESSION['userid'] = mysql_insert_id(); echo "
Привет, $_POST[username]
". setlocation( 'chat', 0, 'input.php' ). setlocation( 'main', 2, 'main.php' ); echo ""; $query = "INSERT INTO `chat` VALUES ( $_SESSION[userid], 'я пришел!!!!', '".mktime()."' )"; @mysql_query( $query ); }//if( mysql_num_rows( $r ) ) }//if( !empty( $_POST['username'] ) ) } else { echo "
"; }//if( 'POST' == $_SERVER['REQUEST_METHOD'] ) exit; }//if( empty( $_SESSION['userid'] ) ) if( "exit" == $action ) { session_destroy(); $query = "INSERT INTO `chat` VALUES ( $user[userid], 'я ушел!!!!', '".mktime()."' )"; @mysql_query( $query ); $query = "DELETE FROM `user` WHERE `userid`='$user[userid]'"; mysql_query( $query ); echo setlocation( 'main', 0, 'main.php' ); exit; }//if( "exit" == $action ) echo "
Чат для $user[username] выйти
"; $query = "SELECT * FROM `chat` LEFT JOIN `user` USING (`userid`) ORDER BY `date` DESC"; $result = mysql_query ( $query ) or die ( "Query failed: $query" ); echo "
    ".PHP_EOL; while( $line = mysql_fetch_assoc( $result ) ) { echo "
  • ".getdatestring( $line['date'] )." $line[username]: $line[message]
  • "; }//while print "
".PHP_EOL; mysql_free_result( $result ); $query = "UPDATE `user` SET `lastupdate` = '".mktime()."' WHERE `userid` = $_SESSION[userid] "; @mysql_query( $query ); echo ""; ?>

ну вот....все скрипты...
ничего сложного ;)

Установка на рабочий сервер занимает около 1 минуты, если, конечно, знать что делать.
Загружаем дамп таблиц в БД,
Настраиваем global.php - прописываем данные для доступа к БД
И смотрим в броузере.

AttachmentSize
phpchat.zip5.63 KB