Suma inteligencji na planecie jest stała. Liczba ludności rośnie.
PHP i MySQL

Interfej serwera MySQL w PHP

PHP zawiera dwa moduły do współpracy z MySQL:

  1. Moduł mysql używany we wszystkich wersjach PHP. O ile to możliwe, to nie korzystaj z tego przestarzałego modułu.
     
  2. Moduł mysqli przeznaczony dla wersji MySQL 4.1.3 i późniejszych, dostępny w PHP od wersji 5.
    Poniżej omówiony jest ten właśnie moduł.
    Poprawia on w stosunku do poprzedniego wydajność i bezpieczeństwo.
     

Mysqli

Poniżej znajdziesz kilka przykładowych skryptów pokazujących typowe metody odczytywania i przetwarzania danych. Dokładny opis modułu mysqli wraz z przykładami znajdziesz tutaj.

Praca z bazą danych - metoda proceduralna

<?php

/* Nawiązanie połączenia z serwerem */
$link = mysqli_connect(
	'localhost',	/* serwer, na którym działa MySQL */
	'user',         /* nazwa użytkownika */
	'password',     /* hasło użytkownika */
	'baza');        /* wybrana baza danych */

if (!$link)
{	echo "Nie mogę połączyć się z serwerem MySQL. Kod błędu: " . mysqli_connect_error();
	exit;
}
?>

Zmienna $link jest zasobem nawiązanego połączenia. Jeżeli jednak połączenia nie uda się ustanowić, to jej wartością będzie fałsz. Za pomocą warunku if sprawdzamy, czy udało się połączyć z serwerem, a jeżeli nie, to zwracamy kod błędu i przerywamy działanie skryptu.

Po nawiązaniu połączenia możemy wykonywać dowolne instrukcje SQL, a ich wynik wykorzystać w skrypcie, np.:

<?php

/* Wykonujemy zapytanie */
if ($result = mysqli_query($link, 'SELECT nazwisko,imie FROM tabela ORDER BY nazwisko,imie'))
{	echo "Lista osób:<br />\n";

	/* Przetwarzamy wiersze wyniku zapytania */
	while( $row = mysqli_fetch_assoc($result) )
	{	echo $row['nazwisko'] . ' ' . $row['imie'] . '<br />';
	}
	
	/* Usuwamy z pamięci wynik zapytania */
	mysqli_free_result($result);
}

/* Zamykamy połączenie z bazą */
mysqli_close($link);

?>

Kilka uwag do powyższego przykładu:

  1. Zmienna $result zawiera wynik zapytania.
     
  2. Przed wykonania zapytania (wywołania procedury mysqli_query) konieczne jest nawiązanie połączenie z bazą.
     
  3. Instrukcja SQL jest umieszczona w apostrofach i nie jest zakończona średnikiem.
     
  4. Ze względu na prostotę i wydajność sortowanie danych wykonuje serwer MySQL.
     
  5. Zapytanie zwraca tylko potrzebne dane.
     
  6. Wynik zapytania jest zwracany w postaci tablicy (procedura mysqli_fetch_assoc), której kolejne wiersze są wyświetlone na ekranie.
     
  7. Po przetworzeniu wynik zapytania jest usuwany, a pamięć zwalniana.
     
  8. Na koniec zamykamy aktywne połączenie z serwerem MySQL.
     

Ponieważ otwieranie połączeń z bazą wymaga sporo czasu, to warto raz nawiązać je na początku skryptu i zamknąć dopiero na jego końcu.

Praca z bazą danych - metoda obiektowa

Poniższy przykład jest odpowiednikiem poprzedniego, ale tym razem nawiązanie połączenia, wykonanie zapytania, przetworzenie wyników i zamknięcie połączenia realizowane jest obiektowo.

<?php

/* Łączymy się z serwerem */
$mysqli = new mysqli('localhost', 'user', 'password', 'baza');

if (mysqli_connect_errno())
{	echo ""Nie mogę połączyć się z serwerem MySQL. Kod błędu: " . mysqli_connect_error();
	exit;
}

/* Wykonujemy zapytanie */
if ($result = $mysqli->query(SELECT nazwisko,imie FROM tabela ORDER BY nazwisko,imie'))
{	echo "Lista osób:<br />\n";

	/* Przetwarzamy wiersze wyniku zapytania */
	while( $row = $result->fetch_assoc())
	{	echo $row['nazwisko'] . ' ' . $row['imie'] . '<br />';
	}
	
	/* Usuwamy wynik zapytania z pamięci */
	$result->close();
}

/* Zamykamy połączenie z bazą */
$mysqli->close();
?>
  1. W tym wypadku nie musimy przechowywać referencji do połączenia w osobnej zmiennej.
     
  2. Wywołując fetch_assoc(), nie musimy jawnie wskazywać na zmienną zawierającą wynik zapytania.
     

Parameryzowanie instrukcji SQL

Często wygodnie jest użyć w instrukcji SQL zmiennych, których wartości można dynamicznie ustawiać z poziomu PHP. Problem polega na tym, żeby instrukcje typu:

	SELECT nazwisko, imie
	FROM tabela
	WHERE nazwisko LIKE 'K%'

zastąpić instrukcjami typu:

	SELECT nazwisko, imie
	FROM tabela
	WHERE nazwisko =  ?

a w miejsce znaku ? dynamicznie wstawiać odpowiednią wartość przed wykonaniem instrukcji, np:

<?php

$mysqli = new mysqli('localhost', 'user', 'password', 'baza');

/* Test połączenia */
if (mysqli_connect_errno())
{	echo ""Nie mogę połączyć się z serwerem MySQL. Kod błędu: " . mysqli_connect_error();
	exit;
}

/* Przygotowanie instrukcji SQL */
$stmt = $mysqli->prepare("INSERT INTO tabela 
				(nazwisko, imie, wiek) VALUES ( ?, ?, ?)");
$stmt->bind_param('ssi', $nazwisko, $imie, $wiek);

$nazwisko = 'Kowalski';
$imie     = 'Jan';
$wiek     = 18;

/* Wykonujemy przygotowaną instrukcję */
$stmt->execute();

echo "Wstawiono wierszy: " . $stmt->affected_rows);

/* Usuwamy wynik zapytania z pamięci */
$stmt->close();

/* Zamykamy połączenie z bazą */
$mysqli->close();
?>

Uwagi:

  1. Pierwszy parametr metody bind_param() określa typ wiązanych parametrów. W naszym przypadku 'ssi' oznacza, że pierwsze 2 parametry są łańcuchami znaków, a ostatni - liczbą całkowitą.
     
  2. Przed wykonaniem instrukcji SQL musimy określić wartości wszystkich parametrów.
     
  3. Z atrybutu affected_rows, dowiemy się, ile wierszy zostało zmodyfikowanych w ramach ostatnio wykonywanej instrukcji.
     

Parameryzowanie wyników instrukcji SQL

Zwracane przez zapytania wyniki mogą być także wiązane poprzez zmienne PHP z polami tekstowymi, polami wyboru czy polami w tabelach, np.:

<?php
	
$mysqli = new mysqli("localhost", "user", "password", "baza");

/* Test połączenia */
if (mysqli_connect_errno())
{	echo ""Nie mogę połączyć się z serwerem MySQL. Kod błędu: " . mysqli_connect_error();
	exit;
}

/* Przygotowanie i wykonanie instrukcji SQL */
if ($stmt = $mysqli->prepare("SELECT nazwisko,imie,wiek FROM tabela ORDER BY wiek DESC LIMIT 5"))
{	$stmt->execute();

	/* Powiązanie zmiennych z wynikiem zapytania */
	$stmt->bind_result($col1, $col2, $col3);

	/* Przetwarzanie wyniku */
	while ($stmt->fetch())
	{	echo $col1 .' ' . $col2 . ' ma ' . $col3 . ' lat<br />';
	}

	/* Usuwamy wynik zapytania z pamięci */
	$stmt->close();
}

/* Zamykamy połączenie z bazą */
$mysqli->close();
?>

Uwagi na temat bezpieczeństwa

Ponieważ SQL jest językiem interpretowanym, to dowolny ciąg znaków będzie zinterpretowany przez serwer MySQL. Jeżeli ten ciąg będzie poprawną instrukcją języka SQL, to zostanie wykonany. W przeciwnym razie serwer zgłosi błąd. Problem dotyczy przede wszystkim zapisu do bazy danych pobranych z formularzy wypełnianych przez użytkowników.

Podstawowe reguły bezpieczeństwa baz danych:

  • Sprawdzanie poprawności danych wprowadzanych przez użytkowników i filtrowaniu wysyłanych od nich informacji.
     
  • Łączenie się z bazą danych w kontekście konta o jak najmniejszych uprawnieniach - jeżeli zalogowany użytkownik ma jedynie prawo do odczytu danych z określonych widoków i do wywołania kilku procedur, to nawet udany atak nie naruszy bezpieczeństwa bazy danych.
     
  • Jeżeli jest to tylko możliwe, to należy zrezygnować z używania w ciągach znaków znaczników języka SQL. Przede wszystkim apostrofów i myślników.
     
  • Należy zablokować przesyłanie niepoprawnych danych. Takie rozwiązanie powinno być wykorzystane we wszystkich aplikacjach internetowych. Jeżeli wymagany będzie zwiększony poziom bezpieczeństwa, należy dodatkowo zezwolić na przetwarzanie wyłącznie poprawnych danych.
     
  • Wszystkie komunikaty błędów powinny być przechwycone przez aplikację, a informacje o błędzie (w tym oryginalny komunikat błędu) powinny być zapisane w pliku dziennika, a użytkownicy przekierowani na stronę zawierającą ogólną informację o tym, że wystąpił błąd.
     
  • Prostym i skutecznym rozwiązaniem jest zrezygnowanie z dynamicznego budowania instrukcji języka SQL z wykorzystaniem danych wprowadzonych przez użytkowników. Zamiast tego wprowadzane przez użytkowników dane mogą być przekazywane do serwera bazodanowego jako parametry wywołania procedur.
     
« wstecz   dalej »