Mengapa saya tidak menggunakan fungsi mysql_ * di PHP?

2502

Apa alasan teknis mengapa seseorang tidak boleh menggunakan mysql_*fungsi? (misalnya mysql_query(), mysql_connect()atau mysql_real_escape_string())?

Mengapa saya harus menggunakan sesuatu yang lain walaupun mereka bekerja di situs saya?

Jika mereka tidak berfungsi di situs saya, mengapa saya mendapatkan kesalahan seperti

Peringatan: mysql_connect (): Tidak ada file atau direktori tersebut

Hantu Madara
sumber
Kesalahan menjadi seperti: Kesalahan fatal: Kesalahan tidak tertangkap: Panggilan ke fungsi yang tidak terdefinisi mysql_connect () ...
Bimal Poudel
21
Sudah ditinggalkan

Jawaban:

2089

Ekstensi MySQL:

  • Tidak dalam pengembangan aktif
  • Secara resmi tidak digunakan pada PHP 5.5 (dirilis Juni 2013).
  • Telah dihapus seluruhnya pada PHP 7.0 (dirilis Desember 2015)
    • Ini berarti bahwa pada 31 Desember 2018 tidak ada dalam versi PHP yang didukung. Jika Anda menggunakan versi PHP yang mendukungnya, Anda menggunakan versi yang tidak memperbaiki masalah keamanan.
  • Tidak memiliki antarmuka OO
  • Tidak mendukung:
    • Kueri non-pemblokiran, asinkron
    • Pernyataan yang disiapkan atau pertanyaan parameter
    • Prosedur tersimpan
    • Berbagai Pernyataan
    • Transaksi
    • Metode otentikasi kata sandi "baru" (diaktifkan secara default di MySQL 5.6; diperlukan pada 5.7)
    • Salah satu fungsi baru di MySQL 5.1 atau yang lebih baru

Karena sudah tidak digunakan lagi, menggunakannya akan membuat kode Anda kurang menjadi bukti di masa mendatang.

Kurangnya dukungan untuk pernyataan yang disiapkan sangat penting karena memberikan metode yang lebih jelas, tidak rawan kesalahan untuk keluar dan mengutip data eksternal daripada secara manual menghindarinya dengan pemanggilan fungsi yang terpisah.

Lihat perbandingan ekstensi SQL .

Quentin
sumber
287
Sudah ditinggalkan sendirian adalah alasan yang cukup untuk menghindarinya. Mereka tidak akan berada di sana suatu hari, dan Anda tidak akan bahagia jika Anda mengandalkan mereka. Sisanya hanyalah daftar hal-hal yang menggunakan ekstensi lama membuat orang tidak belajar.
Tim Post
111
Penghentian bukanlah peluru ajaib yang tampaknya semua orang pikirkan. PHP sendiri tidak akan ada di sana suatu hari, namun kami mengandalkan alat yang kami miliki saat ini. Ketika kita harus mengubah alat, kita akan melakukannya.
Lightness Races in Orbit
133
@LightnessRacesinOrbit - Penghentian bukanlah peluru ajaib, itu adalah bendera yang mengatakan "Kami mengenali ini menyebalkan jadi kami tidak akan mendukungnya lebih lama". Walaupun memiliki pemeriksaan kode yang lebih baik di masa depan adalah alasan yang baik untuk menjauh dari fitur yang sudah usang, ini bukan satu-satunya (atau bahkan yang utama). Ganti alat karena ada alat yang lebih baik, bukan karena Anda dipaksa. (Dan mengganti alat sebelum Anda dipaksa untuk berarti bahwa Anda tidak mempelajari yang baru hanya karena kode Anda telah berhenti bekerja dan perlu diperbaiki kemarin ... yang merupakan waktu terburuk untuk mempelajari alat baru).
Quentin
18
Satu hal yang belum saya lihat disebutkan tentang kurangnya pernyataan yang disiapkan adalah masalah kinerja. Setiap kali Anda mengeluarkan pernyataan, sesuatu harus dikompilasi sehingga daemon MySQL dapat memahaminya. Dengan API ini, jika Anda mengeluarkan 200.000 dari permintaan yang sama dalam satu lingkaran, itu adalah 200.000 kali permintaan harus dikompilasi agar MySQL dapat memahaminya. Dengan pernyataan yang disiapkan, itu dikompilasi sekali, dan kemudian nilai-nilai parameter ke dalam SQL yang dikompilasi.
Goldentoa11
20
@symcbean, Ini pasti tidak mendukung pernyataan yang disiapkan. Itu sebenarnya alasan utama mengapa itu usang. Tanpa (mudah digunakan) pernyataan yang disiapkan ekstensi mysql sering menjadi korban serangan injeksi SQL.
rustyx
1287

PHP menawarkan tiga API berbeda untuk terhubung ke MySQL. Ini adalah mysql(dihapus pada PHP 7) mysqli,, dan PDOekstensi.

The mysql_*fungsi yang digunakan untuk menjadi sangat populer, tetapi penggunaannya tidak dianjurkan lagi. Tim dokumentasi sedang membahas situasi keamanan basis data, dan mendidik pengguna untuk menjauh dari ekstensi ext / mysql yang umum digunakan adalah bagian dari ini (lihat php.internals: deprecating ext / mysql ).

Dan tim pengembang PHP kemudian telah mengambil keputusan untuk menghasilkan E_DEPRECATEDkesalahan ketika pengguna terhubung ke MySQL, apakah melalui mysql_connect(), mysql_pconnect()atau fungsi koneksi implisit yang dibangun ext/mysql.

ext/mysqlsecara resmi tidak digunakan lagi pada PHP 5.5 dan telah dihapus pada PHP 7 .

Lihat Kotak Merah?

Ketika Anda melanjutkan mysql_* halaman manual fungsi, Anda melihat kotak merah, menjelaskannya tidak boleh digunakan lagi.

Mengapa


Beranjak dari ext/mysqltidak hanya tentang keamanan, tetapi juga tentang memiliki akses ke semua fitur dari database MySQL.

ext/mysqldibangun untuk MySQL 3.23 dan hanya mendapat sedikit tambahan sejak saat itu sementara sebagian besar menjaga kompatibilitas dengan versi lama ini yang membuat kode sedikit lebih sulit untuk dipelihara. Fitur yang hilang yang tidak didukung oleh ext/mysqltermasuk: ( dari manual PHP ).

Alasan untuk tidak menggunakan mysql_*fungsi :

  • Tidak dalam pengembangan aktif
  • Dihapus pada PHP 7
  • Tidak memiliki antarmuka OO
  • Tidak mendukung kueri tak-blokir dan asinkron
  • Tidak mendukung pernyataan yang disiapkan atau permintaan parameter
  • Tidak mendukung prosedur tersimpan
  • Tidak mendukung banyak pernyataan
  • Tidak mendukung transaksi
  • Tidak mendukung semua fungsi di MySQL 5.1

Poin di atas dikutip dari jawaban Quentin

Kurangnya dukungan untuk pernyataan yang disiapkan sangat penting karena memberikan metode yang lebih jelas, lebih tidak rentan untuk melarikan diri dan mengutip data eksternal daripada secara manual menghindarinya dengan pemanggilan fungsi yang terpisah.

Lihat perbandingan ekstensi SQL .


Menekan peringatan penghentian

Saat kode sedang dikonversi ke MySQLi/ PDO, E_DEPRECATEDkesalahan bisa ditekan dengan menetapkan error_reportingdi php.ini untuk dikecualikanE_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Perhatikan bahwa ini juga akan menyembunyikan peringatan penghentian lainnya , yang, bagaimanapun, mungkin untuk hal-hal selain MySQL. ( dari manual PHP )

Artikel PDO vs MySQLi: Yang Harus Anda Gunakan? oleh Dejan Marjanovic akan membantu Anda memilih.

Dan cara yang lebih baik adalah PDO, dan sekarang saya sedang menulis PDOtutorial sederhana .


Tutorial PDO sederhana dan pendek


Q. Pertanyaan pertama yang ada di pikiran saya adalah: apa itu `PDO`?

A. " PDO - Objek Data PHP - adalah lapisan akses basis data yang menyediakan metode seragam untuk akses ke banyak basis data."

teks alternatif


Menghubungkan ke MySQL

Dengan mysql_*fungsi atau kita dapat mengatakannya dengan cara lama (tidak digunakan lagi dalam PHP 5.5 dan lebih tinggi)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Dengan PDO: Yang perlu Anda lakukan adalah membuat PDOobjek baru . Konstruktor menerima parameter untuk menentukan PDOkonstruktor sumber database kebanyakan mengambil empat parameter yaitu DSN(nama sumber data) dan secara opsional username,password .

Di sini saya pikir Anda sudah terbiasa dengan semua kecuali DSN; ini baru dalam PDO. A DSNpada dasarnya adalah serangkaian opsi yang memberi tahu PDOdriver mana yang harus digunakan, dan detail koneksi. Untuk referensi lebih lanjut, periksa PDO MySQL DSN .

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Catatan: Anda juga dapat menggunakan charset=UTF-8, tetapi terkadang menyebabkan kesalahan, jadi lebih baik digunakanutf8 .

Jika ada kesalahan koneksi, itu akan melempar PDOExceptionobjek yang bisa ditangkap untuk menangani Exceptionlebih lanjut.

Bacaan baik : Koneksi dan Manajemen koneksi ¶

Anda juga dapat mengirimkan beberapa opsi driver sebagai larik ke parameter keempat. Saya sarankan meneruskan parameter yang menempatkan PDOke mode pengecualian. Karena beberapa PDOpengemudi tidak mendukung pernyataan persiapan asli, jadi PDOlakukan persaingan persiapan. Ini juga memungkinkan Anda mengaktifkan emulasi ini secara manual. Untuk menggunakan pernyataan yang disiapkan di sisi server asli, Anda harus mengaturnya secara eksplisit false.

Yang lain adalah untuk menonaktifkan persiapan emulasi yang diaktifkan di MySQLdriver secara default, tetapi mempersiapkan emulasi harus dimatikan untuk digunakan PDOdengan aman.

Saya nanti akan menjelaskan mengapa persiapan persaingan harus dimatikan. Untuk menemukan alasan silakan periksa posting ini .

Ini hanya dapat digunakan jika Anda menggunakan versi lama MySQLyang tidak saya rekomendasikan.

Di bawah ini adalah contoh bagaimana Anda dapat melakukannya:

$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));

Bisakah kita menetapkan atribut setelah konstruksi PDO?

Ya , kami juga dapat menetapkan beberapa atribut setelah konstruksi PDO dengan setAttributemetode:

$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);

Penanganan Kesalahan


Menangani kesalahan jauh lebih mudah PDOdaripada mysql_*.

Praktik umum saat menggunakan mysql_*adalah:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()bukan cara yang baik untuk menangani kesalahan karena kita tidak dapat menangani masalahnya die. Ini hanya akan mengakhiri skrip tiba-tiba dan kemudian mengulangi kesalahan ke layar yang biasanya Anda TIDAK ingin ditampilkan kepada pengguna akhir Anda, dan biarkan peretas berdarah menemukan skema Anda. Sebagai alternatif, nilai-nilai mysql_*fungsi yang kembali sering dapat digunakan bersama dengan mysql_error () untuk menangani kesalahan.

PDOmenawarkan solusi yang lebih baik: pengecualian. Apa pun yang kita lakukan PDOharus dibungkus dengan try- catchblok. Kita dapat memaksa PDOke salah satu dari tiga mode kesalahan dengan mengatur atribut mode kesalahan. Tiga mode penanganan kesalahan ada di bawah ini.

  • PDO::ERRMODE_SILENT. Itu hanya mengatur kode kesalahan dan bertindak hampir sama dengan di mysql_*mana Anda harus memeriksa setiap hasil dan kemudian melihat $db->errorInfo();untuk mendapatkan detail kesalahan.
  • PDO::ERRMODE_WARNINGAngkat E_WARNING. (Peringatan run-time (kesalahan non-fatal). Eksekusi skrip tidak dihentikan.)
  • PDO::ERRMODE_EXCEPTION: Melempar pengecualian. Ini merupakan kesalahan yang diangkat oleh PDO. Anda tidak boleh melempar PDOExceptiondari kode Anda sendiri. Lihat Pengecualian untuk informasi lebih lanjut tentang pengecualian dalam PHP. Kerjanya sangat mirip or die(mysql_error());, ketika tidak tertangkap. Tapi tidak seperti itu or die(), PDOExceptiondapat ditangkap dan ditangani dengan anggun jika Anda memilih untuk melakukannya.

Bagus dibaca :

Suka:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Dan Anda dapat membungkusnya dalam try- catch, seperti di bawah ini:

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());
}

Anda tidak harus menangani try- catchsekarang. Anda dapat menangkapnya kapan saja sesuai, tetapi saya sangat menyarankan Anda untuk menggunakan try- catch. Juga mungkin lebih masuk akal untuk menangkapnya di luar fungsi yang memanggil PDObarang:

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

Juga, Anda dapat menangani or die()atau kita dapat mengatakan suka mysql_*, tetapi itu akan sangat bervariasi. Anda dapat menyembunyikan pesan kesalahan berbahaya dalam produksi dengan memutar display_errors offdan hanya membaca log kesalahan Anda.

Sekarang, setelah membaca semua hal di atas, Anda mungkin berpikir: apa sih adalah bahwa ketika saya hanya ingin memulai bersandar sederhana SELECT, INSERT, UPDATE, atau DELETEpernyataan? Jangan khawatir, ini dia:


Memilih Data

Gambar pilihan PDO

Jadi yang Anda lakukan mysql_*adalah:

<?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'];
}

Sekarang PDO, Anda dapat melakukan ini seperti:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

Atau

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

Catatan : Jika Anda menggunakan metode seperti di bawah ini ( query()), metode ini mengembalikan PDOStatementobjek. Jadi jika Anda ingin mengambil hasilnya, gunakan seperti di atas.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

Dalam Data PDO, diperoleh melalui ->fetch()metode penanganan pernyataan Anda. Sebelum menelepon mengambil, pendekatan terbaik adalah memberi tahu PDO bagaimana Anda ingin data diambil. Pada bagian di bawah ini saya menjelaskan ini.

Ambil Mode

Perhatikan penggunaan PDO::FETCH_ASSOCdalam fetch()dan fetchAll()kode di atas. Ini memberitahu PDOuntuk mengembalikan baris sebagai array asosiatif dengan nama bidang sebagai kunci. Ada banyak mode pengambilan lainnya yang akan saya jelaskan satu per satu.

Pertama-tama, saya jelaskan bagaimana memilih mode pengambilan:

 $stmt->fetch(PDO::FETCH_ASSOC)

Di atas, saya telah menggunakan fetch(). Anda juga bisa menggunakan:

Sekarang saya datang untuk mengambil mode:

  • PDO::FETCH_ASSOC: mengembalikan array yang diindeks oleh nama kolom seperti yang dikembalikan di set hasil Anda
  • PDO::FETCH_BOTH (default): mengembalikan array yang diindeks dengan nama kolom dan nomor kolom yang diindeks 0 seperti yang dikembalikan di set hasil Anda

Bahkan ada lebih banyak pilihan! Baca tentang semuanya di PDOStatementAmbil dokumentasi. .

Mendapatkan jumlah baris :

Alih-alih menggunakan mysql_num_rowsuntuk mendapatkan jumlah baris yang dikembalikan, Anda bisa mendapatkan PDOStatementdan melakukannya rowCount(), seperti:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

Mendapatkan ID yang Terakhir Dimasukkan

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

Sisipkan dan Perbarui atau Hapus pernyataan

Masukkan dan perbarui gambar PDO

Apa yang kami lakukan dalam mysql_*fungsi adalah:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

Dan di pdo, hal yang sama dapat dilakukan oleh:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

Dalam kueri di atas, PDO::execjalankan pernyataan SQL dan kembalikan jumlah baris yang terpengaruh.

Sisipkan dan hapus akan dibahas nanti.

Metode di atas hanya berguna ketika Anda tidak menggunakan variabel dalam kueri. Tetapi ketika Anda perlu menggunakan variabel dalam kueri, jangan pernah mencoba seperti di atas dan di sana untuk pernyataan disiapkan atau pernyataan parameter .


Pernyataan Disiapkan

Q. Apa pernyataan yang disiapkan dan mengapa saya membutuhkannya?
A. Pernyataan yang disiapkan adalah pernyataan SQL pra-kompilasi yang dapat dieksekusi beberapa kali dengan mengirimkan hanya data ke server.

Alur kerja khas menggunakan pernyataan yang disiapkan adalah sebagai berikut ( dikutip dari Wikipedia tiga poin 3 ):

  1. Mempersiapkan : Templat pernyataan dibuat oleh aplikasi dan dikirim ke sistem manajemen basis data (DBMS). Nilai-nilai tertentu dibiarkan tidak ditentukan, disebut parameter, placeholder atau variabel terikat (berlabel di ?bawah):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. DBMS mem-parsing, mengkompilasi, dan melakukan optimisasi kueri pada template pernyataan, dan menyimpan hasilnya tanpa menjalankannya.

  3. Jalankan : Di lain waktu, aplikasi memasok (atau mengikat) nilai untuk parameter, dan DBMS mengeksekusi pernyataan (mungkin mengembalikan hasil). Aplikasi dapat menjalankan pernyataan sebanyak yang diinginkan dengan nilai yang berbeda. Dalam contoh ini, ini mungkin menyediakan 'Roti' untuk parameter pertama dan 1.00untuk parameter kedua.

Anda bisa menggunakan pernyataan yang disiapkan dengan menyertakan placeholder dalam SQL Anda. Pada dasarnya ada tiga yang tanpa penampung (jangan coba ini dengan variabel di atas satu), satu dengan penampung yang tidak disebutkan namanya, dan satu dengan penampung yang disebutkan.

P. Jadi sekarang, apa yang disebut placeholder dan bagaimana cara menggunakannya?
A. Penampung yang ditunjuk. Gunakan nama deskriptif yang didahului dengan titik dua, bukan tanda tanya. Kami tidak peduli dengan posisi / urutan nilai pada pemegang nama tempat:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

Anda juga dapat mengikat menggunakan array eksekusi:

<?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);

Fitur bagus lain untuk OOPteman adalah placeholder yang diberi nama memiliki kemampuan untuk menyisipkan objek langsung ke database Anda, dengan asumsi properti cocok dengan bidang yang disebutkan. Sebagai contoh:

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);

Q. Jadi sekarang, apa saja placeholder tanpa nama dan bagaimana cara menggunakannya?
A. Mari kita punya contoh:

<?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();

dan

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

Di atas, Anda dapat melihat itu ?alih - alih nama seperti pada tempat tempat nama. Sekarang dalam contoh pertama, kami menetapkan variabel ke berbagai placeholder ( $stmt->bindValue(1, $name, PDO::PARAM_STR);). Kemudian, kami memberikan nilai kepada placeholder tersebut dan menjalankan pernyataan tersebut. Dalam contoh kedua, elemen array pertama pergi ke yang pertama ?dan yang kedua ke yang kedua ?.

CATATAN : Di placeholder tanpa nama, kita harus menjaga urutan elemen yang tepat dalam array yang kita lewati ke PDOStatement::execute()metode.


SELECT, INSERT, UPDATE, DELETEDisiapkan query

  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();

CATATAN:

Namun PDOdan / atau MySQLitidak sepenuhnya aman. Periksa jawabannya. Apakah pernyataan yang disiapkan PDO cukup untuk mencegah injeksi SQL? oleh ircmaxell . Juga, saya mengutip beberapa bagian dari jawabannya:

$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 /*"));
NullPoiиteя
sumber
15
Apa yang sebaiknya dibaca oleh yang baik di atas: pernyataan yang sudah disiapkan menghilangkan penggunaan yang berarti dari IN (...) construct.
Eugen Rieck
24
Pertanyaannya adalah "Mengapa saya tidak boleh menggunakan fungsi mysql_ * dalam PHP". Jawaban ini, walaupun mengesankan dan penuh dengan informasi yang bermanfaat, keluar dari jangkauan dan seperti yang dikatakan @trejder - 8 dari 10 orang akan kehilangan informasi itu hanya karena mereka tidak memiliki 4 jam untuk dihabiskan untuk mencoba bekerja melalui Itu. Ini akan jauh lebih berharga dipecah dan digunakan sebagai jawaban untuk beberapa pertanyaan yang lebih tepat.
Alex McMillan
Persoanlly Saya lebih suka mysqli dan PDO. Tetapi untuk penanganan mati, saya mencoba pengecualian alternatif function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();Ini bekerja untuk melempar pengecualian.
kuldeep.kamboj
Anda mendaftar Doesn't support non-blocking, asynchronous queriessebagai alasan untuk tidak menggunakan mysql_ - Anda juga harus mencantumkan itu sebagai alasan untuk tidak menggunakan PDO, karena PDO juga tidak mendukungnya. (tapi MySQLi mendukungnya)
hanshenrik
apakah mungkin untuk menggunakan Charset utf8mb4_unicode_ci karena saya memiliki database yang menggunakan ini?
Ryan Stone
301

Pertama, mari kita mulai dengan komentar standar yang kami berikan kepada semua orang:

Tolong, jangan gunakan mysql_*fungsi dalam kode baru . Mereka tidak lagi dipertahankan dan secara resmi ditinggalkan . Lihat kotak merah ? Pelajari lebih lanjut tentang pernyataan yang disiapkan , dan gunakan PDO atau MySQLi - artikel ini akan membantu Anda memutuskan yang mana. Jika Anda memilih PDO, berikut adalah tutorial yang bagus .

Mari kita selesaikan ini, kalimat demi kalimat, dan jelaskan:

  • Mereka tidak lagi dipertahankan, dan secara resmi ditinggalkan

    Ini berarti bahwa komunitas PHP secara bertahap menjatuhkan dukungan untuk fungsi-fungsi yang sangat lama ini. Mereka sepertinya tidak ada dalam versi PHP yang akan datang (baru-baru ini)! Terus menggunakan fungsi-fungsi ini dapat merusak kode Anda di masa depan (tidak begitu).

    BARU! - ext / mysql sekarang secara resmi tidak digunakan lagi pada PHP 5.5!

    Baru! ext / mysql telah dihapus dalam PHP 7 .

  • Sebagai gantinya, Anda harus mempelajari pernyataan yang sudah disiapkan

    mysql_*ekstensi tidak mendukung pernyataan yang disiapkan , yang (antara lain) merupakan penanggulangan yang sangat efektif terhadap SQL Injection . Ini memperbaiki kerentanan yang sangat serius dalam aplikasi yang bergantung pada MySQL yang memungkinkan penyerang mendapatkan akses ke skrip Anda dan melakukan kueri yang mungkin pada database Anda.

    Untuk informasi lebih lanjut, lihat Bagaimana saya bisa mencegah injeksi SQL dalam PHP?

  • Lihat Kotak Merah?

    Ketika Anda pergi ke mysqlhalaman manual fungsi, Anda melihat kotak merah, menjelaskan itu tidak boleh digunakan lagi.

  • Gunakan PDO atau MySQLi

    Ada alternatif yang lebih baik, lebih kuat dan dibangun dengan baik, PDO - PHP Database Object , yang menawarkan pendekatan OOP lengkap untuk interaksi basis data, dan MySQLi , yang merupakan peningkatan spesifik MySQL.

Hantu Madara
sumber
6
Ada satu hal lagi: saya pikir fungsi itu masih ada di PHP hanya untuk satu alasan - kompatibilitas dengan yang lama, ketinggalan zaman tetapi masih menjalankan CMS, e-commerce, sistem papan buletin dll. Akhirnya itu akan dihapus dan Anda harus menulis ulang aplikasi ...
Kamil
4
@ Kamil: Itu benar, tapi itu bukan alasan mengapa Anda tidak boleh menggunakannya. Alasan untuk tidak menggunakannya adalah karena itu kuno, tidak aman, dll :)
Madara's Ghost
4
@Mario - para pengembang PHP memang memiliki proses, dan mereka baru saja memilih untuk secara resmi mencabut ext / mysql pada 5.5. Ini bukan lagi masalah hipotetis.
SDC
2
Menambahkan beberapa baris tambahan dengan teknik yang sudah terbukti seperti PDO atau MySQLi masih memberi kemudahan penggunaan yang selalu ditawarkan PHP. Saya berharap demi pengembang dia tahu bahwa melihat fungsi mysql_ * yang mengerikan ini dalam setiap tutorial benar-benar mengurangi pelajaran, dan harus memberi tahu OP bahwa kode semacam ini sangaaaat 10 tahun yang lalu- dan harus mempertanyakan relevansi tutorial juga!
FredTheWebGuy
1
Apa jawaban yang sebaiknya disebutkan: pernyataan yang sudah disiapkan menghilangkan penggunaan yang berarti dari IN (...) construct.
Eugen Rieck
217

Kemudahan penggunaan

Alasan analitik dan sintetik telah disebutkan. Untuk pendatang baru ada insentif yang lebih signifikan untuk berhenti menggunakan fungsi tanggal mysql_.

API basis data kontemporer lebih mudah digunakan.

Sebagian besar parameter terikat yang dapat menyederhanakan kode. Dan dengan tutorial yang sangat baik (seperti yang terlihat di atas) transisi ke PDO tidak terlalu sulit.

Menulis ulang basis kode yang lebih besar sekaligus namun membutuhkan waktu. Raison d'être untuk alternatif perantara ini:

Fungsi pdo_ * Setara menggantikan mysql_ *

Menggunakan < pdo_mysql.php > Anda dapat beralih dari fungsi mysql_ lama dengan upaya minimal . Ini menambahkan pdo_pembungkus fungsi yang menggantikan mysql_rekan - rekan mereka .

  1. Cukup di setiap skrip doa yang harus berinteraksi dengan database. include_once("pdo_mysql.php");

  2. Hapus mysql_awalan fungsi di mana - mana dan ganti dengan pdo_.

    • mysql_connect() menjadi pdo_connect()
    • mysql_query() menjadi pdo_query()
    • mysql_num_rows() menjadi pdo_num_rows()
    • mysql_insert_id() menjadi pdo_insert_id()
    • mysql_fetch_array() menjadi pdo_fetch_array()
    • mysql_fetch_assoc() menjadi pdo_fetch_assoc()
    • mysql_real_escape_string() menjadi pdo_real_escape_string()
    • dan seterusnya...

  3. Kode Anda akan berfungsi sama dan sebagian besar masih terlihat sama:

    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]";
    }

Dll.
Kode Anda menggunakan PDO.
Sekarang saatnya untuk benar-benar menggunakannya .

Parameter batas bisa mudah digunakan

Anda hanya perlu API yang kurang sulit.

pdo_query()menambahkan dukungan yang sangat mudah untuk parameter terikat. Mengonversi kode lama sangatlah mudah:

Pindahkan variabel Anda dari string SQL.

  • Tambahkan mereka sebagai parameter fungsi yang dibatasi koma ke pdo_query().
  • Tempatkan tanda tanya ?sebagai pengganti di mana variabel sebelumnya.
  • Singkirkan 'tanda kutip tunggal yang sebelumnya berisi nilai / variabel string.

Keuntungannya menjadi lebih jelas untuk kode yang lebih panjang.

Seringkali variabel string tidak hanya diinterpolasi ke dalam SQL, tetapi digabungkan dengan lolosnya panggilan di antaranya.

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")

Dengan ?placeholder yang diterapkan, Anda tidak perlu repot dengan itu:

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

Ingat bahwa pdo_ * masih memungkinkan salah satu atau .
Hanya saja, jangan melarikan diri variabel dan mengikatnya dalam permintaan yang sama.

  • Fitur placeholder disediakan oleh PDO nyata di belakangnya.
  • Dengan demikian juga memungkinkan :nameddaftar placeholder nanti.

Lebih penting lagi, Anda dapat memberikan variabel $ _REQUEST [] dengan aman di balik kueri apa pun. Ketika <form>bidang yang dikirimkan cocok dengan struktur basis data persisnya itu bahkan lebih pendek:

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

Begitu banyak kesederhanaan. Tetapi mari kita kembali ke beberapa saran penulisan ulang dan alasan teknis tentang mengapa Anda mungkin ingin menyingkirkannyamysql_ dan melarikan diri.

Perbaiki atau hapus oldschool sanitize() fungsi

Setelah Anda mengkonversi semua mysql_panggilan pdo_querydengan params terikat, hapus semua berlebihanpdo_real_escape_string panggilan yang .

Secara khusus Anda harus memperbaiki setiap sanitizeatau cleanatau filterThisatau clean_datafungsi seperti yang diiklankan oleh tutorial tanggal dalam satu bentuk atau yang lain:

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

Bug paling mencolok di sini adalah kurangnya dokumentasi. Lebih penting lagi urutan penyaringan berada dalam urutan yang salah.

  • Urutan yang benar seharusnya: ditinggalkan stripslashessebagai panggilan terdalam, kemudian trim, setelah itu strip_tags, htmlentitiesuntuk konteks output, dan hanya yang terakhir _escape_stringkarena aplikasinya harus secara langsung mendahului SQL intersparsing.

  • Tapi sebagai langkah pertama singkirkan saja_real_escape_string panggilan itu.

  • Anda mungkin harus menjaga sisa sanitize()fungsi Anda untuk saat ini jika basis data dan aliran aplikasi Anda mengharapkan string HTML-context-safe. Tambahkan komentar bahwa itu hanya berlaku HTML untuk selanjutnya.

  • Penanganan string / nilai didelegasikan ke PDO dan pernyataan parameternya.

  • Jika ada disebutkan stripslashes()dalam fungsi sanitasi Anda, itu mungkin menunjukkan pengawasan tingkat yang lebih tinggi.

    • Itu biasanya ada di sana untuk membatalkan kerusakan (melarikan diri ganda) dari usang magic_quotes. Namun yang terbaik tetap terpusat , bukan string demi string.

    • Gunakan salah satu pendekatan pembalikan userland . Kemudian menghapus stripslashes()dalam sanitizefungsi.

    Catatan bersejarah tentang magic_quotes. Fitur itu sudah tidak digunakan lagi. Namun sering keliru digambarkan sebagai fitur keamanan yang gagal . Tapi magic_quotes adalah fitur keamanan yang gagal seperti bola tenis gagal sebagai sumber nutrisi. Itu bukan tujuan mereka.

    Implementasi asli dalam PHP2 / FI memperkenalkannya secara eksplisit dengan hanya " tanda kutip akan secara otomatis lolos sehingga lebih mudah untuk mengirimkan data formulir langsung ke msql queries ". Khususnya itu aman untuk digunakan dengan mSQL , karena hanya mendukung ASCII.
    Kemudian PHP3 / Zend memperkenalkan magic_quotes untuk MySQL dan mendokumentasikannya secara salah. Namun pada awalnya itu hanya fitur kenyamanan , bukan bermaksud keamanan.

Bagaimana perbedaan pernyataan yang dipersiapkan

Ketika Anda mengacak variabel string ke dalam query SQL, itu tidak hanya menjadi lebih rumit untuk Anda ikuti. Ini juga merupakan upaya tambahan bagi MySQL untuk memisahkan kode dan data lagi.

Suntikan SQL hanya ketika data berdarah ke dalam kode konteks . Server basis data tidak dapat menemukan tempat di mana PHP awalnya menempel variabel di antara klausa permintaan.

Dengan parameter terikat Anda memisahkan kode SQL dan nilai konteks SQL dalam kode PHP Anda. Tapi itu tidak terseret lagi di belakang layar (kecuali dengan PDO :: EMULATE_PREPARES). Basis data Anda menerima perintah SQL yang tidak divariasikan dan nilai variabel 1: 1.

Sementara jawaban ini menekankan bahwa Anda harus memperhatikan keuntungan keterbacaan dari menjatuhkan mysql_ . Kadang-kadang juga ada keuntungan kinerja (INSERT berulang dengan nilai yang hanya berbeda) karena pemisahan data / kode yang terlihat dan teknis ini.

Hati-hati bahwa pengikatan parameter masih bukan solusi satu atap yang ajaib terhadap semua injeksi SQL. Ini menangani penggunaan paling umum untuk data / nilai. Tetapi tidak dapat membuat daftar putih nama kolom / pengidentifikasi tabel, membantu konstruksi klausa dinamis, atau hanya daftar nilai array sederhana.

Penggunaan PDO hybrid

pdo_*Fungsi wrapper ini membuat stop-gap API yang ramah coding. (Ini cukup banyak apa yang MYSQLIbisa terjadi jika bukan karena pergeseran tanda tangan fungsi istimewa). Mereka juga mengekspos PDO nyata di sebagian besar waktu.
Menulis ulang tidak harus berhenti menggunakan nama fungsi pdo_ baru. Anda dapat melakukan transisi satu per satu setiap pdo_query () menjadi panggilan $ pdo-> prep () -> execute ().

Namun yang terbaik adalah mulai menyederhanakan lagi. Misalnya hasil umum mengambil:

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

Dapat diganti hanya dengan iteach foreach:

foreach ($result as $row) {

Atau lebih baik lagi pencarian array langsung dan lengkap:

$result->fetchAll();

Anda akan mendapatkan lebih banyak peringatan yang bermanfaat daripada PDO atau mysql_ biasanya memberikan setelah permintaan gagal.

Pilihan lain

Jadi ini mudah-mudahan memvisualisasikan beberapa alasan praktis dan jalan yang baik untuk dijatuhkan mysql_.

Hanya beralih ke tidak cukup memotongnya. pdo_query()juga hanya sebuah frontend ke atasnya.

Kecuali jika Anda juga memperkenalkan pengikatan parameter atau dapat menggunakan sesuatu yang lain dari API yang lebih bagus, itu adalah pengalihan tanpa tujuan. Saya harap itu digambarkan cukup sederhana untuk tidak memajukan keputusasaan bagi pendatang baru. (Pendidikan biasanya bekerja lebih baik daripada larangan.)

Meskipun memenuhi syarat untuk kategori hal-hal-yang-bisa-mungkin-mungkin-paling sederhana, itu juga masih merupakan kode yang sangat eksperimental. Saya baru saja menulisnya selama akhir pekan. Namun ada banyak alternatif. Hanya google untuk abstraksi basis data PHP dan jelajahi sedikit. Selalu ada dan akan ada banyak perpustakaan yang sangat baik untuk tugas-tugas seperti itu.

Jika Anda ingin menyederhanakan interaksi basis data Anda lebih lanjut, pembuat peta seperti Paris / Idiorm patut dicoba. Sama seperti tidak ada yang menggunakan DOM hambar dalam JavaScript lagi, Anda tidak perlu mengasuh antarmuka basis data mentah saat ini.

mario
sumber
8
Hati-hati dengan pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);fungsi - yaitu:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
rickyduck
@ Tom Tentu, meskipun itu tidak banyak dipertahankan (0.9.2 adalah yang terakhir), Anda dapat membuat akun fosil , menambah wiki atau mengajukan laporan bug (tanpa registrasi IIRC).
mario
pdo_real_escape_string() <- Apakah ini bahkan fungsi nyata, saya tidak dapat menemukan dokumentasi untuk itu? Silakan kirim sumber untuk ini.
Ryan Stone
144

The mysql_fungsi:

  1. sudah kedaluwarsa - mereka tidak dipertahankan lagi
  2. jangan izinkan Anda pindah dengan mudah ke backend database lain
  3. karenanya, tidak mendukung pernyataan yang disiapkan
  4. mendorong programmer untuk menggunakan gabungan untuk membangun permintaan, yang mengarah ke kerentanan injeksi SQL
Alnitak
sumber
18
# 2 sama benarnyamysqli_
eggyal
16
agar adil, mengingat variasi dalam dialek SQL, bahkan PDO tidak memberi Anda # 2 dengan tingkat kepastian. Anda membutuhkan pembungkus ORM yang tepat untuk itu.
SDC
yang mysql_*fungsi adalah shell ke dalam fungsi mysqlnd untuk versi PHP yang lebih baru. Jadi, bahkan jika perpustakaan klien lama tidak dipelihara lagi, mysqlnd dipertahankan :)
hakre
Masalahnya adalah tidak banyak penyedia hosting web dapat mendukung gaya desain berorientasi objek seperti itu karena versi php yang sudah ketinggalan zaman
Raju yourPepe
@RajuGujarati jadi temukan host web yang bisa. Jika host web Anda tidak, kemungkinannya sangat tinggi sehingga mereka rentan terhadap serangan di server mereka.
Alnitak
106

Berbicara tentang alasan teknis , hanya ada beberapa, sangat spesifik dan jarang digunakan. Kemungkinan besar Anda tidak akan pernah menggunakannya dalam hidup Anda.
Mungkin saya terlalu bodoh, tetapi saya tidak pernah memiliki kesempatan untuk menggunakannya seperti itu

  • permintaan non-blocking, asinkron
  • prosedur tersimpan mengembalikan beberapa hasil
  • Enkripsi (SSL)
  • Kompresi

Jika Anda membutuhkannya - ini tidak diragukan lagi alasan teknis untuk beralih dari ekstensi mysql ke arah sesuatu yang lebih gaya dan tampak modern.

Namun demikian, ada juga beberapa masalah non-teknis, yang dapat membuat pengalaman Anda sedikit lebih sulit

  • penggunaan lebih lanjut dari fungsi-fungsi ini dengan versi PHP modern akan meningkatkan pemberitahuan tingkat yang sudah usang. Mereka hanya bisa dimatikan.
  • di masa depan yang jauh, mereka dapat dihapus dari build PHP default. Bukan masalah besar juga, karena mydsql ext akan dipindahkan ke PECL dan setiap hoster akan senang untuk mengkompilasi PHP dengan itu, karena mereka tidak ingin kehilangan klien yang situsnya bekerja selama beberapa dekade.
  • resistensi kuat dari komunitas Stackoverflow. Е Setiap kali Anda menyebutkan fungsi-fungsi jujur ​​ini, Anda diberi tahu bahwa mereka berada di bawah tabu yang ketat.
  • sebagai pengguna PHP biasa, kemungkinan besar ide Anda untuk menggunakan fungsi-fungsi ini rentan kesalahan dan salah. Hanya karena banyaknya tutorial dan manual yang mengajarkan Anda cara yang salah. Bukan fungsi itu sendiri - saya harus menekankan itu - tetapi cara mereka digunakan.

Masalah terakhir ini adalah masalah.
Tapi, menurut saya, solusi yang diusulkan juga tidak lebih baik.
Sepertinya saya terlalu idealis mimpi bahwa semua pengguna PHP akan belajar bagaimana menangani pertanyaan SQL dengan benar sekaligus. Kemungkinan besar mereka hanya akan mengubah mysql_ * menjadi mysqli_ * secara mekanis, meninggalkan pendekatan yang sama . Terutama karena mysqli membuat pernyataan siap pakai yang luar biasa menyakitkan dan merepotkan.
Belum lagi pernyataan asli yang disiapkan tidak cukup untuk melindungi dari suntikan SQL, dan baik mysqli maupun PDO tidak menawarkan solusi.

Jadi, daripada melawan perpanjangan yang jujur ​​ini, saya lebih suka melawan praktik yang salah dan mendidik orang dengan cara yang benar.

Juga, ada beberapa alasan yang salah atau tidak signifikan, seperti

  • Tidak mendukung Prosedur Tersimpan (kami menggunakan mysql_query("CALL my_proc");selama berabad-abad)
  • Tidak mendukung Transaksi (sama seperti di atas)
  • Tidak mendukung Pernyataan Berganda (siapa yang membutuhkannya?)
  • Tidak dalam pengembangan aktif (jadi apa? Apakah ini memengaruhi Anda dengan cara praktis apa pun?)
  • Tidak memiliki antarmuka OO (untuk membuatnya hanya beberapa jam)
  • Tidak mendukung Pernyataan Disiapkan atau Kueri Parametrized

Yang terakhir adalah poin yang menarik. Meskipun mysql ext tidak mendukung pernyataan yang disiapkan asli , mereka tidak diperlukan untuk keselamatan. Kami dapat dengan mudah memalsukan pernyataan yang disiapkan menggunakan placeholder yang ditangani secara manual (seperti halnya 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 , semuanya parameter dan aman.

Tapi oke, jika Anda tidak suka kotak merah di manual, masalah pilihan muncul: mysqli atau PDO?

Jawabannya adalah sebagai berikut:

  • Jika Anda memahami perlunya menggunakan lapisan abstraksi basis data dan mencari API untuk membuatnya, mysqli adalah pilihan yang sangat baik, karena memang mendukung banyak fitur spesifik mysql.
  • Jika, seperti sebagian besar orang PHP, Anda menggunakan panggilan API mentah langsung dalam kode aplikasi (yang pada dasarnya merupakan praktik yang salah) - PDO adalah satu-satunya pilihan , karena ekstensi ini berpura-pura bukan hanya API tetapi lebih semi-DAL, masih belum lengkap tetapi menawarkan banyak fitur penting, dengan dua di antaranya membuat PDO sangat berbeda dari mysqli:

    • tidak seperti mysqli, PDO dapat mengikat placeholder berdasarkan nilai , yang membuat kueri yang dibangun secara dinamis layak tanpa beberapa layar kode yang cukup berantakan.
    • tidak seperti mysqli, PDO selalu dapat mengembalikan hasil permintaan dalam array biasa yang sederhana, sementara mysqli dapat melakukannya hanya pada instalasi mysqlnd.

Jadi, jika Anda adalah pengguna PHP biasa dan ingin menghemat banyak sakit kepala saat menggunakan pernyataan asli yang disiapkan, PDO - lagi - adalah satu-satunya pilihan.
Namun, PDO bukan peluru perak juga dan memiliki kesulitan.
Jadi, saya menulis solusi untuk semua perangkap umum dan kasus kompleks di wiki tag PDO

Namun demikian, semua orang yang berbicara tentang ekstensi selalu kehilangan 2 fakta penting tentang Mysqli dan PDO:

  1. Pernyataan yang disiapkan bukan peluru perak . Ada pengidentifikasi dinamis yang tidak dapat diikat menggunakan pernyataan yang disiapkan. Ada kueri dinamis dengan sejumlah parameter yang tidak diketahui yang membuat kueri membangun tugas yang sulit.

  2. Baik fungsi mysqli_ * maupun PDO seharusnya tidak muncul dalam kode aplikasi.
    Seharusnya ada lapisan abstraksi antara mereka dan kode aplikasi, yang akan melakukan semua pekerjaan kotor yaitu mengikat, mengulang, menangani kesalahan, dll. Di dalam, membuat kode aplikasi KERING dan bersih. Khusus untuk kasus kompleks seperti bangunan kueri dinamis.

Jadi, hanya beralih ke PDO atau mysqli tidak cukup. Kita harus menggunakan ORM, atau pembuat kueri, atau kelas abstraksi basis data apa pun alih-alih memanggil fungsi API mentah dalam kode mereka.
Dan sebaliknya - jika Anda memiliki lapisan abstraksi antara kode aplikasi Anda dan API mysql - sebenarnya tidak masalah mesin mana yang digunakan. Anda dapat menggunakan mysql ext hingga tidak lagi digunakan lagi dan kemudian dengan mudah menulis ulang kelas abstraksi Anda ke mesin lain, setelah semua kode aplikasi tetap utuh.

Berikut adalah beberapa contoh berdasarkan kelas safemysql saya untuk menunjukkan bagaimana seharusnya kelas abstraksi seperti itu:

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

Bandingkan satu baris ini dengan jumlah kode yang Anda perlukan dengan PDO .
Kemudian bandingkan dengan jumlah kode gila yang Anda perlukan dengan pernyataan mentah Mysqli. Perhatikan bahwa penanganan kesalahan, pembuatan profil, pembuatan log permintaan sudah terpasang dan berjalan.

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

Bandingkan dengan sisipan PDO biasa, ketika setiap nama bidang diulangi enam hingga sepuluh kali - di semua placeholder, binding, dan definisi kueri yang banyak ini.

Contoh lain:

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

Anda tidak dapat menemukan contoh bagi PDO untuk menangani kasus praktis semacam itu.
Dan itu akan terlalu bertele-tele dan kemungkinan besar tidak aman.

Jadi, sekali lagi - ini bukan hanya driver mentah yang harus menjadi perhatian Anda tetapi kelas abstraksi, berguna tidak hanya untuk contoh-contoh konyol dari manual pemula tetapi untuk memecahkan masalah kehidupan nyata apa pun.

Akal Sehat Anda
sumber
20
mysql_*membuat kerentanan sangat mudah didapat. Karena PHP digunakan oleh banyak pengguna pemula, mysql_*secara aktif berbahaya dalam praktiknya, meskipun secara teori dapat digunakan tanpa hambatan.
Hantu Madara
4
everything is parameterized and safe- itu mungkin parameter, tetapi fungsi Anda tidak menggunakan nyata pernyataan siap.
uınbɐɥs
6
Bagaimana Not under active developmenthanya untuk '0,01%' yang dibuat-buat? Jika Anda membangun sesuatu dengan fungsi diam ini, perbarui versi mysql Anda dalam setahun dan berakhir dengan sistem yang tidak berfungsi, saya yakin ada banyak sekali orang yang tiba-tiba dalam '0,01%' itu. Saya akan mengatakan itu deprecateddan not under active developmentterkait erat. Anda dapat mengatakan bahwa "tidak ada alasan [layak]" untuk itu, tetapi kenyataannya adalah bahwa ketika ditawarkan pilihan antara opsi, no active developmenthampir sama buruknya dengan yang deprecatedsaya katakan?
Nanne
1
@MadaraUchiha: Bisakah Anda menjelaskan bagaimana kerentanan sangat mudah didapat? Terutama dalam kasus di mana kerentanan yang sama tidak memengaruhi PDO atau MySQLi ... Karena saya tidak mengetahui satu pun yang Anda bicarakan.
ircmaxell
4
@ShaquinTrifonoff: tentu, tidak menggunakan pernyataan yang sudah disiapkan. Tetapi PDO juga tidak , yang kebanyakan orang merekomendasikan lebih dari MySQLi. Jadi saya tidak yakin itu berdampak signifikan di sini. Kode di atas (dengan sedikit penguraian) adalah apa yang PDO lakukan ketika Anda menyiapkan pernyataan secara default ...
ircmaxell
97

Ada banyak alasan, tetapi mungkin yang paling penting adalah bahwa fungsi-fungsi tersebut mendorong praktik pemrograman yang tidak aman karena tidak mendukung pernyataan yang disiapkan. Pernyataan yang disiapkan membantu mencegah serangan injeksi SQL.

Saat menggunakan mysql_*fungsi, Anda harus ingat untuk menjalankan parameter yang disediakan pengguna mysql_real_escape_string(). Jika Anda lupa hanya di satu tempat atau jika Anda hanya melarikan diri dari sebagian input, basis data Anda mungkin akan diserang.

Menggunakan pernyataan yang sudah disiapkan di PDOatau mysqliakan membuatnya sehingga kesalahan pemrograman semacam ini lebih sulit untuk dibuat.

Trott
sumber
3
Sayangnya, dukungan MySQLi_ * yang buruk untuk melewatkan sejumlah variabel parameter (seperti ketika Anda ingin memberikan daftar nilai untuk diperiksa dalam klausa IN) mendorong tidak digunakannya parameter, mendorong penggunaan kueri gabungan yang persis sama yang biarkan panggilan MySQL_ * rentan.
Kickstart
5
Tapi, sekali lagi, ketidakamanan bukanlah masalah yang melekat pada fungsi mysql_ *, tetapi masalah penggunaan yang salah.
Agamemnus
2
@ Agamemnus Masalahnya adalah bahwa mysql_ * membuatnya mudah untuk mengimplementasikan "penggunaan yang salah", terutama untuk programmer yang tidak berpengalaman. Perpustakaan yang menerapkan pernyataan yang disiapkan membuat lebih sulit untuk membuat jenis kesalahan itu.
Trott
75

Karena (di antara alasan lain) itu jauh lebih sulit untuk memastikan input data disanitasi. Jika Anda menggunakan kueri parametrized, seperti yang dilakukan dengan PDO atau mysqli, Anda sepenuhnya dapat menghindari risiko.

Sebagai contoh, seseorang dapat digunakan "enhzflep); drop table users"sebagai nama pengguna. Fungsi lama akan memungkinkan mengeksekusi beberapa pernyataan per kueri, jadi sesuatu seperti bugger jahat itu dapat menghapus seluruh tabel.

Jika seseorang menggunakan PDO dari mysqli, nama pengguna akan berakhir "enhzflep); drop table users".

Lihat bobby-tables.com .

enhzflep
sumber
10
The old functions will allow executing of multiple statements per query- tidak, mereka tidak akan. Injeksi semacam itu tidak dimungkinkan dengan ext / mysql - satu-satunya cara injeksi semacam ini dimungkinkan dengan PHP dan MySQL adalah ketika menggunakan MySQLi dan mysqli_multi_query()fungsinya. Jenis injeksi yang dimungkinkan dengan ext / mysql dan unescaped string adalah hal-hal seperti ' OR '1' = '1mengekstrak data dari database yang tidak dimaksudkan untuk diakses. Dalam situasi tertentu dimungkinkan untuk menyuntikkan sub kueri, namun masih tidak mungkin untuk memodifikasi database dengan cara ini.
DaveRandom
64

Jawaban ini ditulis untuk menunjukkan betapa sepelenya untuk mem-bypass kode validasi pengguna PHP yang ditulis dengan buruk, bagaimana (dan menggunakan apa) serangan ini bekerja dan bagaimana cara mengganti fungsi MySQL lama dengan pernyataan yang disiapkan dengan aman - dan pada dasarnya, mengapa pengguna StackOverflow (mungkin dengan banyak perwakilan) menggonggong pada pengguna baru yang mengajukan pertanyaan untuk meningkatkan kode mereka.

Pertama, jangan ragu untuk membuat tes ini database mysql (Saya telah memanggil persiapan tambang):

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)

Setelah itu selesai, kita bisa pindah ke kode PHP kita.

Mari kita asumsikan skrip berikut adalah proses verifikasi untuk admin di situs web (disederhanakan tetapi berfungsi jika Anda menyalin dan menggunakannya untuk pengujian):

<?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>

Tampak cukup sah pada pandangan pertama.

Pengguna harus memasukkan login dan kata sandi, bukan?

Cemerlang, tidak masuk sebagai berikut:

user: bob
pass: somePass

dan kirimkan.

Outputnya adalah sebagai berikut:

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

Super! Bekerja seperti yang diharapkan, sekarang mari kita coba nama pengguna dan kata sandi yang sebenarnya:

user: Fluffeh
pass: mypass

Luar biasa! Hai semuanya, kode dengan benar memverifikasi admin. Itu sempurna!

Yah, tidak juga. Katakanlah pengguna adalah orang kecil yang pintar. Katakanlah orang itu adalah saya.

Masukkan yang berikut ini:

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

Dan hasilnya adalah:

The check passed. We have a verified admin!

Selamat, Anda baru saja mengizinkan saya memasukkan bagian admin super-dilindungi Anda saja dengan saya memasukkan nama pengguna palsu dan kata sandi palsu. Serius, jika Anda tidak percaya, buat database dengan kode yang saya berikan, dan jalankan kode PHP ini - yang sekilas BENAR-BENAR tampaknya memverifikasi nama pengguna dan kata sandi dengan baik.

Jadi, sebagai jawaban, ITULAH MENGAPA ANDA DIAMBIL DI.

Jadi, mari kita lihat apa yang salah, dan mengapa saya baru saja masuk ke gua super-admin-satunya-kelelawar Anda. Saya menebak dan berasumsi bahwa Anda tidak berhati-hati dengan input Anda dan hanya meneruskannya ke database secara langsung. Saya membuat input dengan cara yang akan MENGUBAH kueri yang sebenarnya Anda jalankan. Jadi, apa yang seharusnya, dan apa akhirnya?

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

Itulah kueri, tetapi ketika kami mengganti variabel dengan input aktual yang kami gunakan, kami mendapatkan yang berikut:

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

Lihat bagaimana saya membuat "kata sandi" saya sehingga akan terlebih dahulu menutup kutipan tunggal di sekitar kata sandi, kemudian memperkenalkan perbandingan yang sama sekali baru? Kemudian hanya untuk keamanan, saya menambahkan "string" lain sehingga kutipan tunggal akan ditutup seperti yang diharapkan dalam kode yang awalnya kami miliki.

Namun, ini bukan tentang orang yang berteriak pada Anda sekarang, ini tentang menunjukkan kepada Anda bagaimana membuat kode Anda lebih aman.

Oke, jadi apa yang salah, dan bagaimana kita memperbaikinya?

Ini adalah serangan injeksi SQL klasik. Salah satu yang paling sederhana dalam hal ini. Pada skala vektor serangan, ini adalah balita yang menyerang tank - dan menang.

Jadi, bagaimana kami melindungi bagian admin suci Anda dan menjadikannya bagus dan aman? Hal pertama yang harus dilakukan adalah berhenti menggunakan mysql_*fungsi-fungsi yang benar-benar tua dan usang itu . Saya tahu, Anda mengikuti tutorial yang Anda temukan online dan berhasil, tetapi sudah tua, sudah usang dan dalam waktu beberapa menit, saya baru saja melewatinya tanpa banyak berkeringat.

Sekarang, Anda memiliki opsi yang lebih baik untuk menggunakan mysqli_ atau PDO . Saya pribadi penggemar berat PDO, jadi saya akan menggunakan PDO di sisa jawaban ini. Ada pro dan kontra, tetapi secara pribadi saya menemukan bahwa pro jauh melebihi penipu. Ini portabel di beberapa mesin basis data - apakah Anda menggunakan MySQL atau Oracle atau hanya tentang apa saja - hanya dengan mengubah string koneksi, ia memiliki semua fitur mewah yang ingin Anda gunakan dan itu bagus dan bersih. Saya suka bersih.

Sekarang, mari kita lihat kode itu lagi, kali ini ditulis menggunakan objek 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>

Perbedaan utama adalah bahwa tidak ada lagi mysql_*fungsi. Semuanya dilakukan melalui objek PDO, kedua, menggunakan pernyataan yang disiapkan. Sekarang, apa pernyataan yang sudah Anda minta sebelumnya? Ini adalah cara untuk memberi tahu basis data sebelum menjalankan kueri, apa kueri yang akan kita jalankan. Dalam hal ini, kami memberi tahu database: "Hai, saya akan menjalankan pernyataan pilih yang menginginkan id, userid dan pass dari tabel pengguna di mana userid adalah variabel dan pass juga merupakan variabel.".

Kemudian, dalam pernyataan eksekusi, kami melewatkan basis data array dengan semua variabel yang sekarang diharapkan.

Hasilnya luar biasa. Mari kita coba kombinasi nama pengguna dan kata sandi itu sebelumnya:

user: bob
pass: somePass

Pengguna tidak diverifikasi. Luar biasa.

Bagaimana tentang:

user: Fluffeh
pass: mypass

Oh, saya hanya sedikit bersemangat, itu berhasil: Cek lulus. Kami memiliki admin yang diverifikasi!

Sekarang, mari kita coba data yang akan dimasukkan oleh orang pintar untuk mencoba melewati sistem verifikasi kecil kami:

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

Kali ini, kami mendapatkan yang berikut:

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

Inilah sebabnya mengapa Anda diteriaki ketika memposting pertanyaan - itu karena orang dapat melihat bahwa kode Anda dapat dilewati tanpa mencoba. Tolong, gunakan pertanyaan dan jawaban ini untuk meningkatkan kode Anda, untuk membuatnya lebih aman dan untuk menggunakan fungsi yang saat ini.

Terakhir, ini bukan untuk mengatakan bahwa ini adalah kode SEMPURNA. Ada banyak hal lain yang dapat Anda lakukan untuk memperbaikinya, menggunakan kata sandi hash misalnya, memastikan bahwa ketika Anda menyimpan informasi sensitif dalam database, Anda tidak menyimpannya dalam teks biasa, memiliki beberapa tingkat verifikasi - tetapi sungguh, jika Anda hanya mengubah kode rawan injeksi lama Anda ke ini, Anda akan BAIK sepanjang jalan untuk menulis kode yang baik - dan fakta bahwa Anda sudah sejauh ini dan masih membaca memberi saya rasa harapan bahwa Anda tidak hanya akan menerapkan jenis ini kode saat menulis situs web dan aplikasi Anda, tetapi Anda mungkin pergi dan meneliti hal-hal lain yang baru saja saya sebutkan - dan banyak lagi. Tulis kode terbaik yang Anda bisa, bukan kode paling dasar yang nyaris tidak berfungsi.

Fluffeh
sumber
2
Terima kasih atas jawaban Anda! Dapatkan +1 saya! Perlu dicatat bahwa mysql_*itu sendiri tidak aman, tetapi mempromosikan kode tidak aman melalui tutorial yang buruk dan kurangnya pernyataan yang tepat untuk menyiapkan API.
Hantu Madara
2
kata sandi yang tidak rusak, oh horor! = oP atau sebaliknya +1 untuk penjelasan terperinci.
samar ツ
33

Ekstensi MySQL adalah yang tertua dari ketiganya dan merupakan cara asli yang digunakan pengembang untuk berkomunikasi dengan MySQL. Ekstensi ini sekarang tidak digunakan lagi karena dua alternatif lain karena perbaikan yang dibuat dalam rilis baru PHP dan MySQL.

  • MySQLi adalah ekstensi 'ditingkatkan' untuk bekerja dengan database MySQL. Ini mengambil keuntungan dari fitur-fitur yang tersedia dalam versi yang lebih baru dari server MySQL, memperlihatkan antarmuka berorientasi fungsi dan berorientasi objek ke pengembang dan melakukan beberapa hal bagus lainnya.

  • PDO menawarkan API yang menggabungkan sebagian besar fungsi yang sebelumnya tersebar di ekstensi akses basis data utama, yaitu MySQL, PostgreSQL, SQLite, MSSQL, dll. Antarmuka mengekspos objek tingkat tinggi bagi programmer untuk bekerja dengan koneksi basis data, kueri dan set hasil, dan driver tingkat rendah melakukan komunikasi dan penanganan sumber daya dengan server database. Banyak diskusi dan pekerjaan masuk ke PDO dan itu dianggap metode yang tepat untuk bekerja dengan database dalam kode profesional dan modern.

Alexander
sumber
21

Saya menemukan jawaban di atas sangat panjang, jadi untuk meringkas:

Ekstensi mysqli memiliki sejumlah manfaat, peningkatan utama atas ekstensi mysql adalah:

  • Antarmuka berorientasi objek
  • Dukungan untuk Pernyataan Disiapkan
  • Dukungan untuk Banyak Pernyataan
  • Dukungan untuk Transaksi
  • Peningkatan kemampuan debugging
  • Dukungan server tertanam

Sumber: ikhtisar MySQLi


Seperti dijelaskan dalam jawaban di atas, alternatif untuk mysql adalah mysqli dan PDO (Objek Data PHP).

  • API mendukung Pernyataan Disiapkan sisi-server: Didukung oleh MYSQLi dan PDO
  • API mendukung Pernyataan Disiapkan sisi klien: Hanya didukung oleh PDO
  • API mendukung Prosedur Tersimpan: Baik MySQLi dan PDO
  • API mendukung Pernyataan Berganda dan semua fungsi MySQL 4.1+ - Didukung oleh MySQLi dan sebagian besar juga oleh PDO

Baik MySQLi dan PDO diperkenalkan dalam PHP 5.0, sedangkan MySQL diperkenalkan sebelum PHP 3.0. Poin yang perlu diperhatikan adalah bahwa MySQL sudah termasuk dalam PHP5.x meskipun sudah tidak digunakan lagi dalam versi yang lebih baru.

Ani Menon
sumber
2
Jawaban Anda terlalu panjang, sedangkan ringkasan sebenarnya adalah "mysql ext is no more". Itu saja
Akal Sehat Anda
1
@YourCommonSense Jawaban saya adalah mengapa mysqli menggantikan mysql. Intinya bukan untuk mengatakan Mysqli ada hari ini jadi gunakan itu .. Semua orang tahu itu!
Ani Menon
1
Terlepas dari kenyataan bahwa tidak ada yang bertanya mengapa mysqli menggantikan mysql, itu juga tidak menjawab pertanyaan ini. Itu menjawab mengapa mysqli diperkenalkan. Tapi itu tidak menjelaskan mengapa mysql dan mysqli tidak diizinkan untuk hidup paralel
Your Common Sense
@YourCommonSense Juga pertanyaan OP adalah "Mengapa saya harus menggunakan sesuatu yang lain bahkan jika mereka bekerja di situs saya?" dan itulah alasan saya menunjukkan perubahan dan peningkatan. Anda mungkin melihat semua jawaban lain yang panjang, jadi saya pikir saya harus merangkumnya.
Ani Menon
6

Mungkin untuk mendefinisikan hampir semua mysql_*fungsi menggunakan mysqli atau PDO. Cukup sertakan di atas aplikasi PHP lama Anda, dan itu akan berfungsi pada PHP7. Solusi saya di sini .

<?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;
}
Pavel Tzonkov
sumber
Alih-alih menunjukkan tautan untuk solusi Anda, tambahkan di sini sebagai jawaban.
amarnath
1

Fungsi-fungsi yang mirip dengan ini mysql_connect(), mysql_query()ketik adalah versi PHP sebelumnya yaitu (PHP 4) fungsi dan sekarang tidak digunakan.

Ini digantikan oleh mysqli_connect(), mysqli_query()sama di PHP5 terbaru.

Inilah alasan di balik kesalahan tersebut.

Pembunuh
sumber
2
PHP 5 belum terbaru selama lebih dari 2 tahun sekarang.
Madara's Ghost
1

MySQL tidak lagi digunakan dalam PHP 5.5.0, dan dihapus dalam PHP 7.0.0. Untuk aplikasi besar dan lama, ini sulit untuk mencari dan mengganti setiap fungsi.

Kita dapat menggunakan fungsi MySQL dengan membuat fungsi wrapper untuk masing-masing di bawah ini menjalankan kode. Klik disini

Vin
sumber
-9

fungsi mysql_ * sudah usang (pada PHP 5.5 ) mengingat fakta bahwa fungsi dan struktur kode yang lebih baik dikembangkan. Fakta bahwa fungsi tersebut sudah usang berarti bahwa tidak ada lagi upaya yang akan dilakukan untuk meningkatkannya dalam hal kinerja dan keamanan, yang berarti lebih sedikit bukti di masa depan. .

Jika Anda membutuhkan lebih banyak alasan:

  • fungsi mysql_ * tidak mendukung pernyataan yang disiapkan.
  • fungsi mysql_ * tidak mendukung pengikatan parameter.
  • fungsi mysql_ * kurang fungsional untuk Pemrograman Berorientasi Objek.
  • daftar berjalan ...
Webeng
sumber
18
Jawaban ini sudah usang. Selain itu, itu tidak menambah apa pun yang berguna untuk jawaban yang sudah ada.
Akal Sehat Anda