Guia de programação para os drivers Microsoft para PHP para SQL Server

Baixar o driver PHP

Depois de ter uma conexão funcionando no Getting started, use este guia para escrever, fazer commit e ler uma linha com qualquer uma das APIs do PHP. Cada programa inclui sua própria configuração e finalização. Use os links de tarefas para estender o fluxo de trabalho, em vez de copiar opções de uma API para outra.

Prepare uma conexão de teste

Use um banco de dados de teste do SQL Server existente e um login que possa se conectar e criar tabelas temporárias locais. Os exemplos usam autenticação SQL. Para opções de autenticação suportadas e onboarding sem senha do Azure, veja Conectando-se ao servidor.

Carregue a extensão que você escolher, defina SQL_SERVER, SQL_DATABASE, SQL_USER, e SQL_PASSWORD no ambiente do processo PHP. SQL_SERVER pode incluir uma porta explícita, como <server>,1433. O servidor deve apresentar um certificado em que o cliente confia e válido para esse endpoint. Mantenha as senhas fora do programa e do controle de versão.

Armazene o nome real do banco de dados, nome de usuário e senha nas variáveis de ambiente. Cada programa dobra as chaves de fechamento e envolve esses valores em um único par de chaves para o manuseio da conexão no estilo ODBC do driver PHP. Escaping preserva caracteres especiais nos valores de conexão; não é parametrização de consulta. Para mais informações, veja Conectando ao servidor.

As instruções Transact-SQL (T-SQL) criam #PhpGuide, uma tabela temporária visível apenas para a conexão que a cria. Os programas não criam nem alteram uma tabela de aplicação permanente. Mantenha a mesma conexão durante todo o fluxo de trabalho.

Escreva e leia um registro

Os programas inserem um inteiro, texto UTF-8 e SQL NULL através de parâmetros. Eles exigem exatamente uma linha afetada, fazem commit na transação e verificam os valores retornados e os tipos de PHP. A consulta final também verifica se nenhuma transação permanece aberta.

SQLSRV

Salve este programa completo como workflow-sqlsrv.php, e execute php workflow-sqlsrv.php.

<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $value = getenv($name);
    if ($value === false || $value === '') {
        throw new RuntimeException("Set $name before running this example.");
    }
    $config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$conn = sqlsrv_connect($config['SQL_SERVER'], [
    'Database' => $config['SQL_DATABASE'],
    'UID' => $config['SQL_USER'],
    'PWD' => $config['SQL_PASSWORD'],
    'Encrypt' => true,
    'TrustServerCertificate' => false,
    'LoginTimeout' => 30,
    'CharacterSet' => 'UTF-8',
]);
if ($conn === false) {
    throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$stmt = null;
$inTransaction = false;
$failure = null;
try {
    $stmt = sqlsrv_query($conn, 'CREATE TABLE #PhpGuide '
        . '(Id int PRIMARY KEY, Title nvarchar(80), OptionalCount int NULL)');
    if ($stmt === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    sqlsrv_free_stmt($stmt);
    $stmt = null;
    if (!sqlsrv_begin_transaction($conn)) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    $inTransaction = true;
    $id = 1;
    $title = "Caf\u{00E9}";
    $optionalCount = null;
    $params = [
        [&$id, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INT, SQLSRV_SQLTYPE_INT],
        [&$title, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('UTF-8'), SQLSRV_SQLTYPE_NVARCHAR(80)],
        [&$optionalCount, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INT, SQLSRV_SQLTYPE_INT],
    ];
    $stmt = sqlsrv_prepare($conn,
        'INSERT INTO #PhpGuide (Id, Title, OptionalCount) VALUES (?, ?, ?)', $params);
    if ($stmt === false || !sqlsrv_execute($stmt)) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    $affected = sqlsrv_rows_affected($stmt);
    if ($affected === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    if ($affected !== 1) {
        throw new RuntimeException('Expected exactly one inserted row.');
    }
    sqlsrv_free_stmt($stmt);
    $stmt = null;
    if (!sqlsrv_commit($conn)) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    $inTransaction = false;
    $stmt = sqlsrv_query($conn,
        'SELECT Id, Title, OptionalCount, @@TRANCOUNT AS TransactionCount FROM #PhpGuide');
    if ($stmt === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
    if ($row === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    if ($row !== ['Id' => 1, 'Title' => $title, 'OptionalCount' => null, 'TransactionCount' => 0]) {
        throw new RuntimeException('Unexpected committed values, types, or transaction state.');
    }
    $end = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
    if ($end === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    if ($end !== null) {
        throw new RuntimeException('Expected exactly one result row.');
    }
} catch (Throwable $error) {
    $failure = $error;
    if ($inTransaction && !sqlsrv_rollback($conn)) {
        $failure = new RuntimeException(
            'Rollback failed: ' . print_r(sqlsrv_errors(), true), 0, $error);
    }
    throw $failure;
} finally {
    if (is_resource($stmt)) {
        sqlsrv_free_stmt($stmt);
    }
    try {
        $cleanup = sqlsrv_query($conn, 'DROP TABLE IF EXISTS #PhpGuide');
        if ($cleanup === false) {
            throw new RuntimeException(
                'Cleanup failed: ' . print_r(sqlsrv_errors(), true), 0, $failure);
        }
        sqlsrv_free_stmt($cleanup);
    } finally {
        sqlsrv_close($conn);
    }
}
echo "Inserted 1 row; read committed UTF-8 text and null; no open transaction.", PHP_EOL;

PDO_SQLSRV

Salve esse programa separado como workflow-pdo.php, e execute php workflow-pdo.php.

<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $value = getenv($name);
    if ($value === false || $value === '') {
        throw new RuntimeException("Set $name before running this example.");
    }
    $config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$server = $config['SQL_SERVER'];
$database = $config['SQL_DATABASE'];
$pdo = new PDO(
    "sqlsrv:Server=$server;Database=$database;Encrypt=true;"
    . "TrustServerCertificate=false;LoginTimeout=30",
    $config['SQL_USER'],
    $config['SQL_PASSWORD'],
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
        PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE => true,
    ]
);
$stmt = null;
$failure = null;
try {
    $pdo->exec('CREATE TABLE #PhpGuide '
        . '(Id int PRIMARY KEY, Title nvarchar(80), OptionalCount int NULL)');
    $pdo->beginTransaction();
    $title = "Caf\u{00E9}";
    $stmt = $pdo->prepare(
        'INSERT INTO #PhpGuide (Id, Title, OptionalCount) VALUES (?, ?, ?)');
    $stmt->bindValue(1, 1, PDO::PARAM_INT);
    $stmt->bindValue(2, $title, PDO::PARAM_STR);
    $stmt->bindValue(3, null, PDO::PARAM_NULL);
    $stmt->execute();
    if ($stmt->rowCount() !== 1) {
        throw new RuntimeException('Expected exactly one inserted row.');
    }
    $stmt = null;
    $pdo->commit();
    $stmt = $pdo->query(
        'SELECT Id, Title, OptionalCount, @@TRANCOUNT AS TransactionCount FROM #PhpGuide');
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($row !== ['Id' => 1, 'Title' => $title, 'OptionalCount' => null, 'TransactionCount' => 0]) {
        throw new RuntimeException('Unexpected committed values, types, or transaction state.');
    }
    if ($stmt->fetch(PDO::FETCH_ASSOC) !== false || $pdo->inTransaction()) {
        throw new RuntimeException('Expected one result row and no open transaction.');
    }
} catch (Throwable $error) {
    $failure = $error;
    if ($pdo->inTransaction()) {
        try {
            $pdo->rollBack();
        } catch (PDOException $rollbackError) {
            $failure = new RuntimeException(
                'Rollback failed: ' . $rollbackError->getMessage(), 0, $error);
        }
    }
    throw $failure;
} finally {
    $stmt = null;
    try {
        $pdo->exec('DROP TABLE IF EXISTS #PhpGuide');
    } catch (PDOException $cleanupError) {
        throw new RuntimeException(
            'Cleanup failed: ' . $cleanupError->getMessage(), 0, $failure ?? $cleanupError);
    } finally {
        $pdo = null;
    }
}
echo "Inserted 1 row; read committed UTF-8 text and null; no open transaction.", PHP_EOL;

Saída esperada para qualquer um dos programas:

Inserted 1 row; read committed UTF-8 text and null; no open transaction.

A saída só aparece depois que as verificações e a limpeza forem concluídas com êxito. Uma falha de conexão, escrita, busca, rollback ou limpeza de tabela gera uma exceção.

Entenda as escolhas

Use os marcadores de posição ? para valores em vez de concatenar os dados de entrada em T-SQL. Os exemplos mantêm os nomes das tabelas e colunas fixos: um parâmetro de valor não pode substituir um identificador. Instruções preparadas também permitem executar a mesma declaração novamente com valores diferentes. Para mais informações, veja Comparar funções de execução de consultas.

Opção Reason
Métodos explícitos de transação O exemplo trata a escrita como uma unidade que deve ou confirmar ou desfazer. SQLSRV usa sqlsrv_begin_transaction, sqlsrv_commit, e sqlsrv_rollback; PDO usa seus próprios métodos de transação.
Descritores de parâmetros SQLSRV Cada descritor especifica direção, tipo PHP e tipo SQL. As referências mantêm as variáveis de entrada, incluindo a variável que contém null.
PDO bindValue A instrução recebe cada valor atual com um tipo explícito de parâmetro PDO. bindParam é apropriado quando execuções posteriores precisam ler uma variável por referência.
UTF-8 mais nvarchar PHP fornece bytes UTF-8; a extensão e o driver ODBC os convertem para a coluna SQL Unicode. O texto buscado deve corresponder à string PHP original byte por byte.
Busca numérica explícita para PDO A comparação de resultados espera inteiros em PHP. Sem o atributo PDO_SQLSRV, esses resultados inteiros SQL usam a representação padrão do driver como cadeia de caracteres.
Chaves de busca nomeadas SQLSRV_FETCH_ASSOC e PDO::FETCH_ASSOC retorna arrays indexados pelo nome da coluna. As constantes da API não são intercambiáveis.
PDO::exec para configuração da tabela Ele executa diretamente a instrução de criação de tabela. Essa abordagem evita criar uma tabela temporária dentro do escopo de uma instrução preparada pelo servidor.
Resultados somente de avanço Os programas leem cada linha uma vez. Eles não solicitam um resultado bufferizado nem usam uma contagem de linhas de SELECT como prova de que a busca foi bem-sucedida.

O SQLSRV retorna null quando seu array fetch chega ao fim e false em um erro de fetch. O fetch do PDO retorna false no final; com o modo de exceção configurado, um erro de busca do banco de dados lança uma exceção. Em ambas as APIs, o OptionalCount campo em si contém PHP null. Esse valor de campo não é um marcador de fim de resultados.

A operação de gravação faz commits antes da consulta de verificação. Se uma leitura posterior falhar, a gravação confirmada anterior não pode ser revertida. Neste exemplo, a linha pertence apenas a uma tabela temporária que a limpeza remove. Em uma aplicação com tabelas permanentes, reporte a falha com precisão e não repita um insert cegamente após um resultado de commit incerto.

Lidar com falhas e limpeza de recursos

O bloco catch reverte uma transação ativa e relança a exceção. O bloco finally libera a instrução, remove a tabela temporária e libera a conexão. A limpeza é executada no caminho de sucesso e no caminho de falha. Se o rollback ou a limpeza de tabelas também falharem, o programa preserva a exceção original como uma exceção anterior.

Para um exercício de linha de comando, uma exceção não capturada interrompe o PHP com um código de saída diferente de zero e uma mensagem de erro. Em uma aplicação web, capture isso no código que gerencia a solicitação. Remova valores sensíveis antes de registrar diagnósticos no seu registro de aplicação e retorne uma resposta de erro apropriada. Não exponha detalhes de conexão, credenciais ou erros brutos do banco de dados ao navegador.

Failure O que verificar
A função de extensão ou o driver está ausente Confirme que o processo PHP carrega a extensão pretendida com o runtime e a configuração correspondentes. Uma configuração PHP funcional em linha de comando não prova que seu servidor web o utiliza.
Falha de conexão ou de certificado Verifique o endpoint, autenticação, permissões e a confiança do certificado do cliente antes de mudar o código da consulta. Não desative a validação de certificados como solução de produção.
A tabela temporária não foi encontrada Mantenha o estabelecimento, as gravações e as leituras em uma única conexão. Para a configuração do PDO, mantenha a abordagem demonstrada exec em vez de substituir por uma chamada preparada query padrão.
Valor inesperado ou tipo PHP Verifique atributos de fetch e descritores de parâmetros. Não substitua asserções estritas por comparações flexíveis para esconder a diferença.
Falha ao gravar ou reverter Capture imediatamente os erros do SQLSRV ou capture a exceção do PDO. Reporte falhas de rollback também; não diga que a escrita foi desfeita se o rollback falhou.
Falha na limpeza após perda de conexão O programa não consegue confirmar a desconexão no servidor. Liberar os handles PHP não prova que a conexão física subjacente do pool fechou imediatamente.

As extensões PHP fornecem as APIs de binding e fetch. O ODBC se comunica com o servidor e gerencia o pooling de conexões. O SQL Server controla o escopo de tabela temporária, transações, restrições e dados confirmados. Exclua a tabela explicitamente em vez de depender do fechamento da conexão física para a limpeza.

Depois do exercício, exclua workflow-sqlsrv.php e workflow-pdo.php se não precisar mais. Elimine as variáveis do ambiente de credencial do processo de teste quando terminar. Nenhum dos programas deixa uma tabela permanente para remover.

Esses guias passo a passo têm como alvo o SQL Server. Para Banco de Dados SQL do Azure, Instância Gerenciada de SQL do Azure, Azure Synapse Analytics, banco de dados SQL no Fabric e Warehouse no Fabric, primeiro verifique a autenticação do destino e o suporte a T-SQL. Ambos os produtos Fabric exigem autenticação do Microsoft Entra ID, e o Warehouse requer que múltiplos conjuntos de resultados ativos (MARS) sejam desativados. Esses programas do SQL Server não estabelecem suporte específico do serviço a transações ou a tabelas temporárias.

Escolha sua próxima tarefa

Estenda o fluxo de trabalho com o item para o comportamento que você precisa.

Tarefa Guidance
Escolha autenticação ou gerencie conexões Conectando ao servidor.
Reutilize uma instrução preparada com valores diferentes Compare as funções de execução de consultas.
Escolha a execução direta ou preparada do PDO Execução direta da declaração e execução da declaração preparada no driver PDO_SQLSRV.
Buscar valores e escolher formas de resultado Recuperando dados.
Role ou coloque um resultado em buffer Especificando um tipo de cursor e selecionando linhas.
Alterar dados do aplicativo Atualizando dados.
Controlar conversões de tipos de PHP e SQL Convertendo tipos de dados.
Diagnosticar falhas e habilitar o registro de drivers Tratamento de erros e avisos e atividade de logging.
Proteger colunas sensíveis Usando Always Encrypted com os drivers Microsoft para PHP para SQL Server.
Proteja uma aplicação em produção Considerações de segurança para os drivers Microsoft para PHP para SQL Server.