O PHP oferece três APIs diferentes para se conectar ao MySQL. Estes são os mysql
(removidos a partir do PHP 7) mysqli
e as PDO
extensões.
As mysql_*
funções costumavam ser muito populares, mas seu uso não é mais incentivado. A equipe de documentação está discutindo a situação de segurança do banco de dados e educar os usuários a se afastarem da extensão ext / mysql comumente usada faz parte disso (verifique em php.internals: deprecating ext / mysql ).
E a equipe de desenvolvedores PHP mais tarde tomou a decisão para gerar E_DEPRECATED
erros quando os usuários se conectar ao MySQL, seja por meio de mysql_connect()
, mysql_pconnect()
ou a funcionalidade de conexão implícita incorporado ext/mysql
.
ext/mysql
foi oficialmente descontinuado a partir do PHP 5.5 e foi removido a partir do PHP 7 .
Veja a caixa vermelha?
Quando você acessa qualquer mysql_*
página do manual de funções, vê uma caixa vermelha explicando que não deve mais ser usada.
Por quê
Afastar- ext/mysql
se não é apenas segurança, mas também acesso a todos os recursos do banco de dados MySQL.
ext/mysql
foi desenvolvido para o MySQL 3.23 e só recebeu muito poucas adições desde então, mantendo principalmente a compatibilidade com esta versão antiga, o que dificulta a manutenção do código. Recursos ausentes que não são suportados por ext/mysql
incluem: ( do manual do PHP ).
Razão para não usar a mysql_*
função :
- Não está em desenvolvimento ativo
- Removido a partir do PHP 7
- Falta uma interface OO
- Não suporta consultas assíncronas e sem bloqueio
- Não suporta instruções preparadas ou consultas parametrizadas
- Não suporta procedimentos armazenados
- Não suporta múltiplas instruções
- Não suporta transações
- Não suporta todas as funcionalidades do MySQL 5.1
Ponto acima citado na resposta de Quentin
A falta de suporte para instruções preparadas é particularmente importante, pois elas fornecem um método mais claro e menos propenso a erros de escapar e citar dados externos do que escapar manualmente com uma chamada de função separada.
Veja a comparação de extensões SQL .
Suprimindo avisos de descontinuação
Enquanto o código está sendo convertido para MySQLi
/ PDO
, os E_DEPRECATED
erros podem ser suprimidos configurando-se error_reporting
no php.ini para excluirE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Observe que isso também ocultará outros avisos de descontinuação , que, no entanto, podem ser para outras coisas que não o MySQL. ( do manual do PHP )
O artigo DOP vs. MySQLi: Qual você deve usar? por Dejan Marjanovic irá ajudá-lo a escolher.
E uma maneira melhor é PDO
, e agora estou escrevendo um PDO
tutorial simples .
Um tutorial DOP simples e curto
P. A primeira pergunta em minha mente foi: o que é DOP?
R. “O PDO - PHP Data Objects - é uma camada de acesso ao banco de dados que fornece um método uniforme de acesso a vários bancos de dados.”
Conectando ao MySQL
Com mysql_*
function ou podemos dizer da maneira antiga (descontinuado no PHP 5.5 e superior)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Com PDO
: Tudo que você precisa fazer é criar um novo PDO
objeto. O construtor aceita parâmetros para especificar o PDO
construtor da fonte de banco de dados geralmente usa quatro parâmetros que são DSN
(nome da fonte de dados) e username
, opcionalmente password
,.
Aqui eu acho que você está familiarizado com tudo, exceto DSN
; isso é novo no PDO
. A DSN
é basicamente uma série de opções que informam PDO
qual driver usar e detalhes da conexão. Para referência adicional, consulte o PDO MySQL DSN .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Nota: você também pode usar charset=UTF-8
, mas às vezes causa um erro, por isso é melhor usá-lo utf8
.
Se houver algum erro de conexão, ele lançará um PDOException
objeto que pode ser capturado para ser manuseado Exception
ainda mais.
Boa leitura : Gerenciamento de conexões e conexões ¶
Você também pode passar várias opções de driver como uma matriz para o quarto parâmetro. Eu recomendo passar o parâmetro que coloca PDO
no modo de exceção. Como alguns PDO
drivers não suportam instruções preparadas nativas, também PDO
executa a emulação da preparação. Ele também permite que você ative manualmente essa emulação. Para usar as instruções preparadas do lado do servidor nativo, você deve defini-lo explicitamente false
.
O outro é desativar a emulação de preparação, que é ativada no MySQL
driver por padrão, mas a emulação de preparação deve ser desativada para uso PDO
com segurança.
Mais tarde explicarei por que a emulação de preparação deve ser desativada. Para encontrar o motivo, verifique este post .
Só é utilizável se você estiver usando uma versão antiga da MySQL
qual eu não recomendo.
Abaixo está um exemplo de como você pode fazer isso:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Podemos definir atributos após a construção da DOP?
Sim , também podemos definir alguns atributos após a construção do PDO com o setAttribute
método:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Manipulação de erros
O tratamento de erros é muito mais fácil do PDO
que mysql_*
.
Uma prática comum ao usar mysql_*
é:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
não é uma boa maneira de lidar com o erro, pois não podemos lidar com isso die
. Ele terminará abruptamente o script e ecoará o erro na tela que você normalmente NÃO deseja mostrar aos usuários finais, e permitirá que hackers descubram o seu esquema. Como alternativa, os valores de retorno das mysql_*
funções geralmente podem ser usados em conjunto com mysql_error () para manipular erros.
PDO
oferece uma solução melhor: exceções. Tudo o que fazemos com PDO
deve ser envolto em um try
- catch
bloco. Podemos forçar PDO
um dos três modos de erro, definindo o atributo do modo de erro. Três modos de tratamento de erros estão abaixo.
PDO::ERRMODE_SILENT
. É apenas definir códigos de erro e agir da mesma maneira mysql_*
que você deve verificar cada resultado e depois ver $db->errorInfo();
os detalhes do erro.
PDO::ERRMODE_WARNING
Levante E_WARNING
. (Avisos em tempo de execução (erros não fatais). A execução do script não é interrompida.)
PDO::ERRMODE_EXCEPTION
: Lança exceções. Representa um erro gerado pelo PDO. Você não deve lançar um PDOException
do seu próprio código. Veja Exceções para obter mais informações sobre exceções no PHP. Ele age muito parecido or die(mysql_error());
quando não é pego. Mas or die()
, diferentemente , o PDOException
pode ser capturado e manuseado normalmente, se você optar por fazê-lo.
Boa leitura :
Gostar:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
E você pode envolvê-lo try
- catch
como abaixo:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
Você não tem que lidar com try
- catch
agora. Você pode capturá-lo a qualquer momento apropriado, mas eu recomendo fortemente que você use try
- catch
. Também pode fazer mais sentido capturá-lo fora da função que chama o PDO
material:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
Além disso, você pode lidar com or die()
isso ou podemos dizer o mesmo mysql_*
, mas será realmente variado. Você pode ocultar as mensagens de erro perigosas na produção girando display_errors off
e apenas lendo seu log de erros.
Agora, depois de ler todas as coisas acima, você provavelmente está pensando: o que diabos é que, quando eu só quero começar inclinando-se simples SELECT
, INSERT
, UPDATE
, ou DELETE
declarações? Não se preocupe, aqui vamos nós:
Selecionando dados
Então, o que você está fazendo mysql_*
é:
<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());
$num_rows = mysql_num_rows($result);
while($row = mysql_fetch_assoc($result)) {
echo $row['field1'];
}
Agora PDO
, você pode fazer o seguinte:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
Ou
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Nota : Se você estiver usando o método como abaixo ( query()
), esse método retornará um PDOStatement
objeto. Então, se você deseja buscar o resultado, use-o como acima.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
No PDO Data, ele é obtido através do ->fetch()
método de manipulação de instruções. Antes de chamar a busca, a melhor abordagem seria dizer à DOP como você deseja que os dados sejam buscados. Na seção abaixo, estou explicando isso.
Modos de busca
Observe o uso de PDO::FETCH_ASSOC
no código fetch()
e fetchAll()
acima. Isso informa PDO
para retornar as linhas como uma matriz associativa com os nomes dos campos como chaves. Também existem muitos outros modos de busca, os quais explicarei um por um.
Antes de tudo, explico como selecionar o modo de busca:
$stmt->fetch(PDO::FETCH_ASSOC)
No acima, eu tenho usado fetch()
. Você também pode usar:
Agora chego ao modo de busca:
PDO::FETCH_ASSOC
: retorna uma matriz indexada pelo nome da coluna, conforme retornado no seu conjunto de resultados
PDO::FETCH_BOTH
(padrão): retorna uma matriz indexada pelo nome da coluna e pelo número da coluna indexada 0, conforme retornado no seu conjunto de resultados
Existem ainda mais opções! Leia sobre todos eles na PDOStatement
documentação do Fetch. .
Obtendo a contagem de linhas :
Em vez de usar mysql_num_rows
para obter o número de linhas retornadas, você pode obter um PDOStatement
e faça rowCount()
, como:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Obtendo o último ID inserido
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Inserir e atualizar ou excluir instruções
O que estamos fazendo em mysql_*
função é:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
E no pdo, a mesma coisa pode ser feita por:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
Na consulta acima, PDO::exec
execute uma instrução SQL e retorne o número de linhas afetadas.
Inserir e excluir serão abordados mais tarde.
O método acima é útil apenas quando você não está usando variáveis na consulta. Mas quando você precisar usar uma variável em uma consulta, nunca tente como o descrito acima e existe a instrução preparada ou a instrução parametrizada .
Declarações Preparadas
P. O que é uma declaração preparada e por que preciso deles?
R. Uma instrução preparada é uma instrução SQL pré-compilada que pode ser executada várias vezes enviando apenas os dados para o servidor.
O fluxo de trabalho típico do uso de uma instrução preparada é o seguinte ( citado na Wikipedia em três pontos ):
Preparar : o modelo de instrução é criado pelo aplicativo e enviado ao sistema de gerenciamento de banco de dados (DBMS). Certos valores são deixados não especificados, chamados parâmetros, espaços reservados ou variáveis de ligação (identificadas ?
abaixo):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
O DBMS analisa, compila e executa a otimização de consulta no modelo de instrução e armazena o resultado sem executá-lo.
- Executar : posteriormente, o aplicativo fornece (ou vincula) valores para os parâmetros e o DBMS executa a instrução (possivelmente retornando um resultado). O aplicativo pode executar a instrução quantas vezes quiser com valores diferentes. Neste exemplo, ele pode fornecer 'Bread' para o primeiro parâmetro e
1.00
para o segundo parâmetro.
Você pode usar uma instrução preparada incluindo espaços reservados no seu SQL. Existem basicamente três sem espaços reservados (não tente isso com a variável acima de um), um com espaços reservados sem nome e outro com espaços reservados nomeados.
P. Então, agora, quais são os espaços reservados nomeados e como os uso?
A. Espaços reservados nomeados. Use nomes descritivos precedidos por dois pontos, em vez de pontos de interrogação. Não nos preocupamos com a posição / ordem do valor no nome do local:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Você também pode vincular usando uma matriz de execução:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Outro recurso interessante para os OOP
amigos é que os espaços reservados nomeados têm a capacidade de inserir objetos diretamente no seu banco de dados, assumindo que as propriedades correspondam aos campos nomeados. Por exemplo:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
P. Então, agora, o que são espaços reservados sem nome e como os uso?
A. Vamos dar um exemplo:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
e
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
No acima, você pode vê-los em ?
vez de um nome, como em um marcador de lugar. Agora, no primeiro exemplo, atribuímos variáveis aos vários espaços reservados ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Em seguida, atribuímos valores a esses espaços reservados e executamos a declaração. No segundo exemplo, o primeiro elemento da matriz vai para o primeiro ?
e o segundo para o segundo ?
.
NOTA : Nos espaços reservados não nomeados , devemos cuidar da ordem correta dos elementos na matriz que estamos passando para o PDOStatement::execute()
método.
SELECT
, INSERT
, UPDATE
, DELETE
Preparado consultas
SELECT
:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
NOTA:
No entanto PDO
e / ou MySQLi
não são completamente seguros. Verifique a resposta As instruções preparadas pelo DOP são suficientes para impedir a injeção de SQL? por ircmaxell . Além disso, estou citando parte da resposta dele:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));