Programozási útmutató a Microsoft Drivers for PHP for SQL Server számára

PHP-illesztőprogram letöltése

Miután működő kapcsolatot hoztál létre a Getting started útmutató alapján, használd ezt az útmutatót, hogy írj, véglegesíts és olvass egy sort bármelyik PHP API-val. Minden program saját beállítást és eltávolítást tartalmaz. Használja a feladatlinkeket a munkafolyamat bővítésére, ahelyett, hogy az egyik API-ból a másikba másolná a lehetőségeket.

Készíts elő egy tesztkapcsolatot

Használj egy meglévő SQL Server tesztadatbázist és egy bejelentkezést, amely képes kapcsolódni és létrehozni helyi ideiglenes táblákat. A példák SQL-hitelesítést használnak. A támogatott hitelesítési lehetőségek és jelszó nélküli Azure-beállítás érdekében lásd: Csatlakozás a szerverhez.

Töltsd be a választott kiegészítőt, és állítsd be a SQL_SERVER, SQL_DATABASE, SQL_USER és SQL_PASSWORD elemeket a PHP-folyamat környezetében. SQL_SERVER tartalmazhat explicit portot, például <server>,1433. A szervernek olyan tanúsítványt kell bemutatnia, amelyben az ügyfél megbízik, és amely az adott végpontra érvényes. Tartsd a jelszavakat a programon és a verziókezelésen kívül.

Tárold a tényleges adatbázis nevét, felhasználónevet és jelszót a környezeti változókban. Minden program megduplázza a záró kapcsos zárójeleket, és ezeket az értékeket egyszer kapcsos zárójelek közé teszi a PHP illesztőprogram ODBC-stílusú kapcsolatkezeléséhez. Az escape-elés megőrzi a speciális karaktereket a kapcsolatértékekben; ez nem lekérdezési paraméterezés. További információért lásd: Csatlakozás a szerverhez.

A Transact-SQL (T-SQL) utasítások létrehoznak #PhpGuideegy ideiglenes táblát, amely csak az azt létrehozó kapcsolat számára látható. A programok nem hoznak létre és nem módosítanak állandó alkalmazástáblát. Tartsd ugyanazt a kapcsolódást a munkafolyamat során.

Írj és olvass egy sort

A programok egy egész számot, UTF-8 szöveget és SQL-t helyeznek be NULL paramétereken keresztül. Pontosan egy érintett sort igényelnek, véglegesítik a tranzakciót, és ellenőrzik a visszaküldött értékeket és a PHP-típusokat. Az utolsó lekérdezés azt is ellenőrzi, hogy semmilyen tranzakció nem marad nyitva.

SQLSRV

Mentsd el ezt a teljes programot workflow-sqlsrv.php néven, és futtasd a(z) php workflow-sqlsrv.php parancsot.

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

Mentsd el ezt a külön programot workflow-pdo.php néven, és futtasd a php workflow-pdo.php-t.

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

Mindkét program várható kimenete:

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

A kimenet csak akkor jelenik meg, ha a ellenőrzések és a tisztítás sikeresek. Kapcsolat, írás, lehívás, visszagörgetés vagy táblázattisztítási hiba kivételt vált ki.

Értsd meg a választásokat

Használj ? helykitöltőt az értékekhez, ne pedig összekötd a bemenetet a T-SQL-be. A példák rögzítetten tartják a táblázat- és oszlopneveket: egy értékparaméter nem helyettesítheti az azonosítót. Az előkészített utasítások lehetővé teszik, hogy ugyanazt az utasítást más értékekkel is újra végrehajtsd. További információért lásd: Lekérdezésvégrehajtási függvények összehasonlítása.

Választás Jogcím
Explicit tranzakciós módszerek A példa az írást egységként kezeli, amelynek vagy el kell köteleznie magát, vagy vissza kell tekerni. Az SQLSRV a sqlsrv_begin_transaction, sqlsrv_commit és sqlsrv_rollback elemet használja; a PDO saját tranzakciós módszereit használja.
SQLSRV parameter descriptors Minden leíró megadja az irányt, a PHP típust és az SQL típust. A hivatkozások megtartják a bemeneti változókat, beleértve azt a változót is, amely a(z) null értéket tartalmazza.
PDO bindValue Az utasítás minden aktuális értéket egy explicit PDO paramétertípussal kap. bindParam megfelelő, ha a későbbi végrehajtásoknak egy változót referencia alapján kell olvasniuk.
UTF-8 plusz nvarchar A PHP UTF-8 bájtokat szolgáltat; a kiterjesztés és az ODBC illezser átalakítja őket az SQL Unicode oszlophoz. A lehozott szövegnek meg kell egyeznie az eredeti PHP string byte-ról bájtra.
Explicit numerikus lekérés PDO-hoz Az eredmény összehasonlítása PHP egész számokat vár. A PDO_SQLSRV attribútum nélkül ezek az SQL egész értékű eredmények az illesztőprogram alapértelmezett sztringes ábrázolását használják.
Elnevezett fetch kulcsok SQLSRV_FETCH_ASSOC és PDO::FETCH_ASSOC oszlopnév szerint indexelt tömböket visszaadnak. Az API konstansok nem felcserélhetők.
PDO::exec tábla beállításához Közvetlenül végrehajtja a tábla-létrehozási utasítást. Ez a megközelítés elkerüli az ideiglenes tábla létrehozását a szerver által előkészített utasítás hatókörén belül.
Csak előre bejárható eredmények A programok egyszer olvasnak be minden sort. Nem kérnek pufferelt eredményt, és nem használják a SELECT sorszámát annak bizonyítékaként, hogy az adatok lekérése sikeres volt.

Az SQLSRV akkor tér vissza null értékkel, amikor a tömblekérés eléri a végét, és false értékkel fetch hiba esetén. A PDO fetch a végén false értéket ad vissza; kivételmód konfigurálásával egy adatbázis-fetch hiba kivételt dob. Mindkét API-ban maga a OptionalCount mező tartalmazza a PHP null. Ez a mezőérték nem az eredmény végjelzője.

Az írás a hitelesítési lekérdezés előtt véglegesítődik. Ha egy későbbi olvasás nem sikerül, a korábban véglegesített írást nem lehet visszafordítani. Ebben a példában a sor csak egy ideiglenes táblához tartozik, amelyet a tisztítás töröl. Permanens táblákkal rendelkező alkalmazásban pontosan jelezze a meghiúsulást, és ne ismételje meg vakon az INSERT műveletet bizonytalan commit-kimenet után.

A hiba kezelése és tisztítás

A catch blokk visszaforgat egy aktív tranzakciót, és újraindítja a hibát. A finally blokk felszabadítja az utasításobjektumot, ledobja az ideiglenes táblát, és felszabadítja a kapcsolatot. A takarítás a sikeres és a sikertelen végrehajtási ágban is lefut. Ha a visszaállítás vagy a táblázat tisztítása is sikertelen, a program megőrzi az eredeti kivételt korábbi kivételként.

Parancssoros gyakorlatnál egy elfoghatatlan kivétel megállítja a PHP-t egy nulla nélküli kilépési kóddal és hibaüzenettel. Egy webalkalmazásban a kérést kezelő kódban kapd el. Távolítsd el az érzékeny értékeket, mielőtt rögzíted a diagnosztikát az alkalmazásnaplódban, és küldj megfelelő hibaválaszt. Ne tegye elérhetővé a böngésző számára a kapcsolati adatokat, a hitelesítési adatokat vagy a nyers adatbázis-hibákat.

Failure Mit kell ellenőrizni?
Hiányzik a kiterjesztési függvény vagy illesztőprogram Ellenőrizze, hogy a PHP folyamat betölti a kívánt bővítményt a megfelelő futásidővel és konfigurációval. Egy működő parancssoros PHP konfiguráció nem bizonyítja, hogy a webszervered használja.
Kapcsolat vagy tanúsítványhiba Ellenőrizd a végpontot, a hitelesítést, a jogosultságokat és az ügyféltanúsítvány megbízhatóságát, mielőtt lekérdezési kódot változtatnál. Ne tiltsd le a tanúsítvány validálását termelési megoldásként.
Ideiglenes táblázat nem található A beállítást, az írásokat és az olvasásokat egyetlen kapcsolaton végezd. A PDO beállításhoz tartsd meg a bemutatott exec megoldást ahelyett, hogy egy alapértelmezett előkészített query hívásra cserélnéd.
Váratlan érték vagy PHP típus Ellenőrizd a fetch attribútumokat és paraméterleírókat. Ne cseréld a szigorú állításokat laza összehasonlításokra, hogy elrejtsd a különbséget.
Írási vagy visszaállítási hiba Azonnal rögzítsd az SQLSRV hibáit, vagy a PDO kivételét. Jelents visszatekerési hibákat is; ne állítsd, hogy az írás visszavonult, ha a visszaállítás meghibásodott.
Tisztítási hiba a kapcsolat elvesztése után A program nem tudja megerősíteni a szerveroldali eldobást. A PHP handle-ek felszabadítása nem bizonyítja, hogy az alapul szolgáló fizikai kapcsolat azonnal lezáródott.

A PHP kiterjesztések biztosítják a kötési és behozó API-kat. Az ODBC kommunikál a szerverrel és kezeli a kapcsolati poolinget. Az SQL Server az ideiglenes tábla hatókörét, tranzakciókat, korlátokat és elkötelezett adatokat kezeli. Engedd el az asztalt kifejezetten ahelyett, hogy a fizikai kapcsolat záródására hagyatkozz a takarításhoz.

A gyakorlat után töröld workflow-sqlsrv.php , és workflow-pdo.php ha már nincs rájuk szükséged. Töröld a hitelesítési környezet változóit a tesztfolyamatból, amikor végeztél. Egyik program sem hagy állandó táblát, amit el lehet távolítani.

Ezek az útmutatók az SQL Servert célozzák meg. Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, SQL database in Fabric és Warehouse in Fabric esetében először ellenőrizd a célrendszer hitelesítését és a T-SQL-támogatást. Mindkét Fabric termék Microsoft Entra ID hitelesítést igényel, míg a Warehouse esetében a több aktív eredményhalmaz (MARS) le legyen tiltva. Ezek az SQL Server programok nem biztosítanak szolgáltatásspecifikus tranzakció- vagy ideiglenestábla-támogatást.

Válassza ki a következő feladatot

Bővítsd a munkafolyamatot a cikkel a szükséges működéshez.

Task Guidance
Válassz hitelesítést vagy menedzsd a kapcsolatokat Kapcsolódás a kiszolgálóhoz.
Használd újra egy előkészített utasítást különböző értékekkel Hasonlítsd össze a lekérdezés végrehajtási függvényeit.
Válassz PDO közvetlen vagy előkészített végrehajtást Közvetlen utasítás-végrehajtás és előkészített utasítás végrehajtása a PDO_SQLSRV driverben.
Értékek lekérése és eredményalakok kiválasztása Adatok lekérése.
Görgess, vagy tárolj eredményt a pufferben A kurzortípus megadása és a sorok kiválasztása.
Alkalmazásadatok módosítása Adatok frissítése.
A PHP- és SQL-típuskonverziók szabályozása Adattípusok átalakítása.
Hibák diagnosztizálása és illesztőprogram-naplózás engedélyezése Hibák és figyelmeztetések kezelése és tevékenységek naplózása.
Védd az érzékeny oszlopokat Always Encrypted használata a Microsoft Driver-rel PHP-hez az SQL Server-hez.
Tedd biztonságosabbá egy éles alkalmazást Biztonsági szempontok a Microsoft Drivers for PHP for SQL Server esetében.