Report abuse

<?php

/**
 * Classe para controle de login e permissões de usuário
 * 
 * (PHP 4, PHP 5)
 *
 * @author Thiago Belem <contato@thiagobelem.net>
 * @link http://blog.thiagobelem.net/
 * 
 * @version v1.1
 * @todo Criar a funcionalidade "Esqueci minha senha"
 * 
 */
class Usuario {
    /**
     * Nome do banco de dados onde está a tabela de usuários
     * 
     * @var string
     * @since v1.0
     */
    var $bancoDeDados = 'meu_site';
    
    /**
     * Nome da tabela de usuários
     * 
     * @var string
     * @since v1.0
     */
    var $tabelaUsuarios = 'usuarios';
    
    /**
     * Nomes dos campos onde ficam o usuário, a senha e o e-mail de cada usuário
     * 
     * Formato: tipo => nome do campo na tabela
     * 
     * O campo (email) só é necessário para o "Esqueci minha senha"
     * 
     * @var array
     * @since v1.0
     */
    var $campos = array(
        'usuario' => 'usuario',
        'senha' => 'senha'
    );
    
    /**
     * Nomes dos campos que serão pegos da tabela de usuarios e salvos na sessão,
     * caso o valor seja false nenhum dado será consultado
     * 
     * @var mixed
     * @since v1.0
     */
    var $dados = array('id', 'nome');
    
    /**
     * Inicia a sessão se necessário?
     * 
     * @var boolean
     * @since v1.0
     */
    var $iniciaSessao = true;
    
    /**
     * Prefixo das chaves usadas na sessão
     * 
     * @var string
     * @since v1.0
     */
    var $prefixoChaves = 'usuario_';
    
    /**
     * Usa um cookie para melhorar a segurança?
     * 
     * @var boolean
     * @since v1.0
     */
    var $cookie = true;
    
    /**
     * O usuário e senha são case-sensitive?
     * 
     * Em valores case-sensitive "casa" é diferente de "CaSa" e de "CASA"
     * 
     * @var boolean
     * @since v1.1
     */
    var $caseSensitive = true;
    
    /**
     * Filtra os dados antes de consultá-los usando mysql_real_escape_string()?
     * 
     * @var boolean
     * @since v1.1
     */
    var $filtraDados = true;
    
    /**
     * Quantidade (em dias) que o sistema lembrará os dados do usuário ("Lembrar minha senha")
     * 
     * Usado apenas quando o terceiro parâmetro do método Usuario::logaUsuario() for true
     * Os dados salvos serão encriptados usando base64
     * 
     * @var integer
     * @since v1.1
     */
    var $lembrarTempo = 7;
    
    /**
     * Diretório a qual o cookie vai pertencer
     * Atenção: Não edite se você não souber o que está fazendo!
     * 
     * @var string
     * @since v1.1
     */
    var $cookiePath = '/';
    
    /**
     * Armazena as mensagens de erro
     * 
     * @var string
     * @since v1.0
     */
    var $erro = '';
    
    /**
     * Codifica a senha do usuário
     * 
     * Modifique esse método caso você use alguma senha encriptada
     * 
     * @access public
     * @since v1.0
     *
     * @param string $senha A senha que será codificada
     * @return string A senha já codificada
     */
    function codificaSenha($senha) {
        // Altere aqui caso você use, por exemplo, o MD5:
        // return md5($senha);
        return $senha;
    }
    
    /**
     * Verifica se um usuário existe no sistema
     * 
     * @access public
     * @since v1.0
     * @uses Usuario::codificaSenha()
     * 
     * @param string $usuario O usuário que será validado
     * @param string $senha A senha que será validada
     * @return boolean Se o usuário existe
     */
    function validaUsuario($usuario, $senha) {
        $senha = $this->codificaSenha($senha);
        
        // Filtra os dados?
        if ($this->filtraDados) {
            $usuario = mysql_escape_string($usuario);
            $senha = mysql_escape_string($senha);
        }
        
        // Os dados são case-sensitive?
        $binary = ($this->caseSensitive) ? 'BINARY' : '';

        // Procura por usuários com o mesmo usuário e senha
        $sql = "SELECT COUNT(*) AS total
                FROM `{$this->bancoDeDados}`.`{$this->tabelaUsuarios}`
                WHERE
                    {$binary} `{$this->campos['usuario']}` = '{$usuario}'
                    AND
                    {$binary} `{$this->campos['senha']}` = '{$senha}'";
        $query = mysql_query($sql);
        if ($query) {
            // Total de usuários encontrados
            $total = mysql_result($query, 0, 'total');
            
            // Limpa a consulta da memória
            mysql_free_result($query);
        } else {
            // A consulta foi mal sucedida, retorna false
            return false;
        }
        
        // Se houver apenas um usuário, retorna true
        return ($total == 1) ? true : false;
    }
    
    /**
     * Tenta logar um usuário no sistema salvando seus dados na sessão e cookies
     * 
     * @access public
     * @since v1.0
     * @uses Usuario::validaUsuario()
     * @uses Usuario::lembrarDados()
     *
     * @param string $usuario O usuário que será logado
     * @param string $senha A senha do usuário
     * @param boolean $lembrar Salvar os dados em cookies? (Lembrar minha senha)
     * @return boolean Se o usuário foi logado
     */
    function logaUsuario($usuario, $senha, $lembrar = false) {            
        // Verifica se é um usuário válido
        if ($this->validaUsuario($usuario, $senha)) {
        
            // Inicia a sessão?
            if ($this->iniciaSessao AND !isset($_SESSION)) {
                session_start();
            }
        
            // Filtra os dados?
            if ($this->filtraDados) {
                $usuario = mysql_real_escape_string($usuario);
                $senha = mysql_real_escape_string($senha);
            }
            
            // Traz dados da tabela?
            if ($this->dados != false) {
                // Adiciona o campo do usuário na lista de dados
                if (!in_array($this->campos['usuario'], $this->dados)) {
                    $this->dados[] = 'usuario';
                }
            
                // Monta o formato SQL da lista de campos
                $dados = '`' . join('`, `', array_unique($this->dados)) . '`';
        
                // Os dados são case-sensitive?
                $binary = ($this->caseSensitive) ? 'BINARY' : '';

                // Consulta os dados
                $sql = "SELECT {$dados}
                        FROM `{$this->bancoDeDados}`.`{$this->tabelaUsuarios}`
                        WHERE {$binary} `{$this->campos['usuario']}` = '{$usuario}'";
                $query = mysql_query($sql);
                
                // Se a consulta falhou
                if (!$query) {
                    // A consulta foi mal sucedida, retorna false
                    $this->erro = 'A consulta dos dados é inválida';
                    return false;
                } else {
                    // Traz os dados encontrados para um array
                    $dados = mysql_fetch_assoc($query);
                    // Limpa a consulta da memória
                    mysql_free_result($query);
                    
                    // Passa os dados para a sessão
                    foreach ($dados AS $chave=>$valor) {
                        $_SESSION[$this->prefixoChaves . $chave] = $valor;
                    }
                }
            }
            
            // Usuário logado com sucesso
            $_SESSION[$this->prefixoChaves . 'logado'] = true;
            
            // Define um cookie para maior segurança?
            if ($this->cookie) {
                // Monta uma cookie com informações gerais sobre o usuário: usuario, ip e navegador
                $valor = join('#', array($usuario, $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']));
                
                // Encripta o valor do cookie
                $valor = sha1($valor);
                
                // Cria o cookie
                setcookie($this->prefixoChaves . 'token', $valor, 0, $this->cookiePath);
            }
            
            // Salva os dados do usuário em cookies? ("Lembrar minha senha")
            if ($lembrar) $this->lembrarDados($usuario, $senha);
            
            // Fim da verificação, retorna true
            return true;
            
                        
        } else {
            $this->erro = 'Usuário inválido';
            return false;
        }
    }
    
    /**
     * Verifica se há um usuário logado no sistema
     * 
     * @access public
     * @since v1.0
     * @uses Usuario::verificaDadosLembrados()
     *
     * @param boolean $cookies Verifica também os cookies?
     * @return boolean Se há um usuário logado
     */
    function usuarioLogado($cookies = true) {
        // Inicia a sessão?
        if ($this->iniciaSessao AND !isset($_SESSION)) {
            session_start();
        }
        
        // Verifica se não existe o valor na sessão
        if (!isset($_SESSION[$this->prefixoChaves . 'logado']) OR !$_SESSION[$this->prefixoChaves . 'logado']) {
            // Não existem dados na sessão
            
            // Verifica os dados salvos nos cookies?
            if ($cookies) {
                // Se os dados forem válidos o usuário é logado automaticamente
                return $this->verificaDadosLembrados();
            } else {
                // Não há usuário logado
                $this->erro = 'Não há usuário logado';
                return false;
            }
        }
        
        // Faz a verificação do cookie?
        if ($this->cookie) {
            // Verifica se o cookie não existe
            if (!isset($_COOKIE[$this->prefixoChaves . 'token'])) {
                $this->erro = 'Não há usuário logado';
                return false;
            } else {
                // Monta o valor do cookie
                $valor = join('#', array($_SESSION[$this->prefixoChaves . 'usuario'], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']));
    
                // Encripta o valor do cookie
                $valor = sha1($valor);
    
                // Verifica o valor do cookie
                if ($_COOKIE[$this->prefixoChaves . 'token'] !== $valor) {
                    $this->erro = 'Não há usuário logado';
                    return false;
                }
            }
        }
        
        // A sessão e o cookie foram verificados, há um usuário logado
        return true;
    }
    
    /**
     * Faz logout do usuário logado
     * 
     * @access public
     * @since v1.0
     * @uses Usuario::limpaDadosLembrados()
     * @uses Usuario::usuarioLogado()
     * 
     * @param boolean $cookies Limpa também os cookies de "Lembrar minha senha"?
     * @return boolean
     */
    function logout($cookies = true) {
        // Inicia a sessão?
        if ($this->iniciaSessao AND !isset($_SESSION)) {
            session_start();
        }
        
        // Tamanho do prefixo
        $tamanho = strlen($this->prefixoChaves);

        // Destroi todos os valores da sessão relativos ao sistema de login
        foreach ($_SESSION AS $chave=>$valor) {
            // Remove apenas valores cujas chaves comecem com o prefixo correto
            if (substr($chave, 0, $tamanho) == $this->prefixoChaves) {
                unset($_SESSION[$chave]);
            }
        }
        
        // Destrói asessão se ela estiver vazia
        if (count($_SESSION) == 0) {
            session_destroy();
            
            // Remove o cookie da sessão se ele existir
            if (isset($_COOKIE['PHPSESSID'])) {
                setcookie('PHPSESSID', false, (time() - 3600));
                unset($_COOKIE['PHPSESSID']);
            }
        }
        
        // Remove o cookie com as informações do visitante
        if ($this->cookie AND isset($_COOKIE[$this->prefixoChaves . 'token'])) {
            setcookie($this->prefixoChaves . 'token', false, (time() - 3600), $this->cookiePath);
            unset($_COOKIE[$this->prefixoChaves . 'token']);
        }
        
        // Limpa também os cookies de "Lembrar minha senha"?
        if ($cookies) $this->limpaDadosLembrados();
        
        // Retorna SE não há um usuário logado (sem verificar os cookies)
        return !$this->usuarioLogado(false);
    }
    
    /**
     * Salva os dados do usuário em cookies ("Lembrar minha senha")
     * 
     * @access public
     * @since v1.1
     * 
     * @param string $usuario O usuário que será lembrado
     * @param string $senha A senha do usuário
     * @return void
     */
    function lembrarDados($usuario, $senha) {    
        // Calcula o timestamp final para os cookies expirarem
        $tempo = strtotime("+{$this->lembrarTempo} day", time());

        // Encripta os dados do usuário usando base64
        // O rand(1, 9) cria um digito no início da string que impede a descriptografia
        $usuario = rand(1, 9) . base64_encode($usuario);
        $senha = rand(1, 9) . base64_encode($senha);
    
        // Cria um cookie com o usuário
        setcookie($this->prefixoChaves . 'lu', $usuario, $tempo, $this->cookiePath);
        // Cria um cookie com a senha
        setcookie($this->prefixoChaves . 'ls', $senha, $tempo, $this->cookiePath);
    }
    
    /**
     * Verifica os dados do cookie (caso eles existam)
     * 
     * @access public
     * @since v1.1
     * @uses Usuario::logaUsuario()
     * 
     * @return boolean Os dados são validos?
     */
    function verificaDadosLembrados() {
        // Os cookies de "Lembrar minha senha" existem?
        if (isset($_COOKIE[$this->prefixoChaves . 'lu']) AND isset($_COOKIE[$this->prefixoChaves . 'ls'])) {
            // Pega os valores salvos nos cookies removendo o digito e desencriptando
            $usuario = base64_decode(substr($_COOKIE[$this->prefixoChaves . 'lu'], 1));
            $senha = base64_decode(substr($_COOKIE[$this->prefixoChaves . 'ls'], 1));
            
            // Tenta logar o usuário com os dados encontrados nos cookies
            return $this->logaUsuario($usuario, $senha, true);        
        }
        
        // Não há nenhum cookie, dados inválidos
        return false;
    }
    
    /**
     * Limpa os dados lembrados dos cookies ("Lembrar minha senha")
     * 
     * @access public
     * @since v1.1
     * 
     * @return void
     */
    function limpaDadosLembrados() {
        // Deleta o cookie com o usuário
        if (isset($_COOKIE[$this->prefixoChaves . 'lu'])) {
            setcookie($this->prefixoChaves . 'lu', false, (time() - 3600), $this->cookiePath);
            unset($_COOKIE[$this->prefixoChaves . 'lu']);            
        }
        // Deleta o cookie com a senha
        if (isset($_COOKIE[$this->prefixoChaves . 'ls'])) {
            setcookie($this->prefixoChaves . 'ls', false, (time() - 3600), $this->cookiePath);
            unset($_COOKIE[$this->prefixoChaves . 'ls']);            
        }
    }
}

?>