Construindo uma classe em PHP para conexão com Banco de Dados

Ao se construir uma aplicação ou site em PHP que se comunique com algum banco de dados, uma classe de banco de dados de fácil uso e compreensão pode simplificar bastante o nosso trabalho.

Nesse artigo mostrarei como construir uma classe da banco de dados, baseada na classe PDO (nativa do PHP), capaz de fazer múltiplas conexões simultâneas e com suporte aos principais gerenciadores de banco de dados usados hoje em dia.

Requisitos

Como iremos usar a classe PDO do PHP na implementação de uma segunda classe, precisaremos que as extensões php_pdo_mssql, php_pdo_mysql, php_pdo_oci, php_pdo_pgsql, php_pdo_sqlite estejam ativas nas configurações do seu servidor PHP.

Para facilitar o entendimento do código abaixo, aconselho a leitura da documentação das classes PDO e Exception no site www.php.net.

1º Passo – Criando uma classe de configuração

Antes de mais nada, precisaremos construir uma classe para guardar as configurações de cada conexão, como por exemplo, endereço do banco, tipo do banco, porta, nome do banco, usuário e senha.

class DDConnectSettings
{
	const MYSQL = 1;
	const PGSQL = 2;
	const MSSQL = 3;
	const ORACLE = 4;
	const SQLite = 5;
	
	private $f_application;
	private $st_host;
	private $in_port;
	private $st_dbase;
	private $st_user;
	private $st_password;
	
	/**
	* define o gerenciador de banco de dados 
	* @param int $f_aplication
	* @example $ob->setAplication(DDConnectSettings::MYSQL);
	*	Use
	* 	DDConnectSettings::MYSQL - para MySQL
	* 	DDConnectSettings::PGSQL - para PostgreSQL
	* 	DDConnectSettings::MSSQL - para Microsoft SQL Server
	* 	DDConnectSettings::ORACLE - para Oracle
	*	DDConnectSettings::SQLite - para SQLLite
	*/
	public function setApplication($f_aplication)
	{
		$this->f_application = $f_aplication;
	}
	
	/**
	* retorna qual o gerenciador de banco de dados definido 
	* @return int
	*/
	public function getApplication()
	{
		return $this->f_application;
	}
	
	/**
	* define endereço (IP ou domínio) do servidor
	* @param string $st_host
	*/
	public function setHost($st_host)
	{
		$this->st_host = $st_host;
	}
	
	/**
	* retorna o endereço do servidor definido 
	* @return string
	*/
	public function getHost()
	{
		return $this->st_host;
	}
	
	/**
	* define a porta a ser usada na conexão
	* @param int $in_port
	*/
	public function setPort($in_port)
	{
		$this->in_port = $in_port;
	}
	
	/**
	* retorna a porta definida
	* @return int
	*/
	public function getPort()
	{
		return $this->in_port;
	}
	
	/**
	* define o nome do banco de dados ou arquivo
	* @param string $st_dbase
	*/
	public function setDatabase($st_dbase)
	{
		$this->st_dbase = $st_dbase;
	}

	/**
	* retorna o nome do banco de dados ou arquivo definido
	* @return string
	*/
	public function getDatabase()
	{
		return $this->st_dbase;
	}
	
	/**
	* define o nome do usuário a ser usado em uma conexão protegida
	* @param string $st_user
	*/
	public function setUser($st_user)
	{
		$this->st_user = $st_user;
	}
	
	/**
	* retorna o nome do usuário definido no caso de uma conexão pretegida
	* @return string
	*/
	public function getUser()
	{
		return $this->st_user;
	}
	
	/**
	* define a senha de usuário a ser usado em uma conexão protegida
	* @param string $st_password
	*/
	public function setPassword($st_password)
	{
		$this->st_password = $st_password;
	}
	
	/**
	* retorna a senha de usuário definido no caso de uma conexão pretegida
	* @return string
	*/
	public function getPassword()
	{
		return $this->st_password;
	}
}

2º Passo – Criando uma classe de tratamento de erros

Também precisaremos de uma classe de tratamento de erros e para que ela funcione adequadamente, deverá ser herdeira da classe Exception (também nativa do PHP).

class DDDException extends Exception
{
	
}

Você deve estar se perguntando “Por que não usar a classe Exception, já que a classe herdeira não tem nada implementado?”. Minha resposta é “Se em algum momento quisermos adicionar algum controle de erros ainda não implementado na classe Exception, basta modificar a classe DDDException e tudo será resolvido”.

3º Passo – Implementando classe de dados

Esta classe de dados será responsável por encapsular os dados retornados na consulta SQL, como por exemplo, quantidade de linhas, colunas e dados propriamente ditos.

class DDData
{
	const OBJECT = 1;
	const ARRAY_ASSOC = 2;
	const ARRAY_NUM = 3;
	const ARRAY_BOTH = 4;	
	
	private $o_PDOStatment;	
	
	/**
	* Retorna dados de uma consulta SQL
	*  
	* @param int $f_tout - Usada para formatar a saída dos dados
	* @example $ob->getData(DDData::ARRAY_NUM)
	* 	Use
	* 	DDData::ARRAY_NUM - para retornar um array multidimensional de índices numéricos
	*	DDData::OBJECT - para retornar um array contendo como valores, objetos stdClass com os dados de cada linha
	*	DDData::ARRAY_ASSOC - para retornar um array multidimensional contendo como índices os nomes dos campos
	*   DDData::ARRAY_BOTH - para retornar um array contendo índices numericos e os nomes dos campos
	* @return array
	* @throws DDDException
	*/
	public function getData($f_tout = self::OBJECT)
	{
		if(!is_a($this->o_PDOStatment,'PDOStatement'))
			throw new DDDException('Nothing to return');
		
		if($this->o_PDOStatment->columnCount() > 0)
		{
			$v_return = array();
		
			try
			{
				switch($f_tout)
				{
					case self::OBJECT:
						while($o_line = $this->o_PDOStatment->fetchObject())
							array_push($v_return, $o_line);
					break;
					
					case self::ARRAY_ASSOC:
						$v_return = $this->o_PDOStatment->fetchAll(PDO::FETCH_ASSOC);
					break;
					
					case self::ARRAY_NUM;
						$v_return = $this->o_PDOStatment->fetchAll(PDO::FETCH_NUM);
					break;
					
					case self::ARRAY_BOTH;
						$v_return = $this->o_PDOStatment->fetchAll(PDO::FETCH_BOTH);
					break;
				}
			}
			catch(PDOException $e)
			{
				throw  new DDDException($e->getMessage());
			}	
		}
		else
			$v_return = FALSE;
		return $v_return;
	}
	
	/**
	* Recebe um objeto da classe PDOStatment,
	* este objeto será usado apenas pela classe DDDatabase
	* @param PDOStatement $o_PDOStatment
	*/
	public function setData(PDOStatement $o_PDOStatment)
	{
		$this->o_PDOStatment = $o_PDOStatment;
	}
	
	/**
	* Retorna o numero de linhas geradas na consulta SQL
	* @throws DDDException
	* @return integer
	*/
	public function getNRows()
	{
		if(!is_a($this->o_PDOStatment,'PDOStatement'))
			throw new DDDException('Nothing to return');
		
		return $this->o_PDOStatment->rowCount();
	}
		
	/**
	* Retorma o numero de colunas geradas na consulta SQL
	* @return integer
	* @throws DDDException
	*/
	public function getNCols()
	{
		if(!is_a($this->o_PDOStatment,'PDOStatement'))
			throw new DDDException('Nothing to return');
		
		return $this->o_PDOStatment->columnCount();
	}
}

4º Passo – Implementando a classe principal

A classe principal será responsavel por administrar as conexões e as requisições feitas por consultas SQL

class DDDatabase
{
	private $v_connections;
	private $v_database;
	
	/**
	* Estabele conexão com o gerenciador de banco de dados
	* @param string $st_database - Alias definido pelo desenvolvedor para uma conexão
	* @param DDConnectSettings $o_DDCSettings - Objeto contendo as configurações de uma conexão
	* @throws DDDException
	*/
	public function setConnectSettings($st_database, DDConnectSettings $o_DDCSettings)
	{
		$v_database[$st_database] = $o_DDCSettings;
		
		$st_dbname = $o_DDCSettings->getDatabase();
		
		switch($o_DDCSettings->getApplication())
		{
			case DDConnectSettings::MYSQL:
				$st_dsn = "mysql:dbname=$st_dbname";
			break;
		
			case DDConnectSettings::PGSQL:
				$st_dsn = "pgsql:dbname=$st_dbname";
			break;
		
			case DDConnectSettings::MSSQL:
				$st_dsn = "mssql:dbname=$st_dbname";
			break;
		
			case DDConnectSettings::ORACLE:
				$st_dsn = "oci:dbname=$st_dbname";
			break;

			case DDConnectSettings::SQLite:
				$st_dsn = "sqlite:$st_dbname";
			break;
			
			default:
				throw new DDDException('Invalid drive'); 
			break;
		}
		
		$st_host = $o_DDCSettings->getHost();
		$st_username = $o_DDCSettings->getUser();
		$st_password = $o_DDCSettings->getPassword();
		$in_port = $o_DDCSettings->getPort();
		
		try
		{
			if(isset($st_host))
			{
				$st_dsn .= ";host=$st_host";
				if(isset($in_port))
					$st_dsn .= ";port=$in_port";
			}
			
			
			if(isset($st_username) && isset($st_password))
			{
				$this->v_connections[$st_database] = new PDO($st_dsn, $st_username, $st_password );
			}	
			else
			{
				$this->v_connections[$st_database] = new PDO($st_dsn);
			}	
				
			$this->v_connections[$st_database]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		}
		catch (PDOException $e)
		{
			throw new DDDException($e->getMessage());
		}	
	}
	
	/**
	* Retorna um objeto contendo os dados das configurações de uma conexão
	* @param string $st_database - Alias defindo pelo desenvolvedor para a conexão em questão
	*/
	public function getConnectSettings($st_database)
	{
		return $this->v_database[$st_database];
	}
	
	/**
	* Executa uma consulta SQL, retornando uma instância da classe DDData
	* @param string $st_database - Alias da conexão que desenvolvedor deseja usar
	* @param  string $st_query - Consulta SQL
	* @return DDData
	* @throws DDDException
	*/
	public function execQuery($st_database,$st_query)
	{
		try 
		{
			$v_row = $this->v_connections[$st_database]->query($st_query);		
			$o_DDData = new DDData();
			$o_DDData->setData($v_row);
		}
		catch (PDOException $e)
		{
			throw new DDDException($e->getMessage());
		}
		return $o_DDData;
	}
}

5º Passo – Exemplo de como usar a classe

//Setando as configurações da conexão
$o_dbconfig = new DDConnectSettings();
$o_dbconfig->setApplication(DDConnectSettings::PGSQL);
$o_dbconfig->setHost('localhost');
$o_dbconfig->setUser('xxx');
$o_dbconfig->setPassword('xxx');
$o_dbconfig->setDatabase('digitaldev');
$o_dbconfig->setPort(5432);

//Instanciando a classe de banco
$o_db = new DDDatabase();
$o_db->setConnectSettings('ddev', $o_dbconfig);
$st_query = "SELECT * FROM control.tbl_usuario";
$o_data = $o_db->execQuery('ddev', $st_query);

//imprimindo dados
var_dump($o_data->getData(DDData::OBJECT));

//imprimindo numero de linhas
echo 'Linhas = '.$o_data->getNRows();

//imprimindo numero de colunas
echo 'Colunas = '.$o_data->getNCols();

Faça download de um outro exemplo de uso, dessa com SQLite, clicando aqui.

Apesar de poder ser usada na maioria das interações com banco de dados em uma aplicação PHP, essa classe ainda não possui métodos que permitem controle de transições. Fica a encargo dos desenvolvedores continuar a implementação de acordo com as necessidades de suas aplicações.

Quem quiser usar a classe, pode fazer o download da mesma clicando aqui.

Qualquer dúvida, usem a área de respostas abaixo do post.

Artigos relacionados
Classe, encapsulamento, herança, composição e agregação
O que é PHP e qual é sua historia?
Instalando o ambiente de desenvolvimento PHP (PHP 5.3.x + Apache + Eclipse)

5 comentários

1 menção

Pular para o formulário de comentário

    • Jose Pereira em 2 de agosto de 2012 às 22:44
    • Responder

    Tarcisio, boa noite/tarde.

    Trabalho como analista de negocio e estou cursando TI. Gostaria de sua opnião, pois trabalho no ramo saude onde o detalhamento é muito complexo.
    Tenho interesse em fazer um curso de programação, pois meu objetivo é o desenvolvimento e também a modelagem.
    Vc pode me indicar qual linguagem seria mais interessante no meu caso ???

    Obrigado.

      • Somário Canuto em 21 de fevereiro de 2013 às 10:28
      • Responder

      Jose Pereira, qualquer curso de programação deve te dar a mais importante base que é algoritmo, ou lógica de programação como alguns chamam, é aonde você realmente aprende a programar a linguagem a ser usada fica muito a critério das necessidades, uma das linguagens mais utilizadas para internet é a PHP para desktop a briga é grande entre Delphi e Java que também briga com PHP, se for seguir tendências aprenda JAVA.

    • Renan em 24 de abril de 2013 às 8:24
    • Responder

    Voce esta no caminho certo, apesar de ter muitos erros de quebra de padroes
    mas com o tempo voce vai melhorando e aprendendo com os proprios erros

    vou citar alguns:
    os dados de conexao nao é uma classe, nao se herda dados de conexao
    essas informações voce poderia colocar num xml, ou num arquivo php
    sua DDData tem um nome muito estranho dificil de entender e esta engessada, voce ja criou ela pra usar com PDO
    nao precisa reinventar a roda criando metodos como self::OBJECT

    setConnectSettings em fez de fazer aquela salada voce poderia ter criado adapters
    da uma estudada em Registry, Strategy, PSR-0,1,2 , Responsibility etc

    1. Obrigado pelas dicas Renan, conheço bem os padrões de projeto citados, só optei por não usar.
      No nome DDData, os dois primeiros “D” se referem ao nome do blog DigitalDev.
      Se você quiser mostrar como se faz, escrevendo sua própria classe ou adaptando a já existente, o DigitalDev está aberto à postar seu texto / código e, é claro, atribuindo os créditos a você.

      1. poderia demonstrar como essa classe mostra os valores dos campos da query solicitada?

  1. […] Construindo uma classe em PHP para conexão com Banco de Dados SQL Injection, como evitar esse tipo de ataque em páginas PHP CompartilharFacebook […]

Deixe uma dúvida, resposta ou sugestão

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.