PHP Conectar com PDO

Na aula anterior você viu o que é um banco de dados e como é o SQL. Agora o PHP vai conversar com o banco. Quem faz essa ponte é o PDO (PHP Data Objects, "objetos de dados do PHP"): uma forma única de conectar, mandar SQL e ler respostas, que funciona igual para SQLite, MySQL, PostgreSQL e outros. Nesta aula você vai abrir uma conexão, criar uma tabela, ler os dados de várias formas e entender as mensagens de erro.

Você vai aprender
  • O que é o PDO e o que é um driver.
  • Como conectar com new PDO("sqlite:loja.db") e o que é o DSN.
  • Por que usar PDO::ERRMODE_EXCEPTION e como tratar o erro de conexão.
  • A diferença entre exec (comandos) e query (perguntas).
  • Como ler o resultado com fetch, fetchAll e fetchColumn, e os modos FETCH_ASSOC, FETCH_NUM e FETCH_OBJ.

Os programas foram executados com o PHP 8.5 na máquina fictícia da Ana, na pasta ~/loja. O PDO já vem no PHP; para o SQLite, nada precisa ser instalado. O editor do guia também tem o SQLite, então você pode repetir tudo nas caixas "Experimente".


A ideia: o adaptador de tomada

Quem viaja leva um adaptador universal de tomada: o carregador do celular é sempre o mesmo, e só a pecinha que encaixa na parede muda de país para país. O PDO é esse adaptador:

  • O seu código PHP (o carregador) usa sempre os mesmos comandos: new PDO, query, exec, fetch…
  • O driver (a pecinha) sabe falar com um banco específico: pdo_sqlite para SQLite, pdo_mysql para MySQL, pdo_pgsql para PostgreSQL.

Para ver quais "pecinhas" o seu PHP tem:

$ php -r 'print_r(PDO::getAvailableDrivers());'
Array
(
    [0] => mysql
    [1] => pgsql
    [2] => sqlite
)

A máquina da Ana tem três. Nesta aula usamos o sqlite; o mysql fica para a aula MySQL e MariaDB.


1. Conectar: new PDO

<?php
$pdo = new PDO("sqlite:loja.db");

echo "Conectado!\n";
var_dump($pdo);
new PDO(...)
Cria um objeto PDO: a conexão aberta com o banco. A palavra new cria objetos (você vai estudá-los em Classes e Objetos; por enquanto, pense no objeto como "a linha telefônica com o banco").
"sqlite:loja.db"
O DSN (Data Source Name, "nome da fonte de dados"): um texto que diz qual driver usar (antes dos dois-pontos) e onde está o banco (depois). Aqui: "SQLite, no arquivo loja.db".
$pdo
A variável que guarda a conexão. Tudo o que você fizer com o banco passa por ela: $pdo->query(...), $pdo->exec(...). A seta -> quer dizer "use esta função do objeto".
$ php conectar.php
Conectado!
object(PDO)#1 (0) {          « um objeto da classe PDO: a conexão
}
$ ls -l loja.db
-rw-r--r-- 1 ana ana 0 out 16 21:02 loja.db       « o arquivo foi criado (vazio, por enquanto)

Se o arquivo loja.db não existe, o SQLite o cria na hora, vazio. Isso é prático, mas tem um lado ruim: errar o nome do arquivo não dá erro, só cria outro banco vazio (você vai ver isso em CRUD com PDO).

Os DSNs mais comuns

DSNSignifica
sqlite:loja.dbArquivo loja.db na pasta em que você está ao rodar o programa.
sqlite:/home/ana/loja/loja.dbCaminho completo: sempre o mesmo arquivo.
"sqlite:" . __DIR__ . "/loja.db"O arquivo ao lado do programa, de qualquer pasta que ele seja rodado. A forma recomendada.
sqlite::memory:Um banco na memória, que some quando o programa termina. Ótimo para testes e para as caixas do editor.
mysql:host=localhost;dbname=loja;charset=utf8mb4Um servidor MySQL/MariaDB (só para conhecer; veja MySQL e MariaDB). Usuário e senha vão no 2º e 3º argumentos: new PDO($dsn, "ana", "senha").

2. Comandos: exec

Para mandar um SQL que faz alguma coisa e não devolve linhas (criar tabela, inserir, alterar, apagar), use exec ("executar"):

<?php
$pdo = new PDO("sqlite:loja.db");

$pdo->exec("CREATE TABLE IF NOT EXISTS produtos (
    id INTEGER PRIMARY KEY,
    nome TEXT NOT NULL,
    preco REAL NOT NULL,
    estoque INTEGER NOT NULL
)");

$quantos = $pdo->exec("INSERT INTO produtos (nome, preco, estoque) VALUES
    ('Caderno', 15.90, 30),
    ('Caneta azul', 2.50, 100),
    ('Mochila', 89.90, 5)");

echo "Linhas inseridas: $quantos\n";
$ php montar.php
Linhas inseridas: 3
$ ls -l loja.db
-rw-r--r-- 1 ana ana 8192 out 16 21:02 loja.db       « agora tem tabela e dados
$pdo->exec("CREATE TABLE …")
Cria a tabela (o SQL foi explicado em Banco de Dados: o Básico). O texto pode ocupar várias linhas.
$quantos = $pdo->exec("INSERT …")
O exec devolve quantas linhas o comando afetou: aqui, 3 inseridas.
exec e query só com SQL fixo

Nesta aula, todo SQL é escrito por você, sem nada que venha de fora. Quando um valor vier de um formulário ou do usuário (um nome, um id), não cole o valor no texto do SQL: use prepare e execute, que você vai ver em CRUD com PDO. O motivo (SQL injection) é a aula Prepared Statements.


3. Perguntas: query e fetch

Para um SELECT, que devolve linhas, use query ("consulta"). Ele não devolve as linhas direto: devolve um resultado (um objeto PDOStatement), de onde você busca as linhas com fetch ("buscar", "ir pegar").

<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELECT id, nome, preco FROM produtos");
$linha = $stmt->fetch();
print_r($linha);
$ php ler-uma.php
Array
(
    [id] => 1
    [0] => 1
    [nome] => Caderno
    [1] => Caderno
    [preco] => 15.9
    [2] => 15.9
)
$stmt = $pdo->query(...)
Manda o SELECT. $stmt (de statement, "comando") é o resultado, como uma pilha de fichas prontas para pegar.
$stmt->fetch()
Pega a próxima linha da pilha, como array. Chamando de novo, vem a seguinte. Quando acabam, devolve false.

Repare que cada valor veio duas vezes: pelo nome da coluna ([nome]) e pela posição ([1]). É o modo padrão, PDO::FETCH_BOTH ("os dois"). Repare também nos tipos: o id veio como número inteiro e o preço como número com casas (15.9, sem o zero do fim; para mostrar 15,90, use number_format).

Escolhendo o formato da linha

Você pode dizer ao fetch como quer cada linha:

<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELECT nome, preco FROM produtos WHERE id = 1");
print_r($stmt->fetch(PDO::FETCH_ASSOC));

$stmt = $pdo->query("SELECT nome, preco FROM produtos WHERE id = 1");
print_r($stmt->fetch(PDO::FETCH_NUM));

$stmt = $pdo->query("SELECT nome, preco FROM produtos WHERE id = 1");
$obj = $stmt->fetch(PDO::FETCH_OBJ);
echo $obj->nome, " custa ", $obj->preco, "\n";
$ php modos.php
Array                          « FETCH_ASSOC: chaves com os nomes das colunas
(
    [nome] => Caderno
    [preco] => 15.9
)
Array                          « FETCH_NUM: chaves 0, 1, 2…
(
    [0] => Caderno
    [1] => 15.9
)
Caderno custa 15.9             « FETCH_OBJ: um objeto, lido com ->
ModoA linha vem comoVocê lê comQuando usar
PDO::FETCH_ASSOCArray associativo$linha["nome"]O mais usado; o padrão deste guia
PDO::FETCH_NUMArray indexado$linha[0]Raramente; com [$nome, $preco] = $linha
PDO::FETCH_BOTHOs dois juntos (padrão)$linha["nome"] ou $linha[1]Evite: duplica tudo
PDO::FETCH_OBJObjeto simples$linha->nomeQuem prefere a seta (comum em sistemas prontos)

Experimente: rode e compare. Depois troque PDO::FETCH_ASSOC por PDO::FETCH_NUM, por PDO::FETCH_BOTH e por PDO::FETCH_OBJ (com o FETCH_OBJ, o print_r mostra stdClass Object).

<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, preco REAL)");
$pdo->exec("INSERT INTO produtos (nome, preco) VALUES ('Caderno', 15.90), ('Caneta azul', 2.50)");

$stmt = $pdo->query("SELECT id, nome, preco FROM produtos");
print_r($stmt->fetch(PDO::FETCH_ASSOC));

Todas as linhas: while com fetch

Como o fetch devolve false quando as linhas acabam, ele cabe direitinho num while: "enquanto houver linha, mostre":

<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELECT nome, preco FROM produtos ORDER BY preco");

while ($linha = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $linha["nome"], ": R$ ", number_format($linha["preco"], 2, ",", "."), "\n";
}
$ php listar.php
Caneta azul: R$ 2,50
Caderno: R$ 15,90
Mochila: R$ 89,90

A condição $linha = $stmt->fetch(...) faz duas coisas: guarda a linha em $linha e, ao mesmo tempo, serve de teste. Uma linha (array com dados) conta como verdadeiro; false encerra o laço.

Todas de uma vez: fetchAll

Se você quer todas as linhas num array (para contar, ordenar, passar para outra função), use fetchAll:

<?php
$pdo = new PDO("sqlite:loja.db");

$produtos = $pdo->query("SELECT nome, estoque FROM produtos")->fetchAll(PDO::FETCH_ASSOC);

echo "Quantos: ", count($produtos), "\n";
print_r($produtos);
$ php todas.php
Quantos: 3
Array
(
    [0] => Array
        (
            [nome] => Caderno
            [estoque] => 30
        )

    [1] => Array
        (
            [nome] => Caneta azul
            [estoque] => 100
        )

    [2] => Array
        (
            [nome] => Mochila
            [estoque] => 5
        )

)

Um array de arrays: cada posição é uma linha (veja Arrays Multidimensionais). Repare que query(...)->fetchAll(...) está encadeado: o resultado do query já recebe o fetchAll, sem a variável $stmt no meio.

Um valor só: fetchColumn

Para perguntas com uma resposta só (quantos produtos? qual a soma do estoque?), o fetchColumn devolve direto o valor da primeira coluna da primeira linha:

<?php
$pdo = new PDO("sqlite:loja.db");

$total = $pdo->query("SELECT COUNT(*) FROM produtos")->fetchColumn();
$soma  = $pdo->query("SELECT SUM(estoque) FROM produtos")->fetchColumn();

echo "Produtos: $total\n";
echo "Itens no estoque: $soma\n";
var_dump($total);
$ php contar.php
Produtos: 3
Itens no estoque: 135          « 30 + 100 + 5
int(3)

foreach direto no resultado

O resultado do query também pode ir direto num foreach, que pega uma linha por volta. É a forma mais curta, e você vai vê-la muito:

<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE recados (texto TEXT)");
$pdo->exec("INSERT INTO recados VALUES ('Comprar papel'), ('Ligar para o fornecedor')");

foreach ($pdo->query("SELECT texto FROM recados") as $linha) {
    echo "- ", $linha["texto"], "\n";
}
$ php memoria.php
- Comprar papel
- Ligar para o fornecedor

Este exemplo usa sqlite::memory:: o banco nasce, recebe dados e some quando o programa termina. Nenhum arquivo foi criado. É assim que funcionam as caixas "Experimente" desta aula.

Quero…UseDevolve
Rodar um comando (CREATE, INSERT, UPDATE, DELETE)$pdo->exec($sql)Número de linhas afetadas
Fazer uma pergunta (SELECT)$pdo->query($sql)Um resultado (PDOStatement)
A próxima linha$stmt->fetch()Array (ou objeto); false no fim
Todas as linhas$stmt->fetchAll()Array de linhas (vazio se nenhuma)
Um valor$stmt->fetchColumn()O valor; false se não há linha
Percorrerforeach ($stmt as $linha)Uma linha por volta

Missão: o banco tem 3 produtos. Com exec, insira mais um: 'Lápis', preço 1.20. Depois, com SELECT COUNT(*) e fetchColumn(), mostre:

Total: 4 produtos
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, preco REAL)");
$pdo->exec("INSERT INTO produtos (nome, preco) VALUES
    ('Caderno', 15.90), ('Caneta azul', 2.50), ('Mochila', 89.90)");

// 1. insira o Lápis
// 2. conte os produtos e mostre "Total: N produtos"
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, preco REAL)");
$pdo->exec("INSERT INTO produtos (nome, preco) VALUES
    ('Caderno', 15.90), ('Caneta azul', 2.50), ('Mochila', 89.90)");

$pdo->exec("INSERT INTO produtos (nome, preco) VALUES ('Lápis', 1.20)");

$total = $pdo->query("SELECT COUNT(*) FROM produtos")->fetchColumn();
echo "Total: $total produtos\n";

4. Erros: PDO::ERRMODE_EXCEPTION

O que acontece quando o SQL está errado? Depende do modo de erro da conexão. Desde o PHP 8.0+, o padrão é PDO::ERRMODE_EXCEPTION: todo erro vira uma exceção (veja Exceções), que para o programa com uma mensagem clara, se não for tratada:

<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELECT nome FROM produto");   // faltou o "s"
$lista = $stmt->fetchAll();
echo "Fim do programa\n";
$ php excecao.php

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1 no such table: produto in /home/ana/loja/excecao.php:4
Stack trace:
#0 /home/ana/loja/excecao.php(4): PDO->query('SELECT nome FRO...')
#1 {main}
  thrown in /home/ana/loja/excecao.php on line 4

A mensagem diz o quê (no such table: produto, "não existe a tabela produto") e onde (linha 4, no query). Agora o mesmo programa no modo antigo, PDO::ERRMODE_SILENT ("silencioso"), que era o padrão até o PHP 7:

<?php
$pdo = new PDO("sqlite:loja.db");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);

$stmt = $pdo->query("SELECT nome FROM produto");   // faltou o "s"
var_dump($stmt);
print_r($pdo->errorInfo());

$lista = $stmt->fetchAll();
echo "Fim do programa\n";
$ php silencioso.php
bool(false)                    « o query falhou calado e devolveu false
Array
(
    [0] => HY000
    [1] => 1
    [2] => no such table: produto     « o motivo só aparece se você perguntar
)

Fatal error: Uncaught Error: Call to a member function fetchAll() on false in /home/ana/loja/silencioso.php:9
Stack trace:
#0 {main}
  thrown in /home/ana/loja/silencioso.php on line 9

No modo silencioso, o query não reclama: só devolve false. O motivo fica guardado em errorInfo(), e o programa só quebra depois, na linha 9, com um erro que não fala nada de banco (Call to a member function fetchAll() on false, "chamar a função fetchAll em false"). Imagine procurar o problema na linha 9 quando ele está na linha 5. Por isso: deixe sempre no modo exceção.

Opções na conexão

Mesmo sendo o padrão, é costume escrever o modo de erro na conexão (deixa claro, e protege se o código rodar num PHP antigo). Junto, vale definir o formato padrão das linhas, para não repetir PDO::FETCH_ASSOC em todo fetch. As opções vão num array, no 4º argumento:

<?php
$pdo = new PDO("sqlite:loja.db", null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

foreach ($pdo->query("SELECT nome, estoque FROM produtos") as $linha) {
    echo $linha["nome"], " (", $linha["estoque"], ")\n";
}
$ php opcoes.php
Caderno (30)
Caneta azul (100)
Mochila (5)
null, null
O 2º e o 3º argumentos são usuário e senha. O SQLite não usa, então vão vazios (null).
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
"Atributo modo de erro: exceção."
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
"Formato padrão das linhas: associativo." Agora fetch(), fetchAll() e o foreach já entregam $linha["nome"].

Com argumentos nomeados (PHP 8.0+) dá para pular os dois null: new PDO("sqlite:loja.db", options: [...]). É a forma usada em CRUD com PDO. Depois de conectado, também dá para mudar uma opção com $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) e consultar com getAttribute:

<?php
$pdo = new PDO("sqlite:loja.db");

echo "Driver: ", $pdo->getAttribute(PDO::ATTR_DRIVER_NAME), "\n";
echo "Versão do SQLite: ", $pdo->getAttribute(PDO::ATTR_SERVER_VERSION), "\n";
echo "Modo de erro: ", $pdo->getAttribute(PDO::ATTR_ERRMODE), " (", PDO::ERRMODE_EXCEPTION, " = exceção)\n";
$ php atributos.php
Driver: sqlite
Versão do SQLite: 3.45.2
Modo de erro: 2 (2 = exceção)          « o padrão do PHP 8

O erro de conexão

A conexão também pode falhar: o arquivo está numa pasta que não existe, sem permissão, ou (no MySQL) o servidor está desligado. O new PDO lança uma PDOException, e você decide o que mostrar com try/catch:

<?php
try {
    $pdo = new PDO("sqlite:/dados/loja.db");
    echo "Conectado!\n";
} catch (PDOException $e) {
    echo "Não consegui abrir o banco.\n";
    echo "Detalhe: ", $e->getMessage(), "\n";
    echo "Código: ", $e->getCode(), "\n";
}
$ php tratar.php
Não consegui abrir o banco.
Detalhe: SQLSTATE[HY000] [14] unable to open database file
Código: 14
try { … }
"Tente": o código que pode falhar.
catch (PDOException $e)
"Se der erro de PDO, pegue-o em $e."
$e->getMessage()
A mensagem: unable to open database file ("não consegui abrir o arquivo do banco"). A pasta /dados não existe, e o SQLite não cria pastas, só o arquivo.
$e->getCode()
O código do erro (14, no SQLite, é "não consegui abrir").
Detalhes para você, mensagem simples para o visitante

Num site, não mostre o getMessage() ao visitante: ele pode revelar caminhos de pastas e nomes de servidores. Mostre "O sistema está fora do ar, tente mais tarde" e grave o detalhe num log com error_log($e->getMessage()) (veja Depurar). A aula MySQL e MariaDB mostra isso com as mensagens reais de um servidor que não responde.

Fechar a conexão

A conexão fecha sozinha quando o programa termina (no fim de cada página, num site). Se precisar fechar antes, apague a variável: $pdo = null;.


Quando dá errado

unable to open database file
<?php
$pdo = new PDO("sqlite:/dados/loja.db");
$ php sem-pasta.php

Fatal error: Uncaught PDOException: SQLSTATE[HY000] [14] unable to open database file in /home/ana/loja/sem-pasta.php:2
Stack trace:
#0 /home/ana/loja/sem-pasta.php(2): PDO->__construct('sqlite:/dados/l...')
#1 {main}
  thrown in /home/ana/loja/sem-pasta.php on line 2

O que significa: o SQLite não conseguiu abrir nem criar o arquivo. Causas: a pasta não existe (aqui, /dados), ou o PHP não tem permissão de escrever nela (comum num servidor web, que roda com outro usuário).

Como consertar: confira o caminho; crie a pasta (mkdir); confira as permissões da pasta e do arquivo. Prefira __DIR__ . "/loja.db".

could not find driver
<?php
$pdo = new PDO("sqlite3:loja.db");
$ php driver-errado.php

Fatal error: Uncaught PDOException: could not find driver in /home/ana/loja/driver-errado.php:2
Stack trace:
#0 /home/ana/loja/driver-errado.php(2): PDO->__construct('sqlite3:loja.db')
#1 {main}
  thrown in /home/ana/loja/driver-errado.php on line 2

O que significa: "não encontrei o driver" sqlite3. O prefixo do DSN para SQLite é sqlite:, sem o 3. A mesma mensagem aparece quando o nome está certo mas a extensão não está instalada (como mysql: sem o pdo_mysql).

Como consertar: confira o prefixo e rode php -r 'print_r(PDO::getAvailableDrivers());'.

must be a valid data source name
<?php
$pdo = new PDO("loja.db");
$ php dsn-errado.php

Fatal error: Uncaught PDOException: PDO::__construct(): Argument #1 ($dsn) must be a valid data source name in /home/ana/loja/dsn-errado.php:2
Stack trace:
#0 /home/ana/loja/dsn-errado.php(2): PDO->__construct('loja.db')
#1 {main}
  thrown in /home/ana/loja/dsn-errado.php on line 2

O que significa: "o argumento nº 1 ($dsn) precisa ser um nome de fonte de dados válido". Faltou o driver e os dois-pontos: o PDO recebeu só o nome do arquivo.

Como consertar: new PDO("sqlite:loja.db").

near "SELEC": syntax error
<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELEC nome FROM produtos");
$ php sintaxe.php

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1 near "SELEC": syntax error in /home/ana/loja/sintaxe.php:4
Stack trace:
#0 /home/ana/loja/sintaxe.php(4): PDO->query('SELEC nome FROM...')
#1 {main}
  thrown in /home/ana/loja/sintaxe.php on line 4

O que significa: o PHP está certo; o SQL dentro do texto está errado. O SQLite mostra onde se perdeu: near "SELEC" ("perto de SELEC"). Erros de SQL só aparecem quando o programa roda, porque, para o PHP, o SQL é só um texto.

Como consertar: leia a palavra depois de near e a anterior: o erro está ali. Na dúvida, teste o SQL sozinho, com echo antes do query.

Call to a member function fetchAll() on false

Aparece no modo silencioso (veja silencioso.php, acima): o query falhou, devolveu false, e o programa tentou usar o resultado.

Como consertar: não use ERRMODE_SILENT. Com ERRMODE_EXCEPTION, o erro aparece na linha certa e com o motivo real.

Undefined array key "nome"
<?php
$pdo = new PDO("sqlite:loja.db");

$stmt = $pdo->query("SELECT nome, preco FROM produtos WHERE id = 1");
$linha = $stmt->fetch(PDO::FETCH_NUM);

echo $linha["nome"], "\n";
$ php modo-errado.php

Warning: Undefined array key "nome" in /home/ana/loja/modo-errado.php on line 7

O que significa: "chave de array indefinida: nome". Com FETCH_NUM, a linha tem as chaves 0 e 1, não "nome". O mesmo aviso aparece se o nome da chave não bate com o da coluna ($linha["Nome"], ou uma coluna que não está no SELECT).

Como consertar: use FETCH_ASSOC (ou defina-o como padrão na conexão) e escreva as chaves exatamente como as colunas do SELECT. Na dúvida, print_r($linha).

Missão: este programa deveria listar os produtos do mais barato para o mais caro, mas enche a tela de avisos. Rode, leia o primeiro aviso e corrija (é uma palavra só). A saída correta é:

Caneta azul: R$ 2,50
Caderno: R$ 15,90
Mochila: R$ 89,90
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, preco REAL)");
$pdo->exec("INSERT INTO produtos (nome, preco) VALUES
    ('Caderno', 15.90), ('Caneta azul', 2.50), ('Mochila', 89.90)");

$stmt = $pdo->query("SELECT nome, preco FROM produtos ORDER BY preco");

while ($linha = $stmt->fetch(PDO::FETCH_NUM)) {
    echo $linha["nome"], ": R$ ", number_format($linha["preco"], 2, ",", "."), "\n";
}
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE produtos (id INTEGER PRIMARY KEY, nome TEXT, preco REAL)");
$pdo->exec("INSERT INTO produtos (nome, preco) VALUES
    ('Caderno', 15.90), ('Caneta azul', 2.50), ('Mochila', 89.90)");

$stmt = $pdo->query("SELECT nome, preco FROM produtos ORDER BY preco");

while ($linha = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $linha["nome"], ": R$ ", number_format($linha["preco"], 2, ",", "."), "\n";
}

Referência

CódigoO que faz
new PDO($dsn)Conecta (SQLite)
new PDO($dsn, $usuario, $senha, $opcoes)Conecta com usuário, senha e opções (MySQL e outros)
PDO::getAvailableDrivers()Lista os drivers deste PHP
$pdo->exec($sql)Roda um comando; devolve as linhas afetadas
$pdo->query($sql)Roda um SELECT; devolve um PDOStatement
$stmt->fetch($modo)Próxima linha, ou false
$stmt->fetchAll($modo)Todas as linhas num array
$stmt->fetchColumn()Valor da 1ª coluna da próxima linha
PDO::FETCH_ASSOC / FETCH_NUM / FETCH_BOTH / FETCH_OBJFormato da linha
PDO::ATTR_ERRMODEModo de erro: ERRMODE_EXCEPTION (padrão no PHP 8), ERRMODE_WARNING, ERRMODE_SILENT
PDO::ATTR_DEFAULT_FETCH_MODEFormato padrão das linhas
$pdo->setAttribute($attr, $valor) / getAttribute($attr)Muda / consulta uma opção
$pdo->errorInfo()Detalhes do último erro (útil no modo silencioso)
$e->getMessage(), $e->getCode()Mensagem e código de uma PDOException
$pdo = nullFecha a conexão antes do fim do programa

Exercícios

1. O DSN

Complete para abrir o arquivo escola.db com SQLite:

$pdo = new PDO("");

2. Banco temporário

Complete o DSN para criar um banco SQLite que só existe na memória:

$pdo = new PDO("");

3. Linhas com nome

Complete para cada linha vir como array associativo:

$linha = $stmt->fetch();

4. Qual método usar para CREATE TABLE e INSERT com SQL fixo?

5. O que $stmt->fetch() devolve quando não há mais linhas?

6. Por que preferir PDO::ERRMODE_EXCEPTION a PDO::ERRMODE_SILENT?

7. $pdo->query("SELECT COUNT(*) FROM produtos")->fetchColumn() devolve…


Resumo

  • O PDO é a forma única de o PHP falar com bancos; o driver (pdo_sqlite, pdo_mysql…) sabe falar com cada um.
  • Conecte com new PDO("sqlite:loja.db"). O DSN diz o driver e onde está o banco; sqlite::memory: é um banco temporário.
  • exec para comandos (devolve linhas afetadas); query para SELECT (devolve um resultado).
  • Leia o resultado com fetch (uma linha, false no fim), fetchAll (todas), fetchColumn (um valor) ou foreach.
  • Use PDO::FETCH_ASSOC, de preferência como padrão: PDO::ATTR_DEFAULT_FETCH_MODE.
  • Deixe PDO::ERRMODE_EXCEPTION (o padrão do PHP 8) e trate o erro de conexão com try/catch.
  • Valores que vêm de fora nunca vão colados no SQL: isso é prepare/execute, na próxima aula.

Próxima aula: CRUD com PDO, para cadastrar, listar, atualizar e apagar os produtos da lojinha.