PHP oferuje trzy różne interfejsy API do łączenia się z MySQL. Są to mysql
(usunięte z PHP 7) mysqli
i PDO
rozszerzenia.
Te mysql_*
funkcje kiedyś bardzo popularne, ale ich stosowanie nie jest zalecane więcej. Zespół dokumentacji omawia sytuację w zakresie bezpieczeństwa bazy danych i częścią tego jest szkolenie użytkowników, jak odejść od powszechnie używanego rozszerzenia ext / mysql (sprawdź php.internals: wycofywanie ext / mysql ).
A później zespół PHP developer podjął decyzję, aby generować E_DEPRECATED
błędy podczas łączenia się użytkowników MySQL, czy przez mysql_connect()
, mysql_pconnect()
lub niejawny funkcjonalność połączenia wbudowany ext/mysql
.
ext/mysql
został oficjalnie uznany za przestarzały od wersji PHP 5.5 i został usunięty od wersji PHP 7 .
Widzisz czerwone pudełko?
Gdy przejdziesz do dowolnej mysql_*
strony podręcznika funkcji, zobaczysz czerwone pole wyjaśniające, że nie należy jej już używać.
Dlaczego
Odejście od ext/mysql
dotyczy nie tylko bezpieczeństwa, ale także dostępu do wszystkich funkcji bazy danych MySQL.
ext/mysql
został zbudowany dla MySQL 3.23 i od tego czasu otrzymał bardzo niewiele dodatków, przy jednoczesnym zachowaniu zgodności z tą starą wersją, co utrudnia utrzymanie kodu. Brakujące funkcje, które nie są obsługiwane przez ext/mysql
: ( z podręcznika PHP ).
Powód nieużywania mysql_*
funkcji :
- Nie w trakcie aktywnego rozwoju
- Usunięto z PHP 7
- Brakuje interfejsu OO
- Nie obsługuje nieblokujących zapytań asynchronicznych
- Nie obsługuje przygotowanych instrukcji ani sparametryzowanych zapytań
- Nie obsługuje procedur przechowywanych
- Nie obsługuje wielu instrukcji
- Nie obsługuje transakcji
- Nie obsługuje wszystkich funkcji MySQL 5.1
Powyższy punkt cytowany z odpowiedzi Quentina
Brak wsparcia dla przygotowanych instrukcji jest szczególnie ważny, ponieważ zapewniają one bardziej przejrzystą, mniej podatną na błędy metodę ucieczki i cytowania danych zewnętrznych niż ręczne ucieczkowanie ich osobnym wywołaniem funkcji.
Zobacz porównanie rozszerzeń SQL .
Pomijanie ostrzeżeń o wycofaniu
Natomiast kod jest konwertowana do MySQLi
/ PDO
, E_DEPRECATED
błędy mogą być tłumione przez ustawienie error_reporting
w php.ini wykluczaniaE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Zauważ, że to ukryje także inne ostrzeżenia o wycofaniu , które jednak mogą dotyczyć rzeczy innych niż MySQL. ( z instrukcji PHP )
Artykuł PDO vs. MySQLi: Którego należy użyć? autor: Dejan Marjanovic pomoże ci wybrać.
I jest lepszy sposób PDO
, a teraz piszę prosty PDO
samouczek.
Prosty i krótki samouczek PDO
P: Pierwsze pytanie w mojej głowie brzmiało: co to jest „PDO”?
A. „ PDO - PHP Data Objects - to warstwa dostępu do bazy danych zapewniająca jednolitą metodę dostępu do wielu baz danych.”
Łączenie z MySQL
Z mysql_*
funkcją lub możemy to powiedzieć po staremu (przestarzałe w PHP 5.5 i nowszych)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Z PDO
: Wszystko, co musisz zrobić, to utworzyć nowy PDO
obiekt. Konstruktor przyjmuje parametry określające źródło bazy PDO
„S konstruktor przeważnie czterech parametrów, które są DSN
(nazwa źródła danych) i ewentualnie username
, password
.
Tutaj myślę, że znasz wszystko oprócz DSN
; to jest nowy w PDO
. A DSN
to w zasadzie ciąg opcji określających, PDO
którego sterownika należy użyć, i szczegóły połączenia. Aby uzyskać dodatkowe informacje, sprawdź PDO MySQL DSN .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Uwaga: możesz także użyć charset=UTF-8
, ale czasami powoduje to błąd, więc lepiej jest użyć utf8
.
Jeśli wystąpi jakikolwiek błąd połączenia, wyrzuci PDOException
obiekt, który można złapać w celu Exception
dalszej obsługi .
Dobra lektura : Połączenia i zarządzanie połączeniami ¶
Możesz również przekazać kilka opcji sterownika jako tablicę do czwartego parametru. Polecam przekazanie parametru, który przechodzi PDO
w tryb wyjątku. Ponieważ niektóre PDO
sterowniki nie obsługują natywnie przygotowanych instrukcji, PDO
wykonuje emulację przygotowywania. Pozwala także ręcznie włączyć tę emulację. Aby użyć natywnych instrukcji przygotowanych po stronie serwera, należy je jawnie ustawić false
.
Drugim jest wyłączenie przygotowania emulacji, która jest MySQL
domyślnie włączona w sterowniku, ale przygotowanie emulacji powinno być wyłączone, aby można było PDO
bezpiecznie korzystać .
Wyjaśnię później, dlaczego należy przygotować emulację. Aby znaleźć powód, sprawdź ten post .
Można go używać tylko wtedy, gdy używasz starej wersji, MySQL
której nie zaleciłem.
Poniżej znajduje się przykład tego, jak możesz to zrobić:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Czy możemy ustawić atrybuty po budowie PDO?
Tak , możemy również ustawić niektóre atrybuty po konstrukcji PDO za pomocą setAttribute
metody:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Obsługa błędów
Obsługa błędów jest znacznie łatwiejsza PDO
niż mysql_*
.
Powszechną praktyką podczas używania mysql_*
jest:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
nie jest dobrym sposobem na poradzenie sobie z błędem, ponieważ nie możemy sobie poradzić die
. To po prostu nagle zakończy skrypt, a następnie powtórzy błąd na ekranie, którego zwykle NIE chcesz pokazywać użytkownikom końcowym, i pozwoli cholernym hakerom odkryć twój schemat. Alternatywnie, zwracane wartości mysql_*
funkcji mogą być często używane w połączeniu z mysql_error () do obsługi błędów.
PDO
oferuje lepsze rozwiązanie: wyjątki. Wszystko robimy z PDO
powinny być zapakowane w try
- catch
bloku. Możemy wymusić PDO
przejście do jednego z trzech trybów błędów, ustawiając atrybut trybu błędu. Poniżej przedstawiono trzy tryby obsługi błędów.
PDO::ERRMODE_SILENT
. Po prostu ustawia kody błędów i działa prawie tak samo, jak mysql_*
tam, gdzie trzeba sprawdzić każdy wynik, a następnie spojrzeć, $db->errorInfo();
aby uzyskać szczegółowe informacje o błędzie.
PDO::ERRMODE_WARNING
Podbicie E_WARNING
. (Ostrzeżenia w czasie wykonywania (błędy niekrytyczne). Wykonanie skryptu nie jest zatrzymane.)
PDO::ERRMODE_EXCEPTION
: Zgłaszaj wyjątki. Reprezentuje błąd zgłoszony przez ChNP. Nie powinieneś wyrzucać PDOException
własnego kodu. Zobacz Wyjątki, aby uzyskać więcej informacji o wyjątkach w PHP. Zachowuje się bardzo podobnie or die(mysql_error());
, gdy nie zostanie złapany. Ale w przeciwieństwie do tego or die()
, PDOException
można je złapać i traktować z wdziękiem, jeśli zdecydujesz się to zrobić.
Dobra lektura :
Lubić:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
I możesz to owinąć try
- catch
jak poniżej:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
Nie musisz sobie z tym poradzić try
- w catch
tej chwili. Możesz go złapać w dowolnym momencie, ale zdecydowanie zalecamy użycie try
- catch
. Bardziej sensowne może być złapanie go poza funkcją wywołującą te PDO
rzeczy:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
Możesz sobie poradzić or die()
lub możemy to powiedzieć mysql_*
, ale będzie to bardzo zróżnicowane. Możesz ukryć niebezpieczne komunikaty o błędach podczas produkcji, obracając display_errors off
i po prostu czytając dziennik błędów.
Teraz, po przeczytaniu wszystkich rzeczy powyżej, prawdopodobnie myśląc: co do cholery jest, że kiedy tylko chcesz rozpocząć pochylony proste SELECT
, INSERT
, UPDATE
lub DELETE
oświadczenia? Nie martw się, zaczynamy:
Wybieranie danych
Więc to, co robisz, mysql_*
to:
<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());
$num_rows = mysql_num_rows($result);
while($row = mysql_fetch_assoc($result)) {
echo $row['field1'];
}
Teraz PDO
możesz to zrobić w następujący sposób:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
Lub
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Uwaga : Jeśli używasz metody jak poniżej ( query()
), ta metoda zwraca PDOStatement
obiekt. Więc jeśli chcesz pobrać wynik, użyj go jak powyżej.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
W PDO Data jest uzyskiwany za ->fetch()
pomocą metody obsługi wyciągu. Przed wywołaniem funkcji pobierania najlepszym rozwiązaniem byłoby określenie PDO, w jaki sposób dane mają być pobierane. W poniższej sekcji wyjaśniam to.
Tryby pobierania
Zwróć uwagę na użycie PDO::FETCH_ASSOC
w fetch()
i fetchAll()
kodu powyżej. Mówi PDO
to o zwróceniu wierszy jako tablicy asocjacyjnej z nazwami pól jako kluczami. Istnieje również wiele innych trybów pobierania, które wyjaśnię jeden po drugim.
Przede wszystkim wyjaśniam, jak wybrać tryb pobierania:
$stmt->fetch(PDO::FETCH_ASSOC)
W powyższym używam fetch()
. Możesz także użyć:
Teraz przechodzę do trybu pobierania:
PDO::FETCH_ASSOC
: zwraca tablicę indeksowaną według nazwy kolumny, tak jak zwrócono w zestawie wyników
PDO::FETCH_BOTH
(domyślnie): zwraca tablicę zindeksowaną zarówno przez nazwę kolumny, jak i numer kolumny o indeksie 0, jak zwrócono w zestawie wyników
Jest jeszcze więcej możliwości! Przeczytaj o nich wszystkich w PDOStatement
dokumentacji Fetch. .
Uzyskiwanie liczby wierszy :
Zamiast używać mysql_num_rows
do uzyskania liczby zwróconych wierszy, możesz uzyskać PDOStatement
i zrobić rowCount()
, na przykład:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Uzyskiwanie ostatniego wstawionego identyfikatora
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Wstawianie i aktualizowanie lub usuwanie instrukcji
To, co robimy w mysql_*
funkcji, to:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
I w pdo, to samo można zrobić poprzez:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
W powyższym zapytaniu PDO::exec
uruchom instrukcję SQL i zwróci liczbę dotkniętych wierszy.
Wstaw i usuń zostaną omówione później.
Powyższa metoda jest przydatna tylko wtedy, gdy nie używasz zmiennej w zapytaniu. Ale kiedy potrzebujesz użyć zmiennej w zapytaniu, nigdy nie próbuj tak jak wyżej, a tam jest gotowa instrukcja lub instrukcja sparametryzowana .
Przygotowane oświadczenia
P: Co to jest przygotowane oświadczenie i dlaczego ich potrzebuję?
Odp .: Przygotowana instrukcja to wstępnie skompilowana instrukcja SQL, którą można wykonać wiele razy, wysyłając tylko dane do serwera.
Typowy przepływ pracy przy użyciu przygotowanej instrukcji jest następujący ( cytowany z Wikipedii trzy 3 punkty ):
Przygotuj : Szablon instrukcji jest tworzony przez aplikację i wysyłany do systemu zarządzania bazą danych (DBMS). Niektóre wartości pozostają nieokreślone, nazywane parametrami, symbolami zastępczymi lub zmiennymi powiązania (oznaczone ?
poniżej):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMS analizuje, kompiluje i wykonuje optymalizację zapytań na szablonie instrukcji i zapisuje wynik bez jego wykonania.
- Wykonaj : w późniejszym czasie aplikacja dostarcza (lub wiąże) wartości parametrów, a DBMS wykonuje instrukcję (ewentualnie zwraca wynik). Aplikacja może wykonać instrukcję tyle razy, ile chce z różnymi wartościami. W tym przykładzie może podać „Chleb” dla pierwszego parametru i
1.00
dla drugiego parametru.
Możesz użyć przygotowanej instrukcji, umieszczając symbole zastępcze w kodzie SQL. Zasadniczo są trzy bez symboli zastępczych (nie próbuj tego ze zmienną powyżej jednego), jeden z nienazwanymi symbolami zastępczymi i jeden z nazwanymi symbolami zastępczymi.
P: Więc jakie są teraz nazwane symbole zastępcze i jak ich używać?
A. Nazwane symbole zastępcze. Używaj opisowych nazw poprzedzonych dwukropkiem zamiast znaków zapytania. Nie dbamy o pozycję / porządek wartości w nazwie zastępczej:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Możesz również powiązać za pomocą tablicy wykonawczej:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Inną ciekawą funkcją dla OOP
znajomych jest to, że nazwane symbole zastępcze mają możliwość wstawiania obiektów bezpośrednio do bazy danych, zakładając, że właściwości pasują do nazwanych pól. Na przykład:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
P: Więc jakie są teraz nienazwane symbole zastępcze i jak ich używać?
A. Weźmy przykład:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
i
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
Powyżej możesz zobaczyć te ?
zamiast nazwy, jak w miejscu na nazwę. Teraz w pierwszym przykładzie przypisujemy zmienne do różnych symboli zastępczych ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Następnie przypisujemy wartości do tych symboli zastępczych i wykonujemy instrukcję. W drugim przykładzie pierwszy element tablicy przechodzi do pierwszego, ?
a drugi do drugiego ?
.
UWAGA : W nienazwanych symbolach zastępczych musimy zadbać o prawidłową kolejność elementów w tablicy, którą przekazujemy do PDOStatement::execute()
metody.
SELECT
, INSERT
, UPDATE
, DELETE
Przygotowany zapytania
SELECT
:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
UWAGA:
Jednak PDO
i / lub MySQLi
nie są całkowicie bezpieczne. Sprawdź odpowiedź Czy instrukcje przygotowane przez PDO są wystarczające, aby zapobiec wstrzyknięciu SQL? przez ircmaxell . Cytuję też część jego odpowiedzi:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));