() translation by (you can also view the original English article)
Ao querer acessar uma base de dados, nós temos duas opções: MySQLie PDO - Não, MySQL não é opção. Então, o que você deveria saber antes de fazer sua escolha? As diferenças, suporte aos variados gerenciadores de banco de dados, estabilidade e performance são alguns dos pontos levantados nesse artigo.
Resumo
PDO | MySQLi | |
---|---|---|
Suporte a Bancos de Dados | 12 drivers diferentes | Somente MySQL |
API | Orientada a Objetos | Orientada a Objetos + Procedural |
Conexão | Fácil | Fácil |
Parâmetros Nomeados | Sim | Não |
Mapeamento de Objetos | Sim | Sim |
Sentenças Preparadas (lado do cliente) | Sim | Não |
Performance | Rápido | Rápido |
Procedimentos Armazenados | Sim | Sim |
Conexão
Criar uma conexão com uma base de dados é moleza, com ambos:
1 |
// PDO
|
2 |
$pdo = new PDO("mysql:host=servidor;dbname=banco_de_dados", 'usuario', 'senha'); |
3 |
|
4 |
// MySQLi, forma procedural
|
5 |
$mysqli = mysqli_connect('servidor', 'usuario', 'senha', 'banco_de_dados'); |
6 |
|
7 |
// MySQLi, forma via orientação a objetos
|
8 |
$mysqli = new mysqli('servidor', 'usuario', 'senha', 'banco_de_dados'); |
Por favor, considere que esses objetos de conexões/recursos estejam presentes durante o resto desse tutorial.
Suporte de API
Tanto PDO quanto MySQli oferecem um API orientada a objetos, mas o MySQLi também oferece uma API procedural - facilitando o entendimento por parte dos novatos. Se você já é familiarizado com o driver nativo do MySQl para PHP, migrar para a interface procedural do MySQLi será extremamente simples. Por outro lado, uma vez que você compreender a PDO, você poderá usá-la com qualquer banco de dados que desejar!
Suporte a Bancos de Dados
A principal vantagem do PDO sobre o MySQLi é seu suporte aos drivers de diversos bancos de dados. No momento em que traduzo esse artigo, oPDO dá suporte a 12 tipos diferents de dirvers, enquanto o MySQLi só dá suporte ao MySQL.
Caso queira saber quais os drivers que o PDO dá suporte no momento, use o código a seguir:
1 |
var_dump(PDO::getAvailableDrivers()); |
O que isso significa? Bem, ~em situações que você precisar usar outro banco de dados, a PDO torna esse processo transparente. Então, a única coisa que você precisará fazer é mudar os parâmetros de conexão e algumas consultas &ndash isso se elas usarem algum método que não seja suportado por outra base de dados. Já com o MySQLi, você precisará reescrever todo o código que lida com banco de dados – inclusive as consultas.
Parâmetros Nomeados
Essa é outra característica importante que o PDO tem; vincular parâmetros com PDO é muito mais fácil usando a vinculação nomeada:
1 |
$params = [':usuario' => 'teste', ':email' => $email => ':ultimo_login' => time() - 3600]; |
2 |
|
3 |
$pdo->prepare('SELECT * FROM users WHERE username = :usuario AND email = :email AND las_login = :ultimo_login'); |
4 |
|
5 |
$pdo->execute($params); |
Ao contrário da maneira do MySQLi:
1 |
$query = $mysqli->prepare('SELECT * FROM users WHERE username = ? AND email = ? AND lasT_login = ?'); |
2 |
|
3 |
$query->bind_param('teste', $mail, time() - 3600); |
4 |
$query->execute(); |
A vinculação dos parâmetros na forma de interrogação pode parecer menor, mas, nem de longe, é tão flexível quanto aos parâmetros nomeados, devido ao fato do desenvolvedor ser obrigado a saber a ordem dos parâmetros. Ela parece um hack feito para permitir vinculação de parâmetros em algumas circunstâncias.
Infelizmente, MySQLi não dá suporte a parâmetros nomeados.
Mapeamento de Objetos
Tanto PDO quanto MySQLi podem mapear resultados em objetos. Isso vem bem a calhar se você não quer usar uma camada de abstração de banco de dados customizada, mas ainda quer um comportamento semelhante ao de um ORM. Vamos imaginar que nós temos uma classeUser
com algumas propriedades nomeadas de forma idêntica aos campos da tabela do Banco de Dados:
1 |
class User { |
2 |
public $id; |
3 |
public $first_name; |
4 |
public $last_name; |
5 |
|
6 |
public function info() |
7 |
{
|
8 |
return "#" . $this->id . ": " . $this->first_name . " " . $this->last_name; |
9 |
}
|
10 |
}
|
Sem o mapeamento de objetos, nós precisaríamos preencher cada um dos campos (fosse manualmente ou através de um método construtor), anes de podermos usar o método info()
corretamente.
Isso permite que nós predefinamos as propriedades antes mesmo do objeto ser construído! Por exemplo:
1 |
$query = "SELECT id, first_name, last_name FROM users"; |
2 |
|
3 |
// PDO
|
4 |
$result = $pdo->query($query); |
5 |
$result->setFetchMode(PDO::FETCH_CLASS, 'User'); |
6 |
|
7 |
while ($user = $result->fetch()) { |
8 |
echo $user->info() . "\n"; |
9 |
}
|
10 |
|
11 |
// MySQLi, forma procedural
|
12 |
if ($result = mysqli_query($mysqli, $query)) { |
13 |
while ($user = mysqli_fetch_object($result, 'User')) { |
14 |
echo $user->info() . "\n"; |
15 |
}
|
16 |
}
|
17 |
|
18 |
// MySQLi, forma orientada a objetos
|
19 |
if ($result = $mysqli->query($query)) { |
20 |
while ($user = $result->fetch_object('User')) { |
21 |
echo $user->info() . "\n"; |
22 |
}
|
23 |
}
|
Segurança
Ambas as bibliotecas proveem segurança contra injeção de SQL, desde que o desenvolvedor as use da maneira que elas foram planejadas para usar (leia-se: limpando variáveis, vinculando parâmetros com sentenças preparadas).
Digamos que um hacker esteja tentando injetar algum código malicioso através de um parâmetro 'username' de uma requisição HTTP GET:
1 |
$_GET['username'] = "'; DELETE FROM users; /*" |
Se nós falharmos em limpá-lo, ele será incluído na consulta exatamente como está &ndash e deletará todas linhas da tabela users
(tanto PDO quanto MySQLi suporta múltiplas consultas).
1 |
// PDO, limpeza manual
|
2 |
$username = PDO::quote($_GET['username']); |
3 |
|
4 |
$pdo->query("SELECT * FROM users WHERE username = $username"); |
5 |
|
6 |
// MySQLi, limpeza manual
|
7 |
$username = mysqli_real_escape_string($_GET['username']); |
8 |
|
9 |
$mysqli->query("SELECT * FROM users WHERE username = '$username'"); |
Como pode ver, PDO::quote()
não só valida, mas fixa as aspas. Por outro lado, mysqli_real_escape_string()
só limpará, forçando você a fixar as aspas manualmente.
1 |
// PDO com sentenças preparadas
|
2 |
$pdo->prepare('SELECT * FROM users WHERE username = :username'); |
3 |
$pdo->execute([':username' => $_GET['username']]); |
4 |
|
5 |
// MySQLi, sentenças preparadas
|
6 |
$query = $mysqli->prepare('SELECT * FROM users WHERE username = ?'); |
7 |
$query->bind_param('s', $_GET['username']); |
8 |
$query->execute(); |
Recomendo que você sempre use sentenças preparadas com consultas vinculadas ao invés da
PDO::quote()
e damysqli_real_scape_string()
.
Performance
Tanto PDO quanto MySQLi são bem rápidas, porém, MySQLi se sai, insignificantemente, mais rápida nos comparativos *ndash; ~2,5% para sentenças não preparadas e ~6,5% para as sentenças preparadas. Porém, a extensão nativa do MySQL é ainda mais rápida que ambas. Mas, como afirmei no começo, ela não é mais uma opção.
Conclusão
No fim das contas, PDO ganha essa, e fácil. Com suporte a 12 drivers de bases de dados diferentes e parâmetros nomeados, nós podemos ignorar a pequena perda de performance que ela tem, comparada à MySQLi. Com relação à segurança, ambas são seguras, desde que o desenvolvedor saiba usá-las da forma correta.
Então, se você ainda está usando MySQLi, talvez seja hora de mudar!
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!