Programmieranleitung für die Microsoft-Treiber für PHP für SQL Server

PHP-Treiber herunterladen

Nachdem du eine funktionierende Verbindung aus Getting started hast, nutze diesen Leitfaden, um eine Zeile mit einer der beiden PHP-APIs zu schreiben, zu committen und zu lesen. Jedes Programm hat sein eigenes Setup und Cleanup. Nutzen Sie die Aufgaben-Links, um den Workflow zu erweitern, anstatt Optionen von einer API in die andere zu kopieren.

Bereite eine Testverbindung vor

Verwenden Sie eine bestehende SQL Server-Testdatenbank und einen Login, der sich verbinden und lokale temporäre Tabellen erstellen kann. Die Beispiele verwenden SQL-Authentifizierung. Für unterstützte Authentifizierungsoptionen und passwortloses Azure-Onboarding siehe Verbinden mit dem Server.

Lade die von dir gewählte Erweiterung und setze SQL_SERVER, SQL_DATABASE, SQL_USER, und SQL_PASSWORD in der Umgebung des PHP-Prozesses. SQL_SERVER kann einen expliziten Port enthalten, wie zum Beispiel <server>,1433. Der Server muss ein vom Client vertrauenswürdiges und gültiges Zertifikat für diesen Endpunkt vorlegen. Passwörter außerhalb des Programms und der Versionsverwaltung aufbewahren.

Speichere den tatsächlichen Datenbanknamen, Benutzernamen und Passwort in den Umgebungsvariablen. Jedes Programm verdoppelt die schließenden geschweiften Klammern und schließt diese Werte einmal in geschweifte Klammern ein für die ODBC-ähnliche Verbindungsbehandlung des PHP-Treibers. Maskieren bewahrt spezielle Zeichen in Verbindungswerten; es ist keine Abfrageparameterisierung. Für weitere Informationen siehe Verbinden mit dem Server.

Die Transact-SQL (T-SQL)-Anweisungen erzeugen #PhpGuide, eine temporäre Tabelle, die nur für die Verbindung sichtbar ist, die sie erzeugt. Die Programme erstellen keine permanente Anwendungstabelle und ändern sie auch nicht. Behalten Sie die Verbindung während des gesamten Arbeitsablaufs aufrecht.

Schreibe und lies eine Zeile

Die Programme fügen eine ganze Zahl, UTF-8-Text und SQL NULL über Parameter ein. Sie benötigen genau eine betroffene Zeile, committen die Transaktion und überprüfen die zurückgegebenen Werte und PHP-Typen. Die letzte Abfrage prüft außerdem, ob keine Transaktion offen bleibt.

SQLSRV

Speichere dieses vollständige Programm als workflow-sqlsrv.php, und führe php workflow-sqlsrv.php aus.

<?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

Speichere dieses separate Programm als workflow-pdo.php, und führe php workflow-pdo.php aus.

<?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;

Erwartetes Ergebnis für eines der Programme:

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

Die Ausgabe erscheint erst, nachdem die Überprüfungen und die Bereinigung erfolgreich abgeschlossen wurden. Ein Verbindungs-, Schreib-, Abruf-, Rollback- oder Tabellenbereinigungsfehler löst stattdessen eine Ausnahme aus.

Optionen verstehen

Verwenden Sie ? Platzhalter für Werte, anstatt Eingaben in T-SQL zu verketten. Die Beispiele lassen die Tabellen- und Spaltennamen unverändert: Ein Wertparameter kann keinen Identifikator ersetzen. Prepared Statements erlauben es außerdem, dieselbe Statement erneut mit anderen Werten auszuführen. Weitere Informationen finden Sie unter Vergleich von Abfrageausführungsfunktionen.

Auswahl Grund
Explizite Transaktionsmethoden Das Beispiel behandelt den Schreibvorgang als eine Einheit, der entweder festgeschrieben oder zurückgerollt werden muss. SQLSRV verwendet sqlsrv_begin_transaction, sqlsrv_commit, und sqlsrv_rollback; PDO verwendet eigene Transaktionsmethoden.
SQLSRV-Parameterdeskriptoren Jeder Deskriptor gibt Richtung, PHP-Typ und SQL-Typ an. Referenzen behalten die Eingabevariablen, einschließlich der Variable, die null enthält.
PDO bindValue Die Anweisung erhält jeden aktuellen Wert mit einem expliziten PDO-Parametertyp. bindParam ist angemessen, wenn spätere Ausführungen auf eine Variable per Referenz zugreifen müssen.
UTF-8 plus nvarchar PHP liefert UTF-8-Bytes; die Erweiterung und der ODBC-Treiber konvertieren sie für die SQL-Unicode-Spalte. Der abgerufene Text muss mit dem ursprünglichen PHP-String übereinstimmen, byte für byte.
Explizites numerisches Abrufen für PDO Der Ergebnisvergleich erwartet PHP-Ganzzahlen. Ohne das PDO_SQLSRV-Attribut verwenden diese SQL-Integer-Ergebnisse die Standard-String-Darstellung des Treibers.
Benannte Abrufschlüssel SQLSRV_FETCH_ASSOC und PDO::FETCH_ASSOC Return-Arrays, die nach Spaltennamen indexiert sind. Die API-Konstanten sind nicht austauschbar.
PDO::exec für die Tabelleneinrichtung Sie führt die Tabellenerstellungsanweisung direkt aus. Dieser Ansatz vermeidet das Erstellen einer temporären Tabelle innerhalb des Bereichs einer servervorbereiteten Anweisung.
Nur vorwärts durchlaufbare Ergebnisse Die Programme lesen jede Zeile einmal. Sie fordern kein gepuffertes Ergebnis an und verwenden keine SELECT-Zeilenzahl als Beweis, dass das Abrufen erfolgreich war.

SQLSRV gibt null zurück, wenn sein Array-Fetch das Ende erreicht und false bei einem Abruffehler. PDOs fetch gibt am Ende false zurück; bei konfiguriertem Ausnahmemodus löst ein Datenbank-Abruffehler aus. In beiden APIs enthält das Feld OptionalCount selbst PHP null. Dieser Wert ist keine Ergebnisendemarkierung.

Der Schreibvorgang wird vor der Verifizierungsanfrage festgeschrieben. Wenn ein späterer Lesevorgang fehlschlägt, kann der zuvor festgeschriebene Schreibvorgang nicht zurückgesetzt werden. In diesem Beispiel gehört die Zeile nur zu einer temporären Tabelle, die vom Cleanup entfernt wird. Melden Sie in einer Anwendung mit permanenten Tabellen den Fehler genau und wiederholen Sie nicht blind einen Insert nach einem unsicheren Commit-Ergebnis.

Fehlerbehandlung und Aufräumen

Der Block catch rollt eine aktive Transaktion zurück und löst die Ausnahme erneut aus. Der Block finally gibt die Anweisung frei, löscht die temporäre Tabelle und löst die Verbindung. Die Reinigung läuft auf dem Erfolgspfad und dem Fehlschlagspfad. Wenn auch der Rollback oder die Tabellenbereinigung fehlschlägt, bewahrt das Programm die ursprüngliche Ausnahme als vorherige Ausnahme.

Bei einer Kommandozeilenübung stoppt eine nicht abgefangene Ausnahme PHP mit einem von null verschiedenen Exit-Code und einer Fehlermeldung. In einer Webanwendung fangen Sie sie im Code auf, der die Anfrage verarbeitet. Entfernen Sie sensible Werte, bevor Sie Diagnosedaten in Ihrem Anwendungsprotokoll protokollieren, und geben Sie eine entsprechende Fehlerantwort zurück. Stellen Sie keine Verbindungsdetails, Zugangsdaten oder rohe Datenbankfehler gegenüber dem Browser offen.

Fehler Was soll überprüft werden?
Eine Erweiterungsfunktion oder ein Treiber fehlt Bestätigen Sie, dass der PHP-Prozess die vorgesehene Erweiterung mit der passenden Laufzeit und Konfiguration lädt. Eine funktionierende Kommandozeilen-PHP-Konfiguration beweist nicht, dass dein Webserver sie nutzt.
Verbindungs- oder Zertifikatsfehler Überprüfen Sie den Endpunkt, die Authentifizierung, die Berechtigungen und das Vertrauen in das Client-Zertifikat, bevor Sie den Abfragecode ändern. Deaktivieren Sie die Zertifikatsvalidierung nicht als Lösung für den Produktiveinsatz.
Temporäre Tabelle wird nicht gefunden Behalte die Einrichtungsvorgänge, das Schreiben und das Lesen auf einer Verbindung. Für die PDO-Einrichtung behalten Sie den demonstrierten exec Pfad, anstatt einen standardmäßig vorbereiteten query Aufruf zu verwenden.
Unerwarteter Wert oder PHP-Typ Überprüfen Sie die Fetch-Attribute und Parameterdeskriptoren. Ersetzen Sie strenge Behauptungen nicht durch lose Vergleiche, um den Unterschied zu verbergen.
Schreib- oder Rollback-Fehler Erfassen Sie die Fehler von SQLSRV sofort, oder die PDO-Ausnahme. Melde auch Rollback-Fehlschläge; behaupte nicht, dass das Schreiben rückgängig gemacht wurde, wenn der Rollback fehlgeschlagen ist.
Reinigungsfehler nach Verbindungsverlust Das Programm kann den serverseitigen Drop nicht bestätigen. Das Freigeben der PHP-Handles beweist nicht, dass die zugrunde liegende Pooled physische Verbindung sofort geschlossen wurde.

Die PHP-Erweiterungen stellen die Binding- und Fetch-APIs bereit. ODBC kommuniziert mit dem Server und verwaltet das Connection Pooling. SQL Server kontrolliert den Scope-Bereich temporärer Tabellen, Transaktionen, Einschränkungen und verpflichtete Daten. Lösche die Tabelle explizit, anstatt dich darauf zu verlassen, dass die physische Verbindung für die Bereinigung geschlossen wird.

Nach der Übung löschen Sie workflow-sqlsrv.php und workflow-pdo.php, falls Sie sie nicht mehr brauchen. Löschen Sie die Variablen der Zugangsdaten aus dem Testprozess, wenn Sie fertig sind. Keines der Programme hinterlässt eine permanente Tabelle, die entfernt werden muss.

Diese Walkthroughs richten sich an SQL Server. Für Azure SQL-Datenbank, Azure SQL Managed Instance, Azure Synapse Analytics, SQL Database in Fabric und Warehouse in Fabric überprüfen Sie zunächst die Authentifizierung des Ziels und die T-SQL-Unterstützung. Beide Fabric-Produkte erfordern eine Microsoft Entra ID-Authentifizierung, und Warehouse erfordert, dass mehrere aktive Ergebnissets (MARS) deaktiviert werden. Diese SQL Server-Programme bieten keine dienstspezifische Transaktions- oder temporäre Tabellenunterstützung.

Wählen Sie Ihre nächste Aufgabe aus

Erweitern Sie den Workflow mit dem Beitrag für das Verhalten, das Sie benötigen.

Aufgabe Guidance
Authentifizierung auswählen oder Verbindungen verwalten Verbindung mit dem Server wird hergestellt.
Wiederverwenden Sie eine vorbereitete Anweisung mit anderen Werten Vergleichen Sie Abfrageausführungsfunktionen.
Wählen Sie direkte PDO-Ausführung oder vorbereitete PDO-Ausführung Direkte Anweisungsausführung und Ausführung vorbereiteter Anweisungen im PDO_SQLSRV-Treiber.
Werte abrufen und Ergebnisformate auswählen Daten werden abgerufen.
Ein Ergebnis scrollen oder zwischenspeichern Einen Cursor-Typ angeben und Zeilen auswählen.
Anwendungsdaten ändern Daten werden aktualisiert.
PHP- und SQL-Typumwandlungen steuern Datentypen konvertieren.
Fehler diagnostizieren und Treiberprotokollierung aktivieren Umgang mit Fehlern und Warnungen und Protokollaktivitäten.
Empfindliche Spalten schützen Verwenden von Always Encrypted mit den Microsoft-Treibern für PHP für SQL Server.
Absichern Sie eine Produktionsanwendung Sicherheitsüberlegungen für die Microsoft-Treiber für PHP für SQL Server.