Úvodem
Nejdříve bych se rád zmínil o tom, že tento chat, na kterém budu demonstrovat dynamické načítání dat, je jen praktická ukázka a aby jej bylo možné umístit na stránky, muselo by se ještě pořešit zabezpečení a provést pár úprav. Tím ale nechci říci, že chat o kterém budu dále psát je nefunkční.
Co bude chat umět
Chat o kterém píšu bude pracovat pouze s daty z databáze. Uvádím to proto, že data z chatu, jako například zprávy, je možné načítát ze souboru i je do něj ukládat. Tento jednoduchý chat bude umět pravidelně zobrazovat nové zprávy, online uživatele a mazat neaktivní uživatele. Dále bude mít validaci formuláře, kterou si nebudeme stahovat, ale vytvoříme si ji. Validace bude zahrnovat zobrazování zbývajích znaků, které je ještě možno napsat do jména/zprávy a obarvení inputů podle splněné nebo nesplněné podmínky.
Ukázky z chatu
Jak to bude fungovat
Při načtení dokumentu se provede funkce rf();. Nejdříve se u chatu zobrazí vstupní adresář. Ten zahrnuje input pro vložení přezdívky. Jakmile si uživatel zvolí přezdívku a klikne na tlačítko vstoupit, skryje se vstupní formulář, spustí se interval funkce rf(); a zobrazí se mu právě probíhající konverzace na chatu, online uživatelé a formulář pro psaní zpráv. Při každé odeslané zprávě se provede aktualizace času poslední akce tohoto uživatele a kontrola, jestli nebyl uživatel odhlášen kvůli nečinnosti. Pokud byl odhlášen, skript ho vrátí na úvodní stránku.
Začínáme
Na začátek si jako v předchozím článku stáhneme knihovnu jQuery a vytvoříme adresáře se soubory ze kterých se bude náš chat skládat. Budou to tyto soubory:
- chat.php
- js
- css
- php
- config.php
- send.php
- messages.php
- users.php
- login.php
- logout.php
- cldb.php
Tabulky v databázi
Databáze, ve které budou uloženy tyto dvě tabulky se jmenuje test a porovnávánání v této db je nastaveno na UTF_8_czech_ci. K tomuto příkladu bude potřeba vytvořit 2 tabulky. Jedna bude chatusers a druhá chatmsg. V tabulce chatusers budou následující sloupce:
- id-(INT)
- name-(VARCHAR) omezení počtu znaků na 20
- time-(INT)
Tabulka chatmsg bude vypadat takto:
- id-(INT)
- name-(VARCHAR) max. počet znaků bude 20
- text-(VARCHAR) omezení počtu znaků na 500
- time-(TIME)
Skript pro vytvoření tabulek
<?php
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Nastavení řízení chyb
try {
$stmt=$db->query('CREATE TABLE chatusers (id INT NOT NULL AUTO_INCREMENT ,name VARCHAR( 20 ) NOT NULL ,
time INT NOT NULL ,PRIMARY KEY ( id ))');
echo 'Tabulka chatusers byla úspěšně vytvořena!<br/>';
} catch(PDOException $e) {
echo 'Nastala chyba:'.$e;
};
try {
$stmt=$db->query('CREATE TABLE chatmsg (id INT NOT NULL AUTO_INCREMENT ,name VARCHAR( 20 ) NOT NULL ,
text VARCHAR( 500 ) NOT NULL ,time TIME NOT NULL ,PRIMARY KEY ( id ))');
echo '<br/>Tabulka chatmsg byla úspěšně vytvořena!';
} catch(PDOException $e) {
echo 'Nastala chyba'.$e;
}
?>
Chat.php
V tomto souboru bude hlavní struktura chatu. Chat bude obsahovat 2 formuláře. Jeden bude vstupní, ten bude pro přezdívku a druhý bude pro komentář. Formuláře si budou vzájemně prohazovat pozice a to tak, že se jim budou měnit hodnoty z display:none na display:block a opačně.
Struktura tohoto souboru je následující:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chat.js"></script>
<link rel="stylesheet" href="css/chat.css" />
<title>Chat</title>
</head>
<body>
<div id="chat">
<div id="chenter">
<h3>Chat</h3>
<form method="post" name="cheform" id="cheform">
<label for="chnick">Nick:</label><input type="text" name="chnick" id="chnick" placeholder="3-20 znaků" />
<input type="button" id="ech" disabled="disabled" onclick="eCh();" value="Vstoupit" />
Zbývající znaky:<span id="nlch"></span>
<span id="errors"></span>
</form>
</div>
<div id="chroom">
<div id="chmessages">
<?php
include('php/messages.php');
?>
</div>
<div id="chusers">
<?php
include('php/users.php');
?>
</div>
<div id="chbottom">
<form method="post" name="chform" id="chform">
<label for="chmessage">Zpráva:<br /><span id="mlch"></span></label>
<textarea placeholder="3-300 znaků." name="chmessage" id="chmessage"></textarea>
<input type="button" id="chsm" disabled="disabled" onclick="sChM();" value="Odeslat"/>
</form>
</div>
</div>
</div>
</body>
</html>
Chat.css
V tomto souboru je css stylování chatu. Je na vás, jaký vzhled si vytvoříte. Důležité jsou pouze následující hodnoty:
#chroom {
display: none;
}
#chenter {
display:block;
}
#chusers {
overflow:auto;
}
#chmessages {
overflow: auto;
}
Chat.js
Na tomto souboru to vše stojí. Jsou zde vytvořeny funkce pro vstup do chatu, odeslání zpráv, refreshování zpráv a uživatelů a validace formuláře. Každý řádek skriptu je pro jednoduchost, přehlednost a lepší pochopení popsán.
// JavaScript Document
$(document).ready(function(){//Načtení dokumentu
rf();//Provedení refreshe zpráv a uživatelů v chatu
chControll();//Provedení kontroly formulářů
$('#chnick').keyup(chControll);//Začátek zaznamenávání akce kláves
$('#chmessage').keyup(chControll);//Totéž
});
function eCh() {//Funkce enter chat(eCh)
var nl=$('#chnick').val().length;
if(nl>=3) {
$('#chenter').css('display','none');
$('#chroom').css('display','block');
$.post('php/login.php',{nick:cheform.chnick.value},
function(output) {//Funkce která vypíše vrácená data
if(output){//Pokud byla nějaká data vrácena
$('#chenter').css('display','block');//Změna displeje z none na block
$('#chroom').css('display','none');//Změna displeje z block na none
$('#errors').html(output);//#chenter tyto data vypíše
} else {
$('#chform').css('display','block');//Pokud ne, #chform se nastaví na display:block
}
}
);
setInterval(rf,2500);//Nastavení intervalu refreshe
} else {
$('#errors').html('Váš nick je příliš krátký');//Pokud je Nick příliš krátký, vypiš varovaní
}
};
function sChM() {//Funkce send chat message(sChM)
var ml=$('#chmessage').val().length;
var nl=$('#chnick').val().length;
if(ml>=3 && nl>=3) {
$.post('php/send.php',{nick:cheform.chnick.value, message:chform.chmessage.value});
$.post('php/logout.php',{nick:cheform.chnick.value},//Zjisti, jestli nebyl uživatel odhlášen z důvodu neaktivity
function(output){
if(output) {
$('#chenter').css('display','block');//Změna displeje z none na block
$('#chroom').css('display','none');//Změna displeje z block na none
$('#errors').html(output);//Vypiš output
};
}
);
} else {
$('#chbottom').append('<span class="chlwarning">Vaše zpráva je příliš krátká.</span>');
}
};
function rf() {//Funkce refreshování zpráv a uživatelů
$('#chmessages').load('php/messages.php');//Načtení požadovaného souboru
$('#chusers').load('php/users.php');//Totéž
};
function chControll() {//Funkce pro kontrolu formuláře
var lname=$('#chnick').val().length;//Počet znaků #chnick
var lmessage=$('#chmessage').val().length;//Počet znaků v #chmessage
var name=$('#chnick').val();//Získání hodnoty #chnick
var message=$('#chmessage').val();//Získání hodnoty #chmessage
var nsubstr=name.substring(0,20);//Zkrácení počtu znaků name substring(nsubstr) na maximálních 20
var msubstr=message.substring(0,500);//Zkrácení počtu znaků message substring(msubstr) na maximálních 500
var nchleft=20-lname;//Zjištění zbývajících počtu znaků v #chnick
var mchleft=500-lmessage;//Zjištění zbývajícího počtu znaků v #chmessage
$('#nlch').html(nchleft);//Vložení zbývajícíh počtu znaků do nick left characters(nlch)
$('#mlch').html(mchleft);//Vložení zbývajícího počtu znaků do message left characters(mlch)
$('#chnick').val(nsubstr);//Vložení omezeného počtu znaků do #chnick
$('#chmessage').val(msubstr);//Vložení omezeného počtu znaků do #chmessage
if(lname>=3 && lmessage>=3) {//Pokud jsou splněny podmínky lname a lmessage
$('#chsm').removeAttr('disabled');//Odeber atribut disable u tlačítka chat send message(chsm)
} else {//Pokud pomínky nejsou splněny
$('#chsm').attr('disabled','disabled');//Přidej atribut disabled k tlačítku #chsm
};
if(lname<3) {//Pokud je splěna podmínka
$('#chnick').css('border','3px solid #F00');//Vytvoř červený rámeček okolo #chnick
$('#ech').attr('disabled','disabled');//Přidej atribut disabled ke tlačítku enter chat(ech)
} else {//Pokud pomínka není splněna
$('#chnick').css('border','3px solid #00ff00');//Změn barvu rámečku na zelenou
$('#ech').removeAttr('disabled');//Odeber atribut disabled
};
if(lmessage<3) {//Pokud je podmínka splněna
$('#chmessage').css('border','3px solid #F00');//Vytvoř červený rámeček kolem zprávy
} else {//Pokud podmínka není splněna
$('#chmessage').css('border','3px solid #00ff00');//Změn barvu rámečku na zelenou
};
};
Validace formuláře:Validaci formuláře má na starost funkce chControll();. Jsou v ní zaznamenávány hodnoty z inputů #chnick a #chmessage. U nich se měří počet znaků a ten je vkládán na určitá místa. Konkrétně u zprávy to je #mlch a u přezdívky (nicku) #nlch. Obarvení rámečku je zelené pokud je v inputech více znaků než 3. Červeně pokud není. Překročení maximálního počtu znaků je chráněno pomocí nchleft a mchleft. Hodnoty těchto dvou proměnných jsou vkládány na místo základního textu.
Config.php
V tomto souboru bude pouze jméno databáze, přistupovací jméno a heslo, připojení k databázi a proměnná $rewrite.
<?php
$dbname='';//Jméno databáze
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Nastavení řízení chyb
$rewrite=array('<'=>'<', '&'=>'&')//Změna prvku v poli
?>
Login.php
Do tohoto souboru se odešle přezdívka, kterou si uživatel zvolil. Zkontroluje se, zdali již přezdívka neexistuje. Pokud ne, žádná data se nevrátí a uživateli se zobrazí formulář pro psaní zpráv a do tabulky se vloží jeho jméno. Pokud se ale jméno již v tabulce nachází, uživatel bude vrácen na vstupní formulář a bude požádán o zvolení jiné přezdívky.
<?php
include('config.php');//Načti soubor
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);
$time=Time();
try {
$stmt = $db->prepare("SELECT * FROM `$dbname`.`chatusers` WHERE `name`=?");//Připrav dotaz
$stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Oddělení parametru od hlavního programu
$stmt->execute();//Provedení dotazu
if($stmt->rowCount()==NULL) {//Pokud se počet vrácených řádků rovná 0
$stmt = $db->prepare("INSERT INTO `$dbname`.`chatusers` (`id` ,`name` ,`time`)VALUES (NULL, ?, ?)");//Vlož do tabulky nick
$stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Oddělení parametru od hlavního programu
$stmt ->bindParam(2, $time, PDO::PARAM_INT);//Totéž
$stmt->execute();//Provedení dotazu
} else {//Pokud se řádek rovná 1
echo 'Toto jméno už existuje, musíte zvolit jiné.';//Vypiš varování
}
} catch (PDOException $e){//Pokud nastala chyba
exit("Nepodařilo se přihlásit.".$e->getMessage());//Vypiš chybu a hlášku
}
?>
Logout.php
Skript v tomto souboru zajišťuje, aby uživatel, který byl již odhlášen pro neaktivitu, nemohl pokračovat v konverzaci a musel se znovu přihlásit.
<?php
include('config.php');//Načti soubor config.php
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);//Zkrať počet znaků, odstraň tagy a nahraď "<" za <
try {
$stmt = $db->prepare("SELECT * FROM `$dbname`.`chatusers` WHERE `name`=?");//Připravení dotazu
$stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Rozdělení parametru od hl. programu
$stmt->execute();//Provedení dotazu
if($stmt->rowCount()==NULL) echo 'Byly jste odhlášeni pro neaktivitu.';//Pokud nebyl nalezen řádek se jménem vypiš zprávu
} catch (PDOException $e) {//Pokud nastala chyba
exit("Nepodařilo se vypsat zprávy.".$e->getMessage());//Vypiš chybu a hlášku
}
?>
Users.php
Zde se získají všechny řádky v tabulce chusers a vypíší se. Jeden řádek = jeden uživatel.
<?php
include('config.php');//Načti soubor
try {
$stmt = $db->query("SELECT * FROM `$dbname`.`chatusers` ");//Dotaz do db
while ($row = $stmt->fetch()) {//Vypsání dat
echo '<div class="user">'.$row['name'].'</div>';
}
} catch (PDOException $e) {//Pokud nastala chyba
exit("Nepodařilo se vypsat uživatele.".$e->getMessage());//Vypiš chybu a hlášku
}
?>
Messages.php
Následujících pár řádků skriptu zajišťuje získání všech zpráv z databáze, seřazení a následný výpis.
<?php
include('config.php');//Načti soubor config.php
try {
$stmt = $db->query("SELECT * FROM `$dbname`.`chatmsg` ORDER BY `time`");//Dotaz do db
while ($row = $stmt->fetch()) {//Vypsání dat podle potřeby
echo '<div class="msg"><div class="msginf"><span class="msgnick">'.$row['name'].'</span>
<span class="msgdate">'.$row['time'].'</span></div><div class="msgtext">'.$row['text'].'</div></div>';
}
} catch (Exception $e) {//Pokud nastala chyba
exit("Nepodařilo se vypsat zprávy.".$e->getMessage());//Vypiš chybu a hlášku
}
?>
Send.php
V tomto skriptu jsou 2 try-catch bloky. Uvnitř prvního se provádí odesílání dat do databáze a ve druhém updatovaní času poslední akce uživatele. Čas se updatuje proto aby skript, který bude spouštěn CRONem, mohl smazat offline uživatele, kteří byly po určitou dobu neaktivni. Nastavení této hodnoty je na vás. Je dobré to přizpůsobit minimálnímu časovému intervalu spouštění CRONu. Tak zajistíte, že bude seznam online uživatelů pořád a co nejvíce aktuální.
<?php
include('config.php');//Načti soubor config.php
$time=Time();
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);//Odstraň tagy, přepiš "<" na entitu < a zkrať počet znaků
$message=substr(strip_tags(StrTr($_POST['message'],$rewrite)),0,500);//Totéž
$strftime=StrFTime("%H:%M:%S");//Získání času typu 14:22:36
try {
$stmt = $db->prepare("INSERT INTO `$dbname`.`chatmsg` (`id` ,`name` ,`text` ,`time`)VALUES (NULL, ?, ?, ?)");//Připravení dotazu
$stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Rozdělení parametru od hlavního programu
$stmt ->bindParam(2, $message, PDO::PARAM_STR);//Totéž
$stmt ->bindParam(3, $strftime, PDO::PARAM_STR);//Totéž
$stmt->execute();//Proveď dotaz
} catch (PDOException $e){//Pokud se dotaz nezdařil
exit("Zprávu se nepodařilo odeslat.".$e->getMessage());//Vypiš zprávu a chybovou hlášku
}
try {//Zde se provede update času poslední akce tohoto uživatele
$stmt = $db->prepare("UPDATE `$dbname`.`chatusers` SET `time` =? WHERE `chatusers`.`name` ==?");//Připravení dotazu
$stmt ->bindParam(1, $strftime, PDO::PARAM_INT);//Rozdělení parametru od hlavního programu
$stmt ->bindParam(2, $nick, PDO::PARAM_STR);//Totéž
$stmt->execute();//Proveď dotaz
} catch (PDOException $e){//Pokud se dotaz nezdařil
exit("Nepodařilo se aktualizovat váš stav.".$e->getMessage());//Vypiš zprávu a chybovou hlášku
}
?>
Mazání neaktivních uživatelů
Tento skript musí být spouštěn CRONem. Záleží na vás, jaký interval nastavíte. Pokud nastavíte například na 5 minut, tak musíte u $timeout nastavit na místo hodnoty 3600 jen 300.
<?php
$dbname='test';//Jméno databáze
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$time=Time();
$timeout=$time-3600;
$stmt = $db->query("DELETE FROM `$dbname`.`chatusers` WHERE `chatusers`.`time < $timeout;");//Dotaz do db
$stmt = $db->query("SELECT * FROM `$dbname`.`chatmsg`");
$nrows=$stmt->rowCount();
if($nrows>=80) {//Pokud je počet zpráv větší než 80
$stmt = $db->query("DELETE FROM `$dbname`.`chatmsg` LIMIT 40");//Smaž polovinu zpráv
};
?>