Programmeringsguide för Microsoft Drivers för PHP för SQL Server

Ladda ned PHP-drivrutin

När du har en fungerande anslutning från Getting Start, använd denna guide för att skriva, commita och läsa en rad med något av PHP-API:erna. Varje program har sin egen installation och rensning. Använd uppgiftslänkarna för att utöka arbetsflödet istället för att kopiera alternativ från ett API till det andra.

Förbered en testanslutning

Använd en befintlig SQL Server-testdatabas och en inloggning som kan ansluta och skapa lokala temporära tabeller. Exemplen använder SQL-autentisering. För stödda autentiseringsval och lösenordslös Azure-onboarding, se Anslutning till servern.

Ladda det tillägg du väljer och sätt SQL_SERVER, SQL_DATABASE, SQL_USER och SQL_PASSWORD i miljön för PHP-processen. SQL_SERVER kan inkludera en explicit port, såsom <server>,1433. Servern måste presentera ett certifikat som klienten litar på och som är giltigt för den endpointen. Håll lösenorden utanför programmet och versionskontrollen.

Spara det faktiska databasnamnet, användarnamnet och lösenordet i miljövariablerna. Varje program dubblerar avslutande klammerparenteser och omsluter dessa värden med en uppsättning klammerparenteser för PHP-drivrutinens ODBC-liknande anslutningshantering. Escapning bevarar specialtecken i anslutningsvärden; det är inte frågeparameterisering. För mer information, se Ansluta till servern.

Transact-SQL (T-SQL)-satserna skapar #PhpGuide, en tillfällig tabell som endast är synlig för den anslutning som skapar den. Programmen skapar eller ändrar inte en permanent applikationstabell. Behåll samma anslutning genom hela arbetsflödet.

Skriv och läs en rad

Program infogar ett heltal, UTF-8-text och SQL NULL via parametrar. De kräver exakt en påverkad rad, commitar transaktionen och verifierar de returnerade värdena och PHP-typerna. Den sista frågan kontrollerar också att ingen transaktion är öppen.

SQLSRV

Spara hela programmet som workflow-sqlsrv.php, och kör 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

Spara detta separata program som workflow-pdo.php, och kör 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;

Förväntad utdata för endera programmet:

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

Resultatet visas först efter att kontrollerna och rensningen har lyckats. Ett misslyckande med anslutning, skriv, hämtning, rollback eller tabellrensning ger istället ett undantag.

Förstå valen

Använd ? platshållare för värden istället för att sammanfoga indata i T-SQL. Exemplen behåller tabell- och kolumnnamnen fasta: en värdeparameter kan inte ersätta en identifierare. Förberedda SQL-satser låter dig också köra samma sats igen med olika värden. För mer information, se Jämför frågeexekveringsfunktioner.

Val Förnuft
Uttryckliga transaktionsmetoder Exemplet behandlar skrivningen som en enhet som antingen måste committa eller rulla tillbaka. SQLSRV använder sqlsrv_begin_transaction, sqlsrv_commit, och sqlsrv_rollback; PDO använder sina egna transaktionsmetoder.
SQLSRV-parameterdeskriptorer Varje beskrivare anger riktning, PHP-typ och SQL-typ. Referenser behåller indatavariablerna, inklusive variabeln som innehåller null.
PDO bindValue Satsen tar emot varje aktuellt värde med en explicit PDO-parametertyp. bindParam är lämpligt när senare körningar måste läsa en variabel via referens.
UTF-8 plus nvarchar PHP tillhandahåller UTF-8-bytes; tillägget och ODBC-drivrutinen konverterar dem för SQL Unicode-kolumnen. Den hämtade texten måste matcha den ursprungliga PHP-strängen byte för byte.
Explicit numerisk hämtning för PDO Resultatjämförelsen förväntar sig PHP-heltal. Utan attributet PDO_SQLSRV använder dessa SQL-heltalsresultat drivrutinens standardsträngrepresentation.
Namngivna hämtningsnycklar SQLSRV_FETCH_ASSOC och PDO::FETCH_ASSOC returnerar arrayer indexerade efter kolumnnamn. API-konstanterna är inte utbytbara.
PDO::exec för tabellinställning Kör satsen för att skapa tabellen direkt. Denna metod undviker att skapa en tillfällig tabell inom ramarna för en serverförberedd sats.
Framåtriktade resultat Programmen läser varje rad en gång. De begär varken ett buffrat resultat eller använder en SELECT-radräkning som bevis på att hämtningen lyckades.

SQLSRV returnerar null när dess array-hämtning når slutet och false vid ett hämtningsfel. PDO:s hämtning returnerar false i slutet; med undantagsläget konfigurerat kastas ett undantag vid databashämtningsfel. I båda API:erna OptionalCount innehåller fältet självt PHP null. Det fältvärdet är inte en slutresultatmarkör.

Skrivningen commitar innan verifieringsfrågan. Om en senare läsning misslyckas kan den tidigare bekräftade skrivningen inte rullas tillbaka. I detta exempel tillhör raden endast en tillfällig tabell som tas bort vid rensning. I en applikation med permanenta tabeller, rapportera felet korrekt och upprepa inte ett insert blint efter ett osäkert commit-resultat.

Hantera fel och sanering

Blocket catch rullar tillbaka en aktiv transaktion och kastar felet på nytt. finally-blocket frigör instruktionen, tar bort den tillfälliga tabellen och frigör anslutningen. Sanningen sker på framgångsvägen och misslyckanden. Om rollback eller tabellrensning också misslyckas, bevarar programmet det ursprungliga undantaget som ett tidigare undantag.

Vid en kommandoradsövning stoppar ett ofångat undantag PHP med en icke-noll avslutningskod och ett felmeddelande. I en webbapplikation, fånga det i koden som hanterar förfrågan. Ta bort känsliga värden innan du registrerar diagnostik i din applikationslogg och returnera ett lämpligt felsvar. Exponera inte anslutningsdetaljer, inloggningsuppgifter eller råa databasfel i webbläsaren.

Failure Vad du bör kontrollera
Utvidgningsfunktion eller drivrutin saknas Bekräfta att PHP-processen laddar det avsedda tillägget med matchande körtid och konfiguration. En fungerande kommandorads-PHP-konfiguration bevisar inte att din webbserver använder den.
Anslutnings- eller certifikatfel Kontrollera endpoint, autentisering, behörigheter och förtroende för klientcertifikat innan du ändrar frågekoden. Inaktivera inte certifikatvalidering som en produktionsåtgärd.
Tillfällig tabell hittas inte Håll konfigurering, skrivningar och läsningar i en och samma anslutning. För PDO-uppsättning, behåll den visade exec sökvägen istället för att ersätta ett standardförberett query-anrop.
Oväntat värde eller PHP-typ Kontrollera hämtaattribut och parameterbeskrivare. Byt inte ut strikta påståenden mot lösa jämförelser för att dölja skillnaden.
Skriv- eller rollbackfel Fånga SQLSRV:s fel omedelbart, eller fånga PDO-undantaget. Rapportera även misslyckade återställningar; påstå inte att skrivningen ångrades om återställningen misslyckades.
Rensningsfel efter anslutningsförlust Programmet kan inte bekräfta bortfall på serversidan. Att släppa PHP-handtag innebär inte att den underliggande poolade fysiska anslutningen stängdes omedelbart.

PHP-tilläggen tillhandahåller bindnings- och hämt-API:erna. ODBC kommunicerar med servern och hanterar anslutningspoolning. SQL Server kontrollerar temporära tabellers omfattning, transaktioner, begränsningar och incheckade data. Ta bort tabellen explicit istället för att förlita dig på att den fysiska anslutningen stängs för upprensning.

Efter övningen, radera workflow-sqlsrv.php och workflow-pdo.php om du inte längre behöver dem. Rensa referensmiljöns variabler från testprocessen när du är klar. Inget av programmen lämnar en permanent tabell att ta bort.

Dessa genomgångar riktar sig till SQL Server. För Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, SQL database in Fabric och Warehouse in Fabric, kontrollera först målserverns autentisering och T-SQL-stöd. Båda Fabric-produkterna kräver Microsoft Entra ID-autentisering, och Warehouse kräver att flera aktiva resultatuppsättningar (MARS) inaktiveras. Dessa SQL Server-program har inte stöd för tjänstespecifika transaktioner eller temporära tabeller.

Välj nästa uppgift

Utöka arbetsflödet med en artikel för det beteende du behöver.

Task Riktlinjer
Välj autentisering eller hantera anslutningar Ansluter till servern.
Återanvänd ett prepared statement med andra värden Jämför funktioner för frågeexekvering.
Välj PDO direkt eller förberett utförande Direkt körning av satser och körning av förberedda satser i den PDO_SQLSRV-drivrutinen.
Hämta värden och välj resultatformat Hämtar data.
Scrolla eller buffra ett resultat Ange en markörtyp och välj rader.
Ändra applikationsdata Data uppdateras.
Kontrollera PHP- och SQL-typkonverteringar Konvertering av datatyper.
Diagnostisera fel och aktivera drivrutinsloggning Hantering av fel och varningar samt Loggning.
Skydda känsliga kolumner Använda Always Encrypted med Microsoft-drivrutinerna för PHP för SQL Server.
Säkra en produktionsapplikation Säkerhetsöverväganden för Microsoft Drivers för PHP för SQL Server.