Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Dopo aver stabilito una connessione funzionante da Getting started, usa questa guida per scrivere, eseguire il commit e leggere una riga con una qualsiasi delle due API PHP. Ogni programma include la propria installazione e pulizia. Usa i link delle attività per estendere il flusso di lavoro invece di copiare le opzioni da un'API all'altra.
Prepara una connessione di prova
Usa un database di test SQL Server esistente e un login che possa connettersi e creare tabelle temporanee locali. Gli esempi utilizzano l'autenticazione SQL. Per le scelte di autenticazione supportate e l'onboarding Azure senza password, vedi Connessione al server.
Carica l'estensione che scegli e imposta SQL_SERVER, SQL_DATABASE, SQL_USER, e SQL_PASSWORD nell'ambiente del processo PHP.
SQL_SERVER può includere una porta specificata esplicitamente, come <server>,1433. Il server deve presentare un certificato considerato attendibile dal client e valido per quell'endpoint. Tieni le password fuori dal programma e dal controllo del codice sorgente.
Memorizza il nome effettivo del database, il nome utente e la password nelle variabili ambientali. Ogni programma raddoppia le parentesi graffe di chiusura e racchiude questi valori una volta tra parentesi graffe per la gestione della connessione in stile ODBC del driver PHP. Scappare preserva caratteri speciali nei valori della connessione; non è parametrizzazione di query. Per maggiori informazioni, vedi Connessione al server.
Le istruzioni Transact-SQL (T-SQL) creano #PhpGuide, una tabella temporanea visibile solo alla connessione che la crea. I programmi non creano né modificano una tabella applicativa permanente. Mantieni la stessa connessione durante tutto il flusso di lavoro.
Scrivi e leggi una riga
I programmi inseriscono un intero, un testo UTF-8 e SQL NULL attraverso parametri. Richiedono esattamente una riga interessata, eseguono il commit della transazione e verificano i valori restituiti e i tipi PHP. L'ultima query verifica anche che nessuna transazione rimanga aperta.
SQLSRV
Salva questo programma completo come workflow-sqlsrv.php, e esegui 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
Salva questo programma separato come workflow-pdo.php, e esegui 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;
Output atteso per uno dei due programmi:
Inserted 1 row; read committed UTF-8 text and null; no open transaction.
Il risultato viene visualizzato solo dopo che i controlli e l'operazione di pulizia sono riusciti. Un fallimento di connessione, scrittura, recupero, rollback o pulizia della tabella genera invece un'eccezione.
Comprendi le scelte
Usa ? segnaposto per i valori invece di concatenare l'input in T-SQL. Gli esempi mantengono fissi i nomi di tabelle e colonne: un parametro di valore non può sostituire un identificatore. Le istruzioni preparate permettono anche di eseguire di nuovo la stessa istruzione con valori diversi. Per maggiori informazioni, vedi Confronta le funzioni di esecuzione delle query.
| Scelta | Motivo |
|---|---|
| Metodi di transazione espliciti | L'esempio tratta la scrittura come un'unità che deve o fare commit o tornare indietro. SQLSRV utilizza sqlsrv_begin_transaction, sqlsrv_commit, e sqlsrv_rollback; PDO utilizza i propri metodi di transazione. |
| Descrittori dei parametri SQLSRV | Ogni descrittore specifica direzione, tipo PHP e tipo SQL. I riferimenti mantengono le variabili di input, inclusa la variabile che contiene null. |
PDO bindValue |
L'istruzione riceve ogni valore corrente con un tipo esplicito di parametro PDO.
bindParam è appropriato quando le esecuzioni successive devono leggere una variabile per riferimento. |
| UTF-8 più nvarchar | PHP fornisce byte UTF-8; l'estensione e il driver ODBC li convertono per la colonna SQL Unicode. Il testo recuperato deve corrispondere al byte originale della stringa PHP per byte. |
| Recupero numerico esplicito per PDO | Il confronto dei risultati prevede numeri interi PHP. Senza l'attributo PDO_SQLSRV, questi risultati interi SQL utilizzano la rappresentazione della stringa predefinita del driver. |
| Chiavi di recupero denominate |
SQLSRV_FETCH_ASSOC e PDO::FETCH_ASSOC restituire array indicizzati per nome di colonna. Le costanti API non sono intercambiabili. |
PDO::exec per la disposizione dei tavoli |
Esegue direttamente l'istruzione di creazione della tabella. Questo approccio evita di creare una tabella temporanea all'interno dell'ambito di un'istruzione preparata dal server. |
| Risultati solo in avanti | I programmi leggono ogni riga una volta. Non richiedono un risultato bufferizzato né usano un conteggio di righe SELECT come prova che il recupero è riuscito. |
SQLSRV restituisce null quando il suo array fetch raggiunge la fine e false su un errore di fetch. Il fetch del PDO ritorna false alla fine; con la modalità eccezione configurata, viene lanciato un errore di recupero del database. In entrambe le API, il OptionalCount campo stesso contiene PHP null. Quel valore del campo non è un marcatore di fine dei risultati.
La scrittura effettua un commit prima della query di verifica. Se una lettura successiva fallisce, la scrittura commessa precedente non può essere annullata. In questo esempio, la riga appartiene solo a una tabella temporanea che viene eliminata dalla procedura di pulizia. In un'applicazione con tabelle permanenti, segnala il guasto con precisione e non ripetere ciecamente un inserimento dopo un commit incerto.
Gestione dei guasti e pulizia
Il catch blocco annulla una transazione attiva e rilancia il fallimento. Il finally blocco rilascia la dichiarazione, elimina la tabella temporanea e rilascia la connessione. La pulizia viene eseguita nel percorso di successo e in quello di errore. Se anche il rollback o la pulizia delle tabelle falliscono, il programma conserva l'eccezione originale come eccezione precedente.
Per un esercizio da riga di comando, un'eccezione non catturata ferma PHP con un codice di uscita non nullo e un messaggio di errore. In un'applicazione web, catturalo nel codice che gestisce la richiesta. Rimuovi i valori sensibili prima di registrare la diagnostica nel registro applicativo e restituisci una risposta di errore appropriata. Non esporre dettagli di connessione, credenziali o errori grezzi del database al browser.
| Failure | Controlli da eseguire |
|---|---|
| Manca una funzione di estensione o driver | Conferma che il processo PHP carichi l'estensione prevista con l'runtime e la configurazione corrispondenti. Una configurazione PHP funzionante in riga di comando non dimostra che il tuo server web lo utilizzi. |
| Errore di connessione o del certificato | Controlla endpoint, autenticazione, permessi e trust dei certificati client prima di cambiare il codice di query. Non disabilitare la validazione dei certificati come rimedio di produzione. |
| Non si trova il tavolo temporaneo | Mantieni configurazione, scritture e letture su un'unica connessione. Per l'impostazione di PDO, mantenere la procedura mostrata exec invece di sostituirla con una chiamata preparata query predefinita. |
| Valore inaspettato o tipo PHP | Controlla attributi fetch e descrittori di parametri. Non sostituire asserzioni rigorose con confronti non rigorosi per nascondere la differenza. |
| Scrittura o rollback non riusciti | Cattura immediatamente gli errori di SQLSRV, in caso contrario l'eccezione PDO. Segnala anche i fallimenti del rollback; non dire che la scrittura sia stata annullata se il rollback è fallito. |
| Errore durante la pulizia in seguito alla perdita della connessione | Il programma non può confermare il drop lato server. Rilasciare i handle PHP non dimostra che la connessione fisica aggregata sottostante sia stata chiusa immediatamente. |
Le estensioni PHP forniscono le API di binding e fetch. ODBC comunica con il server e gestisce il pooling delle connessioni. SQL Server controlla l'ambito temporaneo della tabella, le transazioni, i vincoli e i dati confermati. Elimina la tabella in modo esplicito anziché affidarsi alla chiusura della connessione fisica per la pulizia.
Dopo l'esercizio, cancella workflow-sqlsrv.php e workflow-pdo.php se non ne hai più bisogno. Elimina le variabili dell'ambiente delle credenziali dal processo di test una volta terminato. Nessuno dei due programmi lascia una tabella permanente da rimuovere.
Queste guide mirano a SQL Server. Per database SQL di Azure, Istanza gestita di SQL di Azure, Azure Synapse Analytics, Database SQL in Fabric e Warehouse in Fabric, verifica innanzitutto l'autenticazione del target e il supporto T-SQL. Entrambi i prodotti Fabric richiedono l'autenticazione Microsoft Entra ID e Warehouse richiede la disabilitazione dei set di risultati attivi multipli (MARS). Questi programmi SQL Server non forniscono il supporto per transazioni specifiche del servizio o tabelle temporanee specifiche del servizio.
Scegliere l'attività successiva
Estendi il flusso di lavoro con l'articolo della guida per il comportamento di cui hai bisogno.
| Task | Guidance |
|---|---|
| Scegli l'autenticazione o gestisci le connessioni | Connessione al server in corso. |
| Riutilizza un'istruzione preparata con valori diversi | Confronta le funzioni di esecuzione delle query. |
| Scegli l'esecuzione diretta o preparata di PDO | Esecuzione diretta delle dichiarazioni ed esecuzione delle istruzioni preparate nel driver PDO_SQLSRV. |
| Recupera i valori e scegli le forme di risultato | Recupero dei dati. |
| Scorri o metti in buffer un risultato | Specificare un tipo di cursore e selezionare righe. |
| Modifica i dati dell'applicazione | Aggiornamento dati in corso. |
| Controlla le conversioni di tipi PHP e SQL | Conversione dei tipi di dati. |
| Diagnosticare i problemi e abilitare la registrazione dei driver | Gestione di errori e avvisi e attività di logging. |
| Proteggi le colonne sensibili | Uso Always Encrypted con i driver Microsoft per PHP per SQL Server. |
| Proteggere un'applicazione in produzione | Considerazioni di sicurezza per i driver Microsoft per PHP per SQL Server. |