A partir do PHP 5.5, a biblioteca MySQL, do PHP, com funções como mysql_connect e mysql_query é considerada obsoleta. A alternativa mais recomendada é usar a extensão PDO. Porém, muitos programadores ainda não sabem como usar PDO. Vou mostrar neste artigo como é simples usar essa classe.
É muito importante usar PDO, por duas razões em especial:
- Seu código fica mais limpo e moderno;
- É fácil migrar para outro SGBD, caso seja necessário futuramente.
Mas, se você leu este meu artigo, já deve saber disso.
O que é PDO
PDO significa PHP Data Objects (Objetos de Dados do PHP, em tradução livre). Ela é uma classe para gerenciar conexões com bancos de dados. Sim, bancos de dados. Ou seja, é possível usar PDO para conectar com diversos SGBDs, dentre eles MySQL, PostreSQL, SQLite, MS SQL Server, Oracle e outros.
PDO é orientado a objetos
PDO é uma classe. Ela segue o padrão da orientação a objetos. Caso você não conheça absolutamente nada sobre orientação a objetos, recomendo estudar um pouco sobre isso antes de usar PDO.
Não precisa ir muito a fundo. Apenas entenda o que são classes, objetos e métodos. Veja como instanciar objetos e chamar seus métodos. Isso já é o suficiente para trabalhar com PDO.
Recomendo ler estes links:
- Exemplo prático de Orientação a Objetos (php), diferenças e vantagens em relação à um código estruturado
- Introdução à Orientação a Objetos em PHP
Tabela usada para este artigo
Criei uma tabela chamada “programadores”, com ID, nome e site de cada um deles. Utilizei a seguinte SQL para gerar a tabela e seus dados
CREATE TABLE programadores( id INT UNSIGNED NOT NULL AUTO_INCREMENT, nome VARCHAR(80) NOT NULL, site VARCHAR(120) DEFAULT NULL, PRIMARY KEY(id) ); INSERT INTO programadores(nome, site) VALUES ('Beraldo', 'http://rberaldo.com.br'), ('João', NULL), ('Maria', 'http://mariaprogramadora.com.br'), ('José', 'http://joseprogramador.com.br'), ('Linus Torvalds', 'http://kernel.org'), ('Mark Zuckerberg', 'http://facebook.com'), ('Steve Wozniak', 'http://apple.com');
Conexão com MySQL usando PDO
Vamos definir constantes para armazenar os dados de conexão.
define( 'MYSQL_HOST', 'localhost' ); define( 'MYSQL_USER', 'root' ); define( 'MYSQL_PASSWORD', '' ); define( 'MYSQL_DB_NAME', 'pdo_tutorial' );
A conexão é feita pelo construtor da classe PDO. Ou seja, basta passarmos os dados de conexão como parâmetro ao instanciarmos a classe PDO, desta forma:
$PDO = new PDO( 'mysql:host=' . MYSQL_HOST . ';dbname=' . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD );
Isso funciona. Mas caso a conexão falhe, será disparada uma exceção e a execução será interrompida.
Para evitarmos isso, devemos colocar esse trecho em um bloco try…catch e tratar a exceção PDOException. O código ficará assim:
try { $PDO = new PDO( 'mysql:host=' . MYSQL_HOST . ';dbname=' . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD ); } catch ( PDOException $e ) { echo 'Erro ao conectar com o MySQL: ' . $e->getMessage(); }
Executando SELECT com PDO
Tendo a instância da classe PDO na variável $PDO, podemos realizar um simples SELECT desta forma:
$sql = "SELECT * FROM programadores"; $result = $PDO->query( $sql ); $rows = $result->fetchAll(); print_r( $rows );
O método query da classe PDO
executa uma consulta e retorna, no caso do SELECT, um objeto do tipo PDOStatement. Esse objeto possui o método fetchAll, que é semelhante à função mysql_fetch_array, retornando um array com todos os registros. Usamos a função print_r para exibir esse array. A saída é a seguinte:
Array ( [0] => Array ( [id] => 1 [0] => 1 [nome] => Beraldo [1] => Beraldo [site] => http://rberaldo.com.br [2] => http://rberaldo.com.br ) [1] => Array ( [id] => 2 [0] => 2 [nome] => João [1] => João [site] => [2] => ) [2] => Array ( [id] => 3 [0] => 3 [nome] => Maria [1] => Maria [site] => http://mariaprogramadora.com.br [2] => http://mariaprogramadora.com.br ) [3] => Array ( [id] => 4 [0] => 4 [nome] => José [1] => José [site] => http://joseprogramador.com.br [2] => http://joseprogramador.com.br ) [4] => Array ( [id] => 5 [0] => 5 [nome] => Linus Torvalds [1] => Linus Torvalds [site] => http://kernel.org [2] => http://kernel.org ) [5] => Array ( [id] => 6 [0] => 6 [nome] => Mark Zuckerberg [1] => Mark Zuckerberg [site] => http://facebook.com [2] => http://facebook.com ) [6] => Array ( [id] => 7 [0] => 7 [nome] => Steve Wozniak [1] => Steve Wozniak [site] => http://apple.com [2] => http://apple.com ) )
Note que para cada registro, há valores repetidos. Eles aparecem em dois índices: um é o número do campo na tabela e o outro é o nome do campo. Isso ocupa o dobro da memória. Por isso é recomendado retornar apenas o registro com o nome do campo.
Para isso, basta passarmos o parâmetro PDO::FETCH_ASSOC
para o método fetchAll. Dessa forma, esse método funcionará de forma semelhante à função mysql_fetch_assoc, retornando apenas o array associativo com os nomes dos campos.
Vamos alterar apenas a linha que chama o método fetchAll. Ela ficará assim:
$rows = $result->fetchAll( PDO::FETCH_ASSOC );
O array exibido será este:
Array ( [0] => Array ( [id] => 1 [nome] => Beraldo [site] => http://rberaldo.com.br ) [1] => Array ( [id] => 2 [nome] => João [site] => ) [2] => Array ( [id] => 3 [nome] => Maria [site] => http://mariaprogramadora.com.br ) [3] => Array ( [id] => 4 [nome] => José [site] => http://joseprogramador.com.br ) [4] => Array ( [id] => 5 [nome] => Linus Torvalds [site] => http://kernel.org ) [5] => Array ( [id] => 6 [nome] => Mark Zuckerberg [site] => http://facebook.com ) [6] => Array ( [id] => 7 [nome] => Steve Wozniak [site] => http://apple.com ) )
Filtrando consultas
Geralmente, nossos SELECTs possuem a cláusula WHERE, para filtrar os resultados.
Isso é muito fácil de fazer usando PDO.
Vamos considerar a situação seguinte: vamos solicitar, por meio de um formulário, que o usuário defina uma string para usar no filtro do SELECT. A filtragem será feita pelo campo site
da nossa tabela.
$search = $_GET['search']; $sql = "SELECT * FROM programadores WHERE site LIKE '%" . $search . "%'"; $result = $PDO->query( $sql ); $rows = $result->fetchAll( PDO::FETCH_ASSOC );
Vamos supor que o usuário digitou a palavra “programador”. Logo, $_GET[‘search’] será a string “programador”.
O array $rows será este:
Array ( [0] => Array ( [id] => 3 [nome] => Maria [site] => http://mariaprogramadora.com.br ) [1] => Array ( [id] => 4 [nome] => José [site] => http://joseprogramador.com.br ) )
Mas existe uma forma mais eficiente e segura de passar parâmetros para as consultas: usando Prepared Statements.
O que são Prepared Statements e como usá-los
Prepared Statements são úteis para executar uma mesma consulta diversas vezes, com parâmetros distintos, de forma eficiente. Porém, também há outra utilidade: filtragem nativa de consultas, a fim de evitar SQL Injection. Isso dá mais segurança ao seu sistema.
Nesse tipo de consulta, os parâmetros não são enviados diretamente na consulta. Eles são enviados em um “pacote” separado ao servidor MySQL. O servidor, por sua vez, é quem faz a associação entre string SQL e parâmetros.
Em outras palavras, não vamos colocar a variável $search diretamente na consulta.
Nosso código, usando Prepared Statements, ficará assim:
$search = $_GET['search']; $search = '%' . $search . '%'; $sql = "SELECT * FROM programadores WHERE site LIKE :search"; $stmt = $PDO->prepare( $sql ); $stmt->bindParam( ':search', $search ); $result = $stmt->execute();
Veja que nossa query agora possui apenas o valor “:search” na cláusula WHERE. É assim que a consulta chegará para o MySQL. Com o método bindParam nós dizemos que queremos enviar o valor de $search, que será substituído em :search.
Como estamos usando o LIKE, tivemos que adicionar os sinais de percentual (%) na segunda linha manualmente. Se estivéssemos buscando por strings exatas (com o operador =
), não precisaríamos disso.
O array $row será o mesmo do código anterior.
Usando Prepared Statements, seu código estará muito mais seguro.
E é possível definir o tipo do dado, passando o terceiro parâmetro de bindParam. Os valores possíveis são:
- PDO::PARAM_BOOL
- PDO::PARAM_NULL
- PDO::PARAM_INT
- PDO::PARAM_STR (esse é o valor padrão)
- PDO::PARAM_LOB
Esses parâmetros dizem para o MySQL como cada valor deve ser tratado.
Por exemplo, strings devem receber aspas simples ao seu redor. Por outro lado, inteiros não. Por isso é importante passar esses parâmetros, quando o valor não for string.
INSERT, DELETE e UPDATE usando PDO
Esses três comandos serão tratados de forma semelhante.
Vejamos como fazer um INSERT.
$nome = 'Bill Gates'; $site = 'http://microsoft.com'; $sql = "INSERT INTO programadores(nome, site) VALUES(:nome, :site)"; $stmt = $PDO->prepare( $sql ); $stmt->bindParam( ':nome', $nome ); $stmt->bindParam( ':site', $site ); $result = $stmt->execute(); if ( ! $result ) { var_dump( $stmt->errorInfo() ); exit; } echo $stmt->rowCount() . "linhas inseridas";
Vamos considerar que $none e site vêm de um formulário também. Por isso precisamos filtrar esses valores usando Prepared Statements.
Para o UPDATE, vamos considerar que desejamos alterar o valor de site para o nome “Bill Gates”. O código é o seguinte:
$nome = 'Bill Gates'; $site = 'http://ruindows.com.br'; $sql = "UPDATE programadores set site = :site WHERE nome = :nome"; $stmt = $PDO->prepare( $sql ); $stmt->bindParam( ':nome', $nome ); $stmt->bindParam( ':site', $site ); $result = $stmt->execute(); if ( ! $result ) { var_dump( $stmt->errorInfo() ); exit; } echo $stmt->rowCount() . "linhas alteradas";
Já o DELETE ficará assim:
$nome = 'Bill Gates'; $sql = "DELETE FROM programadores WHERE nome = :nome"; $stmt = $PDO->prepare( $sql ); $stmt->bindParam( ':nome', $nome ); $result = $stmt->execute(); if ( ! $result ) { var_dump( $stmt->errorInfo() ); exit; } echo $stmt->rowCount() . "linhas removidas";
Conclusão
Espero que tenha achado útil este artigo. Há muitos outros recursos da extensão PDO. Você pode ler todos eles na Documentação Oficial da Extensão PDO.