Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Gdy już masz działające połączenie z Getting Started, użyj tego przewodnika, aby napisać, zatwierdzić i odczytać wiersz za pomocą dowolnego interfejsu API PHP. Każdy program zawiera własną instalację i czyszczenie. Używaj linków do zadań, aby rozszerzyć workflow, zamiast kopiować opcje z jednego API do drugiego.
Przygotuj połączenie testowe
Użyj istniejącej bazy testowej SQL Server oraz loginu, który umożliwia łączenie się i tworzenie lokalnych tabel tymczasowych. Przykłady wykorzystują uwierzytelnianie SQL. Aby poznać opcje obsługiwanego uwierzytelniania i onboarding Azure bez hasła, zobacz Łączenie z serwerem.
Załaduj wybrane rozszerzenie i ustaw SQL_SERVER, SQL_DATABASE, SQL_USER i SQL_PASSWORD w środowisku procesu PHP.
SQL_SERVER może zawierać port jawny, taki jak <server>,1433. Serwer musi przedstawić certyfikat zaufany przez klienta i ważny dla tego punktu końcowego. Trzymaj hasła poza programem i kontrolą wersji wersji.
Przechowuj rzeczywistą nazwę bazy danych, nazwę użytkownika i hasło w zmiennych środowiskowych. Każdy program podwaja zamykające nawiasy klamrowe i jednokrotnie ujmuje te wartości w nawiasy klamrowe na potrzeby obsługi połączeń w stylu ODBC przez sterownik PHP. Escaping zachowuje specjalne znaki w wartościach połączeń; nie jest to parametryzacja zapytań. Więcej informacji można znaleźć w artykule Łączenie z serwerem.
Instrukcje Transact-SQL (T-SQL) tworzą #PhpGuide, tymczasową tabelę widoczną tylko dla połączenia, które ją tworzy. Programy nie tworzą ani nie zmieniają trwałej tabeli aplikacji. Utrzymuj to samo połączenie przez cały proces pracy.
Zapisz i odczytaj wiersz
Programy wstawiają liczbę całkowitą, tekst UTF-8 oraz SQL NULL poprzez parametry. Wymagają dokładnie jednego dotkniętego wiersza, zatwierdzają transakcję oraz weryfikują zwrócone wartości i typy PHP. Ostatnie zapytanie sprawdza również, czy żadna transakcja nie pozostaje otwarta.
SQLSRV
Zapisz ten pełny program jako workflow-sqlsrv.php, i uruchom 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
Zapisz ten osobny program jako workflow-pdo.php, i uruchom 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;
Oczekiwany wynik dla każdego programu:
Inserted 1 row; read committed UTF-8 text and null; no open transaction.
Dane wyjściowe pojawiają się dopiero po pomyślnym sprawdzeniu i oczyszczeniu. Niepowodzenie połączenia, zapisu, pobierania, cofania lub czyszczenia tabeli zamiast tego zgłasza wyjątek.
Zrozum wybory
Używaj ? symboli zastępczych dla wartości zamiast doklejania danych wejściowych do zapytania T-SQL. Przykłady utrzymują nazwy tabel i kolumn niezmienne: parametr wartości nie może zastąpić identyfikatora. Przygotowane instrukcje SQL pozwalają także wykonać tę samą instrukcję ponownie, ale z innymi wartościami. Więcej informacji można znaleźć w artykule Porównaj funkcje wykonywania zapytań.
| Wybór | Powód |
|---|---|
| Jawne metody transakcyjne | Przykład traktuje zapis jako jednostkę, którą należy albo zatwierdzić, albo wycofać. SQLSRV używa sqlsrv_begin_transaction, sqlsrv_commit i sqlsrv_rollback; PDO używa własnych metod transakcyjnych. |
| Deskryptory parametrów SQLSRV | Każdy deskryptor określa kierunek, typ PHP oraz typ SQL. Odwołania zachowują zmienne wejściowe, w tym zmienną zawierającą null. |
PDO bindValue |
Instrukcja otrzymuje każdą aktualną wartość z wyraźnym typem parametru PDO.
bindParam jest odpowiednie, gdy późniejsze wykonania muszą odczytywać zmienną przez odwołanie. |
| UTF-8 plus nvarchar | PHP dostarcza bajty UTF-8; rozszerzenie i sterownik ODBC konwertują je na kolumnę SQL Unicode. Pobrany tekst musi odpowiadać oryginalnemu bajtowi ciągu PHP dla bajtu. |
| Jawne pobieranie numeryczne dla PDO | Porównanie wyników zakłada liczby całkowite PHP. Bez atrybutu PDO_SQLSRV te wyniki SQL będące liczbami całkowitymi wykorzystują domyślną reprezentację ciągów znaków sterownika. |
| Nazwane klucze fetch |
SQLSRV_FETCH_ASSOC oraz PDO::FETCH_ASSOC zwracają tablice indeksowane według nazwy kolumny. Stałe API nie są zamienne. |
PDO::exec do konfiguracji tabeli |
Bezpośrednio wykonuje polecenie tworzenia tabeli. Takie podejście unika tworzenia tymczasowej tabeli w obrębie instrukcji przygotowanej przez serwer. |
| Wyniki tylko jednokierunkowe | Programy odczytują każdy wiersz raz. Nie żądają buforowanego wyniku ani nie używają liczby wierszy zwróconych przez SELECT jako dowodu powodzenia pobierania. |
SQLSRV zwraca null, gdy pobieranie tablicy dociera do końca, i false w przypadku błędu pobierania. Pobieranie danych przez PDO zwraca false na końcu; przy skonfigurowanym trybie wyjątków w przypadku błędu pobierania danych z bazy zostaje zgłoszony wyjątek. W obu API OptionalCount pole samo zawiera PHP null. Ta wartość pola nie jest znacznikiem końca wyników.
Operacja zapisu jest zatwierdzana przed zapytaniem weryfikacyjnym. Jeśli późniejszy odczyt się nie powiedzie, wcześniejszy zatwierdzony zapis nie może zostać cofnięty. W tym przykładzie wiersz należy tylko do tymczasowej tabeli, którą usuwa się podczas czyszczenia. W aplikacji z tabelami stałymi dokładnie zgłoś niepowodzenie i nie powtarzaj bezrefleksyjnie operacji INSERT po niepewnym wyniku zatwierdzenia.
Obsługa awarii i czyszczenie
Blok catch cofa aktywną transakcję i ponownie zgłasza błąd. Blok finally zwalnia instrukcję, usuwa tabelę tymczasową i zwalnia połączenie. Czyszczenie odbywa się zarówno w przypadku powodzenia, jak i niepowodzenia. Jeśli cofnięcie lub czyszczenie tabeli również się nie powiedzie, program zachowuje oryginalny wyjątek jako wyjątek poprzedni.
W ćwiczeniu dotyczącym wiersza poleceń niezłapany wyjątek kończy działanie PHP z niezerowym kodem wyjścia i komunikatem o błędzie. W aplikacji webowej złap ją w kodzie, który obsługuje żądanie. Usuń wrażliwe wartości przed zapisaniem diagnostyki w logu aplikacji i zwróć odpowiedni komunikat o błędzie. Nie ujawniaj szczegółów połączenia, danych uwierzytelniających ani surowych błędów bazy danych w przeglądarce.
| Failure | Co należy sprawdzić? |
|---|---|
| Brak funkcji rozszerzenia lub sterownika | Potwierdź, że proces PHP ładuje zamierzone rozszerzenie z pasującym runtime i konfiguracją. Działająca konfiguracja PHP w wierszu poleceń nie dowodzi, że twój serwer WWW go używa. |
| Awaria połączenia lub certyfikatu | Sprawdź punkt końcowy, uwierzytelnianie, uprawnienia oraz zaufanie do certyfikatów klienta przed zmianą kodu zapytania. Nie wyłączaj walidacji certyfikatów jako rozwiązania produkcyjnego. |
| Tymczasowa tabela nie została znaleziona | Wykonuj konfigurację, zapis i odczyt na jednym połączeniu. Do ustawienia PDO zachowaj demonstrowaną exec ścieżkę zamiast podstawiać domyślnie przygotowane query wywołanie. |
| Wartość nieoczekiwana lub typ PHP | Sprawdź atrybuty pobierania i deskryptory parametrów. Nie zastępuj sztywnych twierdzeń luźnymi porównaniami, by ukryć różnicę. |
| Niepowodzenie zapisu lub wycofania zmian | Natychmiast rejestruj błędy SQLSRV lub wyjątek PDO. Zgłaszaj też niepowodzenia rollbacków; nie twierdz, że zapis został cofnięty, jeśli rollback się nie powiódł. |
| Awaria sprzątania po utracie połączenia | Program nie może potwierdzić odrzucenia po stronie serwera. Udostępnienie uchwytów PHP nie dowodzi, że podstawowe połączenie fizyczne w puli zostało natychmiast zamknięte. |
Rozszerzenia PHP zapewniają API do wiązania i pobierania. ODBC komunikuje się z serwerem i zarządza pulą połączeń. SQL Server kontroluje zakres tabel tymczasowych, transakcje, ograniczenia oraz dane zatwierdzone. Usuń stół wprost, zamiast polegać na zamknięciu fizycznego połączenia podczas sprzątania.
Po ćwiczeniu usuń workflow-sqlsrv.php i workflow-pdo.php jeśli już nie potrzebujesz ich więcej. Po zakończeniu usuń zmienne środowiska uwierzytelniania z procesu testowego. Żaden z tych programów nie zostawia stałej tabeli do usunięcia.
Te poradniki dotyczą programu SQL Server. Dla Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, SQL database in Fabric oraz Warehouse in Fabric, najpierw sprawdź uwierzytelnianie i obsługę języka T-SQL obiektu docelowego. Oba produkty Fabric wymagają uwierzytelniania Microsoft Entra ID, a Warehouse wymaga wyłączenia wielu aktywnych zestawów wyników (MARS). Te programy SQL Server nie ustanawiają transakcji specyficznych dla usługi ani obsługi tabel tymczasowych.
Wybierz następne zadanie
Rozszerzyj workflow o artykuł dotyczący zachowania, którego potrzebujesz.
| Zadanie | Wskazówki |
|---|---|
| Wybierz uwierzytelnianie lub zarządzaj połączeniami | Łączę się z serwerem. |
| Wykorzystaj ponownie przygotowaną instrukcję SQL z innymi wartościami | Porównaj funkcje wykonywania zapytań. |
| Wybierz bezpośrednie lub przygotowane wykonanie PDO | Bezpośrednie wykonywanie instrukcji i wykonywanie przygotowanych instrukcji w sterowniku PDO_SQLSRV. |
| Pobieranie wartości i wybieranie kształtów wyników | Pobieranie danych. |
| Przewiń lub buforować wynik | Określenie typu kursora i zaznaczenie wierszy. |
| Zmień dane aplikacji | Aktualizowanie danych. |
| Sterowanie konwersjami typów PHP i SQL | Konwersja typów danych. |
| Zdiagnozuj awarie i włącz logowanie sterowników | Obsługa błędów i ostrzeżeń oraz rejestracja aktywności. |
| Chroń wrażliwe kolumny | Korzystanie z Always Encrypted przy użyciu sterowników Microsoft Drivers for PHP for SQL Server. |
| Utwardzanie aplikacji produkcyjnej | Kwestie bezpieczeństwa dla sterowników Microsoft dla PHP dla SQL Server. |