Dlaczego nie powinienem używać funkcji mysql_ * w PHP?


2502

Jakie są techniczne powody, dla których nie należy korzystać z mysql_*funkcji? (na przykład mysql_query(), mysql_connect()i mysql_real_escape_string())?

Dlaczego powinienem używać czegoś innego, nawet jeśli działają na mojej stronie?

Jeśli nie działają w mojej witrynie, dlaczego dostaję takie błędy

Ostrzeżenie: mysql_connect (): Brak takiego pliku lub katalogu


Błąd podobny do: Błąd krytyczny: Nieprzechwycony błąd: Wywołanie niezdefiniowanej funkcji mysql_connect () ...
Bimal Poudel

Odpowiedzi:


2088

Rozszerzenie MySQL:

  • Nie jest w trakcie aktywnego rozwoju
  • Jest oficjalnie przestarzałe od wersji PHP 5.5 (wydanej w czerwcu 2013 r.).
  • Został całkowicie usunięty z PHP 7.0 (wydany w grudniu 2015)
    • Oznacza to, że na dzień 31 grudnia 2018 r . Nie istnieje w żadnej obsługiwanej wersji PHP. Jeśli używasz wersji PHP, która ją obsługuje, używasz wersji, która nie rozwiązuje problemów bezpieczeństwa.
  • Brakuje interfejsu OO
  • Nie obsługuje:
    • Nieblokujące, asynchroniczne zapytania
    • Przygotowane instrukcje lub sparametryzowane zapytania
    • Procedury składowane
    • Wiele wyciągów
    • Transakcje
    • „Nowa” metoda uwierzytelniania hasła (domyślnie włączona w MySQL 5.6; wymagana w 5.7)
    • Każda z nowych funkcji w MySQL 5.1 lub nowszej

Ponieważ jest przestarzałe, użycie go sprawia, że ​​Twój kod jest mniej odporny na przyszłe problemy.

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 .


286
Samo wycofanie jest wystarczającym powodem, aby ich uniknąć. Nie będzie ich tam pewnego dnia i nie będziesz szczęśliwy, jeśli na nich polegasz. Reszta to tylko lista rzeczy, które przy użyciu starych rozszerzeń powstrzymały ludzi od nauki.
Tim Post

111
Odstąpienie nie jest magiczną kulą, jak się wydaje, wszyscy tak myślą. Sam PHP nie będzie tego dnia, ale polegamy na narzędziach, którymi dysponujemy dzisiaj. Kiedy będziemy musieli zmienić narzędzia, zrobimy to.
Wyścigi lekkości na orbicie

133
@LightnessRacesinOrbit - Deprecation nie jest magiczną kulą, jest flagą, która mówi: „Rozpoznajemy to do bani, więc nie będziemy go wspierać dłużej”. Chociaż lepsza korekta kodu w przyszłości jest dobrym powodem do odejścia od przestarzałych funkcji, nie jest to jedyna (a nawet główna). Zmień narzędzia, ponieważ istnieją lepsze narzędzia, a nie dlatego, że jesteś do tego zmuszony. (A zmiana narzędzi przed zmuszeniem oznacza, że ​​nie uczysz się nowych tylko dlatego, że Twój kod przestał działać i wymaga naprawy wczoraj… co jest najgorszym czasem na naukę nowych narzędzi).
Quentin,

18
Jedną rzeczą, o której nie wspomniałem o braku przygotowanych oświadczeń, jest kwestia wydajności. Za każdym razem, gdy wydajesz instrukcję, coś musi ją skompilować, aby demon MySQL mógł ją zrozumieć. Dzięki temu API, jeśli wydasz 200 000 takich samych zapytań w pętli, to 200 000 razy trzeba je skompilować, aby MySQL je zrozumiał. Dzięki przygotowanym instrukcjom jest on kompilowany raz, a następnie wartości są parametryzowane w skompilowanym SQL.
Goldentoa11

20
@symcbean, Z pewnością nie obsługuje przygotowanych instrukcji. To w rzeczywistości główny powód, dla którego jest przestarzały. Bez (łatwych w użyciu) przygotowanych instrukcji rozszerzenie mysql często pada ofiarą iniekcji SQL.
rustyx

1287

PHP oferuje trzy różne interfejsy API do łączenia się z MySQL. Są to mysql(usunięte z PHP 7) mysqlii PDOrozszerzenia.

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_DEPRECATEDbłędy podczas łączenia się użytkowników MySQL, czy przez mysql_connect(), mysql_pconnect()lub niejawny funkcjonalność połączenia wbudowany ext/mysql.

ext/mysqlzostał 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/mysqldotyczy nie tylko bezpieczeństwa, ale także dostępu do wszystkich funkcji bazy danych MySQL.

ext/mysqlzostał 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_DEPRECATEDbłędy mogą być tłumione przez ustawienie error_reportingw 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 PDOsamouczek.


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.”

alternatywny tekst


Łą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 PDOobiekt. 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 DSNto w zasadzie ciąg opcji określających, PDOktó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 PDOExceptionobiekt, który można złapać w celu Exceptiondalszej 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 PDOw tryb wyjątku. Ponieważ niektóre PDOsterowniki nie obsługują natywnie przygotowanych instrukcji, PDOwykonuje 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 MySQLdomyślnie włączona w sterowniku, ale przygotowanie emulacji powinno być wyłączone, aby można było PDObezpiecznie 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, MySQLktó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ą setAttributemetody:

$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 PDOniż 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.

PDOoferuje lepsze rozwiązanie: wyjątki. Wszystko robimy z PDOpowinny być zapakowane w try- catchbloku. Możemy wymusić PDOprzejś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_WARNINGPodbicie 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ć PDOExceptionwł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(), PDOExceptionmoż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- catchjak 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 catchtej 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 PDOrzeczy:

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 offi 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, UPDATElub DELETEoświadczenia? Nie martw się, zaczynamy:


Wybieranie danych

PDO wybierz obraz

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 PDOmoż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 PDOStatementobiekt. 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_ASSOCw fetch()i fetchAll()kodu powyżej. Mówi PDOto 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 PDOStatementdokumentacji Fetch. .

Uzyskiwanie liczby wierszy :

Zamiast używać mysql_num_rowsdo uzyskania liczby zwróconych wierszy, możesz uzyskać PDOStatementi 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

Wstaw i zaktualizuj obraz PDO

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::execuruchom 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 ):

  1. 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 (?, ?)

  2. DBMS analizuje, kompiluje i wykonuje optymalizację zapytań na szablonie instrukcji i zapisuje wynik bez jego wykonania.

  3. 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.00dla 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 OOPznajomych 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, DELETEPrzygotowany zapytania

  1. 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);
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();

UWAGA:

Jednak PDOi / lub MySQLinie 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 /*"));

15
To, o czym powinien przeczytać powyższy dobro, powinno słusznie: przygotowane oświadczenie zabiera wszelkie sensowne użycie IN (...) construct.
Eugen Rieck

24
Pytanie brzmiało: „Dlaczego nie powinienem używać funkcji mysql_ * w PHP”? Ta odpowiedź, choć imponująca i pełna pomocnych informacji, wychodzi poza zakres i, jak mówi @trejder - 8 na 10 osób przegapi te informacje tylko dlatego, że nie mają 4 godzin na próby przepracowania to. Byłoby to o wiele bardziej wartościowe rozbite i wykorzystane jako odpowiedzi na kilka bardziej precyzyjnych pytań.
Alex McMillan,

Persoanlly Wolę mysqli i PDO. Ale do obsługi matryc próbowałem alternatywy function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();dla wyjątków. Działa w przypadku zgłaszania wyjątków.
kuldeep.kamboj

podajesz Doesn't support non-blocking, asynchronous queriesjako powód, aby nie używać mysql_ - powinieneś również podać ten powód, aby nie używać PDO, ponieważ PDO też tego nie obsługuje. (ale MySQLi to obsługuje)
hanshenrik

czy można użyć Charset utf8mb4_unicode_ci, ponieważ mam bazę danych, która tego używa?
Ryan Stone

301

Najpierw zacznijmy od standardowego komentarza, który dajemy wszystkim:

Proszę nie używać mysql_*funkcji w nowym kodzie . Nie są już utrzymywane i oficjalnie przestarzałe . Widzisz czerwone pole ? Dowiedz sięzamiast tegoo przygotowanych instrukcjach i użyj PDO lub MySQLi - ten artykuł pomoże ci zdecydować, które. Jeśli wybierzesz PDO, oto dobry tutorial .

Przejdźmy przez to zdanie po zdaniu i wyjaśnimy:

  • Nie są już utrzymywane i oficjalnie przestarzałe

    Oznacza to, że społeczność PHP stopniowo rezygnuje z obsługi tych bardzo starych funkcji. Prawdopodobnie nie będą istnieć w przyszłej (najnowszej) wersji PHP! Dalsze korzystanie z tych funkcji może uszkodzić kod w (nie tak odległej) przyszłości.

    NOWY! - ext / mysql jest teraz oficjalnie przestarzałe od PHP 5.5!

    Nowszy! ext / mysql został usunięty w PHP 7 .

  • Zamiast tego powinieneś nauczyć się przygotowanych wypowiedzi

    mysql_*rozszerzenie nie obsługuje przygotowanych instrukcji , co jest (między innymi) bardzo skutecznym środkiem zaradczym przeciwko SQL Injection . Naprawiono bardzo poważną lukę w aplikacjach zależnych od MySQL, która pozwala atakującym uzyskać dostęp do skryptu i wykonać dowolne zapytanie w bazie danych.

    Aby uzyskać więcej informacji, zobacz Jak zapobiec iniekcji SQL w PHP?

  • Widzisz czerwone pudełko?

    Gdy przejdziesz do dowolnej mysqlstrony podręcznika funkcji, zobaczysz czerwone pole wyjaśniające, że nie należy jej już używać.

  • Użyj PDO lub MySQLi

    Istnieją lepsze, bardziej niezawodne i dobrze zbudowane alternatywy, PDO - PHP Database Object , który oferuje pełne podejście OOP do interakcji z bazami danych, oraz MySQLi , który jest poprawą specyficzną dla MySQL.


6
Jest jeszcze jedna rzecz: myślę, że ta funkcja wciąż istnieje w PHP tylko z jednego powodu - kompatybilności ze starymi, nieaktualnymi, ale wciąż działającymi systemami CMS, e-commerce, biuletynami itp. Wreszcie zostanie usunięty i będziesz musiał przepisać swoje wniosek ...
Kamil

4
@Kamil: To prawda, ale tak naprawdę nie jest to powód, dla którego nie powinieneś tego używać. Powodem, dla którego go nie używasz, jest to, że jest starożytny, niepewny itp. :)
Duch Madary

4
@Mario - deweloperzy PHP mają proces i właśnie głosowali za formalnym wycofaniem ext / mysql od wersji 5.5. To już nie jest kwestia hipotetyczna.
SDC,

2
Dodanie kilku dodatkowych wierszy za pomocą sprawdzonej techniki, takiej jak PDO lub MySQLi, wciąż zapewnia łatwość użycia, jaką PHP zawsze oferowało. Mam nadzieję, ze względu na programistę, on / ona wie, że zobaczenie tych okropnych funkcji mysql * w dowolnym tutorialu faktycznie szkodzi lekcji i powinien powiedzieć OP, że ten rodzaj kodu jest taki dobry 10 lat temu - i powinien zakwestionować znaczenie samouczka!
FredTheWebGuy

1
Na co należy odpowiedzieć w odpowiedzi: przygotowane oświadczenie zabiera wszelkie sensowne użycie IN (...) construct.
Eugen Rieck

217

Łatwość użycia

Powody analityczne i syntetyczne zostały już wspomniane. Dla nowicjuszy jest bardziej znacząca zachęta do zaprzestania korzystania z datowanych funkcji mysql_.

Współczesne interfejsy API baz danych są po prostu łatwiejsze w użyciu.

W większości są to związane parametry, które mogą uprościć kod. A dzięki doskonałym samouczkom (jak widać powyżej) przejście na PDO nie jest zbyt uciążliwe.

Jednak przepisywanie większej bazy kodu jednocześnie wymaga czasu. Raison d'être dla tej pośredniej alternatywy:

Równoważne funkcje pdo_ * zamiast mysql_ *

Używając < pdo_mysql.php > możesz przełączyć się ze starych funkcji mysql_ przy minimalnym wysiłku . Dodaje pdo_opakowania funkcji, które zastępują ich mysql_odpowiedniki.

  1. Po prostu w każdym skrypcie wywołania, który musi wchodzić w interakcje z bazą danych. include_once("pdo_mysql.php");

  2. Usuń mysql_prefiks funkcji wszędzie i zastąp go pdo_.

    • mysql_connect() staje się pdo_connect()
    • mysql_query() staje się pdo_query()
    • mysql_num_rows() staje się pdo_num_rows()
    • mysql_insert_id() staje się pdo_insert_id()
    • mysql_fetch_array() staje się pdo_fetch_array()
    • mysql_fetch_assoc() staje się pdo_fetch_assoc()
    • mysql_real_escape_string() staje się pdo_real_escape_string()
    • i tak dalej...

  3. Twój kod będzie działał podobnie i nadal będzie wyglądał tak samo:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }

Zrobione.
Twój kod używa PDO.
Teraz nadszedł czas, aby z niego skorzystać .

Związane parametry mogą być łatwe w użyciu

Potrzebujesz tylko mniej nieporęcznego API.

pdo_query()dodaje bardzo łatwą obsługę powiązanych parametrów. Konwersja starego kodu jest prosta:

Przenieś zmienne z ciągu SQL.

  • Dodaj je jako parametry funkcji rozdzielane przecinkami do pdo_query().
  • Umieść znaki zapytania ?jako symbole zastępcze tam, gdzie wcześniej były zmienne.
  • Pozbądź się 'pojedynczych cudzysłowów, które poprzednio zawierały wartości / zmienne łańcuchowe.

Korzyść staje się bardziej oczywista w przypadku dłuższego kodu.

Często zmienne łańcuchowe są nie tylko interpolowane w SQL, ale łączone z ucieczkowymi wywołaniami pomiędzy nimi.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

Po ?zastosowaniu symboli zastępczych nie musisz się tym przejmować:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Pamiętaj, że pdo_ * nadal pozwala na albo .
Po prostu nie zmieniaj zmiennej i nie wiąż jej w tym samym zapytaniu.

  • Funkcja zastępcza jest zapewniona przez prawdziwy PDO.
  • W ten sposób dozwolone są również :namedlisty zastępcze później.

Co ważniejsze, możesz bezpiecznie przekazać zmienne $ _REQUEST [] za dowolnym zapytaniem. Gdy przesłane <form>pola odpowiadają strukturze bazy danych, jest ona jeszcze krótsza:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

Tyle prostoty. Wróćmy jednak do kilku porad dotyczących przepisywania i technicznych powodów, dla których możesz chcieć się pozbyć mysql_i uciec.

Napraw lub usuń dowolną sanitize()funkcję oldschool

Po przekonwertowaniu wszystkich mysql_połączeń na pdo_querypowiązane parametry usuń wszystkie zbędne pdo_real_escape_stringpołączenia.

W szczególności powinieneś naprawić dowolne sanitizelub cleanlub filterThislub clean_datafunkcje reklamowane w datowanych samouczkach w takiej lub innej formie:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Najbardziej rażącym błędem tutaj jest brak dokumentacji. Co ważniejsze, kolejność filtrowania była dokładnie w niewłaściwej kolejności.

  • Prawidłowa kolejność byłaby: deprecatedly stripslashesjako wezwanie najgłębszej, potem trim, potem strip_tags, htmlentitiesza kontekście wyjścia i tylko wreszcie _escape_stringjako jego stosowania powinien bezpośrednio poprzedzać SQL intersparsing.

  • Ale jako pierwszy krok po prostu pozbądź się_real_escape_string połączenia.

  • Być może będziesz musiał zatrzymać resztę sanitize()funkcji na razie, jeśli przepływ bazy danych i aplikacji oczekuje ciągów bezpiecznych w kontekście HTML. Dodaj komentarz, że odtąd stosuje się tylko znaki HTML.

  • Obsługa ciągów / wartości jest delegowana do PDO i jej sparametryzowanych instrukcji.

  • Jeśli wspomniano o stripslashes()Twojej funkcji odkażania, może to wskazywać na nadzór wyższego poziomu.

    Historyczna notatka na temat magicznych cytatów. Ta funkcja jest słusznie przestarzała. Często jest jednak nieprawidłowo przedstawiany jako nieudana funkcja bezpieczeństwa . Ale magiczne cytaty są tak samo nieudanym zabezpieczeniem, jak zawiodły piłki tenisowe jako źródło pożywienia. To po prostu nie było ich celem.

    Oryginalna implementacja w PHP2 / FI wprowadziła ją jawnie za pomocą „ cytatów zostanie automatycznie zmienionych, co ułatwi przesyłanie danych bezpośrednio do zapytań msql ”. W szczególności korzystanie z mSQL było przypadkowo bezpieczne , ponieważ obsługuje on tylko ASCII.
    Następnie PHP3 / Zend ponownie wprowadził magic_quotes dla MySQL i źle go udokumentował. Ale pierwotnie była to tylko wygoda , a nie bezpieczeństwo.

Czym różnią się przygotowane oświadczenia

Gdy wmieszasz zmienne łańcuchowe do zapytań SQL, nie jest to tylko bardziej skomplikowane. To także dodatkowy wysiłek, aby MySQL ponownie segregował kod i dane.

Zastrzyki SQL są po prostu gdy dane spadają do kontekstu kodu . Serwer bazy danych nie może później wykryć, gdzie PHP pierwotnie skleił zmienne między klauzulami zapytania.

Za pomocą powiązanych parametrów oddzielasz kod SQL i wartości kontekstu SQL w kodzie PHP. Ale nie pojawia się ponownie za kulisami (z wyjątkiem PDO :: EMULATE_PREPARES). Twoja baza danych odbiera niezróżnicowane polecenia SQL i wartości zmiennych 1: 1.

Chociaż ta odpowiedź podkreśla, że ​​powinieneś dbać o czytelność zalet upuszczania mysql_. Czasami występuje również przewaga wydajności (powtarzane WSTAWKI z tylko różnymi wartościami) dzięki temu widocznemu i technicznemu rozdzieleniu danych / kodów.

Uważaj, że wiązanie parametrów nadal nie jest magicznym kompleksowym rozwiązaniem dla wszystkich zastrzyków SQL. Obsługuje najczęściej używane dane / wartości. Nie można jednak dodać do białej listy nazw kolumn / identyfikatorów tabel, pomocy w dynamicznej konstrukcji klauzul ani po prostu list wartości tablic.

Zastosowanie hybrydowego PDO

Te pdo_*funkcje otoki tworzą przyjazny dla kodu interfejs API stop-gap. (Jest to właściwie to, co MYSQLImogłoby być, gdyby nie przesunięcie podpisu funkcji idiosynkratycznej). W większości przypadków ujawniają również prawdziwą chronioną nazwę pochodzenia.
Przepisywanie nie musi kończyć się na użyciu nowych nazw funkcji pdo_. Możesz po kolei przejść każde pdo_query () do zwykłego wywołania $ pdo-> prepar () -> execute ().

Jednak najlepiej zacząć od uproszczenia. Na przykład typowe pobieranie wyników:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Można zastąpić tylko iteracją Foreach:

foreach ($result as $row) {

Lub jeszcze lepiej bezpośrednie i pełne wyszukiwanie tablicy:

$result->fetchAll();

W większości przypadków otrzymasz bardziej pomocne ostrzeżenia niż PDO lub mysql_, które zwykle dostarczają po nieudanych zapytaniach.

Inne opcje

Mamy nadzieję, że zwizualizowałem kilka praktycznych powodów i wartą uwagi ścieżkę do upuszczenia mysql_.

Właśnie zmieniam na nie do końca to. pdo_query()jest także tylko nakładką na to.

O ile nie wprowadzisz również powiązania parametrów lub nie możesz użyć czegoś innego z ładniejszego interfejsu API, jest to bezcelowy przełącznik. Mam nadzieję, że obraz ten jest na tyle prosty, że nie zniechęca nowych przybyszów. (Edukacja zwykle działa lepiej niż prohibicja).

Chociaż kwalifikuje się do kategorii najprostszej rzeczy, która mogłaby ewentualnie działać, jest także bardzo eksperymentalnym kodem. Właśnie napisałem to w weekend. Istnieje jednak mnóstwo alternatyw. Wystarczy google dla abstrakcji bazy danych PHP i przeglądać trochę. Zawsze było i będzie wiele doskonałych bibliotek do takich zadań.

Jeśli chcesz jeszcze bardziej uprościć interakcję z bazą danych, warto spróbować twórców map , takich jak Paris / Idiorm . Tak jak nikt już nie używa nijakiego DOM w JavaScript, nie musisz obecnie opiekować się interfejsem surowej bazy danych.


8
Uważaj na pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);funkcję - tj .:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
rickyduck

@Tom Pewnie, choć nie jest to zbytnio utrzymywane (0.9.2 było ostatnim), możesz utworzyć konto kopalne , dodać do wiki lub zgłosić błąd (bez rejestracji IIRC).
mario

pdo_real_escape_string() <- Czy to nawet prawdziwa funkcja, nie mogę znaleźć dla niej dokumentacji? Proszę zamieścić źródło tego.
Ryan Stone

144

Te mysql_funkcje:

  1. są nieaktualne - nie są już utrzymywane
  2. nie pozwalają na łatwe przejście do innego zaplecza bazy danych
  3. dlatego nie obsługuj przygotowanych oświadczeń
  4. zachęcać programistów do korzystania z konkatenacji do tworzenia zapytań, co prowadzi do podatności na iniekcje SQL

18
# 2 jest równie prawdziwemysqli_
jajoal

16
szczerze mówiąc, biorąc pod uwagę różnice w dialekcie SQL, nawet PDO nie daje ci # 2 żadnego stopnia pewności. Potrzebujesz do tego odpowiedniego opakowania ORM.
SDC,

mysql_*funkcja to powłoka na funkcje mysqlnd nowsze wersje PHP. Więc nawet jeśli stara biblioteka klienta nie jest już utrzymywana, mysqlnd jest utrzymywany :)
hakre

Problemem jest to, że
niewielu

@RajuGujarati, więc znajdź hosta, który może. Jeśli Twój hosting nie, istnieje duże prawdopodobieństwo, że są podatne na ataki na ich serwery.
Alnitak,

106

Mówiąc o przyczynach technicznych , jest tylko kilka, wyjątkowo specyficznych i rzadko używanych. Najprawdopodobniej nigdy nie użyjesz ich w swoim życiu.
Może jestem zbyt nieświadomy, ale nigdy nie miałem okazji korzystać z takich rzeczy

  • nieblokujące, asynchroniczne zapytania
  • procedury składowane zwracające wiele zestawów wyników
  • Szyfrowanie (SSL)
  • Kompresja

Jeśli ich potrzebujesz - to bez wątpienia techniczne powody, aby odejść od rozszerzenia mysql w kierunku czegoś bardziej stylowego i nowocześnie wyglądającego.

Niemniej jednak istnieją również problemy pozatechniczne, które mogą sprawić, że twoje wrażenia będą nieco trudniejsze

  • dalsze korzystanie z tych funkcji w nowoczesnych wersjach PHP spowoduje pojawienie się przestarzałych powiadomień. Można je po prostu wyłączyć.
  • w odległej przyszłości można je ewentualnie usunąć z domyślnej kompilacji PHP. Nie jest to również wielka sprawa, ponieważ mydsql ext zostanie przeniesiony do PECL, a każdy hosting chętnie skompiluje z nim PHP, ponieważ nie chce tracić klientów, których strony działały przez dziesięciolecia.
  • silny opór społeczności Stackoverflow. Kiedy raz wspominasz o tych uczciwych funkcjach, powiedziano ci, że są pod ścisłym tabu.
  • będąc przeciętnym użytkownikiem PHP, najprawdopodobniej twój pomysł korzystania z tych funkcji jest podatny na błędy i niewłaściwy. Tylko z powodu tych wszystkich licznych samouczków i instrukcji, które uczą cię niewłaściwego sposobu. Nie same funkcje - muszę to podkreślić - ale sposób ich użycia.

Ten ostatni problem stanowi problem.
Ale moim zdaniem proponowane rozwiązanie też nie jest lepsze.
Wydaje mi się zbyt idealistycznym snem, że wszyscy ci użytkownicy PHP nauczą się, jak poprawnie obsługiwać zapytania SQL. Najprawdopodobniej po prostu zmieniliby mechanicznie mysql_ * na mysqli_ *, pozostawiając to samo podejście . Zwłaszcza, że ​​mysqli sprawia, że ​​korzystanie z przygotowanych wyciągów jest niesamowicie bolesne i kłopotliwe.
Nie wspominając, że natywnie przygotowane oświadczenia nie wystarczą do ochrony przed wstrzyknięciami SQL i ani mysqli, ani PDO nie oferują rozwiązania.

Zamiast walczyć z tym uczciwym rozszerzeniem, wolałbym zwalczać złe praktyki i edukować ludzi we właściwy sposób.

Ponadto istnieje kilka fałszywych lub nieistotnych przyczyn, takich jak

  • Nie obsługuje procedur przechowywanych (używaliśmy mysql_query("CALL my_proc");od wieków)
  • Nie obsługuje transakcji (tak jak powyżej)
  • Nie obsługuje wielu instrukcji (kto ich potrzebuje?)
  • Nie jest w trakcie aktywnego rozwoju (więc co to wpływa na ciebie w jakikolwiek praktyczny sposób?)
  • Brakuje interfejsu OO (utworzenie go jest kwestią kilku godzin)
  • Nie obsługuje przygotowanych instrukcji ani zapytań parametryzowanych

Ten ostatni jest interesującym punktem. Chociaż mysql ext nie obsługuje natywnie przygotowanych instrukcji, nie są one wymagane ze względu na bezpieczeństwo. Możemy łatwo sfałszować przygotowane wyciągi, używając ręcznie obsługiwanych symboli zastępczych (podobnie jak PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

voila , wszystko jest sparametryzowane i bezpieczne.

Ale dobrze, jeśli nie podoba ci się czerwone pole w instrukcji, pojawia się problem z wyborem: mysqli lub PDO?

Odpowiedź byłaby następująca:

  • Jeśli rozumiesz konieczność korzystania z warstwy abstrakcji bazy danych i szukania interfejsu API do jej utworzenia, mysqli jest bardzo dobrym wyborem, ponieważ rzeczywiście obsługuje wiele funkcji specyficznych dla mysql.
  • Jeśli, jak większość ludzi PHP, używasz surowych wywołań API bezpośrednio w kodzie aplikacji (co jest zasadniczo niewłaściwą praktyką) - PDO jest jedynym wyborem , ponieważ to rozszerzenie udaje nie tylko API, ale raczej pół-DAL, wciąż niekompletny, ale oferuje wiele ważnych funkcji, a dwie z nich wyraźnie odróżniają ChNP od mysqli:

    • w przeciwieństwie do mysqli, PDO może wiązać symbole zastępcze według wartości , co sprawia, że ​​dynamicznie budowane zapytania są możliwe bez kilku ekranów z dość niechlujnym kodem.
    • w przeciwieństwie do mysqli, PDO zawsze może zwrócić wynik zapytania w zwykłej zwykłej tablicy, podczas gdy mysqli może to zrobić tylko w instalacjach mysqlnd.

Tak więc, jeśli jesteś przeciętnym użytkownikiem PHP i chcesz zaoszczędzić mnóstwo kłopotów z użyciem natywnie przygotowanych instrukcji, PDO - znowu - jest jedynym wyborem.
Jednak ChNP nie jest też srebrną kulą i ma swoje trudności.
Tak więc napisałem rozwiązania wszystkich typowych pułapek i skomplikowanych przypadków na wiki tagu PDO

Niemniej jednak wszystkim, którzy mówią o rozszerzeniach, zawsze brakuje 2 ważnych faktów na temat Mysqli i PDO:

  1. Przygotowane oświadczenie nie jest srebrną kulą . Istnieją identyfikatory dynamiczne, których nie można powiązać przy użyciu przygotowanych instrukcji. Istnieją dynamiczne zapytania o nieznanej liczbie parametrów, co sprawia, że ​​tworzenie zapytań jest trudnym zadaniem.

  2. W kodzie aplikacji nie powinny pojawić się ani funkcje mysqli_ *, ani PDO. Pomiędzy nimi a kodem aplikacji
    powinna znajdować się warstwa abstrakcji , która wykona całą brudną robotę związania, zapętlenia, obsługi błędów itp. Wewnątrz, dzięki czemu kod aplikacji stanie się SUCHY i czysty. Zwłaszcza w skomplikowanych przypadkach, takich jak dynamiczne budowanie zapytań.

Tak więc samo przejście na PDO lub mysqli nie wystarczy. Należy użyć ORM, konstruktora zapytań lub dowolnej klasy abstrakcji bazy danych zamiast wywoływać surowe funkcje API w kodzie.
Przeciwnie - jeśli masz warstwę abstrakcji między kodem aplikacji a interfejsem API mysql - nie ma znaczenia, który silnik jest używany. Możesz używać mysql ext, dopóki nie stanie się przestarzały, a następnie łatwo przepisać klasę abstrakcji na inny silnik, zachowując cały kod aplikacji.

Oto kilka przykładów opartych na mojej klasie safemysql, które pokazują, jak powinna wyglądać taka klasa abstrakcji:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Porównaj tę pojedynczą linię z ilością kodu, której będziesz potrzebować w PDO .
Następnie porównaj z szaloną ilością kodu, której będziesz potrzebować z surowymi instrukcjami przygotowanymi przez Mysqli. Należy pamiętać, że obsługa błędów, profilowanie, rejestrowanie zapytań jest już wbudowane i uruchomione.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Porównaj go ze zwykłymi wstawkami PDO, gdy każda nazwa pola jest powtarzana sześć do dziesięciu razy - we wszystkich tych licznych nazwanych symbolach zastępczych, powiązaniach i definicjach zapytań.

Inny przykład:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Trudno znaleźć przykład PDO do obsługi tak praktycznego przypadku.
I będzie zbyt pracowity i najprawdopodobniej niebezpieczny.

Więc jeszcze raz - nie tylko surowy sterownik powinien być twoją troską, ale klasa abstrakcyjna, przydatna nie tylko dla niemądrych przykładów z podręcznika dla początkujących, ale do rozwiązania rzeczywistych problemów.


20
mysql_*sprawia, że ​​luki w zabezpieczeniach są bardzo łatwe do znalezienia. Ponieważ PHP jest używane przez wielu początkujących użytkowników, mysql_*jest aktywnie szkodliwe w praktyce, nawet jeśli teoretycznie można go używać bez żadnych problemów.
Madara's Ghost

4
everything is parameterized and safe- może to być parametryzowane, ale czynność nie używa prawdziwych przygotowanych sprawozdań.
uınbɐɥs

6
Jak to jest Not under active developmenttylko w przypadku tego „0,01%”? Jeśli zbudujesz coś z tą funkcją zatrzymania, zaktualizujesz wersję mysql w ciągu roku i skończysz z niedziałającym systemem, jestem pewien, że nagle jest tak dużo ludzi w tym „0,01%”. Powiedziałbym to deprecatedi not under active developmentsą blisko spokrewnieni. Można powiedzieć, że „nie ma [uzasadnionego] powodu”, ale faktem jest, że kiedy oferuje się wybór między opcjami, no active developmentjest prawie tak samo zły, jak deprecatedpowiedziałbym?
Nanne

1
@MadaraUchiha: Czy możesz wyjaśnić, w jaki sposób luki są bardzo łatwe do znalezienia? Zwłaszcza w przypadkach, w których te same luki nie wpływają na PDO ani MySQLi ... Ponieważ nie znam żadnej z nich, o której mówisz.
ircmaxell

4
@ShaquinTrifonoff: jasne, nie używa przygotowanych instrukcji. Ale nie ma też PDO , który większość ludzi poleca nad MySQLi. Nie jestem więc pewien, czy ma to tutaj znaczący wpływ. Powyższy kod (z nieco większą analizą) jest tym, co robi PDO, gdy domyślnie przygotowujesz instrukcję ...
ircmaxell

97

Istnieje wiele powodów, ale być może najważniejszym jest to, że funkcje te zachęcają do niepewnych praktyk programowania, ponieważ nie obsługują przygotowanych instrukcji. Przygotowane instrukcje pomagają zapobiegać atakom typu SQL injection.

Korzystając z mysql_*funkcji, musisz pamiętać, aby przez parametry uruchamiać parametry podane przez użytkownikamysql_real_escape_string() . Jeśli zapomnisz tylko w jednym miejscu lub jeśli zdarzy ci się uciec tylko części danych, baza danych może zostać zaatakowana.

Korzystanie z przygotowanych instrukcji w PDOlub mysqlisprawi, że tego rodzaju błędy programistyczne będą trudniejsze do wykonania.


3
Niestety słaba obsługa MySQLi_ * w przekazywaniu zmiennej liczby parametrów (np. Gdy chcesz przekazać listę wartości do sprawdzenia w klauzuli IN) zachęca do nieużywania parametrów, zachęcając do używania dokładnie takich samych połączonych zapytań, które pozostawić połączenia MySQL_ * wrażliwe.
Kickstart

5
Ale po raz kolejny niepewność nie jest nieodłącznym problemem funkcji mysql *, ale problemem nieprawidłowego użycia.
Agamemnus

2
@Agamemnus Problem polega na tym, że mysql_ * ułatwia wdrożenie „niepoprawnego użycia”, szczególnie dla niedoświadczonych programistów. Biblioteki, które implementują przygotowane instrukcje, utrudniają popełnienie tego typu błędu.
Trott

75

Ponieważ (między innymi) znacznie trudniej jest zapewnić, że dane wejściowe zostaną zdezynfekowane. Jeśli używasz sparametryzowanych zapytań, podobnie jak w przypadku PDO lub mysqli, możesz całkowicie uniknąć ryzyka.

Na przykład ktoś może użyć "enhzflep); drop table users"jako nazwy użytkownika. Stare funkcje pozwalają na wykonywanie wielu instrukcji na zapytanie, więc coś w rodzaju tego paskudnego robala może usunąć całą tabelę.

Gdyby użyć PDO mysqli, nazwa użytkownika byłaby ostatecznie "enhzflep); drop table users".

Zobacz bobby-tables.com .


10
The old functions will allow executing of multiple statements per query- nie, nie będą. Tego rodzaju wstrzykiwanie nie jest możliwe w przypadku ext / mysql - jedynym sposobem takiego wstrzykiwania jest możliwe w PHP i MySQL, gdy używa się MySQLi i mysqli_multi_query()funkcji. Rodzaj zastrzyku, który jest możliwy przy użyciu ext / mysql i ciągów bezkształtnych, to takie rzeczy, jak ' OR '1' = '1wyodrębnianie danych z bazy danych, która nie miała być dostępna. W niektórych sytuacjach możliwe jest wstrzykiwanie pod-zapytań, jednak nadal nie jest możliwe zmodyfikowanie bazy danych w ten sposób.
DaveRandom

64

Ta odpowiedź została napisana, aby pokazać, jak banalne jest omijanie źle napisanego kodu sprawdzania poprawności użytkownika PHP, jak (i ​​za pomocą czego) te ataki działają oraz jak zastąpić stare funkcje MySQL bezpieczną przygotowaną instrukcją - i, w zasadzie, dlaczego użytkownicy StackOverflow (prawdopodobnie z dużą liczbą powtórzeń) szczekają na nowych użytkowników, zadając pytania w celu ulepszenia kodu.

Po pierwsze, proszę utworzyć tę testową bazę danych mysql (nazwałem mój prep):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

Po wykonaniu tej czynności możemy przejść do naszego kodu PHP.

Załóżmy, że następujący skrypt to proces weryfikacji dla administratora w witrynie (uproszczony, ale działa, jeśli skopiujesz go i użyjesz do testowania):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Na pierwszy rzut oka wydaje się wystarczająca.

Użytkownik musi wprowadzić login i hasło, prawda?

Genialny, nie wpisuj:

user: bob
pass: somePass

i prześlij to.

Dane wyjściowe są następujące:

You could not be verified. Please try again...

Wspaniały! Działając zgodnie z oczekiwaniami, spróbujmy teraz rzeczywistej nazwy użytkownika i hasła:

user: Fluffeh
pass: mypass

Niesamowity! Cześć piątki, kod poprawnie zweryfikował administratora. Jest idealny!

Cóż, nie za bardzo. Powiedzmy, że użytkownik jest sprytną małą osobą. Powiedzmy, że ta osoba to ja.

Wpisz następujące dane:

user: bob
pass: n' or 1=1 or 'm=m

Wyjście to:

The check passed. We have a verified admin!

Gratulacje, właśnie pozwoliłeś mi wpisać twoją super-chronioną sekcję tylko dla administratorów, a ja wpisałem fałszywą nazwę użytkownika i fałszywe hasło. Poważnie, jeśli mi nie wierzysz, utwórz bazę danych z podanym przeze mnie kodem i uruchom ten kod PHP - który na pierwszy rzut oka NAPRAWDĘ wydaje się raczej ładnie weryfikować nazwę użytkownika i hasło.

Tak więc, w odpowiedzi, DLACZEGO JESTEŚ WKRĘTY.

Zobaczmy więc, co poszło nie tak i dlaczego właśnie dostałem się do twojej jaskini superadministratora. Zgadłem i założyłem, że nie byłeś ostrożny ze swoimi danymi wejściowymi i po prostu przekazałem je bezpośrednio do bazy danych. Skonstruowałem dane wejściowe w taki sposób, aby ZMIENIĆ zapytanie, które faktycznie uruchomiłeś. Więc co to miało być i czym się to stało?

select id, userid, pass from users where userid='$user' and pass='$pass'

To jest zapytanie, ale kiedy zamieniamy zmienne na rzeczywiste dane wejściowe, których użyliśmy, otrzymujemy:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

Zobacz, jak skonstruowałem moje „hasło”, aby najpierw zamknęło pojedynczy cytat wokół hasła, a następnie wprowadzić zupełnie nowe porównanie? Następnie dla bezpieczeństwa dodałem kolejny „ciąg”, aby pojedynczy cytat został zamknięty zgodnie z oczekiwaniami w kodzie, który pierwotnie mieliśmy.

Nie chodzi tu jednak o ludzi krzyczących na ciebie, chodzi o pokazanie, jak zwiększyć bezpieczeństwo kodu.

Okej, więc co poszło nie tak i jak możemy to naprawić?

Jest to klasyczny atak typu SQL injection. Jeden z najprostszych pod tym względem. W skali wektorów ataku jest to maluch atakujący czołg - i wygrywający.

Jak więc chronimy waszą świętą sekcję administracyjną i sprawiamy, że jest ładna i bezpieczna? Pierwszą rzeczą do zrobienia będzie zaprzestanie używania tych naprawdę starych i przestarzałychmysql_* funkcji. Wiem, że obejrzałeś samouczek, który znalazłeś w Internecie i działa, ale jest stary, jest przestarzały i w ciągu kilku minut właśnie go przekroczyłem, nie tracąc nawet potu.

Teraz masz lepsze opcje korzystania z mysqli_ lub PDO . Osobiście jestem wielkim fanem PDO, więc w dalszej części tej odpowiedzi będę używać PDO. Są plusy i minusy, ale osobiście uważam, że pro znacznie przewyższają minusy. Jest przenośny w wielu silnikach baz danych - bez względu na to, czy korzystasz z MySQL, Oracle czy po prostu coś cholernego - po prostu zmieniając ciąg połączenia, ma wszystkie fantazyjne funkcje, których chcemy używać, i jest ładny i czysty. Lubię czyste.

Teraz spójrzmy jeszcze raz na ten kod, tym razem napisany przy użyciu obiektu PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Główne różnice polegają na tym, że nie ma już żadnych mysql_*funkcji. Wszystko to odbywa się za pośrednictwem obiektu PDO, po drugie, używa przygotowanej instrukcji. A teraz, o co pytasz? Jest to sposób na poinformowanie bazy danych przed uruchomieniem zapytania, jakie to zapytanie zostanie uruchomione. W tym przypadku mówimy do bazy danych: „Cześć, zamierzam uruchomić instrukcję select, szukając identyfikatora, identyfikatora użytkownika i przekazania od użytkowników tabeli, w których identyfikator użytkownika jest zmienną, a hasło jest również zmienną.”.

Następnie w instrukcji wykonania przekazujemy do bazy danych tablicę ze wszystkimi zmiennymi, których teraz oczekuje.

Rezultaty są fantastyczne. Spróbujmy jeszcze raz te kombinacje nazwy użytkownika i hasła:

user: bob
pass: somePass

Użytkownik nie został zweryfikowany. Niesamowite.

Co powiesz na:

user: Fluffeh
pass: mypass

Och, właśnie się trochę podnieciłem, zadziałało: czek mija. Mamy zweryfikowanego administratora!

Teraz spróbujmy danych, które wprowadziłby sprytny facet, aby ominąć nasz mały system weryfikacji:

user: bob
pass: n' or 1=1 or 'm=m

Tym razem otrzymujemy:

You could not be verified. Please try again...

Właśnie dlatego jesteś krzyczany, gdy piszesz pytania - to dlatego, że ludzie widzą, że Twój kod można ominąć nawet bez próby. Proszę, skorzystaj z tego pytania i odpowiedzi, aby ulepszyć swój kod, aby był bardziej bezpieczny i korzystać z aktualnych funkcji.

Na koniec nie oznacza to, że jest to kod PERFECT. Jest wiele innych rzeczy, które możesz zrobić, aby to poprawić, na przykład użyj zaszyfrowanych haseł, upewnij się, że podczas przechowywania wrażliwych informacji w bazie danych nie przechowujesz ich jako zwykłego tekstu, masz wiele poziomów weryfikacji - ale tak naprawdę, jeśli po prostu zmieniasz swój stary podatny na wstrzykiwanie kod na ten, będziesz DOBRZE na drodze do pisania dobrego kodu - a fakt, że zaszedłeś tak daleko i wciąż czytasz, daje mi poczucie nadziei, że nie tylko zaimplementujesz ten typ kodu podczas pisania stron internetowych i aplikacji, ale możesz wyjść i zbadać inne rzeczy, o których właśnie wspomniałem - i więcej. Napisz najlepszy możliwy kod, a nie najbardziej podstawowy, który ledwo funkcjonuje.


2
Dziękuję za Twoją odpowiedź! Mam moje +1! Warto zauważyć, że mysql_*sam w sobie nie jest niebezpieczny, ale promuje niepewny kod poprzez złe samouczki i brak odpowiedniej instrukcji przygotowującej API.
Duch Madary

2
niezasłonięte hasła, oh horror! = oP W przeciwnym razie +1 dla szczegółowego wyjaśnienia.
tajemniczy ツ

33

Rozszerzenie MySQL jest najstarszym z trzech i było oryginalnym sposobem, w jaki programiści używali komunikacji z MySQL. To rozszerzenie jest obecnie przestarzałe na korzyść dwóch pozostałych alternatyw z powodu ulepszeń wprowadzonych w nowszych wersjach zarówno PHP, jak i MySQL.

  • MySQLi to „ulepszone” rozszerzenie do pracy z bazami danych MySQL. Korzysta z funkcji, które są dostępne w nowszych wersjach serwera MySQL, udostępnia deweloperowi zarówno interfejs zorientowany na funkcje, jak i zorientowany obiektowo oraz robi kilka innych fajnych rzeczy.

  • PDO oferuje interfejs API, który konsoliduje większość funkcji, które wcześniej były rozproszone w głównych rozszerzeniach dostępu do bazy danych, tj. MySQL, PostgreSQL, SQLite, MSSQL itp. Interfejs udostępnia obiekty wysokiego poziomu dla programisty do pracy z połączeniami z bazami danych, zapytaniami i zestawy wyników i sterowniki niskiego poziomu wykonują komunikację i obsługę zasobów z serwerem bazy danych. Dużo dyskusji i pracy dotyczy PDO i jest uważana za odpowiednią metodę pracy z bazami danych w nowoczesnym, profesjonalnym kodzie.


21

Uważam, że powyższe odpowiedzi są bardzo długie, więc podsumowując:

Rozszerzenie mysqli ma wiele zalet, a najważniejsze ulepszenia w stosunku do rozszerzenia mysql to:

  • Interfejs obiektowy
  • Obsługa przygotowanych wyciągów
  • Obsługa wielu instrukcji
  • Obsługa transakcji
  • Ulepszone możliwości debugowania
  • Obsługa wbudowanego serwera

Źródło: Przegląd MySQLi


Jak wyjaśniono w powyższych odpowiedziach, alternatywą dla mysql są mysqli i PDO (PHP Data Objects).

  • Interfejs API obsługuje przygotowane przez serwer instrukcje: Obsługiwane przez MYSQLi i PDO
  • Interfejs API obsługuje przygotowane przez klienta instrukcje: Obsługiwane tylko przez PDO
  • Interfejs API obsługuje procedury przechowywane: zarówno MySQLi, jak i PDO
  • Interfejs API obsługuje wiele instrukcji i wszystkie funkcje MySQL 4.1+ - obsługiwane przez MySQLi, a przede wszystkim również przez PDO

Zarówno MySQLi, jak i PDO zostały wprowadzone w PHP 5.0, natomiast MySQL został wprowadzony przed PHP 3.0. Należy zauważyć, że MySQL jest zawarty w PHP5.x, ale jest przestarzały w późniejszych wersjach.


2
Twoja odpowiedź jest zbyt długa, a prawdziwe podsumowanie brzmi: „mysql ext is not more”. To wszystko
Twój zdrowy rozsądek,

1
@ YourCommonSense Moja odpowiedź brzmi: dlaczego mysqli zastąpiło mysql. Nie chodzi o to, że Mysqli istnieje dzisiaj, więc używaj go ... Wszyscy to wiedzą!
Ani Menon,

1
Cóż, poza tym, że nikt nie pytał, dlaczego mysqli zastąpił mysql, nie odpowiada również na to pytanie. Odpowiada, dlaczego wprowadzono mysqli. Ale to nie tłumaczy, dlaczego mysql i mysqli nie mogli żyć równolegle
Your Common Sense

@YourCommonSense Także pytanie PO brzmi: „Dlaczego powinienem używać czegoś innego, nawet jeśli działają na mojej stronie?” i dlatego wskazałem zmiany i ulepszenia. Możesz spojrzeć na wszystkie inne odpowiedzi, które są długie, więc pomyślałem, że powinienem to streścić.
Ani Menon,

6

Możliwe jest zdefiniowanie prawie wszystkich mysql_*funkcji za pomocą mysqli lub PDO. Wystarczy dołączyć je do starej aplikacji PHP, a będzie działać na PHP7. Moje rozwiązanie tutaj .

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}

Zamiast wyświetlać link do swojego rozwiązania, dodaj je tutaj jako odpowiedź.
amarnath

1

Funkcje, które są podobne do tego mysql_connect(), mysql_query()są poprzednimi wersjami PHP tj. (PHP 4) i teraz nie są używane.

Są one zastępowane przez mysqli_connect(), mysqli_query()podobnie jak w najnowszym PHP5.

To jest przyczyna błędu.


2
PHP 5 nie jest najnowszy od ponad 2 lat.
Duch Madary

1

MySQL jest przestarzałe w PHP 5.5.0 i usunięte w PHP 7.0.0. W przypadku dużej i starej aplikacji trudno jest wyszukać i zastąpić każdą funkcję.

Możemy korzystać z funkcji MySQL, tworząc funkcję otoki dla każdego uruchomionego kodu. Kliknij tutaj


-9

Funkcje mysql_ * były przestarzałe (od PHP 5.5 ), ponieważ opracowano lepsze funkcje i struktury kodu. Fakt, że funkcja została wycofana, oznacza, że ​​nie będzie się więcej starać o jej poprawienie pod względem wydajności i bezpieczeństwa, co oznacza, że ​​jest mniej odporna na przyszłość .

Jeśli potrzebujesz więcej powodów:

  • Funkcje mysql_ * nie obsługują przygotowanych instrukcji.
  • Funkcje mysql_ * nie obsługują wiązania parametrów.
  • Funkcje mysql_ * nie mają funkcji programowania obiektowego.
  • i tak dalej ...

18
Ta odpowiedź jest nieaktualna. Poza tym nie dodaje nic użytecznego do odpowiedzi, które już istnieją.
Twój zdrowy rozsądek
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.