Archive

Posts Tagged ‘instância’

Uma classe PHP-MySQL usando mysqli:__constructor e __destructor

 

Introdução

Programar é uma tarefa difícil. Difícil e trabalhosa. É uma arte, que exige experiência, dedicação, conhecimento, habilidade, criatividade, etc. O presente texto mostra, aos interessados, como preparar uma classe para manipular tabelas em bases de dados.

Há três recomendações para tornar a difícil arte, menos traumática:

  1. Refinamentos sucessivos
  2. Programação estruturada
  3. Programação Orientada a Objetos

Além das recomendações acima, a disciplina e o conhecimento das técnicas associadas a linguagens específicas são fundamentais. E as ferramentas, finalmente. Para base de dados uso o MySQL Workench e o MySQL Administator. Para editar os programas em PHP, uso o PHPDezigner e o Enterprise Architect para modelar meus projetos de desenvolvimento, em UML (Unified Modeling Language). Uso UML, também, para modelagem de negócios e modelagem de topologias de redes, de longa distância ou complexas, sobre as quais aplica-se os protocolos BGP, OSPF, RIP e associados (MPLS, por exemplo). A UML é ótima para modelar, inclusive, os componentes dinâmicos envolvidos em tais protocolos. Falo sobre tais ferramentas não porque sejam as melhores. As melhores ferramentas são aquelas com as quais você projeta e desenvolve seu estilo, de forma gratificante.

O principal objetivo deste trabalho é o desenvolvimento de programação orientada a objetos, em PHP. Usaremos o método direto, onde a didática tem valor significativo. Não é um texto para programadores muito experientes. É um texto para os iniciantes ou aqueles experientes interessados no aperfeiçoamento pessoal.

Construção da classe

Inicio as classes, usualmente, com três componentes: a propriedade (variável) que irá identificar o número de instâncias abertas, e os métodos construtor e destrutor. O método construtor conta as instâncias. E, um pequeno programa serve para testar cada etapa do processo de refinamento sucessivo. Eis a primeira versão, abaixo:

<?php
// Versão 1
class BancodeDados extends mysqli {
    
    static $idBancodeDados = 0;
    
    /**
     * Construtor:
     * 
     * 1. Conta o número de instâncias.
    */
    
    public function __construct() {
        self::$idBancodeDados++;        
    }
    
    public function __destruct() {
        --self::$idBancodeDados;
    }
}

Eis o teste e respectivo resultado:

<?php
// Versão 1
require_once ('../includes/funcoesgerais.phpm');
require_once ('../includes/db.phpm');

$db_faleok = new db();
echo "
Instâncias abertas: " . BancodeDados::$idBancodeDados; $db_faleok1 = new db(); echo "
Instâncias abertas: " . BancodeDados::$idBancodeDados; ?> Instâncias abertas: 1 Instâncias abertas: 2

O interessante a observar acima são os seguintes pontos:

  • A propriedade estática $idBancodeDados é incrementada no método __construtor, que como sabemos é executado na criação de uma instância da classe BancodeDados.
  • Uma propriedade estática é referenciada externamente como NomedaClasse::$NomedaPropriedade, pois ela pertence à própria classe.
  • A classe BancodeDados, herda métodos e funções da classe msqli, do PHP, uma vez que estamos usando extends mysqli, na sua declaração.
  • É bom lembrar, que o método __constructor não pode retornar valores. Esta noção será muito útil mais à frente. Entretanto pode-se observar que o exemplo acima ele altera o valor de uma propriedade estática, isto é, $idBancodeDados, que é como se fosse uma variável global.
  • O trecho:
    /**
     * Construtor:
     *
     * 1. Conta o número de instâncias.
    */
    

    é a documentação da classe. Como uso o phpdoc esta tarefa torna-se, em particular, muito importante.

Não se pode esquecer do registro de atividades. Foi construída uma classe chamada Mensagens que possui dois métodos. Um método exibe uma mensagem na tela, como acima, e o outro, grava a mensagem em um arquivo de “log”. Embora a classe Mensagens não seja exibida aqui (assim como outras que não a classe BancodeDados), o contexto a deixa claro. Eis como fica a versão 2 e seu resultado:

<?php
$db_faleok1 = new BancodeDados();
Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

$db_faleok2 = new BancodeDados();
$msg->Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

?>

20120121 11:55:43 - Instâncias abertas: 1
20120121 11:55:43 - Instâncias abertas: 2

As observações sobre a Versão 2 do teste são:

  • Aperfeiçoamos o nome das instâncias da classe BancodeDados, dando mais consistência visual ($db_faleok1 e $db_faleok2).
  • A mensagem é exibida com a data. Fica mais claro o exemplo, além do fato de que elas estão sendo registradas no “log”, para eventuais acessos futuros.

Melhor ficaria, se a saída fosse aperfeiçoada, como a versão 3 do teste:

<?php
Mensagem = "Teste da classe db, Versão 3";
$msg->Exibe();

$db_faleok1 = new BancodeDados();
$msg->Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

$db_faleok2 = new BancodeDados();
$msg->Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

?>

20120121 12:09:30 - Teste da classe db, Versão 3
20120121 12:09:30 - Instâncias abertas: 1
20120121 12:09:30 - Instâncias abertas: 2

O método direto exige algumas anotações sobre a Versão 3 do teste:

  • Retiramos o comentário // Teste x do início e deixamos somente na mensagem a ser exibida na tela. Evitamos duas alterações de versões.
  • A mensagem da versão só é exibida na tela. Não é registrada no “log”.
  • Já percebe-se que o método construtor executa, imediatamente, ao se definir uma instância da classe. Mas, não temos noção do momento no qual o método destrutor é executado. Geralmente é depois que o programa principal termina (no nosso caso, o testegeral.php), ou logo depois da execução de algumas instruções após a última instrução que atua sobre a instância de uma classe.

A fim de fixar bem a questão do destrutor, abaixo está a versão 2, da classe, na qual adicionamos comentários e alteramos o método __destruct incluindo dois comandos. O primeiro, subtraindo 1 da propriedade estática $idInstanciaDB, pois ela representa o número de instâncias. Destruir significa, também, não possuir nenhuma instância! Incluímos uma saída na tela, para imprimir o momento da destruição e somente mostramos os trechos importantes:

<?php

      .
      .
      .
class db extends mysqli {
    
    static $idBancodeDados = 0;
      .
      .
      .    
    public function __construct() {
        self::$idBancodeDados++;
    }
    
    public function __destruct() {
        --self::$idBancodeDados;
        echo '
Executou a destrutora as ' . date("Ymd H:i:s"); } } Mensagem = "Teste da classe db, Versão 4"; $msg->Exibe(); $db_faleok1 = new BancodeDados(); $msg->Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados; $msg->Exibe(); $msg->LogFaleOK(); $db_faleok2 = new BancodeDados(); $msg->Mensagem = "Instâncias abertas: " . BancodeDados::$idBancodeDados; $msg->Exibe(); $msg->LogFaleOK(); ?> 20120121 17:19:01 - Teste da classe db, Versão 4 20120121 17:19:01 - Instâncias abertas: 1 20120121 17:19:01 - Instâncias abertas: 2 Executou a destrutora as 20120121 17:19:01 Executou a destrutora as 20120121 17:19:01

Os comentários sobre o código acima:

  • Duas instâncias foram abertas, portanto, duas execuções de destrutores.
  • Os métodos destrutores foram executados em algum momento após o término do programa.

A versão final, completa e bem ilustrativa seria:

<?php

/**
 *  @author     JB 
 *  @version    2.0 (última revisão: Janeiro 18, 2012)
 *  @copyright  (c) 2005-2012 Julião Braga
 *  @license    No licence
 *  @package    Banco de dados
 *
 * 
 * Classe BandodeDados:
 * 
 */

class BancodeDados extends mysqli {
    
    static $idBancodeDados = 0;
    
    public $nome_da_base;
    
    /**
     * Construtor:
     * 
     * 
    */
    
    public function __construct() {
        self::$idBancodeDados++;        
    }
    
    public function __destruct() {
        --self::$idBancodeDados;    
    }
}

?>

Mensagem = "Teste da classe BancodeDados, Versão 4";
$msg->Exibe();

$db_faleok1 = new BancodeDados();
$db_faleok2 = new BancodeDados();

$msg->Mensagem = "1 Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

$db_faleok1 = NULL;

$msg->Mensagem = "2 Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();

$db_faleok2 = NULL;

$msg->Mensagem = "3 Instâncias abertas: " . BancodeDados::$idBancodeDados;
$msg->Exibe();
$msg->LogFaleOK();
?>

20120122 14:20:25 - Teste da classe db, Versão 4
20120122 14:20:25 - 1 Instâncias abertas: 2
20120122 14:20:25 - 2 Instâncias abertas: 1
20120122 14:20:25 - 3 Instâncias abertas: 0

Eis as observações sobre o código acima:

  • A forma de executar o destrutor, para uma determinada instância da classe é: $nome_da_Instância->NULL;.
  • Executar ou não o destrutor é uma decisão do programador. Se o programador gosta de exercer o controle total sobre seu código, então ele mesmo destrói.

 

O próximo método

Uma questão aparece, imediatamente está relacionada com o velho dilema: “quem veio primeiro? O ovo ou a galinha? No nosso contexto:

  1. As informações sobre Bases de Dados remotas, geralmente são repassadas e há alguma dificuldade para alterar a senha original. Mas, o desejável, sob o ponto de vista da segurança é que a senha não seja conhecida nem mesmo pelo usuário da base. Em outras palavras, não se deseja ver a senha ou, na melhor das hipóteses, o menor número de pessoas deveriam ver a senha.
  2. Neste sentido, a senha pode ser complicada e deverá ser gerada automaticamente pela classe e alterada automaticamente pela classe, preferencialmente.
  3. Na mesma linha de raciocínio, as informações deveriam ser incluídas pela classe, devidamente criptografada, como é o desejo inicial.

Assim, o próximo método a ser criado é o insereDB, cujo objetivo é inserir na base as informações que serão passadas pelo usuário da classe. Eis o cabeçalho de tal método:

public function insereBD($nomeBD, $usuarioBD, $senhaBD, $hostBD, $portaBD, $descricaoBD, $email_criadorBD) {

// corpo do método
}

Os primeiros cinco parâmetros são autoexplicativos. O parâmetro $descricaoDB é uma descrição sucinta da base de dados e o parâmetro $email_criadorBD identifica o e-mail do “dono” da base, que será usado, para contato automático, em oportunidades sensíveis à segurança. O código abaixo é o refinamento preliminar, que antecederia o tratamento dos dados entrantes e a inclusão na tabela:

class BancodeDados extends mysqli {
    
    static $idBancodeDados = 0;
    static $BancodeDadosAbertos = array();
    
    private $_cripto;
    
    private $_caracteres;
    private $_maximo;
    private $_passwd;
    private $_nome; 
    private $_usuario;
    private $_senha;
    private $_host;
    private $_porta;
    private $_descricao;
    private $_email_criador;
    private $_senha_alternativa;
    private $_senha_alternativaBD;
    
    private $_bd;
    
    /**
     * Construtor:
     * 
     * 1. Verifica se o banco de dados já existe
     * 2. Se não existir cria, sem popular
     * 3. Se existir, abre
     * 4. {@usa: throw new DBConnectException($this->connect_error, $this->connect_errno);}
     *    para cobrir exceções
     * 
    */
    
    public function __construct() {
        self::$idBancodeDados++; 
                       
    }
    
    /**
        *   @author      JB 
        *   @version     1.0 (última revisão: Janeiro 18, 2012)
        *   @copyright   (c) 2005 - 2012 Julião Braga
        *   @license     No licence
        *   @package     funcoesgerais.phpn
        *   @nome        criaDB($nomeBD) 
        *   @funcao      Cria dados criptografados na tabela `basesdedados` da base faleok
        * 
        *   @Colunas:
        *       id_bd: INT
        *       nome_bd: VARCHAR
        *       usuario_bd: VARCHAR
        *       senha_bd: VARCHAR
        *       host_bd: VARCHAR
        *       porta_bd: VARCHAR
        *       descricao_bd: VARCHAR
        *       email_criador_bd: VARCHAR
        *       senha_alternativa: VARCHAR
        * 
    */
    public function insereBD($nomeBD, 
                             $usuarioBD, 
                             $senhaBD, 
                             $hostBD, 
                             $portaBD, 
                             $descricaoBD, 
                             $email_criadorBD) {
        
        $this->_cripto        = new cripto; 
        
        $this->_caracteres = 'abcdxywzABCDZYWZ0123456789@#*'; 

        $this->_maximo = strlen($this->_caracteres)-1;  
        $this->passwd = null; 
        mt_srand(make_seed()); 
        for($i=0; $i passwd .= $this->_caracteres{mt_rand(0, $this->_maximo)}; 
        }
        $this->_senha_alternativaBD = $this->passwd;
        
        $this->_nome          = $this->_cripto->encrypt($nomeBD); 
        $this->_usuario       = $this->_cripto->encrypt($usuarioBD);
        $this->_senha         = $this->_cripto->encrypt($senhaBD);
        //$this->_senha_alternativaBD = base_convert(mt_rand() , 10 , 16);
        $this->_host          = $this->_cripto->encrypt($hostBD);
        $this->_porta         = $this->_cripto->encrypt($portaBD);
        $this->_descricao     = $this->_cripto->encrypt($descricaoBD);
        $this->_email_criador = $this->_cripto->encrypt($email_criadorBD);
        $this->_senha_alternativa = $this->_cripto->encrypt($this->_senha_alternativaBD);
        
        // Tratamento dos dados
        
        echo "Aqui entra o código para imprimir o resultado apresentado na figura, logo abaixo";
            
        self::$BancodeDadosAbertos[self::$idBancodeDados] = $this->_cripto->decrypt($this->_nome);
        echo '
Indice do vetor de BancodeDadosAbertos: ' . self::$idBancodeDados . "...pre..."; print_r(self::$BancodeDadosAbertos); echo ".../pre..."; return true; } /** * * listaBDs() */ public function listaBDs() { return $bds; } public function __destruct() { --self::$idBancodeDados; //$this->_bd->close(); } } function make_seed() { list($usec, $sec) = explode(' ', microtime()); return (float) $sec + ((float) $usec * 100000); } <?php require_once ($_SERVER['DOCUMENT_ROOT'] . "faleok/includes/funcoesgerais.phpm"); $bd_faleok = new BancodeDados(); $nomeBD = "nomedoBD"; $usuarioBD = "usuarioautorizado"; $senhaBD = "teste"; $hostBD = "127.0.0.1"; $portaBD = "3306"; $descricaoBD = "BD de teste"; $email_criadorBD = 'usuario@exemplo.com.br'; echo '
Retorno do método; ' . $bd_faleok->insereBD($nomeBD, $usuarioBD, $senhaBD, $hostBD, $portaBD, $descricaoBD, $email_criadorBD); ?>

O resultado do programa acima pode ser visto na figura abaixo:


 

Os comentários são os seguintes:

  1. Uma outra propriedade estática foi incluída motivada pelo interesse em saber, para efeitos de gerenciamento e eficiência, quais as bases estão abertas em um determinado instante. É um “array” e sua atribuição é feita dentro do método insereBD:
    static $BancodeDadosAbertos = array();
    

    Já é possível antever um método listaDB, lembrado no códito, que informaria quais as bases abertas até o momento.

  2. Insistentemente, declaramos propriedades. Sempre que possível escondendo informações para outros componentes (encapsulamento), através do uso de private.
  3. A senha alternativa, isto é, aquela desconhecida de todos é gerada aleatoriamente, e com 15 caracteres, já que o ser humano não a manipula. Os algoritmos para isto estão disponíveis na Internet. Para aumentar a dificuldade, além dos 15 caracteres foi seguida a sugestão de usar um “alimentador” (‘make_seed’), para a função ‘mt_rand’, disponível no sítio do PHP. A função ‘make_seed’ está isolada no programa principal para, oportunamente, definir seu destino.