Sistema de chat público em PHP

Introdução

chat_publico_0.3Embora a base deste sistema seja o PHP, também serão utilizadas outras tecnologias. A nossa “caixa de ferramentas” tem, então, o seguinte conteúdo e a respetiva utilização:

  • PHP: Linguagem de programação base;
  • HTML: Estrutura das páginas;
  • CSS: Design das páginas;
  • jQuery/JavaScript: Utilização do AJAX;
  • MySQL/MariaDB: Base de dados;
  • Apache: Servidor web;

Objectivo

No fim deste artigo o leitor terá uma visão abrangente do que é essencial para criar aplicações web e a ajuda necessária para começar a desbravar este mundo com o PHP.

Funcionalidades

O nosso sistema de chat terá as seguintes funcionalidades:

  • Sala de chat única e pública;
  • Escolha de um nickname exclusivo;
  • Envio de mensagens;
  • Consulta de mensagens;

“Caixa de Ferramentas”

Para implementar este chat vamos utilizar várias ferramentas que se complementam umas às outras. Algumas destas ferramentas foram instaladas com o XAMPP, permitindo, assim, instalar facilmente um servidor web na nossa máquina local.

PHP

PHP, acrónimo recursivo para “PHP: Hypertext Preprocessor” (originalmente Personal Home Page) é uma linguagem de programação server-side de utilização livre e de código aberto. O PHP é, por exemplo, utilizado pelo Facebook e WordPress. Em 2014 foi a linguagem de eleição de 82% dos sites (onde a linguagem de programação é conhecida).

No sistema de chat, que vamos construir, o PHP tem como função a comunicação com a base de dados para armazenamento e consulta das mensagens.

MySQL

O MySQL é um sistema de gestão de base de dados (SGBD) que utiliza a linguagem SQL (Structured Query Language). A sua licença é livre para desenvolvimento, mas, caso seja utilizada em aplicações comerciais, passa a ser paga. Utilizada pela NASA, Friendster, HP, Google, entre outras empresas, é, atualmente, uma das bases de dados mais populares, com mais de 10 milhões de instalações em todo o mundo.

A função do MySQL, no nosso sistema de chat, será o armazenamento das mensagens dos utilizadores (também se pode utilizar o MariaDB). Serão utilizadas duas tabelas, uma para armazenar os nicknames e outra para armazenar as mensagens.

HTML

O HTML (HyperText Markup Language) é uma linguagem de marcação utilizada na construção de páginas web. Quando o PHP é interpretado no servidor produz o HTML que será lido pelo Browser (Os browsers não conhecem o PHP).

O HTML será, então, utilizado para criar a estrutura das nossas páginas.

CSS

O CSS (Cascading Style Sheets) é uma linguagem de folhas de estilo utilizada para definir a apresentação de páginas web.

No nosso sistema de chat, todas as cores, tamanhos, tipos de letra, etc. serão definidos no CSS.

jQuery / JavaScript

O jQuery é uma framework de JavaScript, de código aberto, desenvolvida para facilitar a programação no lado do cliente (Browser) e é utilizado por cerca de 77% dos sites mais visitados do mundo. O seu slogan é revelador: “Write Less, Do More” (por exemplo, 20 linhas de código em JavaScript podem ser substituídas por uma em jQuery).

O jQuery será usado no nosso chat para facilitar o uso de outra “tecnologia” denominada de AJAX (Asynchronous Javascript and XML) que, na verdade, é um conjunto de tecnologias que torna as páginas mais interativas.

Apache

O Apache é o servidor web livre melhor sucedido. Estima-se que foi utilizado por metade do sites em 2015.

É através do Apache que o nosso computador conseguirá interpretar o PHP.

Procedimento

Passo 1 – Instalação e configuração das ferramentas necessárias

1. Instalar o XAMPP

Fazer o download em https://www.apachefriends.org e executar a instalação.

1.1. Configurar o XAMPP

Abrir o Control Panel e iniciar o Apache. Depois de iniciado clique no botão “Admin” que deverá abrir uma página no Browser (página de apresentação do XAMPP). A configuração está concluída.

1.2. Testar o PHP

Abrir o editor de texto, por exemplo, o Notepad++ e escrever o seguinte script:

<?php
echo 'Olá PHP';
?>

Guardar o script com o nome teste.php na pasta c:\xampp\htdocs\chat.

NOTA: é necessário criar a pasta chat.

Depois de guardar o ficheiro no disco, digite o seguinte URL no browser:

http://localhost/chat/teste.php

Apareceu o texto “Olá PHP”? Então o PHP está a funcionar.

2. Base de dados

Vamos agora colocar a base de dados em funcionamento. Obviamente, será aqui que serão armazenadas todas as conversas.

2.1. Criação da base de dados

Com o Control Panel do XAMPP aberto, clique no botão “Admin” referente ao MySQL que abrirá o browser no phpMyAdmin, o que nos permitirá criar uma base de dados e o respetivo utilizador. Para tal, deverá clicar na aba “User accounts” e clicar no link “Add user account”. Vamos então escrever os seguintes dados onde diz “Informação de login”:

  • Nome de usuário: chat
  • Host name: localhost
  • Senha: programar
  • Re-digite: programar

Onde diz “Database for user account” selecionar a checkbox que diz:

  • Criar banco de dados com o mesmo nome e conceder todos os privilégios.

No fim da página, clique no botão “Executar”. Neste momento, a base de dados e o respetivo utilizador estão criados.

2.2 Criação das tabelas

No lado esquerdo selecione a tabela “chat” e no lado direito selecione a aba “SQL”. Onde diz “Rodar consulta(s) SQL no banco de dados chat” fazer copiar/colar das seguintes queries:

CREATE TABLE `messages` (
  `IdMessage` int(8) NOT NULL COMMENT '(PK) ID da mensagem',
  `FromNickname` varchar(32) NOT NULL COMMENT 'Nickname do remetente',
  `Message` text COMMENT 'Mensagem',
  `MessageDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Data da mensagem'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Mensagens do chat';

CREATE TABLE `users` (
  `Nickname` varchar(32) NOT NULL COMMENT '(PK) Nickname do utilizador',
  `LoginDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Data do login'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Utilizadores do chat';

ALTER TABLE `messages`
  ADD PRIMARY KEY (`IdMessage`);

ALTER TABLE `users`
  ADD PRIMARY KEY (`Nickname`);

ALTER TABLE `messages`
  MODIFY `IdMessage` int(8) NOT NULL AUTO_INCREMENT COMMENT '(PK) ID da mensagem';

Depois de carregar no botão “Executar” será criada a tabela users que armazenará os nicknames dos utilizadores e a tabela messages que armazenará as mensagens.

3. Script das constantes

Crie um ficheiro com o nome constants.inc.php e copie o seguinte conteúdo:

<?php
# Base de dados
define('DB_HOSTNAME', 'localhost');
define('DB_USERNAME', 'chat');
define('DB_PASSWORD', 'programar');
define('DB_NAME', 'chat');

# Tabelas
// Utilizadores do chat
define('USERS', 'users');
// Mensagens do chat
define('MESSAGES', 'messages');

Este ficheiro deve ser guardado na pasta chat. Ele contém os dados necessários para a comunicação com a base de dados.

Mais tarde, vamos testar se a ligação à base de dados está a funcionar, mas antes disso vamos precisar de mais alguns scripts.

Passo 2 – Criação das classes

Optou-se pela programação orientada a objetos, modelando, assim, o sistema de chat através das classes Users e Chat.

1. Classe DbConnPDO

A classe DbConnPDO representa a ligação à base de dados.  Vamos utilizar o PDO através da classe DbConnPDO (extensão da classe PDO, que já se encontra instalada no PHP) para comunicar com a base de dados.

A classe DbConnPDO tem como única função a ligação à base de dados. Esta ligação é efetuada no seu construtor utilizando as constantes definidas, anteriormente, no script constants.inc.php.

Vamos, então, criar um ficheiro na pasta chat com o nome DbConnPDO.class.php e escrever o seguinte código:

<?php
/**
 * PDO Database connection
 * Representa a ligação global à base de dados
 *
 * @package Chat\PDO
 * @author Sandro Miguel Marques <sandromiguel@produlogia.com>
 * @version v.1.0 (13/01/2016)
 */

class DbConnPDO extends PDO {
 /** @var string|null Mensagem de erro */
 private $_error = null;

 /**
 * Construtor
 * Abrir a ligação à base de dados
 */
 public function __construct() {
 $connection_string = sprintf('mysql:host=%s;dbname=%s;charset=UTF8', DB_HOSTNAME, DB_NAME);
 try {
 parent::__construct($connection_string, DB_USERNAME, DB_PASSWORD);
 $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 } catch (PDOException $e) {
 $this->error = $e->getMessage();
 throw $e;
 }
 }

 /**
 * Mostra o erro.
 *
 * @return string Devolve o erro.
 */
 public function getError() {
 return $this->_error;
 }
 
 /**
 * Fecha a ligação à base de dados quando o objeto é destruido
 */
 public function __destruct() {
 $this->conn = null;
 }
}

2. Classe User

A classe User representa os utilizadores do chat. Uma vez que esta classe faz ligação à base de dados, estende a classe DbConnPDO.  O método checkNicknameExists() verifica se um determinado nickname existe. O método insert() insere o nickname na base de dados, nomeadamente, na tabela users.

Cria-se agora o ficheiro na pasta chat com o nome User.class.php e escreve-se o seguinte código:

<?php
/**
 * Utilizador do chat
 *
 * @package Chat\User
 * @author Sandro Miguel Marques <sandromiguel@produlogia.com>
 * @version v.1.0 (06/04/2016)
 * @copyright Copyright (c) 2016, Sandro
 */

class User extends DbConnPDO {
 /** @var string|null Query SQL */
 private $_sql;
 
 /**
 * Construtor
 */
 public function __construct() {
 parent::__construct();
 }

 /**
 * Verifica se um determinado nickname existe.
 *
 * @param string $nickname Nickname do utilizador
 *
 * @return boolean Devolve 'true' se o nickname já existir.
 */
 public function checkNicknameExists($nickname) {
 try {
 $this->_sql = '
 SELECT 
 NIckname 
 FROM 
 `'. USERS .'` 
 WHERE 
 `Nickname`=:nickname 
 LIMIT 1
 ';
 $stmt = $this->prepare($this->_sql);
 $stmt->bindParam(':nickname', $nickname, PDO::PARAM_STR);
 $stmt->execute();
 $num_rows = $stmt->rowCount();
 if ($num_rows == 0) {
 return false;
 }
 return true;
 } catch (Exception $e) {
 throw $e;
 }
 }

 /**
 * Insere um utilizador da base de dados.
 *
 * @param string $nickname Nickname
 *
 * @return boolean Devolve 'true' em caso de sucesso ou 'false' em caso de erro.
 */
 public function insert($nickname) {
 $this->_sql = 'INSERT INTO `'.USERS.'` 
 (Nickname) 
 VALUES 
 (:nickname)
 ';
 try {
 $stmt = $this->prepare($this->_sql);
 $stmt->bindParam(':nickname', $nickname, PDO::PARAM_STR);
 if ($stmt->execute()) {
 $stmt->closeCursor();
 return true;
 }
 $stmt->closeCursor();
 return false;
 } catch (Exception $e) {
 throw $e;
 }
 }

 /**
 * @return string Devolve a query SQL.
 */
 public function __toString() {
 if (is_null($this->_sql)) {
 return 'NULL';
 }
 return $this->_sql;
 }
}

3. Classe Chat

A classe Chat representa o chat propriamente dito. Tal classe estende a classe DbConnPDO pois, também, faz ligação à base de dados. Para inserir as mensagens na tabela messages utiliza-se o método insert() e para as ler utiliza-se o método getMessages().

Para esta classe criamos um ficheiro na pasta chat com o nome Chat.class.php, com o seguinte código:

<?php
/**
 * Mensagens do chat
 *
 * @package Chat\Messages
 * @author Sandro Miguel Marques <sandromiguel@produlogia.com>
 * @version v.1.0 (07/04/2016)
 * @copyright Copyright (c) 2016, Sandro
 */

class Chat extends DbConnPDO {
 /** @var string|null Query SQL */
 private $_sql;
 
 /**
 * Construtor
 */
 public function __construct() {
 parent::__construct();
 }

 /**
 * Lê as últimas x mensagens.
 *
 * @param int $limit Número de mensagens
 *
 * @return boolean Devolve 'true' se o nickname já existir.
 */
 public function getMessages($limit) {
 try {
 $this->_sql = '
 SELECT 
 `FromNickname`, 
 `Message` 
 FROM 
 `'. MESSAGES .'` 
 ORDER BY 
 IdMessage DESC 
 LIMIT :limit
 ';
 $stmt = $this->prepare($this->_sql);
 $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
 $stmt->execute();
 $obj = $stmt->fetchAll(PDO::FETCH_OBJ);
 $stmt->closeCursor();
 return $obj;
 } catch (Exception $e) {
 throw $e;
 }
 }

 /**
 * Insere uma mensagem na base de dados.
 *
 * @param string $from Nickname que enviou a mensagem
 * @param string $message Mensagem
 *
 * @return boolean Devolve 'true' em caso de sucesso ou 'false' em caso de erro.
 */
 public function insert($from, $message) {
 $this->_sql = 'INSERT INTO `'.MESSAGES.'` 
 (`FromNickname`, `Message`) 
 VALUES 
 (:from, :message)
 ';
 try {
 $stmt = $this->prepare($this->_sql);
 $stmt->bindParam(':from', $from, PDO::PARAM_STR);
 $stmt->bindParam(':message', $message, PDO::PARAM_STR);
 if ($stmt->execute()) {
 $stmt->closeCursor();
 return true;
 }
 $stmt->closeCursor();
 return false;
 } catch (Exception $e) {
 throw $e;
 }
 }
 
 /**
 * @return string Devolve a query SQL.
 */
 public function __toString() {
 if (is_null($this->_sql)) {
 return 'NULL';
 }
 return $this->_sql;
 }
}

4. Script para testar a ligação à base de dados

Já temos os scripts necessários para fazer o teste de ligação à base de dados. Crie um ficheiro com o nome teste2.php e escreva o seguinte código:

<?php
error_reporting(E_ALL);
ini_set('display_errors','On');

require 'constants.inc.php';
require 'DbConnPDO.class.php';
require 'Chat.class.php';

# 1. Ler as mensagens da base de dados
try {
 $chat = new Chat();
 $messages = $chat->getMessages(10);
 echo json_encode($messages);
} catch (Exception $e) {
 $message = 'Ocorreu um erro.';
 echo json_encode(
 array(
 "action" => "insert",
 "notification" => "error",
 "message" => $message
 )
 );
 exit;
}

Ao executar este script, caso não existam erros, deverão aparecer dois parêntesis retos [].

Passo 3 – Comunicação via AJAX

Para comunicar com a base de dados vamos utilizar o AJAX com JSON através do jQuery.

1. Definir o nickname

O script nickname.ajax.php é executado quando o utilizador introduz o seu nickname. Este script verifica, então, se o nickname contém pelo menos três caracteres, e se já existe. Caso o nickname não exista, o script insere-o na base de dados.

O script nickname.ajax.php contém o seguinte código:

<?php
session_start();

require 'constants.inc.php';
require 'DbConnPDO.class.php';
require 'User.class.php';

// Definir uma variável com o nickname recebido pelo método POST
$nickname = filter_input(INPUT_POST, 'nickname', FILTER_SANITIZE_STRING);

# 1. Verificar se o nickname contém o número mínimo de carateres
if (strlen($nickname) < 3) {
 $message = 'O nickname deve ter no mínimo 3 carateres';
 echo json_encode(
 array(
 'action' => 'insert',
 'notification' => 'error',
 'message' => $message
 )
 );
 exit;
}

# 2. Verificar se o nickname já existe na base de dados
try {
 $user = new User();
 $nickname_exists = $user->checkNicknameExists($nickname);
 if ($nickname_exists) {
 $message = 'Este nickname já existe.';
 echo json_encode(
 array(
 'action' => 'insert',
 'notification' => 'error',
 'message' => $message
 )
 );
 exit;
 }
} catch (Exception $e) {
 $message = 'Ocorreu um erro.';
 echo json_encode(
 array(
 "action" => "insert",
 "notification" => "error",
 "message" => $message
 )
 );
 exit;
}

# 3. Inserir o utilizador na base de dados
try {
 $user->insert($nickname);
 $_SESSION['nickname'] = $nickname;
 echo json_encode(
 array(
 'action' => 'replace',
 'notification' => 'success',
 'message' => 'Olá '.$nickname.', aguarde por favor...'
 )
 ); 
} catch (Exception $e) {
 $message = 'Ocorreu um erro.';
 echo json_encode(
 array(
 "action" => "insert",
 "notification" => "error",
 "message" => $message
 )
 );
 exit;
}

2. Inserir mensagem

Sempre que é enviada uma mensagem é executado o script set_message.ajax.php que insere a mensagem e o respetivo nickname na base de dados.

O código deste script é o seguinte:

<?php
require 'constants.inc.php';
require 'DbConnPDO.class.php';
require 'Chat.class.php';

$nickname = filter_input(INPUT_POST, 'nickname', FILTER_SANITIZE_STRING);
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);

# 1. Inserir a mensagen na base de dados
try {
 $chat = new Chat();
 $chat->insert($nickname, $message);
} catch (Exception $e) {
 echo 'ocorreu um erro ao fazer o INSERT na BD<br>';
 echo $e->getMessage().'<br>';
 echo $chat->__toString();
}

3. Ler as mensagens

Para ler as mensagens do chat é executado o script get_messages.ajax.php que devolve as mensagens no formato JSON através da função json_encode() do PHP. No caso deste chat são devolvidas as últimas 10 mensagens, mas este número pode ser definido, por exemplo, no script constants.inc.php bastando adicionar mais uma constante.

Aqui está o código para este script:

<?php
error_reporting(E_ALL);
ini_set('display_errors','On');

require 'constants.inc.php';
require 'DbConnPDO.class.php';
require 'Chat.class.php';

# 1. Ler as mensagens da base de dados
try {
 $chat = new Chat();
 $messages = $chat->getMessages(10);
 echo json_encode($messages);
} catch (Exception $e) {
 $message = 'Ocorreu um erro.';
 echo json_encode(
 array(
 "action" => "insert",
 "notification" => "error",
 "message" => $message
 )
 );
 exit;
}

Passo 4 – Interface

A nossa interface será provida de uma página inicial onde se introduz o nickname e a página do chat.

1. Página inicial

Chat Público PHP: página inicial

Na página inicial o utilizador é convidado a introduzir o seu nickname. A submissão do formulário é feita pelo jQuery através da função ajax(), nativa do jQuery, que envia o nickname introduzido para o script nickname.ajax.php. Caso o nickname não esteja a ser utilizado o utilizador é redirecionado para o script chat.php.

Segue-se o código do ficheiro index.php:

<?php
session_start();

error_reporting(E_ALL);
ini_set('display_errors','On');
?>
<!doctype html>
<html lang="pt">
<head>
 <meta charset="utf-8">
 <title>Chat público</title>
 <meta name="description" content="Chat público" />
 <meta name="author" content="Sandro Marques">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <link href="styles.css" rel="stylesheet" />
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
</head>

<body>
 <div class="outer">
 <div class="middle">
 <div class="inner">
 <div id="logo"></div>
 <div id="container_nickname">
 <div id="content_insert"></div>
 <div id="content_replace">
 <form name="form_nickname" id="form_nickname" method="post" action="nickname.ajax.php">
 <p>Nickname</p>
 <input name="nickname" type="text" autofocus required class="general_textbox" id="nickname" maxlength="32">
 <button>Entrar</button>
 </form> 
 </div>
 </div>
 </div>
 </div>
 </div> 
</body>

<script>
'use strict';
function resetContentInsert() {
 if ($('#content_insert').children().length > 0) {
 // existe uma mensagem > remover conteúdo com animação
 $('#content_insert').animate({
 height: 0
 }, "fast", function() {
 $(this).empty();
 $('#content_insert').removeAttr('style');
 }); 
 }
}
$(document).ready(function() {
 // submeter formulário pela função sendForm()
 $('#form_nickname').on('submit', function(e) {
 e.preventDefault();
 sendForm();
 });
 
 function sendForm() {
 var msg_error = 'Ocorreu um erro';
 var msg_timeout = 'O servidor não está a responder';
 var message = '';
 var form = $('#form_nickname');
 resetContentInsert();
 $.ajax({
 data: form.serialize(),
 url: form.attr('action'),
 type: form.attr('method'),
 dataType: "json",
 error: function(xhr, status, error) {
 if (status==="timeout") {
 message = msg_timeout;
 message = '<div class="bg-error">'+ message +'</div>';
 $('#content_insert').empty().html(message).hide().fadeIn('slow');
 } else {
 message = msg_error;
 message = '<div class="bg-error">'+ message +'</div>';
 $('#content_insert').empty().html(message).hide().fadeIn('slow');
 }
 },
 success: function(response) {
 var action = response.action;
 var notification = response.notification;
 var bg_notification = null;
 switch (notification) {
 case 'success':
 bg_notification = 'bg-success';
 break; 
 case 'error':
 bg_notification = 'bg-error';
 break; 
 }
 message = '<div class="'+ bg_notification +'">'+ response.message +'</div>';
 if (action === 'insert') {
 $('#content_insert').finish();
 $('#content_insert').removeAttr('style');
 $('#content_insert').empty().html(message).hide().fadeIn('slow');
 } else {
 $('#content_replace').empty().html(message).hide().fadeIn('slow');
 setTimeout(function(){window.location="chat.php"} , 1000);
 }
 },
 timeout: 7000
 });
 }
});
</script>
</html>

2. Página do chat

Chat Público PHP: conversa

Na página do chat o utilizador pode, efetivamente, ver e enviar mensagens. A submissão do formulário, ou seja, a submissão do texto da mensagem, é realizada para o script set_message.ajax.php, através da função ajax() (nativa do jQuery), invocada pela função sendForm(). Neste script o jQuery é, também, utilizado para ler as últimas mensagens do chat através da função getMessages(). Esta função é recursiva, chamando-se a si própria de dois em dois segundos.

NOTA: para simular vários utilizadores no chat, o leitor pode abrir a página em diferentes browsers.

Apresenta-se de seguida o código do script chat.php:

<?php
session_start();

error_reporting(E_ALL);
ini_set('display_errors','On');

require 'constants.inc.php';
require 'DbConnPDO.class.php';
?>
<!doctype html>
<html lang="pt">
<head>
 <meta charset="utf-8">
 <title>Chat público</title>
 <meta name="description" content="Chat público" />
 <meta name="author" content="Sandro Marques">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <link href="styles.css" rel="stylesheet" />
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
</head>

<body>
<div id="sidebar"></div>
<div id="primary">
 <div id="log">
 <span class="long-content">&nbsp;</span>
 </div>
 <div id="composer">
 <form name="form_message" id="form_message" method="post" action="set_message.ajax.php">
 <input name="nickname" type="hidden" id="nickname" value="<?php echo $_SESSION['nickname']; ?>">
 <input name="message" type="text" autofocus required class="textbox_message" id="message">
 <button id="btn_send">Enviar</button>
 </form>
 </div>
</div> 
</body>
<script>
'use strict';
$(document).ready(function() {
 getMessages();

 function getMessages() {
 $('.long-content').empty();
 var msg_error = 'Ocorreu um erro...';
 var msg_timeout = 'O servidor não está a responder';
 var message = '';
 $.ajax({
 url: 'get_messages.ajax.php',
 dataType: "json",
 error: function(xhr, status, error) {
 if (status==="timeout") {
 message = msg_timeout;
 alert(message);
 } else {
 message = msg_error;
 alert(message + ': ' + error);
 }
 },
 success: function(response) {
 $.each(response, function(i, item) {
 $('.long-content').prepend('<p><b>'+item.FromNickname + '</b>: ' + item.Message+'</p>');
 });
 setTimeout(getMessages, 2000);
 },
 timeout: 7000
 });
 } 

 // submeter formulário pela função sendForm()
 $('#form_message').on('submit', function(e) {
 e.preventDefault();
 sendForm();
 });
 
 function sendForm() {
 var msg_error = 'Ocorreu um erro..';
 var msg_timeout = 'O servidor não está a responder';
 var message = '';
 var form = $('#form_message');
 $.ajax({
 data: form.serialize(),
 url: form.attr('action'),
 type: form.attr('method')
 })
 $('#message').val('');
 }

});
</script>
</html>

3. Estilos CSS

Para estilizar as páginas no chat foi utilizado o ficheiro CSS styles.css. Este ficheiro está disponível para download na íntegra e importa salientar as seguintes partes:

CSS Reset

O CSS Reset é uma técnica que “normaliza” o CSS de forma a manter o visual do site em qualquer browser.

CSS responsivo

Foram utilizadas as media queries para adaptar o layout do chat às dimensões dos vários dispositivos. O seguinte exemplo ilustra a técnica utilizada para alterar a largura do “formulário para a introdução do nickname“, consoante a largura do viewport do browser:

/* Formulário nickname centrado */
.outer {
 display: table;
 position: absolute;
 height: 100%;
 width: 100%;
}
.middle {
 display: table-cell;
 vertical-align: middle;
}
.inner {
 margin-left: auto;
 margin-right: auto; 
 width: 90%;
}
@media (min-width: 768px) {
 /* Small devices (tablets, 768px and up) */
 .inner {
 width: 40%;
 }
}
@media (min-width: 992px) {
 /* Medium devices (desktops, 992px and up) */
 .inner {
 width: 35%;
 }
}
@media (min-width: 1200px) {
 /* Large devices (large desktops, 1200px and up) */
 .inner {
 width: 30%;
 }
}

O que ficou por fazer?

Todos os sistemas têm sempre algo a melhorar e este não é exceção. Então, o que ficou por fazer?

No que diz respeito à segurança, a aplicação está protegida contra SQL injection mas não foram tidos em conta outros potenciais problemas tais como XSS, CSRF, etc.

A nível de performance este chat poderá ser melhorado de forma a que o servidor notifique cada cliente (push) sempre que há mensagens novas. Isto pode ser feito através de websockets, ou outras técnicas.

Também poderão ser implementadas novas funcionalidades (mostrar os utilizadores ativos, criar outras salas de chat, públicas ou privadas, registar utilizadores, definir uma imagem ou avatar, verificar se o utilizador ainda está ativo, implementar emoticons, enviar mensagens privadas, etc.).

Conclusão

Neste artigo apresentou-se o desenvolvimento de um pequeno sistema de chat. Tratou-se de um sistema simples, mas que abordou técnicas avançadas, tais como POO, AJAX e JSON.

Código fonte da aplicação: https://github.com/SandroMiguel/public-chat

Bibliografia

  1. Benedetti, R., & Cranley, R. (2011). Head First jQuery. Sebastopol: O’Reilly Media, Inc.
  2. Curioso, A., Bradford, R., & Galbraith, P. (2010). Expert PHP and MySQL®. Indianapolis: Wiley Publishing, Inc.
  3. Hansen, T., & Lengstorf, J. (2014). PHP for Absolute Beginners. New York: Apress.
  4. Kennedy, A., & León, I. d. (2011). Pro CSS for High Traffic Websites. New York: Apress.
  5. MacIntyre, P. B. (2010). PHP: The Good Parts. Sebastopol: O’Reilly Media, Inc.
  6. MacIntyre, P., Danchilla, B., & Gogala, M. (2011). Pro PHP Programming. New York: Apress.
  7. Powers, D. (2014). PHP Solutions: Dynamic Web Design Made Easy (3rd ed.). New York: Apress.
  8. Powers, S. (2015). JavaScript Cookbook (2nd ed.). Sebastopol: O’Reilly Media, Inc.
  9. Prettyman, S. (2016). Learn PHP 7: Object-Oriented Modular Programming using HTML5, CSS3, JavaScript, XML, JSON, and MySQL. Georgia: Apress.
  10. Tatroe, K., MacIntyre, P., & Lerdorf, R. (2013). Programming PHP (3rd ed.). Sebastopol: O’Reilly Media, Inc.
  11. Ullman, L. (2012). PHP and MySQL for Dynamic Web Sites (4th ed.). Berkeley: Peachpit Press.