Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Depois de teres uma ligação a funcionar desde o Getting Started, usa este guia para escrever, fazer commit e ler uma linha com uma das duas APIs PHP. Cada programa inclui a sua própria configuração e limpeza. Utilize os links de tarefas para estender o fluxo de trabalho em vez de copiar opções de uma API para outra.
Prepare uma ligação de teste
Use uma base de dados de teste do SQL Server existente e um início de sessão que possa ligar e criar tabelas temporárias locais. Os exemplos utilizam autenticação SQL. Para escolhas de autenticação suportadas e integração do Azure sem palavra-passe, veja Ligação ao servidor.
Carrega a extensão que escolheres, define 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 confie e válido para esse endpoint. Mantém as palavras-passe fora do programa e do controlo de versão.
Armazene o nome real da base de dados, nome de utilizador e palavra-passe nas variáveis de ambiente. Cada programa duplica as chaves de fecho e envolve estes valores em chaves uma vez para a gestão da ligação ao estilo ODBC do controlador PHP. Escapar preserva caracteres especiais nos valores da ligação; não é parametrização de consulta. Para mais informações, consulte Ligação 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 da aplicação permanente. Mantém a mesma conexão durante todo o fluxo de trabalho.
Escreve e lê um registo
Os programas inserem um inteiro, texto UTF-8 e SQL NULL através de parâmetros. Exigem exatamente uma linha afetada, fazem commit da transação e verificam os valores devolvidos e os tipos PHP. A consulta final também verifica que nenhuma transação permanece aberta.
SQLSRV
Guarde 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
Guarde este 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 de as verificações e a limpeza terem sucesso. Uma falha de ligação, escrita, obtição, rollback ou limpeza de tabela levanta uma exceção em vez disso.
Compreenda as escolhas
Utilize ? marcadores de posição para valores em vez de concatenar a entrada em T-SQL. Os exemplos mantêm fixos os nomes da tabela e das colunas: um parâmetro de valor não pode substituir um identificador. As instruções preparadas também permitem executar a mesma instrução novamente com valores diferentes. Para mais informações, consulte Comparar funções de execução de consultas.
| Opção | Justificação |
|---|---|
| Métodos explícitos de transação | O exemplo trata a escrita como uma unidade que tem de fazer commit ou fazer rollback. O SQLSRV utiliza sqlsrv_begin_transaction, sqlsrv_commit, e sqlsrv_rollback; O PDO utiliza os 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 têm de ler uma variável por referência. |
| UTF-8 plus nvarchar | O PHP fornece bytes UTF-8; a extensão e o driver ODBC convertem-nos para a coluna Unicode SQL. O texto obtido deve corresponder, byte por byte, à string PHP original. |
| Obtenção numérica explícita para PDO | A comparação de resultados espera inteiros PHP. Sem o atributo PDO_SQLSRV, estes resultados inteiros SQL usam a representação predefinida em string do controlador. |
| Chaves fetch com nome |
SQLSRV_FETCH_ASSOC e PDO::FETCH_ASSOC devolvem os arrays indexados pelo nome da coluna. As constantes da API não são intercambiáveis. |
PDO::exec para configuração da tabela |
Executa diretamente a instrução de criação de tabela. Esta abordagem evita criar uma tabela temporária dentro do âmbito de uma instrução preparada pelo servidor. |
| Resultados apenas de avanço | Os programas leem cada linha uma vez. Eles não pedem um resultado em memória nem usam uma contagem de linhas de SELECT como prova de que a obtenção teve sucesso. |
O SQLSRV retorna null quando o seu array fetch chega ao fim e false após um erro de fetch. O fetch do PDO retorna false no final; com o modo exceção configurado, é lançada uma exceção de erro de busca da base de dados. 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 escrita é confirmada 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 elimina. Numa aplicação com tabelas permanentes, reporte a falha com precisão e não repita cegamente um insert após um resultado de commit incerto.
Lidar com falhas e limpeza de recursos
O bloco catch anula uma transação ativa e lança novamente a exceção. O bloco finally liberta a declaração, remove a tabela temporária e liberta a ligaçã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 exceção anterior.
Para um exercício de linha de comandos, uma exceção não apanhada interrompe o PHP com um código de saída não nulo e uma mensagem de erro. Numa aplicação web, capture-o no código que gere o pedido. Remova valores sensíveis antes de registar os diagnósticos no registo da sua aplicação e devolva uma resposta de erro apropriada. Não exponha detalhes de ligação, credenciais ou erros brutos da base de dados ao navegador.
| Failure | O que deve verificar |
|---|---|
| A função de extensão ou o controlador está em falta | Confirme que o processo PHP carrega a extensão pretendida com o ambiente de execução e configuração correspondentes. Uma configuração PHP funcional na linha de comandos não prova que o seu servidor web o utiliza. |
| Falha de ligação ou de certificado | Verifique o endpoint, autenticação, permissões e confiança do certificado do cliente antes de alterar o código de consulta. Não desative a validação de certificados como solução de produção. |
| A tabela temporária não foi encontrada | Mantém a configuração, as escritas e as leituras numa só conexão. Para a configuração do PDO, mantenha o caminho demonstrado exec em vez de substituir por uma chamada preparada query por defeito. |
| Valor inesperado ou tipo PHP | Verifique atributos de fetch e descritores de parâmetros. Não substituas asserções estritas por comparações flexíveis para esconder a diferença. |
| Falha de escrita ou de reversão | Capture imediatamente os erros do SQLSRV ou capture a exceção PDO. Reporte também falhas de rollback; 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 queda do lado do servidor. A libertação dos handles PHP não prova que a ligação física subjacente em pool fechou imediatamente. |
As extensões PHP fornecem as APIs de binding e fetch. O ODBC comunica com o servidor e gere o pooling de ligações. O SQL Server controla o âmbito da tabela temporária, transações, restrições e dados comprometidos. Elimine a tabela explicitamente em vez de depender do fecho da ligação física para a limpeza.
Depois do exercício, apaga workflow-sqlsrv.php e workflow-pdo.php se já não precisares deles. Elimina as variáveis do ambiente das credenciais do processo de teste quando terminares. Nenhum dos programas deixa qualquer tabela permanente.
Estes guias práticos destinam-se ao SQL Server. Para Base de Dados SQL do Azure, Azure SQL Managed Instance, Azure Synapse Analytics, base de dados SQL no Fabric e Warehouse no Fabric, verifique primeiro a autenticação do destino e o suporte a T-SQL. Ambos os produtos Fabric requerem autenticação Microsoft Entra ID, e o Warehouse requer que múltiplos conjuntos de resultados ativos (MARS) sejam desativados. Estes programas do SQL Server não estabelecem suporte a transações específicas de serviço ou a tabelas temporárias.
Escolhe a tua próxima tarefa
Estende o fluxo de trabalho com o artigo sobre o comportamento de que precisas.
| Task | Documentação de orientação |
|---|---|
| Escolher autenticação ou gerir ligações | A ligar ao servidor. |
| Reutilizar uma prepared statement com valores diferentes | Compare as funções de execução da consulta. |
| Escolha a execução direta ou preparada do PDO | Execução direta da declaração e executação da instrução preparada no driver PDO_SQLSRV. |
| Buscar valores e escolher os formatos de resultado | A recuperar dados. |
| Deslocar ou armazenar em buffer um resultado | Especificar um tipo de cursor e selecionar linhas. |
| Alterar dados da aplicação | A atualizar dados. |
| Controlo das conversões de tipos PHP e SQL | Conversão de tipos de dados. |
| Diagnosticar falhas e ativar o registo de drivers | Tratar erros e avisos e registo de atividade. |
| Proteger colunas sensíveis | Usar Always Encrypted com os drivers Microsoft para PHP para SQL Server. |
| Reforce a segurança de uma aplicação em produção | Considerações de segurança para os drivers Microsoft para PHP para SQL Server. |