Guide de programmation pour les pilotes Microsoft pour PHP pour SQL Server

Télécharger le pilote PHP

Après avoir établi une connexion fonctionnelle depuis Getting started, utilisez ce guide pour écrire, enregistrer et lire une ligne avec l’une ou l’autre API PHP. Chaque programme comprend sa propre configuration et son propre nettoyage. Utilisez les liens de tâches pour étendre le flux de travail plutôt que de copier des options d’une API vers l’autre.

Préparer une connexion d’essai

Utilisez une base de données de test SQL Server existante et un identifiant de connexion qui peut se connecter et créer des tables temporaires locales. Les exemples utilisent l’authentification SQL. Pour les choix d’authentification pris en charge et l’intégration Azure sans mot de passe, voir Connexion au serveur.

Chargez l’extension de votre choix, et définissez SQL_SERVER, SQL_DATABASE, SQL_USER, et SQL_PASSWORD dans l’environnement du processus PHP. SQL_SERVER peut inclure un port explicite, tel que <server>,1433. Le serveur doit présenter un certificat auquel le client fait confiance et valide pour ce point de terminaison. Gardez les mots de passe en dehors du programme et de la gestion de versions.

Stockez le nom réel de la base de données, le nom d’utilisateur et le mot de passe dans les variables d’environnement. Chaque programme double les accolades fermantes et place ces valeurs entre accolades une fois pour la gestion de la connexion de type ODBC du pilote PHP. L’échappement préserve les caractères spéciaux dans les valeurs de connexion ; ce n’est pas une paramétrisation par requête. Pour plus d’informations, voir Connexion au serveur.

Les instructions Transact-SQL (T-SQL) créent #PhpGuide, une table temporaire visible uniquement pour la connexion qui la crée. Les programmes ne créent pas et ne modifient pas de table d’application permanente. Gardez la même connexion tout au long du flux de travail.

Écrivez et lisez une rangée

Les programmes insèrent un entier, un texte UTF-8 et SQL NULL à travers des paramètres. Ils nécessitent exactement une ligne affectée, effectuent le commit de la transaction et vérifient les valeurs retournées ainsi que les types PHP. La requête finale vérifie également qu’aucune transaction ne reste ouverte.

SQLSRV

Enregistrez ce programme complet sous le nom workflow-sqlsrv.php, et exécutez 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

Enregistrez ce programme séparé sous le nom de workflow-pdo.php, et exécutez 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;

Résultats attendus pour l’un ou l’autre programme :

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

La sortie n’apparaît qu’après la réussite des vérifications et du nettoyage. Une défaillance de connexion, d’écriture, de récupération, de retour en arrière ou de nettoyage de table lève une exception à la place.

Comprenez les choix

Utilisez des espaces réservés ? pour les valeurs plutôt que de concaténer les entrées dans T-SQL. Les exemples maintiennent fixement les noms des tables et des colonnes : un paramètre de valeur ne peut pas remplacer un identifiant. Les instructions préparées permettent également d’exécuter à nouveau la même instruction avec des valeurs différentes. Pour plus d’informations, voir Comparer les fonctions d’exécution des requêtes.

Choix Reason
Méthodes de transaction explicites L’exemple traite l’écriture comme une unité qui doit soit valider, soit annuler. SQLSRV utilise sqlsrv_begin_transaction, sqlsrv_commit, et sqlsrv_rollback; PDO utilise ses propres méthodes de transaction.
Descripteurs de paramètres SQLSRV Chaque descripteur spécifie la direction, le type PHP et le type SQL. Les références conservent les variables d’entrée, y compris la variable contenant null.
PDO bindValue L’instruction reçoit chaque valeur courante avec un type explicite de paramètre PDO. bindParam est approprié lorsque les exécutions ultérieures doivent lire une variable par référence.
UTF-8 plus nvarchar PHP fournit des octets UTF-8 ; l’extension et le pilote ODBC les convertissent pour la colonne SQL Unicode. Le texte récupéré doit correspondre à la chaîne PHP d’origine, octet pour octet.
Récupération numérique explicite pour PDO La comparaison des résultats nécessite des entiers PHP. Sans l’attribut PDO_SQLSRV, ces résultats SQL de type entier utilisent la représentation sous forme de chaîne par défaut du pilote.
Clés de récupération nommées SQLSRV_FETCH_ASSOC et PDO::FETCH_ASSOC retournent des tableaux indexés par nom de colonne. Les constantes API ne sont pas interchangeables.
PDO::exec pour la configuration des tables Il exécute directement l’instruction création de table. Cette approche évite de créer une table temporaire dans le cadre d’une instruction préparée par le serveur.
Résultats en lecture seule vers l’avant Les programmes lisent chaque ligne une fois. Ils ne demandent pas un résultat tamponné ni n’utilisent le nombre de lignes renvoyées par SELECT comme preuve que la récupération a réussi.

SQLSRV renvoie null lorsque sa récupération de tableau atteint la fin et false sur une erreur de récupération. Le fetch de PDO revient false à la fin ; avec le mode exception configuré, une erreur de récupération de base de données lève une exception. Dans les deux API, le champ OptionalCount lui-même contient PHP null. Cette valeur de champ n’est pas un marqueur de fin de résultats.

L’écriture est validée avant la requête de vérification. Si une lecture ultérieure échoue, l’écriture validée antérieure ne peut pas être annulée. Dans cet exemple, la ligne appartient uniquement à une table temporaire que le nettoyage supprime. Dans une application avec des tables permanentes, signalez l’échec avec précision et ne répétez pas aveuglément un insert après un commit incertain.

Gérer l’échec et le nettoyage

Le catch bloc annule une transaction active et relance l’exception. Le finally bloc libère l’instruction, supprime la table temporaire et libère la connexion. Le nettoyage se fait sur le chemin d’exécution en cas de réussite et en cas d’échec. Si le rollback ou le nettoyage de table échoue également, le programme conserve l’exception originale comme une exception précédente.

Pour un exercice en ligne de commande, une exception non détectée arrête PHP avec un code de sortie non nul et un message d’erreur. Dans une application web, capturez-le dans le code qui gère la requête. Supprimez les valeurs sensibles avant d’enregistrer les diagnostics dans votre journal d’application, et renvoyez une réponse d’erreur appropriée. N’exposez pas les détails de connexion, les identifiants ou les erreurs brutes de base de données au navigateur.

Failure Que vérifier
La fonction d’extension ou le pilote manque Confirmez que le processus PHP charge l’extension prévue avec l’environnement d’exécution correspondant et la configuration correspondante. Une configuration PHP en ligne de commande fonctionnelle ne prouve pas que votre serveur web l’utilise.
Défaillance de connexion ou de certificat Vérifiez le point de terminaison, l’authentification, les permissions et la confiance accordée au certificat client avant de changer le code de requête. Ne désactivez pas la validation des certificats comme solution de production.
Table temporaire introuvable Gardez l’initialisation, l’écriture et la lecture sur une seule connexion. Pour la configuration de PDO, conservez l’approche illustrée exec au lieu de la remplacer par un appel préparé query par défaut.
Valeur inattendue ou type PHP Vérifiez les attributs de fetch et les descripteurs de paramètres. Ne remplacez pas les assertions strictes par des comparaisons non strictes pour masquer la différence.
Échec d’écriture ou de restauration Capturez immédiatement les erreurs de SQLSRV, ou l’exception PDO. Signalez aussi les échecs de retour en arrière ; ne prétendez pas que l’écriture a été annulée si le retour en arrière a échoué.
Échec du nettoyage après la perte de connexion Le programme ne peut pas confirmer la chute côté serveur. Libérer les handles PHP ne prouve pas que la connexion physique en pool sous-jacente a fermé immédiatement.

Les extensions PHP fournissent les API de liaison et de récupération. ODBC communique avec le serveur et gère le pooling de connexions. SQL Server contrôle la portée des tables temporaires, les transactions, les contraintes et les données validées. Supprimer la table explicitement plutôt que de compter sur la fermeture physique de la connexion pour le nettoyage.

Après l’exercice, supprime workflow-sqlsrv.php et workflow-pdo.php si tu n’en as plus besoin. Effacez les variables d’environnement des identifiants du processus de test à la fin. Aucun des deux programmes ne laisse de table permanente à retirer.

Ces guides ciblent SQL Server. Pour Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, SQL database dans Fabric et Warehouse dans Fabric, vérifiez d'abord l'authentification de la cible et le support T-SQL. Les deux produits Fabric nécessitent une authentification Microsoft Entra ID, et Warehouse nécessite de désactiver plusieurs ensembles de résultats actifs (MARS). Ces programmes SQL Server n'établissent pas de prise en charge des transactions spécifiques au service ou des tables temporaires.

Choisir votre prochaine tâche

Étends le flux de travail avec l’article pour le comportement dont tu as besoin.

Task Conseils
Choisissez l’authentification ou gérez les connexions Connexion au serveur.
Réutiliser une requête préparée avec des valeurs différentes Comparez les fonctions d’exécution des requêtes.
Choisissez l’exécution directe ou préparée PDO Exécution directe des instructions et exécution des instructions préparées dans le pilote PDO_SQLSRV.
Récupérer les valeurs et choisir les formats de résultat Récupération des données.
Faire défiler ou mettre en mémoire tampon un résultat Spécifier un type de curseur et sélectionner des lignes.
Modifier les données d’application Mise à jour des données.
Contrôlez les conversions de types PHP et SQL Convertir les types de données.
Diagnostiquer les défaillances et activer la journalisation des drivers Gestion des erreurs, des avertissements et de la journalisation.
Protéger les colonnes sensibles Utiliser Always Encrypted avec les pilotes Microsoft pour PHP pour SQL Server.
Sécuriser une application de production Considérations de sécurité pour les pilotes Microsoft pour PHP pour SQL Server.