Hur fungerar?/Datorer/Programmering/MySQLi och prepared statements i PHP

Hur fungerar egentligen MySQLi och prepared statements i PHP?

Ingen som är intresserad av PHP har väl kunnat undgå att PHP:s mysql-funktioner har varit deprecated sedan PHP 5.5.0, och att det börjar bli dags att använda något av de andra MySQL-API:er som tillhandahålls. Ett bra alternativ är att börja använda den objektorienterade varianten av MySQLi, där i:et står för improved, och givetvis med prepared statements. Prepared statements är för övrigt någonting som verkligen rekommenderas att alla använder! Men, här kommer i alla fall en kort liten guide till objektorienterad MySQLi med prepared statements i PHP (i sin simplaste form)! Jag förutsätter förresten att du som läser det här har hyfsad koll på PHP och MySQL sedan tidigare.

Att ansluta till MySQL

Att ansluta till MySQL är lika enkelt som det alltid har varit, man skapar helt enkelt bara ett mysqli-objekt med hjälp av vilket man sedan kör sina SQL-frågor.

<?php
// Ansluter till MySQL
$sqlHost = 'localhost';
$sqlUsername = 'sqlUsername';
$sqlPassword = 'xxxxxxxxxx';
$sqlDatabase = 'sqlDatabase';
$mysqli = new mysqli($sqlHost, $sqlUsername, $sqlPassword, $sqlDatabase);
?>

Att ställa en fråga till MySQL

När det gäller köra SQL-frågor så blir det några fler rader kod än med den gamla mysql_query()-funktionen, åtminstone om man använder prepared statements. Men vi börjar med ett väldigt simpelt exempel utan några parametrar.

<?php
// Hämta användarnamnet för användaren med ID = 1
// Öppnar och förbereder SQL-frågan
$stmt = $mysqli->prepare("SELECT userName FROM siteUsers WHERE id = 1");
$stmt->execute(); // Kör frågan
$stmt->bind_result($userName); // Binder resultatet till en variabel
$stmt->fetch(); // Hämtar resultatet
$stmt->close(); // Stänger frågan
echo $userName; // Innehåller det eftersöka användarnamnet
?>

Frågan ovan med ett prepared statement

Låt säga att vi inte vet vilket vilket ID som skall användas i frågan ovanför, utan att det värdet hämtas från t.ex. data inmatad från en användare, eller från någon annan variabel. Då är det klokt att binda variabeln till en viss datatyp, och genom att göra detta tala om för MySQL att innehållet i den inmatade variabeln bara får vara av just den datatypen - bundna parametrar behöver inte escapeas, det sköter bind_param()!

Möjliga datatyper är:

<?php
$stmt = $mysqli->prepare("SELECT userName FROM siteUsers WHERE id = ?");
$stmt->bind_param("i", $userID); // Bestämmer att $userID måste vara en integer
$stmt->execute();
$stmt->bind_result($userName);
$stmt->fetch();
$stmt->close();
echo $userName;
?>

Loopa igenom ett result set

Om man kollar på hur man loopar igenom ett result set med de gamla mysql-funktionerna, så var det åtminstone för mig vanligast att skapa en associativ array av varje rad, t.ex. genom while ($row = mysql_fetch_array($res)). Det går att göra med MySQLi också, men vi bygger vidare på de bundna variablerna - och eftersom det är ett result set vi får returnerat, måste vi lagra/buffra resultatet med hjälp av store_result().

<?php
// Hämtar användarnamn för alla användare med användarnivå över eller lika med ?
$stmt = $mysqli->prepare("SELECT userName FROM siteUsers WHERE userLevel >= ?");
$stmt->bind_param("i", $userLevel); // Bestämmer att $userLevel skall vara en integer
$stmt->execute();
$stmt->bind_result($userName);
$stmt->store_result(); // Buffrar resultatet från frågan
// Loopar igenom individuella resultat
while ($stmt->fetch()) {
   echo $userName; // Innehåller resultatet från varje rad
}
$stmt->close();
?>

Exempel med flera variabler

Vi bygger på föregående SQL-fråga med några fler variabler och ser hur det kan se ut med mixade datatyper (och tabeller). Notera att datatyperna och variablerna i bind_param() måste följa samma ordning som de bundna parametrarna i SQL-frågan.

<?php
// Hämtar användarnamn och inloggningstid för användare med användarnivå över eller
// lika med ?, samt som har varit inloggade senast igår
$stmt = $mysqli->prepare("SELECT u.userName, l.lastLogin 
                            FROM siteUsers u, userLogins l 
                           WHERE u.userLevel >= ? 
                             AND u.id = l.userID 
                             AND l.lastLogin > ?");
$stmt->bind_param("is", $userLevel, $yesterday);
$stmt->execute();
$stmt->bind_result($userName, $lastLogin);
$stmt->store_result();
while ($stmt->fetch()) {
   echo $userName.': '.$lastLogin.'<br />';
}
$stmt->close();
?>

Andra nyttiga saker att ha koll på

Exemplen ovan illustrerar ett väldigt enkelt enkelt sätt att använda prepared statements med MySQLi, men sedan finns det ju också fler saker som kan vara bra att ha lite koll på vad gäller MySQLi rent generellt, t.ex. num_rows för att ta reda på hur många rader en fråga resulterar i, eller insert_id för att hämta det ID som den senaste INSERT-queryn genererade. Och så klart finns det mycket mer att använda sig av, men det här borde räcka för att komma igång. Läs gärna vidare här!

<?php
// Exempel på num_rows
$stmt = $mysqli->prepare("SELECT id, postName FROM blogPosts");
$stmt->execute();
$stmt->bind_result($postID, $postName);
$stmt->store_result();
// Kollar om det finns några blogginlägg
if ($stmt->num_rows) {
   echo $stmt->num_rows.' blogginlägg hittades:<br />';
   while ($stmt->fetch()) {
      echo $postID.': '.$postName.'<br />';
   }
} else {
   echo 'Tyvärr hittades inga blogginlägg. :-/';
}
$stmt->close();

// Exempel på insert_id
$stmt = $mysqli->prepare("INSERT INTO siteUsers SET userName = ?");
$stmt->bind_param("s", $userName);
$stmt->execute();
$userID = $stmt->insert_id; // Lagrar den nya användarens ID i en variabel
$stmt->close();
echo $userID; // Skriver ut den nya användarens ID
?>


Kommentarer


#1   Mario   2018-11-26 20:29  
Hej, sitter med 3 hemsidor med gammal kod, php 5.6 gammal sql.
Är det ett jättejobb att översätta/koda om? Mycket hämta och presentera data från tabeller, samt även manipulera befintlig data och lite mailbekräftelser och sånt.

#2   Hur fungerar?   2018-11-26 20:35  
Hej Mario,

Det behöver inte vara ett jättejobb, för du behöver inte skriva om allting till den objektorienterade varianten av MySQLi. Du kan byta ut de gamla mysql-funktionerna mot de nya mysqli-funktionerna. Se t.ex. detta Stack Overflow-svar: https://stackoverflow.com/a/1390625

Skriv en kommentar