ทำไมฉันไม่ควรใช้ฟังก์ชั่น mysql_ * ใน PHP?


2502

อะไรคือเหตุผลทางเทคนิคสำหรับสาเหตุที่ไม่ควรใช้mysql_*ฟังก์ชั่น? (เช่นmysql_query(), mysql_connect()หรือmysql_real_escape_string() )?

เหตุใดฉันจึงควรใช้อย่างอื่นแม้ว่าพวกเขาจะทำงานบนไซต์ของฉัน

หากพวกเขาไม่ทำงานบนเว็บไซต์ของฉันทำไมฉันถึงได้รับข้อผิดพลาดเช่น

คำเตือน: mysql_connect (): ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว


ข้อผิดพลาดที่จะเป็นเช่น: ข้อผิดพลาดร้ายแรง: ข้อผิดพลาดที่ไม่ได้บันทึก: เรียกไปยังฟังก์ชันที่ไม่ได้กำหนด mysql_connect () ...
Bimal Poudel

21
เลิกใช้เพียงอย่างเดียวมีเหตุผลพอที่จะหลีกเลี่ยงพวกเขา
Sasa1234

คำตอบ:


2088

นามสกุล MySQL:

  • ไม่ได้อยู่ภายใต้การพัฒนาที่ใช้งานอยู่
  • เป็นอย่างเป็นทางการเลิกเป็นของ PHP 5.5 (ปล่อยตัวมิถุนายน 2013)
  • ถูกลบทั้งหมดตั้งแต่ PHP 7.0 (เผยแพร่เมื่อธันวาคม 2558)
    • ซึ่งหมายความว่าณ วันที่ 31 ธันวาคม 2018จะไม่มีอยู่ใน PHP เวอร์ชั่นที่รองรับ หากคุณใช้ PHP เวอร์ชันที่สนับสนุนคุณกำลังใช้เวอร์ชันที่ไม่ได้รับการแก้ไขปัญหาความปลอดภัย
  • ขาดอินเตอร์เฟส OO
  • ไม่รองรับ:
    • แบบสอบถามที่ไม่ปิดกั้นและไม่ตรงกัน
    • คำสั่งที่เตรียมไว้หรือแบบสอบถามแบบใช้พารามิเตอร์
    • ขั้นตอนการจัดเก็บ
    • หลายงบ
    • การทำธุรกรรม
    • วิธีการตรวจสอบรหัสผ่าน "ใหม่" (โดยค่าเริ่มต้นใน MySQL 5.6; จำเป็นใน 5.7)
    • ฟังก์ชันใหม่ใด ๆ ใน MySQL 5.1 หรือใหม่กว่า

เนื่องจากมันเลิกใช้แล้วทำให้รหัสของคุณพิสูจน์ได้น้อยลงในอนาคต

การขาดการสนับสนุนงบที่เตรียมไว้มีความสำคัญอย่างยิ่งเนื่องจากมีวิธีการที่ชัดเจนและผิดพลาดน้อยกว่าในการหลบหนีและการอ้างถึงข้อมูลภายนอกมากกว่าการหลบหลีกด้วยตนเองด้วยการเรียกฟังก์ชันที่แยกต่างหาก

ดูการเปรียบเทียบส่วนขยายของ SQL


287
เลิกใช้เพียงอย่างเดียวมีเหตุผลพอที่จะหลีกเลี่ยงพวกเขา พวกเขาจะไม่อยู่ที่นั่นในวันเดียวและคุณจะไม่มีความสุขถ้าคุณพึ่งพาพวกเขา ส่วนที่เหลือเป็นเพียงรายการสิ่งต่าง ๆ ที่ใช้ส่วนขยายเก่าทำให้ผู้คนไม่ได้เรียนรู้
ทิมโพสต์

111
การลดลงไม่ใช่เรื่องมหัศจรรย์ที่ทุกคนคิดว่าเป็น PHP จะไม่มีวันเดียว แต่เราต้องพึ่งพาเครื่องมือที่เรามีอยู่ทุกวัน เมื่อเราต้องเปลี่ยนเครื่องมือเราจะ
การแข่งขัน Lightness ใน Orbit

133
@LightnessRacesinOrbit - การคัดค้านไม่ใช่ bullet มายากลมันเป็นธงที่บอกว่า "เรารับรู้นี้ sucks ดังนั้นเราจะไม่สนับสนุนอีกต่อไปนาน" ในขณะที่การพิสูจน์โค้ดที่ดีขึ้นในอนาคตเป็นเหตุผลที่ดีที่จะย้ายออกจากฟีเจอร์ที่เลิกใช้แล้วมันไม่ได้เป็นเพียงฟีเจอร์เดียวเท่านั้น (หรือแม้แต่ฟีเจอร์หลัก) เปลี่ยนเครื่องมือเพราะมีเครื่องมือที่ดีกว่าไม่ใช่เพราะคุณถูกบังคับ (และการเปลี่ยนเครื่องมือก่อนที่คุณจะถูกบังคับให้หมายความว่าคุณไม่ได้เรียนรู้สิ่งใหม่เพียงเพราะโค้ดของคุณหยุดทำงานและต้องแก้ไขเมื่อวานนี้…ซึ่งเป็นเวลาที่เลวร้ายที่สุดในการเรียนรู้เครื่องมือใหม่)
เควนติน

18
สิ่งหนึ่งที่ฉันไม่ได้เห็นที่กล่าวถึงเกี่ยวกับการขาดงบที่เตรียมไว้คือปัญหาด้านประสิทธิภาพ ทุกครั้งที่คุณออกแถลงการณ์สิ่งที่จะต้องรวบรวมเพื่อให้ดีมอน MySQL สามารถเข้าใจได้ ด้วย API นี้หากคุณออกแบบสอบถามจำนวน 200,000 รายการในลูปนั่นคือ 200,000 เท่าของแบบสอบถามที่จะต้องรวบรวมเพื่อให้ MySQL เข้าใจ ด้วยคำสั่งที่เตรียมไว้มันจะถูกคอมไพล์หนึ่งครั้งจากนั้นค่าต่างๆจะถูกแปรให้เป็น SQL ที่รวบรวม
Goldentoa11

20
@symcbean มันไม่สนับสนุนงบที่เตรียมไว้ ในความเป็นจริงนั้นเป็นเหตุผลหลักว่าทำไมเลิกใช้แล้ว หากไม่มีคำสั่งที่เตรียม (ใช้งานง่าย) ส่วนขยาย mysql มักตกเป็นเหยื่อของการโจมตีการฉีด SQL
rustyx

1287

PHP มี API ที่แตกต่างกันสามตัวเพื่อเชื่อมต่อกับ MySQL เหล่านี้คือmysql(ถูกลบออกจาก PHP 7) mysqli, และPDOส่วนขยาย

mysql_*ฟังก์ชั่นที่ใช้ในการเป็นที่นิยมมาก แต่การใช้ของพวกเขาจะไม่ได้รับการสนับสนุนอีกต่อไป ทีมเอกสารกำลังพูดคุยเกี่ยวกับสถานการณ์ความปลอดภัยของฐานข้อมูลและให้ความรู้แก่ผู้ใช้ในการย้ายออกจากส่วนขยาย ext / mysql ที่ใช้กันทั่วไปเป็นส่วนหนึ่งของสิ่งนี้ (ตรวจสอบphp.internals: deprecating ext / mysql )

และทีมพัฒนา PHP ต่อมาได้ดำเนินการตัดสินใจที่จะสร้างE_DEPRECATEDข้อผิดพลาดเมื่อผู้ใช้เชื่อมต่อกับ MySQL ไม่ว่าจะผ่านmysql_connect(), หรือฟังก์ชั่นการเชื่อมต่อโดยปริยายที่สร้างขึ้นในmysql_pconnect()ext/mysql

ext/mysqlถูกเลิกใช้อย่างเป็นทางการตั้งแต่ PHP 5.5และถูกลบออกเมื่อ PHP 77

เห็นกล่องสีแดง?

เมื่อใดก็ตามที่คุณไป mysql_*หน้าคู่มือการใช้งานคุณจะเห็นกล่องสีแดงอธิบายว่าไม่ควรใช้อีกต่อไป

ทำไม


การย้ายออกไปext/mysqlไม่เพียง แต่เกี่ยวกับความปลอดภัยเท่านั้น แต่ยังเกี่ยวกับการเข้าถึงคุณลักษณะทั้งหมดของฐานข้อมูล MySQL ด้วย

ext/mysqlสร้างขึ้นสำหรับMySQL 3.23และมีการเพิ่มเติมเพียงเล็กน้อยตั้งแต่นั้นมาในขณะที่ส่วนใหญ่รักษาความเข้ากันได้กับเวอร์ชั่นเก่านี้ซึ่งทำให้รหัสยากต่อการบำรุงรักษา คุณสมบัติที่ขาดหายไปซึ่งไม่รองรับโดยext/mysqlรวม: ( จากคู่มือ PHP )

เหตุผลที่ไม่ใช้mysql_*ฟังก์ชัน :

  • ไม่ได้อยู่ภายใต้การพัฒนาที่ใช้งานอยู่
  • ลบออกจาก PHP 7
  • ขาดอินเตอร์เฟส OO
  • ไม่รองรับข้อความค้นหาที่ไม่ปิดกั้นและไม่ตรงกัน
  • ไม่รองรับข้อความที่เตรียมไว้หรือ ข้อความค้นหาที่กำหนดพารามิเตอร์
  • ไม่รองรับขั้นตอนการจัดเก็บ
  • ไม่รองรับคำสั่งหลายรายการ
  • ไม่รองรับธุรกรรม
  • ไม่รองรับฟังก์ชั่นทั้งหมดใน MySQL 5.1

จุดดังกล่าวมาจากคำตอบของเควนติน

การขาดการสนับสนุนงบที่เตรียมไว้มีความสำคัญอย่างยิ่งเนื่องจากมีวิธีการที่ชัดเจนและผิดพลาดน้อยกว่าในการหลบหนีและการอ้างถึงข้อมูลภายนอกมากกว่าการเรียกใช้ด้วยตนเองด้วยการเรียกฟังก์ชันที่แยกต่างหาก

ดูการเปรียบเทียบของนามสกุล SQL


ไม่แสดงคำเตือนการคัดค้าน

ในขณะที่รหัสกำลังถูกแปลงเป็นMySQLi/ PDOคุณE_DEPRECATEDสามารถระงับข้อผิดพลาดได้โดยการตั้งค่าerror_reportingในphp.iniเพื่อยกเว้นE_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

โปรดทราบว่านี่จะซ่อนคำเตือนการคัดค้านอื่น ๆซึ่งอาจเป็นสิ่งอื่นนอกเหนือจาก MySQL ( จากคู่มือ PHP )

บทความPDO กับ MySQLi: คุณควรใช้แบบไหน โดยDejan Marjanovićจะช่วยให้คุณเลือก

และวิธีที่ดีกว่าคือPDOตอนนี้ฉันกำลังเขียนPDOบทแนะนำอย่างง่าย


บทช่วยสอน PDO ที่ง่ายและสั้น


คำถามที่ถามเป็นครั้งแรกในใจของฉันได้รับสิ่งที่ PDO` คือ `?

A. “ PDO - PHP Data Objects - เป็นเลเยอร์การเข้าถึงฐานข้อมูลที่ให้วิธีการเข้าถึงฐานข้อมูลที่หลากหลายอย่างสม่ำเสมอ”

ข้อความแสดงแทน


กำลังเชื่อมต่อกับ MySQL

ด้วยmysql_*ฟังก์ชั่นหรือเราสามารถพูดได้แบบเก่า (เลิกใช้แล้วใน PHP 5.5 ขึ้นไป)

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

ด้วยPDO: สิ่งที่คุณต้องทำคือสร้างPDOวัตถุใหม่ นวกรรมิกยอมรับพารามิเตอร์สำหรับการระบุแหล่งที่มาของฐานข้อมูลPDOของคอนสตรัคส่วนใหญ่ใช้พารามิเตอร์สี่ซึ่งเป็นDSN(ชื่อแหล่งข้อมูล) และเลือกusername,password .

ที่นี่ฉันคิดว่าคุณคุ้นเคยกับทุกคนยกเว้นDSN; PDOนี้คือในใหม่ A DSNนั้นเป็นสตริงของตัวเลือกที่บอกว่าPDOจะใช้ไดรเวอร์ใดและรายละเอียดการเชื่อมต่อ สำหรับการอ้างอิงต่อไปตรวจสอบPDO MySQL DSN

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

หมายเหตุ:คุณสามารถใช้งานcharset=UTF-8ได้ แต่บางครั้งก็ทำให้เกิดข้อผิดพลาดดังนั้นจึงควรใช้งานutf8แต่บางครั้งก็ทำให้เกิดข้อผิดพลาดดังนั้นจึงเป็นเรื่องที่ดีที่จะใช้

หากมีข้อผิดพลาดในการเชื่อมต่อมันจะโยนPDOExceptionวัตถุที่สามารถจับได้Exceptionต่อไป

อ่านดี : การเชื่อมต่อและการจัดการการเชื่อมต่อ¶

คุณยังสามารถส่งผ่านตัวเลือกไดร์เวอร์หลายตัวเป็นอาร์เรย์ไปยังพารามิเตอร์ที่สี่ ฉันขอแนะนำให้ส่งพารามิเตอร์ที่ทำให้PDOอยู่ในโหมดยกเว้น เนื่องจากPDOไดรเวอร์บางตัวไม่รองรับคำสั่งพื้นฐานที่เตรียมไว้ดังนั้นPDOทำการจำลองการเตรียมการ นอกจากนี้ยังช่วยให้คุณเปิดใช้งานการจำลองนี้ด้วยตนเอง falseเมื่อต้องการใช้ด้านเซิร์ฟเวอร์พื้นเมืองเตรียมงบคุณควรกำหนดอย่างชัดเจน

อีกวิธีหนึ่งคือปิดการเตรียมการจำลองซึ่งเปิดใช้งานในMySQLไดรเวอร์ตามค่าเริ่มต้น แต่ควรปิดการจำลองการเตรียมใช้งานPDOอย่างปลอดภัย

ฉันจะอธิบายในภายหลังว่าทำไมการเตรียมการจำลองจึงควรปิด เพื่อหาเหตุผลโปรดตรวจสอบโพสต์นี้นี้

มันสามารถใช้งานได้เฉพาะถ้าคุณใช้รุ่นเก่า MySQLซึ่งฉันไม่แนะนำ

ด้านล่างเป็นตัวอย่างของวิธีที่คุณสามารถทำได้:

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

เราสามารถตั้งค่าแอตทริบิวต์หลังจากการก่อสร้าง PDO ได้หรือไม่?

ใช่เราสามารถกำหนดคุณลักษณะบางอย่างหลังจากการสร้าง PDO ด้วยsetAttributeวิธีการ:

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

การจัดการข้อผิดพลาด


จัดการข้อผิดพลาดเป็นเรื่องง่ายในกว่าPDOmysql_*

วิธีปฏิบัติทั่วไปเมื่อใช้mysql_*คือ:

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

OR die()ไม่ใช่วิธีที่ดีในการจัดการข้อผิดพลาดเนื่องจากเราไม่สามารถจัดการกับสิ่งที่เกิดdieขึ้นได้ มันจะจบสคริปต์ทันทีและจากนั้นสะท้อนข้อผิดพลาดไปที่หน้าจอซึ่งโดยปกติคุณไม่ต้องการแสดงให้ผู้ใช้ปลายทางของคุณทราบ อีกทางหนึ่งค่าส่งคืนของmysql_*ฟังก์ชันมักใช้ร่วมกับmysql_error ()เพื่อจัดการข้อผิดพลาด

PDOเสนอทางออกที่ดีกว่า: ข้อยกเว้น สิ่งใดที่เราทำกับPDOควรจะห่อในtry- catchบล็อก เราสามารถบังคับให้PDOเข้าสู่โหมดข้อผิดพลาดหนึ่งในสามโหมดโดยการตั้งค่าแอตทริบิวต์โหมดข้อผิดพลาด โหมดการจัดการข้อผิดพลาดสามโหมดอยู่ด้านล่าง

  • PDO::ERRMODE_SILENT. มันเป็นเพียงการตั้งรหัสข้อผิดพลาดและทำหน้าที่เหมือนกับmysql_*ที่คุณต้องตรวจสอบผลการค้นหาแต่ละครั้งแล้วดูที่$db->errorInfo();รายละเอียดข้อผิดพลาด
  • PDO::ERRMODE_WARNINGE_WARNINGยก (คำเตือนรันไทม์ (ข้อผิดพลาดที่ไม่ร้ายแรง) การดำเนินการของสคริปต์จะไม่หยุด)
  • PDO::ERRMODE_EXCEPTION: โยนข้อยกเว้น เพราะแสดงถึงข้อผิดพลาดที่เกิดจาก PDO คุณไม่ควรโยนPDOExceptionจากรหัสของคุณเอง ดูข้อยกเว้นสำหรับข้อมูลเพิ่มเติมเกี่ยวกับข้อยกเว้นใน PHP มันทำหน้าที่เหมือนอย่างมากor die(mysql_error());เมื่อไม่ถูกจับ แต่แตกต่างจากor die()ที่PDOExceptionสามารถจับและจัดการได้อย่างสง่างามหากคุณเลือกที่จะทำ

อ่านดี :

ชอบ:

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

และคุณสามารถห่อมันได้try- catchเช่นด้านล่าง:

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

คุณไม่ต้องจัดการกับtry- catchตอนนี้ คุณสามารถจับมันที่เหมาะสมเวลาใด ๆ แต่ผมขอแนะนำให้คุณใช้-try catchนอกจากนี้มันอาจจะสมเหตุสมผลมากกว่าที่จะจับมันที่นอกฟังก์ชั่นที่เรียกใช้PDOสิ่งต่าง ๆ :

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

นอกจากนี้คุณสามารถจัดการโดย or die()หรือเราสามารถพูดชอบmysql_*แต่มันจะแตกต่างกันจริงๆ คุณสามารถซ่อนข้อความแสดงข้อผิดพลาดที่เป็นอันตรายในการผลิตโดยการเปิดdisplay_errors offและเพียงแค่อ่านบันทึกข้อผิดพลาดของคุณ

ตอนนี้หลังจากที่ได้อ่านทุกสิ่งข้างต้นคุณอาจจะคิด: ห่าคือว่าเมื่อผมแค่อยากจะเริ่มต้นพิงง่ายSELECT, INSERT,UPDATEหรือDELETEงบ? ไม่ต้องกังวลไปเลย:


การเลือกข้อมูล

PDO เลือกรูปภาพ

ดังนั้นสิ่งที่คุณกำลังทำ mysql_*คือ:

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

ตอนนี้ใน PDOคุณสามารถทำสิ่งนี้เช่น:

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

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

หรือ

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

//Use $results

หมายเหตุ : หากคุณใช้วิธีการดังต่อไปนี้ ( query()) วิธีการนี้จะคืนค่า aPDOStatementวัตถุ ดังนั้นหากคุณต้องการดึงผลลัพธ์ให้ใช้เหมือนด้านบน

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

ใน PDO Data จะได้รับผ่านทาง ->fetch()วิธีการจัดการรายการบัญชีของคุณ ก่อนที่จะเรียกการดึงข้อมูลวิธีที่ดีที่สุดจะบอก PDO ว่าคุณต้องการดึงข้อมูลอย่างไร ในส่วนด้านล่างนี้ฉันอธิบายเรื่องนี้

ดึงข้อมูลโหมด

สังเกตการใช้งานPDO::FETCH_ASSOCในfetch()และfetchAll()รหัสด้านบน สิ่งนี้จะบอกPDOให้คืนค่าแถวเป็นอาเรย์แบบเชื่อมโยงที่มีชื่อฟิลด์เป็นกุญแจ มีโหมดการดึงข้อมูลอื่น ๆ อีกมากมายเช่นกันซึ่งฉันจะอธิบายทีละคน

ก่อนอื่นฉันอธิบายวิธีเลือกโหมดดึงข้อมูล:

 $stmt->fetch(PDO::FETCH_ASSOC)

fetch()ในข้างต้นผมได้ใช้ คุณยังสามารถใช้:

  • PDOStatement::fetchAll() - ส่งคืนอาร์เรย์ที่มีแถวชุดผลลัพธ์ทั้งหมด
  • PDOStatement::fetchColumn() - ส่งคืนคอลัมน์เดียวจากแถวถัดไปของชุดผลลัพธ์
  • PDOStatement::fetchObject() - ดึงข้อมูลแถวถัดไปและส่งกลับเป็นวัตถุ
  • PDOStatement::setFetchMode() - ตั้งค่าโหมดดึงข้อมูลเริ่มต้นสำหรับคำสั่งนี้

ตอนนี้ฉันมาโหมดดึงข้อมูล:

  • PDO::FETCH_ASSOC: ส่งคืนอาร์เรย์ที่จัดทำดัชนีโดยชื่อคอลัมน์ตามที่ส่งคืนในชุดผลลัพธ์ของคุณ
  • PDO::FETCH_BOTH (ค่าเริ่มต้น): ส่งกลับอาร์เรย์ที่ทำดัชนีโดยทั้งชื่อคอลัมน์และหมายเลขคอลัมน์ 0 ดัชนีที่ส่งคืนในชุดผลลัพธ์ของคุณ

มีตัวเลือกมากขึ้น! อ่านเกี่ยวกับพวกเขาทั้งหมดในPDOStatementเอกสารการดึงข้อมูล .

รับจำนวนแถว :

แทนที่จะใช้mysql_num_rowsเพื่อรับจำนวนแถวที่ส่งคืนคุณสามารถรับPDOStatementและทำrowCount()เช่น:

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

รับ ID ที่แทรกล่าสุด

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

แทรกและอัปเดตหรือลบคำสั่ง

แทรกและอัปเดตอิมเมจ PDO

สิ่งที่เรากำลังทำอยู่mysql_*คือ:

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

และใน pdo สิ่งเดียวกันนี้สามารถทำได้โดย:

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

ในแบบสอบถามข้างต้นPDO::execดำเนินการคำสั่ง SQL และส่งกลับจำนวนแถวที่ได้รับผลกระทบ

การแทรกและการลบจะถูกครอบคลุมในภายหลัง

วิธีการข้างต้นมีประโยชน์เฉพาะเมื่อคุณไม่ได้ใช้ตัวแปรในแบบสอบถาม แต่เมื่อคุณต้องการใช้ตัวแปรในแบบสอบถามไม่เคยลองเหมือนด้านบนและมี คำสั่งที่เตรียมไว้หรือคำสั่งที่กำหนดพารามิเตอร์แล้ว


งบเตรียม

ถามแถลงการณ์ที่เตรียมไว้คืออะไรและทำไมฉันถึงต้องการมัน
A.คำสั่งที่เตรียมไว้เป็นคำสั่ง SQL ที่รวบรวมไว้ล่วงหน้าซึ่งสามารถดำเนินการได้หลายครั้งโดยส่งข้อมูลไปยังเซิร์ฟเวอร์เท่านั้น

เวิร์กโฟลว์ทั่วไปของการใช้คำสั่งที่เตรียมไว้มีดังต่อไปนี้ ( อ้างอิงจาก Wikipedia สามจุด 3 จุด ):

  1. จัดเตรียม : เท็มเพลตคำสั่งถูกสร้างโดยแอปพลิเคชันและส่งไปยังระบบจัดการฐานข้อมูล (DBMS) ค่าบางอย่างถูกทิ้งไว้โดยไม่ระบุซึ่งเรียกว่าพารามิเตอร์ตัวยึดตำแหน่งหรือตัวแปรผูก (ติดป้าย?ด้านล่าง):

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

  2. DBMS แยกวิเคราะห์รวบรวมและดำเนินการปรับให้เหมาะสมแบบสอบถามในแม่แบบคำสั่งและเก็บผลลัพธ์โดยไม่ต้องดำเนินการ

  3. ดำเนินการ : ในภายหลังแอปพลิเคชั่นจะส่งค่า (หรือผูก) สำหรับพารามิเตอร์และ DBMS เรียกใช้คำสั่ง (อาจส่งคืนผลลัพธ์) แอปพลิเคชันอาจดำเนินการคำสั่งหลาย ๆ ครั้งตามที่ต้องการด้วยค่าที่ต่างกัน ในตัวอย่างนี้อาจให้ 'Bread' สำหรับพารามิเตอร์แรกและพารามิเตอร์1.00ที่สอง

คุณสามารถใช้คำสั่งที่เตรียมไว้โดยรวมตัวยึดตำแหน่งใน SQL ของคุณ โดยทั่วไปจะมีสามรายการที่ไม่มีตัวยึดตำแหน่ง (อย่าลองใช้ด้วยตัวแปรด้านบน) กับอันที่ไม่มีตัวยึดและอีกอันที่มีตัวยึดที่ตั้งชื่อไว้

ถามดังนั้นตอนนี้ตัวยึดตำแหน่งชื่ออะไรและฉันจะใช้ได้อย่างไร
A.ตัวยึดที่ระบุชื่อ ใช้ชื่อที่สื่อความหมายนำหน้าด้วยโคลอนแทนที่จะเป็นเครื่องหมายคำถาม เราไม่สนใจเกี่ยวกับตำแหน่ง / คำสั่งของมูลค่าในเจ้าของชื่อ:

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

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

นอกจากนี้คุณยังสามารถผูกโดยใช้อาร์เรย์ดำเนินการเช่นกัน:

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

อีกคุณสมบัติที่ดีสำหรับOOPเพื่อนคือชื่อตัวแทนมีความสามารถในการแทรกวัตถุลงในฐานข้อมูลของคุณโดยตรงโดยสมมติว่าคุณสมบัติตรงกับเขตข้อมูลที่มีชื่อ ตัวอย่างเช่น:

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

ถามดังนั้นตอนนี้ตัวยึดตำแหน่งที่ไม่มีชื่อคืออะไรและฉันจะใช้ได้อย่างไร
A.มีตัวอย่าง:

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

และ

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

ในด้านบนคุณสามารถเห็นสิ่งเหล่านั้น?แทนชื่อเช่นในตัวยึดตำแหน่ง ในตัวอย่างแรกเรากำหนดตัวแปรให้กับตัวยึดตำแหน่งต่างๆ ( $stmt->bindValue(1, $name, PDO::PARAM_STR);) จากนั้นเรากำหนดค่าให้กับตัวแทนเหล่านั้นและดำเนินการคำสั่ง ในตัวอย่างที่สององค์ประกอบแถวแรกที่ไปครั้งแรกและครั้งที่สองที่สอง??

หมายเหตุ : ในตัวยึดตำแหน่งที่ไม่มีชื่อเราจะต้องดูแลลำดับที่เหมาะสมขององค์ประกอบในอาร์เรย์ที่เรากำลังส่งผ่านไปยังPDOStatement::execute()วิธีการ


SELECT, INSERT, UPDATE, DELETEจัดทำคำสั่ง

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

บันทึก:

อย่างไรก็ตามPDOและ / หรือMySQLiไม่ปลอดภัยอย่างสมบูรณ์ ตรวจสอบคำตอบPDO มีการจัดทำงบอย่างเพียงพอเพื่อป้องกันการฉีด SQL หรือไม่? โดยircmaxell นอกจากนี้ฉันกำลังอ้างอิงบางส่วนจากคำตอบของเขา:

$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
สิ่งที่ดีอ่านข้างต้น propably ควรจะกล่าวถึง: IN (...) constructงบเตรียมนำออกไปใช้งานที่มีความหมายใด
Eugen Rieck

24
คำถามคือ "ทำไมฉันไม่ควรใช้ mysql_ * ฟังก์ชั่นใน PHP" คำตอบนี้ในขณะที่ข้อมูลที่น่าประทับใจและเต็มไปด้วยข้อมูลที่เป็นประโยชน์ออกไปนอกขอบเขตและเช่น @trejder พูดว่า - 8 ใน 10 คนจะพลาดข้อมูลนั้นเพียงเพราะพวกเขาไม่มีเวลา 4 ชั่วโมงในการพยายามทำงานผ่าน มัน. สิ่งนี้จะมีค่ายิ่งกว่าเดิมและใช้เป็นคำตอบสำหรับคำถามที่แม่นยำกว่า
Alex McMillan

Persoanlly ฉันชอบ mysqli และ PDO แต่สำหรับการจัดการตายฉันลองใช้ข้อยกเว้นทางเลือก function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();มันใช้งานได้สำหรับการขว้างข้อยกเว้น
kuldeep.kamboj

คุณแสดงรายการDoesn't support non-blocking, asynchronous queriesเหตุผลที่ไม่ใช้ mysql_ - คุณควรระบุว่าเป็นเหตุผลที่ไม่ใช้ PDO เนื่องจาก PDO ไม่สนับสนุนสิ่งนั้น (แต่ MySQLi สนับสนุน)
hanshenrik

เป็นไปได้ไหมที่จะใช้ Charset utf8mb4_unicode_ci เนื่องจากฉันมีฐานข้อมูลที่ใช้สิ่งนี้อยู่
Ryan Stone

301

ก่อนอื่นเรามาเริ่มด้วยความคิดเห็นมาตรฐานที่เราให้ทุกคน:

กรุณาอย่าใช้mysql_*ฟังก์ชั่นในรหัสใหม่ พวกเขาจะไม่เก็บรักษาไว้และมีการยกเลิกอย่างเป็นทางการ เห็นกล่องสีแดง ? เรียนรู้เกี่ยวกับงบที่เตรียมไว้และใช้ PDOหรือ MySQLi -บทความนี้จะช่วยให้คุณตัดสินใจได้ว่า หากคุณเลือกที่ PDO,นี่คือการสอนที่ดี

ลองทำสิ่งนี้กันทีละประโยคและอธิบาย:

  • ไม่มีการบำรุงรักษาอีกต่อไปและเลิกใช้งานแล้ว

    ซึ่งหมายความว่าชุมชน PHP ค่อยๆลดการสนับสนุนฟังก์ชั่นเก่า ๆ เหล่านี้ พวกเขามีแนวโน้มที่จะไม่อยู่ในอนาคต (รุ่นล่าสุด) ของ PHP! การใช้ฟังก์ชันเหล่านี้อย่างต่อเนื่องอาจทำให้โค้ดของคุณเสียหายในอนาคตอันใกล้

    ใหม่! - ext / mysql เลิกใช้แล้วอย่างเป็นทางการตั้งแต่ PHP 5.5!

    บทความที่ใหม่กว่า! ext / mysql ถูกลบใน PHP 7แล้ว

  • คุณควรเรียนรู้เกี่ยวกับข้อความที่เตรียมไว้แทน

    mysql_*นามสกุลไม่สนับสนุนงบเตรียมซึ่งเป็น (เหนือสิ่งอื่น) ตอบโต้ที่มีประสิทธิภาพมากกับSQL Injection มันแก้ไขช่องโหว่ที่ร้ายแรงมากในแอพพลิเคชั่นที่ขึ้นกับ MySQL ซึ่งทำให้ผู้โจมตีสามารถเข้าถึงสคริปต์ของคุณและทำการสืบค้นที่เป็นไปได้ในฐานข้อมูลของคุณ

    สำหรับข้อมูลเพิ่มเติมให้ดูที่ฉันจะป้องกันการฉีด SQL ใน PHP ได้อย่างไร

  • เห็นกล่องสีแดง?

    เมื่อคุณไปที่mysqlหน้าคู่มือการใช้งานใด ๆคุณจะเห็นกล่องสีแดงอธิบายว่าไม่ควรใช้อีกต่อไป

  • ใช้ PDO หรือ MySQLi

    มีทางเลือกที่ดีกว่าแข็งแกร่งและสร้างขึ้นอย่างดีPDO - PHP Database Objectซึ่งมีวิธีการ OOP ที่สมบูรณ์ในการโต้ตอบกับฐานข้อมูลและMySQLiซึ่งเป็นการปรับปรุงเฉพาะของ MySQL


6
มีอีกสิ่งหนึ่ง: ฉันคิดว่าฟังก์ชั่นยังคงมีอยู่ใน PHP เพียงหนึ่งเหตุผล - ความเข้ากันได้กับเก่าล้าสมัย แต่ยังคงใช้ CMS, อีคอมเมิร์ซ, ระบบกระดานข่าว ฯลฯ ในที่สุดก็จะถูกลบออกและคุณจะต้องเขียน ใบสมัคร ...
Kamil

4
@ Kamil: จริง แต่ก็ไม่ใช่เหตุผลว่าทำไมคุณไม่ควรใช้มัน เหตุผลที่จะไม่ใช้มันเป็นเพราะมันเป็นโบราณที่ไม่ปลอดภัย ฯลฯ :)
มาดาระผี

4
@Mario - PHP devs มีกระบวนการและพวกเขาเพิ่งลงคะแนนเห็นชอบ ext / mysql อย่างเป็นทางการตั้งแต่ 5.5 มันไม่ใช่เรื่องสมมุติอีกต่อไป
SDC

2
การเพิ่มสองสามบรรทัดพิเศษด้วยเทคนิคที่พิสูจน์แล้วเช่น PDO หรือ MySQLi ยังคงให้ความสะดวกในการใช้งาน PHP ที่นำเสนอเสมอ ฉันหวังว่าเพื่อประโยชน์ของนักพัฒนาเขา / เธอรู้ว่าการเห็น mysql_ * ฟังก์ชั่น god-awful เหล่านี้ในบทช่วยสอนจริง ๆ แล้วเบี่ยงเบนไปจากบทเรียนและควรบอก OP ว่ารหัสประเภทนี้มากเกินไปเมื่อ 10 ปีที่แล้ว - และควรถาม ความเกี่ยวข้องของการสอนเช่นกัน!
FredTheWebGuy

1
คำตอบที่ควรกล่าวถึงคืออะไร: ข้อความที่เตรียมไว้จะนำการใช้ประโยชน์IN (...) constructไป
Eugen Rieck

217

สะดวกในการใช้

เหตุผลการวิเคราะห์และสังเคราะห์ได้ถูกกล่าวถึงแล้ว สำหรับผู้มาใหม่มีสิ่งสำคัญยิ่งกว่าที่จะหยุดใช้ฟังก์ชัน mysql_ ที่เก่ากว่า

API ฐานข้อมูลร่วมสมัยนั้นใช้งานง่ายกว่า

ส่วนใหญ่เป็นพารามิเตอร์ที่ถูกผูกไว้ซึ่งสามารถลดความซับซ้อนของรหัส และด้วยบทเรียนที่ยอดเยี่ยม (ดังที่เห็นด้านบน)การเปลี่ยนไปใช้PDOนั้นไม่ยากเกินไป

การเขียนซ้ำรหัสฐานที่ใหญ่กว่าในครั้งเดียว แต่ต้องใช้เวลา Raison d'êtreสำหรับทางเลือกขั้นกลางนี้:

ฟังก์ชันเทียบเท่า pdo_ * แทนที่mysql_ *

ใช้< pdo_mysql.php >คุณสามารถสลับจากฟังก์ชั่น mysql_ เก่าที่มีความพยายามน้อยที่สุด มันเพิ่มpdo_ฟังก์ชั่นห่อซึ่งแทนที่mysql_คู่ของพวกเขา

  1. เพียงแค่ในสคริปต์การร้องขอแต่ละครั้งที่มีการโต้ตอบกับฐานข้อมูล include_once("pdo_mysql.php");

  2. เอาmysql_คำนำหน้าฟังก์ชั่นทุกที่pdo_และแทนที่ด้วย

    • mysql_connect() กลายเป็น pdo_connect()
    • mysql_query() กลายเป็น pdo_query()
    • mysql_num_rows() กลายเป็น pdo_num_rows()
    • mysql_insert_id() กลายเป็น pdo_insert_id()
    • mysql_fetch_array() กลายเป็น pdo_fetch_array()
    • mysql_fetch_assoc() กลายเป็น pdo_fetch_assoc()
    • mysql_real_escape_string() กลายเป็น pdo_real_escape_string()
    • และอื่น ๆ ...

  3. รหัสของคุณจะทำงานเหมือนกันและยังคงเหมือนเดิม:

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

ฯลฯ
รหัสของคุณใช้ PDO
ตอนนี้มันถึงเวลาที่จะจริงใช้มัน

พารามิเตอร์ที่ถูกผูกไว้สามารถใช้งานง่าย

คุณเพียงแค่ต้องการ API ที่ไม่มั่นคงเท่านี้

pdo_query()เพิ่มการรองรับที่ง่ายมากสำหรับพารามิเตอร์ที่ถูกผูกไว้ การแปลงรหัสเก่านั้นตรงไปตรงมา:

ย้ายตัวแปรของคุณออกจากสตริง SQL

  • pdo_query()เพิ่มพวกเขาเป็นที่คั่นด้วยจุลภาคพารามิเตอร์ฟังก์ชั่น
  • ทำเครื่องหมายคำถาม?ว่าเป็นตัวยึดตำแหน่งที่มีตัวแปรมาก่อน
  • กำจัด'เครื่องหมายคำพูดเดี่ยวที่ปิดไว้ก่อนหน้านี้ค่า / ตัวแปรสตริง

ข้อดีจะชัดเจนมากขึ้นสำหรับรหัสที่ยาวขึ้น

บ่อยครั้งที่ตัวแปรสตริงไม่ได้ถูกแทรกเข้าไปใน SQL เพียงอย่างเดียว

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

เมื่อ?ใช้ตัวยึดตำแหน่งคุณไม่ต้องกังวลกับสิ่งต่อไปนี้:

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

โปรดจำไว้ว่า * pdo_ ยังช่วยให้หรือ
อย่าหนีตัวแปรและผูกมันไว้ในเคียวรีเดียวกัน

  • คุณลักษณะตัวยึดตำแหน่งถูกจัดเตรียมโดย PDO จริงที่อยู่ด้านหลัง
  • ดังนั้นจึงอนุญาต:namedรายการตัวยึดตำแหน่งในภายหลัง

ที่สำคัญคุณสามารถผ่านตัวแปร $ _REQUEST [] ได้อย่างปลอดภัยหลังการสืบค้นใด ๆ เมื่อ<form>เขตข้อมูลที่ส่งตรงกับโครงสร้างฐานข้อมูลมันจะสั้นกว่า:

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

ความเรียบง่ายมาก แต่กลับมาที่คำแนะนำการเขียนใหม่และเหตุผลทางเทคนิคเพิ่มเติมว่าทำไมคุณอาจต้องการกำจัดmysql_และหลบหนี

แก้ไขหรือลบsanitize()ฟังก์ชันoldschool ใด ๆ

เมื่อคุณแปลงการmysql_โทรทั้งหมดเป็นpdo_queryparams ที่ถูกผูกไว้ให้ลบการpdo_real_escape_stringโทรซ้ำซ้อนทั้งหมด

โดยเฉพาะอย่างยิ่งคุณควรแก้ไขใด ๆsanitizeหรือcleanหรือfilterThisหรือclean_dataฟังก์ชั่นตามที่โฆษณาโดยการสอนวันที่ในรูปแบบเดียวหรืออื่น ๆ :

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

ข้อผิดพลาดที่เห็นได้ชัดที่สุดที่นี่คือการขาดเอกสาร ลำดับของตัวกรองที่มีนัยสำคัญยิ่งกว่านั้นเป็นลำดับที่ผิด

  • ลำดับที่ถูกต้องจะได้รับ: deprecatedly stripslashesเป็นโทรสุดแล้วtrimหลังจากนั้นstrip_tags, htmlentitiesบริบทการส่งออกและมีเพียงในที่สุด_escape_stringเป็นโปรแกรมที่ควร preceed โดยตรง intersparsing SQL

  • แต่เป็นขั้นตอนแรกเพียงแค่กำจัด_real_escape_stringสาย

  • คุณอาจต้องรักษาส่วนที่เหลือของsanitize()ฟังก์ชั่นของคุณไว้ในตอนนี้ถ้าฐานข้อมูลและโฟลว์แอปพลิเคชันของคุณคาดว่าสตริง HTML-context-safe เพิ่มความคิดเห็นที่จะใช้เฉพาะ HTML ที่หนีไปนับจากนี้ไป

  • การจัดการสตริง / ค่าถูกมอบหมายให้ PDO และคำสั่งพารามิเตอร์

  • หากมีการพูดถึงstripslashes()ฟังก์ชั่นการฆ่าเชื้อของคุณมันอาจบ่งบอกถึงการกำกับดูแลในระดับที่สูงขึ้น

    บันทึกประวัติศาสตร์ใน magic_quotes คุณสมบัตินั้นเลิกใช้อย่างถูกต้อง บ่อยครั้งที่ภาพไม่ถูกต้องเนื่องจากความปลอดภัยล้มเหลวคุณลักษณะแต่ magic_quotes นั้นมีคุณสมบัติด้านความปลอดภัยที่ล้มเหลวเหมือนกับลูกเทนนิสที่ล้มเหลวในฐานะแหล่งโภชนาการ นั่นไม่ใช่วัตถุประสงค์ของพวกเขา

    การใช้งานดั้งเดิมใน PHP2 / FI แนะนำอย่างชัดเจนด้วยเพียง " เครื่องหมายคำพูดจะถูกหลบหนีโดยอัตโนมัติทำให้ง่ายต่อการส่งผ่านข้อมูลในแบบฟอร์มโดยตรงไปยังแบบสอบถาม msql " โดยเฉพาะอย่างยิ่งมันปลอดภัยที่จะใช้กับmSQLโดยบังเอิญเนื่องจากรองรับ ASCII เท่านั้น
    จากนั้น PHP3 / Zend ได้แนะนำ Magic_quotes สำหรับ MySQL อีกครั้งและพิมพ์ผิด แต่เดิมเป็นเพียงฟีเจอร์อำนวยความสะดวกเท่านั้นไม่ใช่เพื่อความปลอดภัย

ข้อความที่เตรียมแตกต่างกันอย่างไร

เมื่อคุณช่วงชิงตัวแปรสตริงลงในคิวรี SQL มันจะไม่ได้รับความซับซ้อนมากขึ้นสำหรับคุณที่จะติดตาม นอกจากนี้ยังมีความพยายามอย่างมากสำหรับ MySQL ในการแยกรหัสและข้อมูลอีกครั้ง

SQL injections ก็คือเมื่อข้อมูลตกใส่รหัสบริบทของเซิร์ฟเวอร์ฐานข้อมูลไม่สามารถตรวจพบจุดที่ PHP ติดตั้งตัวแปรเดิมไว้ระหว่างส่วนคำสั่ง

ด้วยพารามิเตอร์ที่ถูกผูกไว้คุณแยกรหัส SQL และค่าบริบท SQL ในรหัส PHP ของคุณ แต่มันจะไม่ถูกสับอีกหลัง (ยกเว้น PDO :: EMULATE_PREPARES) ฐานข้อมูลของคุณได้รับคำสั่ง SQL ที่ไม่ได้แยกและค่าตัวแปร 1: 1

ในขณะที่คำตอบนี้เน้นว่าคุณควรใส่ใจกับข้อดีของการอ่านที่ลดลง mysql_ในขณะที่คำตอบนี้เน้นว่าคุณควรดูแลเกี่ยวกับข้อดีของการอ่านลดลงนอกจากนี้ยังมีข้อได้เปรียบด้านประสิทธิภาพเป็นครั้งคราว (INSERT ที่ทำซ้ำโดยมีค่าต่างกัน) เนื่องจากการแยกข้อมูลและรหัส / เทคนิค

ระวังว่าการผูกพารามิเตอร์ยังไม่ได้เป็นโซลูชั่นแบบครบวงจรที่วิเศษสำหรับทุกคนการฉีด SQLมันจัดการการใช้งานทั่วไปสำหรับข้อมูล / ค่า แต่ไม่สามารถยกเว้นรายการชื่อคอลัมน์ / ตัวระบุตารางช่วยในการสร้างส่วนคำสั่งแบบไดนามิกหรือรายการค่าอาร์เรย์ธรรมดา

ใช้ Hybrid PDO

pdo_*ฟังก์ชัน wrapper เหล่านี้สร้าง API การหยุดช่องว่างที่เป็นมิตรกับการเข้ารหัส (มันค่อนข้างMYSQLIจะเป็นไปได้ถ้ามันไม่ได้เกิดจากการเปลี่ยนลายเซ็นของฟังก์ชันที่แปลกใหม่) พวกเขายังเปิดเผย PDO จริงตลอดเวลา
การเขียนใหม่ไม่จำเป็นต้องหยุดใช้ชื่อฟังก์ชัน pdo_ ใหม่ คุณสามารถเปลี่ยนทีละหนึ่ง pdo_query () เป็นการเรียก $ pdo-> prepare () -> execute () แบบธรรมดา

เป็นการดีที่สุดที่จะเริ่มต้นใหม่ให้ง่ายขึ้นอีกครั้งอย่างไรก็ตาม ตัวอย่างเช่นการดึงผลลัพธ์ทั่วไป:

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

สามารถแทนที่ด้วยการทำซ้ำ foreach:

foreach ($result as $row) {

หรือดีกว่าการดึงข้อมูลอาร์เรย์โดยตรงและสมบูรณ์:

$result->fetchAll();

คุณจะได้รับคำเตือนที่เป็นประโยชน์มากขึ้นในกรณีส่วนใหญ่กว่า PDO หรือ mysql_ มักจะมีให้หลังจากการสืบค้นที่ล้มเหลว

ตัวเลือกอื่น

ดังนั้นนี่หวังว่ามองเห็นบางอย่างในทางปฏิบัติด้วยเหตุผลและทางเดิน worthwile mysql_การลดลง

เพียงแค่เปลี่ยนเป็น ไม่ได้ตัดเลย pdo_query()เป็นเพียงส่วนหน้าของมัน

นอกจากว่าคุณจะแนะนำการโยงพารามิเตอร์หรือสามารถใช้อย่างอื่นจาก nicer API ก็เป็นสวิตช์ที่ไม่มีจุดหมาย ฉันหวังว่ามันจะแสดงให้เห็นได้ง่ายพอที่จะไม่ทำให้ผู้ที่มาใหม่ผิดหวัง (การศึกษามักจะทำงานได้ดีกว่าข้อห้าม)

ในขณะที่มันมีคุณสมบัติสำหรับหมวดหมู่สิ่งที่ง่ายที่สุดที่อาจเป็นไปได้ แต่ก็ยังเป็นรหัสทดลอง ฉันเพิ่งเขียนมันในช่วงสุดสัปดาห์ มีทางเลือกมากมายเหลือเฟืออย่างไรก็ตาม เพียง google สำหรับฐานข้อมูล PHP ที่เป็นนามธรรมและเรียกดูเพียงเล็กน้อย มีอยู่เสมอและจะเป็นห้องสมุดที่ยอดเยี่ยมมากมายสำหรับงานดังกล่าว

หากคุณต้องการให้การโต้ตอบกับฐานข้อมูลของคุณง่ายขึ้นผู้ทำแผนที่เช่นParis / Idiormควรลองทำดู เหมือนไม่มีใครใช้ DOM อ่อนโยนใน JavaScript อีกต่อไปคุณไม่จำเป็นต้องดูแลฐานข้อมูลดิบในปัจจุบัน


8
ระวังpdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);ฟังก์ชั่น - เช่น:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
rickyduck

@Tom แน่นอนแม้ว่าจะไม่ได้รับการดูแลรักษามากนัก (0.9.2 เป็นรายการสุดท้าย) คุณสามารถสร้างบัญชีฟอสซิลเพิ่มไปยังวิกิหรือส่งรายงานข้อผิดพลาด (โดยไม่ต้องลงทะเบียน IIRC)
มาริโอ

pdo_real_escape_string() <- นี่เป็นฟังก์ชั่นจริงฉันไม่สามารถหาเอกสารใด ๆ ได้หรือไม่? กรุณาโพสต์แหล่งที่มาสำหรับสิ่งนี้
Ryan Stone

144

mysql_ฟังก์ชั่น:

  1. ล้าสมัย - ไม่มีการบำรุงรักษาอีกต่อไป
  2. ไม่อนุญาตให้คุณย้ายไปยังแบ็กเอนด์ฐานข้อมูลอื่นได้อย่างง่ายดาย
  3. ไม่รองรับข้อความที่เตรียมไว้ดังนั้น
  4. สนับสนุนให้โปรแกรมเมอร์ใช้การต่อข้อมูลเพื่อสร้างคิวรีซึ่งนำไปสู่ช่องโหว่การฉีด SQL

18
# 2 เป็นจริงเท่ากันของmysqli_
eggyal

16
เพื่อความเป็นธรรมเนื่องจากรูปแบบของภาษา SQL แม้ PDO จะไม่ให้ความมั่นใจกับคุณในระดับที่ 2 คุณต้องการ ORM wrapper ที่เหมาะสมสำหรับสิ่งนั้น
SDC

mysql_*ฟังก์ชั่นที่มีเปลือกเข้าสู่ฟังก์ชั่น mysqlnd สำหรับรุ่นใหม่ PHP ดังนั้นแม้ว่าห้องสมุดลูกค้าเก่าไม่ได้รับการบำรุงรักษาใด ๆ เพิ่มเติม mysqlnd จะยังคง :)
hakre

ปัญหามีผู้ให้บริการพื้นที่เว็บไม่มากที่สามารถรองรับรูปแบบการออกแบบเชิงวัตถุดังกล่าวเนื่องจากรุ่น PHP ที่ล้าสมัย
Raju yourPepe

@RajuGujarati หาเว็บโฮสต์ที่สามารถ หากโฮสต์เว็บของคุณไม่มีโอกาสสูงมากที่จะเสี่ยงต่อการถูกโจมตีบนเซิร์ฟเวอร์
Alnitak

106

พูดถึงเหตุผลทางเทคนิคมีเพียงไม่กี่อย่างที่เฉพาะเจาะจงมากและไม่ค่อยได้ใช้ ส่วนใหญ่คุณจะไม่เคยใช้พวกเขาในชีวิตของคุณ
บางทีฉันอาจจะงมงายเกินไป แต่ฉันไม่เคยมีโอกาสใช้สิ่งต่าง ๆ เช่น

  • แบบสอบถามที่ไม่ปิดกั้นและไม่ตรงกัน
  • กระบวนงานที่เก็บไว้คืนชุดผลลัพธ์หลายชุด
  • การเข้ารหัส (SSL)
  • การอัด

หากคุณต้องการ - นี่คือเหตุผลทางเทคนิคที่ไม่ต้องสงสัยเลยที่จะย้ายออกจากส่วนขยายของ mysql ไปยังสิ่งที่มีสไตล์และดูทันสมัยกว่า

อย่างไรก็ตามยังมีปัญหาที่ไม่ใช่ด้านเทคนิคซึ่งอาจทำให้ประสบการณ์ของคุณยากขึ้นเล็กน้อย

  • การใช้งานฟังก์ชั่นเหล่านี้ต่อไปกับเวอร์ชัน PHP ที่ทันสมัยจะทำให้ประกาศระดับเลิกใช้งานได้ พวกเขาสามารถปิดได้
  • ในอนาคตอันใกล้พวกเขาอาจถูกลบออกจากการสร้าง PHP เริ่มต้น ไม่ใช่เรื่องใหญ่เช่นกันเนื่องจาก mydsql จะถูกย้ายไปที่ PECL และผู้ใช้ทุกคนก็ยินดีที่จะรวบรวม PHP ด้วยเนื่องจากพวกเขาไม่ต้องการสูญเสียลูกค้าที่มีเว็บไซต์ที่ทำงานมานานหลายทศวรรษ
  • ความต้านทานที่แข็งแกร่งจากชุมชน Stackoverflow Еทุกครั้งที่คุณพูดถึงหน้าที่ซื่อสัตย์เหล่านี้คุณได้รับการบอกกล่าวว่าพวกเขาอยู่ภายใต้ข้อห้ามที่เข้มงวด
  • การเป็นผู้ใช้ PHP โดยเฉลี่ยน่าจะเป็นความคิดของคุณในการใช้ฟังก์ชั่นเหล่านี้คือข้อผิดพลาดและผิดพลาด เพียงเพราะบทเรียนและคู่มือมากมายเหล่านี้ซึ่งสอนคุณผิดวิธี ไม่ใช่ฟังก์ชั่นของตัวเอง - ฉันต้องเน้น - แต่วิธีที่ใช้

ปัญหาหลังนี้เป็นปัญหา
แต่ในความคิดของฉันการแก้ปัญหาที่เสนอไม่ดีกว่าอย่างใดอย่างหนึ่ง
สำหรับฉันแล้วมันเป็นเรื่องในอุดมคติในฝันที่ผู้ใช้ PHP ทุกคนจะได้เรียนรู้วิธีจัดการกับแบบสอบถาม SQL อย่างถูกต้องในคราวเดียว ส่วนใหญ่มีแนวโน้มพวกเขาก็จะเปลี่ยน mysql_ * * * * * * * * เพื่อ mysqli_ กลออกจากวิธีการเดียวกัน โดยเฉพาะอย่างยิ่งเพราะ mysqli ใช้งบที่เตรียมไว้อย่างไม่น่าเชื่อเจ็บปวดและลำบาก
ไม่พูดถึงว่าพื้นเมืองเตรียมงบไม่พอที่จะปกป้องจากการฉีด SQL และไม่ mysqli มิได้ PDO มีวิธีการแก้ปัญหา

ดังนั้นแทนที่จะต่อสู้กับส่วนขยายที่ซื่อสัตย์นี้ฉันต้องการต่อสู้กับการปฏิบัติที่ผิดและให้การศึกษาแก่ผู้คนในวิธีที่ถูกต้อง

นอกจากนี้ยังมีเหตุผลบางอย่างที่เป็นเท็จหรือไม่สำคัญเช่น

  • ไม่รองรับขั้นตอนการจัดเก็บ (เราใช้mysql_query("CALL my_proc");มานานแล้ว)
  • ไม่รองรับธุรกรรม (เหมือนด้านบน)
  • ไม่รองรับงบหลายรายการ (ใครต้องการบ้าง)
  • ไม่ได้อยู่ภายใต้การพัฒนาที่ใช้งานอยู่ (ดังนั้นอะไรมันส่งผลกระทบต่อคุณในทางปฏิบัติใด ๆ ?)
  • ไม่มีส่วนต่อประสาน OO (ในการสร้างขึ้นมานั้นใช้เวลาหลายชั่วโมง)
  • ไม่รองรับข้อความที่เตรียมไว้หรือข้อความค้นหาแบบ Parametrized

สุดท้ายคือจุดที่น่าสนใจ แม้ว่า MySQL ต่อไม่สนับสนุนพื้นเมืองงบเตรียมพวกเขาจะไม่จำเป็นสำหรับการรักษาความปลอดภัย เราสามารถปลอมข้อความที่เตรียมไว้ได้อย่างง่ายดายโดยใช้ตัวแทนที่จัดการด้วยตนเอง (เช่นเดียวกับที่ 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ทุกอย่างเป็นพารามิเตอร์และปลอดภัย

แต่ไม่เป็นไรถ้าคุณไม่ชอบกล่องสีแดงในคู่มือปัญหาของการเลือกเกิดขึ้น: mysqli หรือ PDO?

คำตอบก็จะเป็นดังนี้:

  • หากคุณเข้าใจถึงความจำเป็นในการใช้เลเยอร์ abstraction ในฐานข้อมูลและมองหา API เพื่อสร้างหนึ่งตัวmysqliเป็นตัวเลือกที่ดีมากเพราะมันรองรับคุณสมบัติเฉพาะของ mysql
  • ถ้าเช่นเดียวกับคน PHP ส่วนใหญ่คุณกำลังใช้การเรียก API แบบดิบในรหัสแอปพลิเคชัน (ซึ่งเป็นหลักปฏิบัติที่ผิด) - PDO เป็นตัวเลือกเดียวเนื่องจากส่วนขยายนี้อ้างว่าไม่ใช่แค่ API แต่เป็น semi-DAL ยังไม่สมบูรณ์ แต่มีคุณสมบัติที่สำคัญมากมายโดยมีสองคุณสมบัติที่ทำให้ PDO แตกต่างอย่างมากจาก mysqli:

    • ซึ่งแตกต่างจาก mysqli PDO สามารถผูกตัวยึดตำแหน่งด้วยมูลค่าซึ่งทำให้การสืบค้นที่สร้างขึ้นแบบไดนามิกทำได้โดยไม่ต้องมีหลายหน้าจอของรหัสที่ค่อนข้างยุ่ง
    • ซึ่งแตกต่างจาก mysqli, PDO สามารถส่งคืนผลการสืบค้นในอาร์เรย์ปกติแบบง่าย ๆ ได้ในขณะที่ mysqli สามารถทำได้เฉพาะในการติดตั้ง mysqlnd

ดังนั้นหากคุณเป็นผู้ใช้ PHP โดยเฉลี่ยและต้องการช่วยให้คุณปวดหัวเมื่อใช้คำสั่งที่เตรียมไว้เอง PDO - อีกครั้ง - เป็นตัวเลือกเดียว
อย่างไรก็ตาม PDO ไม่ใช่กระสุนเงินและมีความลำบาก
ดังนั้นฉันจึงเขียนคำตอบสำหรับข้อผิดพลาดทั่วไปและกรณีที่ซับซ้อนในแท็ก PDO

อย่างไรก็ตามทุกคนที่พูดถึงส่วนขยายมักพลาดข้อเท็จจริงสำคัญ 2 ข้อเกี่ยวกับ Mysqli และ PDO:

  1. งบเตรียมไม่ได้เป็น bullet มีตัวบ่งชี้แบบไดนามิกซึ่งไม่สามารถผูกโดยใช้คำสั่งที่เตรียมไว้ มีคิวรีแบบไดนามิกที่มีจำนวนพารามิเตอร์ที่ไม่รู้จักซึ่งทำให้คิวรีสร้างเป็นงานที่ยาก

  2. ไม่ควรมีฟังก์ชัน mysqli_ * หรือ PDO ในรหัสแอปพลิเคชัน
    ควรมีเลเยอร์ที่เป็นนามธรรมระหว่างพวกเขาและรหัสแอปพลิเคชันซึ่งจะทำหน้าที่สกปรกทั้งหมดของการผูกการวนซ้ำการจัดการข้อผิดพลาด ฯลฯ ภายในทำให้แอปพลิเคชันรหัสแห้งและสะอาด โดยเฉพาะอย่างยิ่งสำหรับกรณีที่ซับซ้อนเช่นการสร้างแบบสอบถามแบบไดนามิก

ดังนั้นเพียงแค่เปลี่ยนเป็น PDO หรือ mysqli ไม่เพียงพอ หนึ่งจะต้องใช้ ORM หรือผู้สร้างแบบสอบถามหรือสิ่งที่เป็นนามธรรมระดับฐานข้อมูลแทนการเรียกฟังก์ชั่น API ดิบในรหัสของพวกเขา
และตรงกันข้าม - ถ้าคุณมี abstraction layer ระหว่างรหัสแอปพลิเคชันของคุณกับ mysql API มันไม่สำคัญว่าเอ็นจิ้นใดที่ใช้ คุณสามารถใช้ mysql ext ได้จนกว่าจะเลิกใช้แล้วเขียนคลาส abstraction ของคุณไปยังเอ็นจิ้นอื่นได้อย่างง่ายดายมีรหัสแอปพลิเคชันทั้งหมดไม่เสียหาย

ต่อไปนี้เป็นตัวอย่างบางส่วนจากคลาส safemysqlของฉันเพื่อแสดงให้เห็นว่าคลาส abstraction นั้นควรเป็นอย่างไร:

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

เปรียบเทียบนี้เส้นเดียวกับปริมาณของรหัสที่คุณจะต้องด้วย PDO
จากนั้นเปรียบเทียบกับโค้ดจำนวนบ้าคลั่งที่คุณต้องใช้กับคำสั่ง Mysqli ที่เตรียมแบบดิบ โปรดทราบว่าการจัดการข้อผิดพลาดการทำโปรไฟล์การบันทึกคิวรีที่มีอยู่แล้วและทำงานอยู่

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

เปรียบเทียบกับแทรก PDO ปกติเมื่อทุกชื่อฟิลด์เดียวซ้ำแล้วซ้ำอีกหกถึงสิบครั้ง - ในตัวยึดตำแหน่งที่มีชื่อเหล่านี้จำนวนมากรวมและคำจำกัดความแบบสอบถาม

ตัวอย่างอื่น:

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

คุณแทบจะไม่สามารถหาตัวอย่างให้ PDO จัดการกับกรณีที่เป็นจริงได้
และมันจะพูดมากเกินไปและไม่ปลอดภัย

ดังนั้นอีกครั้ง - ไม่เพียง แต่เป็นคนขับรถดิบที่คุณควรกังวล แต่เป็นวิชาที่มีประโยชน์ไม่เพียง แต่จะเป็นประโยชน์สำหรับตัวอย่างที่โง่เง่าจากคู่มือผู้เริ่มต้น


20
mysql_*ทำให้เกิดช่องโหว่ได้ง่ายมาก เนื่องจากมีการใช้งาน PHP โดยผู้ใช้มือใหม่จำนวนมากmysql_*จึงเป็นอันตรายในทางปฏิบัติแม้ว่าในทางทฤษฎีแล้วจะสามารถใช้งานได้โดยไม่มีการผูกปม
ผีของมาดาราระ

4
everything is parameterized and safe- มันอาจจะเป็นพารามิเตอร์ แต่ฟังก์ชั่นของคุณไม่ได้ใช้คำสั่งที่เตรียมไว้จริง
uınbɐɥs

6
เป็นอย่างไรNot under active developmentสำหรับการแต่งหน้าที่ '0.01%' หากคุณสร้างบางสิ่งด้วยฟังก์ชั่นแบบสแตนด์อโลนอัปเดตเวอร์ชัน mysql ของคุณในหนึ่งปีและใช้ระบบที่ไม่ทำงานฉันมั่นใจว่ามีผู้คนจำนวนมากในทันใดนั้น '0.01%' ฉันจะบอกว่าdeprecatedและnot under active developmentเกี่ยวข้องอย่างใกล้ชิด คุณสามารถพูดได้ว่ามี "ไม่มี [เหตุผล] เหตุผล" สำหรับมัน แต่ความจริงก็คือเมื่อเสนอตัวเลือกระหว่างตัวเลือกno active developmentเกือบจะไม่ดีเท่าที่deprecatedฉันจะพูด?
Nanne

1
@MadaraUchiha: คุณช่วยอธิบายได้ไหมว่าช่องโหว่นั้นมาง่ายแค่ไหน? โดยเฉพาะอย่างยิ่งในกรณีที่ช่องโหว่เดียวกันนั้นไม่ส่งผลกระทบต่อ PDO หรือ MySQLi ... เพราะฉันไม่ได้ตระหนักถึงช่องโหว่เดียวที่คุณพูดถึง
ircmaxell

4
@ShaquinTrifonoff: แน่ใจว่ามันไม่ได้ใช้งบเตรียม แต่ไม่มี PDOซึ่งคนส่วนใหญ่แนะนำมากกว่า MySQLi ดังนั้นฉันไม่แน่ใจว่ามีผลกระทบอย่างมีนัยสำคัญที่นี่ รหัสข้างต้น (มีน้อยมากในการแยกวิเคราะห์) คือสิ่งที่ PDO ไม่เมื่อคุณเตรียมความพร้อมคำสั่งโดยเริ่มต้น ...
ircmaxell

97

มีเหตุผลหลายประการ แต่บางทีสิ่งที่สำคัญที่สุดคือฟังก์ชั่นเหล่านั้นสนับสนุนการเขียนโปรแกรมที่ไม่ปลอดภัยเพราะไม่สนับสนุนข้อความที่เตรียมไว้ งบเตรียมช่วยป้องกันการโจมตี SQL injection

เมื่อใช้mysql_*ฟังก์ชั่นคุณต้องจำไว้ว่าให้เรียกใช้พารามิเตอร์ที่ผู้ใช้จัดหาผ่านmysql_real_escape_string()ฟังก์ชั่นที่คุณต้องจำไว้ว่าให้เรียกใช้พารามิเตอร์ผู้ใช้จัดผ่านหากคุณลืมที่เดียวหรือหากคุณหนีไปเพียงบางส่วนของฐานข้อมูลฐานข้อมูลของคุณอาจถูกโจมตี

การใช้คำสั่งที่เตรียมไว้ในPDOหรือmysqliจะทำให้มันเกิดข้อผิดพลาดในการเขียนโปรแกรมประเภทเหล่านี้ยากขึ้น


3
น่าเสียดายที่การสนับสนุนที่ไม่ดีใน MySQLi_ * สำหรับการส่งพารามิเตอร์จำนวนตัวแปร (เช่นเมื่อคุณต้องการส่งรายการค่าเพื่อตรวจสอบในส่วนคำสั่ง IN) สนับสนุนการใช้พารามิเตอร์ที่ไม่สนับสนุน ปล่อยให้ MySQL_ * โทรมีความเสี่ยง
กระตุ้น

5
แต่อีกครั้งความไม่มั่นคงไม่ใช่ปัญหาโดยธรรมชาติของฟังก์ชัน mysql_ * แต่เป็นปัญหาของการใช้งานที่ไม่ถูกต้อง
Agamemnus

2
@Agamemnus ปัญหาคือ mysql_ * ทำให้ง่ายต่อการติดตั้ง "การใช้งานที่ไม่ถูกต้อง" โดยเฉพาะอย่างยิ่งสำหรับโปรแกรมเมอร์ที่ไม่มีประสบการณ์ ไลบรารีที่ใช้คำสั่งที่เตรียมไว้จะทำให้เกิดข้อผิดพลาดประเภทนั้นได้ยากขึ้น
Trott

75

เพราะ (ท่ามกลางเหตุผลอื่น ๆ ) มันยากมากที่จะตรวจสอบให้แน่ใจว่าข้อมูลอินพุตนั้นถูกสุขอนามัย หากคุณใช้การสอบถามแบบพารามิเตอร์เช่นเดียวกับ PDO หรือ mysqli คุณสามารถหลีกเลี่ยงความเสี่ยงได้ทั้งหมด

ตัวอย่างเช่นบางคนสามารถใช้ "enhzflep); drop table users"เป็นชื่อผู้ใช้ ฟังก์ชั่นเก่าจะอนุญาตให้ดำเนินการหลาย ๆ คำสั่งต่อการค้นหาดังนั้นสิ่งที่คล้ายกับ bugger ที่น่ารังเกียจสามารถลบทั้งตาราง

หากมีการใช้ PDO ของ mysqli ชื่อผู้ใช้จะสิ้นสุดลง "enhzflep); drop table users"ผู้ใช้ชื่อจะสิ้นสุดขึ้นเป็น

ดูbobby-tables.com


10
The old functions will allow executing of multiple statements per query- ไม่พวกเขาจะไม่ การฉีดแบบนั้นไม่สามารถทำได้กับ ext / mysql วิธีเดียวที่การฉีดประเภทนี้ทำได้ด้วย PHP และ MySQL ก็คือเมื่อใช้ MySQLi และmysqli_multi_query()ฟังก์ชั่น การฉีดชนิดที่เป็นไปได้กับ ext / mysql และสตริงที่ไม่ใช้ค่า Escape เป็นสิ่งที่ต้องการ' OR '1' = '1ดึงข้อมูลจากฐานข้อมูลที่ไม่ได้หมายถึงให้สามารถเข้าถึงได้ ในบางสถานการณ์เป็นไปได้ที่จะฉีดคิวรีย่อยอย่างไรก็ตามยังไม่สามารถแก้ไขฐานข้อมูลด้วยวิธีนี้
DaveRandom

64

คำตอบนี้ถูกเขียนขึ้นเพื่อแสดงให้เห็นว่ามันสำคัญแค่ไหนในการหลีกเลี่ยงโค้ดตรวจสอบผู้ใช้ PHP ที่เขียนได้ไม่ดีการโจมตีเหล่านี้ทำงานอย่างไรและวิธีการแทนที่ฟังก์ชัน MySQL เก่าด้วยคำสั่งที่เตรียมไว้อย่างปลอดภัย (อาจมีตัวแทนจำนวนมาก) กำลังเห่าที่ผู้ใช้ใหม่ถามคำถามเพื่อปรับปรุงรหัสของพวกเขา

ก่อนอื่นโปรดสร้างฐานข้อมูลการทดสอบ mysql นี้ (ฉันเรียกว่าการเตรียมการของฉัน):

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)

เมื่อเสร็จแล้วเราสามารถย้ายไปยังโค้ด PHP ของเรา

ให้ถือว่าสคริปต์ต่อไปนี้เป็นกระบวนการตรวจสอบสำหรับผู้ดูแลระบบในเว็บไซต์ (ง่าย แต่ทำงานได้ถ้าคุณคัดลอกและใช้สำหรับการทดสอบ):

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

ดูเหมือนว่าถูกต้องพอสมควรในครั้งแรกที่เห็น

ผู้ใช้จะต้องใส่ชื่อล็อกอินและรหัสผ่านใช่ไหม?

ยอดเยี่ยมไม่ต้องป้อนข้อมูลต่อไปนี้:

user: bob
pass: somePass

และส่งมัน

ผลลัพธ์จะเป็นดังนี้:

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

Super! ทำงานตามที่คาดไว้ตอนนี้ลองใช้ชื่อผู้ใช้และรหัสผ่านจริง:

user: Fluffeh
pass: mypass

! ที่น่าตื่นตาตื่นใจ สวัสดีห้ารอบรหัสถูกต้องตรวจสอบผู้ดูแลระบบ มันสมบูรณ์แบบ!

ก็ไม่ได้จริงๆ ให้บอกว่าผู้ใช้เป็นคนตัวเล็กที่ฉลาด ให้บอกว่าบุคคลนั้นคือฉัน

ใส่ในต่อไปนี้:

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

และผลลัพธ์คือ:

The check passed. We have a verified admin!

ขอแสดงความยินดีคุณเพียงแค่อนุญาตให้ฉันป้อนส่วนผู้ดูแลระบบที่ได้รับการคุ้มครองขั้นสูงของฉันเท่านั้นเมื่อฉันป้อนชื่อผู้ใช้และรหัสผ่านเท็จ อย่างจริงจังถ้าคุณไม่เชื่อฉันสร้างฐานข้อมูลด้วยรหัสที่ฉันให้ไว้และเรียกใช้รหัส PHP นี้ - ซึ่งอย่างรวดเร็วดูเหมือนจะยืนยันชื่อผู้ใช้และรหัสผ่านค่อนข้างดี

ดังนั้นในคำตอบนั่นคือเหตุผลที่คุณถูกตะโกนว่า

ดังนั้นมาดูสิ่งที่ผิดพลาดและทำไมฉันเพิ่งเข้าไปในถ้ำค้างคาวสุดยอดผู้ดูแลระบบของคุณ ฉันเดาและคิดว่าคุณไม่ได้ระมัดระวังกับอินพุตของคุณและเพียงแค่ส่งพวกเขาไปยังฐานข้อมูลโดยตรง ฉันสร้างอินพุตในลักษณะที่จะเปลี่ยนเคียวรีที่คุณกำลังใช้งานจริง ดังนั้นสิ่งที่มันควรจะเป็นและสิ่งที่มันจบลงด้วยการเป็น?

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

นั่นคือแบบสอบถาม แต่เมื่อเราแทนที่ตัวแปรด้วยอินพุตจริงที่เราใช้เราจะได้รับสิ่งต่อไปนี้:

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

มาดูกันว่าฉันสร้าง "รหัสผ่าน" ของฉันเพื่อที่จะปิดใบเสนอราคาเดียวรอบรหัสผ่านแล้วแนะนำการเปรียบเทียบใหม่อย่างสมบูรณ์ได้อย่างไร จากนั้นเพื่อความปลอดภัยฉันเพิ่ม "string" อีกอันเพื่อให้คำพูดเดี่ยวปิดลงตามที่เราคาดไว้ในรหัสที่เรามี

อย่างไรก็ตามตอนนี้ไม่ได้เกี่ยวกับการตะโกนใส่คุณตอนนี้เป็นการแสดงวิธีทำให้รหัสของคุณปลอดภัยยิ่งขึ้น

ตกลงแล้วมีอะไรผิดพลาดและเราจะแก้ไขได้อย่างไร

นี่คือการโจมตีการฉีด SQL แบบคลาสสิก หนึ่งในสิ่งที่ง่ายที่สุดสำหรับเรื่องนั้น ในระดับของเวกเตอร์การโจมตีนี่คือเด็กวัยหัดเดินที่โจมตีรถถัง - และชนะ

ดังนั้นเราจะปกป้องส่วนผู้ดูแลระบบที่ศักดิ์สิทธิ์ของคุณและทำให้ดีและปลอดภัยได้อย่างไร สิ่งแรกที่ต้องทำคือหยุดใช้mysql_*ฟังก์ชั่นที่เก่าและล้าสมัยเหล่านั้น ฉันรู้ว่าคุณทำตามบทช่วยสอนที่คุณพบทางออนไลน์และใช้งานได้ แต่มันเก่าแล้วมันล้าสมัยและในเวลาไม่กี่นาทีฉันเพิ่งผ่านมันไปได้โดยไม่ต้องเหนื่อย

ตอนนี้คุณมีตัวเลือกที่ดีกว่าในการใช้mysqli_หรือPDO PDOฉันเป็นแฟนตัวยงของ PDO ส่วนตัวดังนั้นฉันจะใช้ PDO ในส่วนที่เหลือของคำตอบนี้ มีอาชีพและการต่อต้าน แต่โดยส่วนตัวแล้วฉันพบว่ามืออาชีพมีน้ำหนักเกินกว่าการต่อต้าน มันสามารถพกพาข้ามเอนจิ้นฐานข้อมูลหลาย ๆ ตัวได้ไม่ว่าคุณจะใช้ MySQL หรือ Oracle หรืออะไรก็ตามที่เป็นเลือด - เพียงแค่เปลี่ยนสายเชื่อมต่อมันมีคุณสมบัติแปลกใหม่ที่เราต้องการใช้และมันก็ดีและสะอาด ฉันชอบความสะอาด

ทีนี้มาดูโค้ดอีกครั้งคราวนี้เขียนโดยใช้วัตถุ 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>

ความแตกต่างที่สำคัญคือไม่มีmysql_*ฟังก์ชั่นเพิ่มเติมอีก มันทำผ่านวัตถุ PDO อย่างที่สองคือการใช้คำสั่งที่เตรียมไว้ ทีนี้คุณต้องการถามอะไร? มันเป็นวิธีที่จะบอกฐานข้อมูลก่อนการเรียกใช้แบบสอบถามสิ่งที่เรากำลังจะเรียกใช้แบบสอบถาม ในกรณีนี้เราบอกฐานข้อมูล: "สวัสดีฉันจะเรียกใช้คำสั่ง select ที่ต้องการ id, userid และ pass จากผู้ใช้ตารางที่ userid เป็นตัวแปรและ pass ยังเป็นตัวแปรด้วย"

จากนั้นในคำสั่ง execute เราจะส่งอาเรย์ไปยังฐานข้อมูลพร้อมกับตัวแปรทั้งหมดที่ตอนนี้คาดว่าจะได้

ผลลัพธ์ที่ยอดเยี่ยม ให้ลองใช้ชื่อผู้ใช้และรหัสผ่านรวมกันก่อนหน้านี้อีกครั้ง:

user: bob
pass: somePass

ผู้ใช้ไม่ได้รับการยืนยัน น่ากลัว

เกี่ยวกับ:

user: Fluffeh
pass: mypass

โอ้ฉันเพิ่งตื่นเต้นนิดหน่อยมันใช้งานได้: เช็คผ่านแล้ว เรามีผู้ดูแลระบบที่ผ่านการตรวจสอบแล้ว!

ตอนนี้ให้ลองข้อมูลที่ชาญฉลาดจะเข้ามาพยายามที่จะผ่านระบบการตรวจสอบเล็ก ๆ ของเรา:

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

เวลานี้เราได้รับสิ่งต่อไปนี้:

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

นี่คือเหตุผลที่คุณถูกตะโกนเมื่อโพสต์คำถาม - เป็นเพราะคนเห็นว่ารหัสของคุณสามารถข้ามได้โดยไม่ต้องลอง โปรดใช้คำถามนี้และคำตอบเพื่อปรับปรุงรหัสของคุณเพื่อให้มีความปลอดภัยมากขึ้นและใช้ฟังก์ชั่นที่เป็นปัจจุบัน

สุดท้ายนี้ไม่ได้บอกว่านี่คือรหัสที่สมบูรณ์แบบ มีหลายสิ่งหลายอย่างที่คุณสามารถทำได้เพื่อปรับปรุงใช้รหัสผ่านที่ถูกแฮชให้แน่ใจว่าเมื่อคุณเก็บข้อมูลที่มีความรู้สึกในฐานข้อมูลคุณไม่ได้เก็บไว้ในรูปแบบข้อความธรรมดามีการตรวจสอบหลายระดับ - แต่จริงๆแล้วถ้า คุณเพิ่งเปลี่ยนรหัสแนวโน้มการฉีดยาเก่าของคุณเป็นแบบนี้คุณจะรู้สึกดีไปพร้อมกับการเขียนโค้ดที่ดี - และความจริงที่ว่าคุณได้รับมาไกลขนาดนี้แล้วและการอ่านยังทำให้ฉันมีความหวังว่าคุณจะไม่เพียง ของรหัสเมื่อเขียนเว็บไซต์และแอปพลิเคชันของคุณ แต่คุณอาจออกไปและค้นคว้าสิ่งอื่น ๆ ที่ฉันเพิ่งพูดถึง - และอีกมากมาย เขียนรหัสที่ดีที่สุดที่คุณสามารถทำได้ไม่ใช่รหัสพื้นฐานที่สุดที่แทบจะไม่ทำงาน


2
ขอบคุณสำหรับคำตอบ! มี +1 ของฉัน! เป็นที่น่าสังเกตว่าmysql_*ในตัวมันเองนั้นไม่ปลอดภัย แต่มันจะโปรโมทโค้ดที่ไม่ปลอดภัยผ่านทางบทเรียนที่ไม่ดีและการขาดคำสั่งที่เหมาะสมในการเตรียม API
Madara's Ghost

2
รหัสผ่านที่ไม่ถูกแฮ็กมันช่างน่ากลัว! = oP มิฉะนั้น +1 สำหรับคำอธิบายโดยละเอียด
ความลับツ

33

ส่วนขยายของ MySQL นั้นเก่าที่สุดในสามตัวและเป็นวิธีดั้งเดิมที่นักพัฒนาใช้ในการสื่อสารกับ MySQL ส่วนขยายนี้ขณะนี้ถูกเลิกในความโปรดปรานของอีกสอง ทางเลือกเพราะของการปรับปรุงที่ทำในรุ่นใหม่ของทั้ง PHP และ MySQL

  • MySQLiเป็นส่วนขยายที่ 'ปรับปรุง' สำหรับการทำงานกับฐานข้อมูล MySQL มันใช้ประโยชน์จากฟีเจอร์ที่มีอยู่ในเซิร์ฟเวอร์ MySQL เวอร์ชั่นใหม่ ๆ ซึ่งมีทั้งฟังก์ชั่นที่เน้นฟังก์ชั่นและอินเทอร์เฟซที่มุ่งเน้นวัตถุไปยังนักพัฒนาและทำสิ่งที่ดี

  • PDOเสนอ API ที่รวมฟังก์ชันการทำงานส่วนใหญ่ที่ก่อนหน้านี้กระจายไปทั่วส่วนขยายการเข้าถึงฐานข้อมูลหลักเช่น MySQL, PostgreSQL, SQLite, MSSQL เป็นต้นส่วนต่อประสานจะแสดงวัตถุระดับสูงสำหรับโปรแกรมเมอร์เพื่อทำงานกับการเชื่อมต่อฐานข้อมูลแบบสอบถามและ ชุดผลลัพธ์และไดรเวอร์ระดับต่ำทำการสื่อสารและการจัดการทรัพยากรกับเซิร์ฟเวอร์ฐานข้อมูล การอภิปรายและการทำงานจำนวนมากกำลังเข้าสู่ PDO และถือว่าเป็นวิธีที่เหมาะสมในการทำงานกับฐานข้อมูลในโค้ดที่ทันสมัยและเป็นมืออาชีพ


21

ฉันพบว่าคำตอบข้างต้นมีความยาวจริง ๆ ดังนั้นเพื่อสรุป:

ส่วนขยาย mysqli มีประโยชน์หลายประการการปรับปรุงที่สำคัญเหนือการขยาย mysql เป็น:

  • ส่วนต่อประสานวัตถุ
  • การสนับสนุนงบเตรียม
  • รองรับหลายงบ
  • รองรับธุรกรรม
  • ความสามารถในการแก้ไขข้อบกพร่องขั้นสูง
  • การสนับสนุนเซิร์ฟเวอร์ในตัว

ที่มา: ภาพรวม MySQLi


ตามที่อธิบายไว้ในคำตอบข้างต้นทางเลือกในการ mysql คือ mysqli และ PDO (PHP Data Objects)

  • API รองรับข้อความที่จัดเตรียมโดยฝั่งเซิร์ฟเวอร์: รองรับโดย MYSQLi และ PDO
  • API รองรับข้อความที่จัดเตรียมโดยฝั่งไคลเอ็นต์: รองรับโดย PDO เท่านั้น
  • API รองรับ Stored Procedure: ทั้ง MySQLi และ PDO
  • API รองรับหลายงบและฟังก์ชั่น MySQL 4.1 ขึ้นไป - รองรับโดย MySQLi และส่วนใหญ่เป็น PDO

ทั้ง MySQLi และ PDO นั้นได้รับการแนะนำใน PHP 5.0 ในขณะที่ MySQL ถูกแนะนำก่อน PHP 3.0 ประเด็นที่ควรทราบคือ MySQL นั้นรวมอยู่ใน PHP5.x แม้ว่าจะไม่รองรับในรุ่นที่ใหม่กว่า


2
คำตอบของคุณยาวเกินไปในขณะที่บทสรุปที่แท้จริงคือ "mysql ext is more" นั่นคือทั้งหมดที่
สามัญสำนึกของคุณ

1
@ YourCommonSense @ คำตอบของฉันคือทำไม mysqli ถูกแทนที่ mysql ประเด็นคือไม่ต้องบอกว่า Mysqli มีอยู่แล้วในวันนี้ดังนั้นจงใช้มัน .. ทุกคนรู้ดีว่า!
Ani Menon

1
นอกจากความจริงที่ว่าไม่มีใครถามว่าทำไม mysqli ถึงเปลี่ยน mysql ก็ไม่ตอบคำถามนี้เช่นกัน มันตอบว่าทำไม mysqli ถูกนำมาใช้ แต่มันไม่ได้อธิบายว่าทำไม mysql และ mysqli ไม่ได้รับอนุญาตให้อยู่คู่ขนานกัน
Your Common Sense

@YourCommonSense คำถามของ OP คือ "ทำไมฉันต้องใช้อย่างอื่นแม้ว่ามันจะทำงานบนไซต์ของฉัน" และนั่นคือเหตุผลที่ฉันชี้ให้เห็นการเปลี่ยนแปลงและการปรับปรุง คุณอาจดูคำตอบอื่น ๆ ทั้งหมดที่พวกเขามีความยาวดังนั้นฉันคิดว่าฉันควรสรุปมัน
Ani Menon

6

เป็นไปได้ที่จะกำหนดmysql_*ฟังก์ชั่นเกือบทั้งหมดโดยใช้ mysqli หรือ PDO เพียงรวมไว้ในแอปพลิเคชัน PHP เก่าของคุณและมันจะทำงานบน PHP7 ทางออกของฉันที่นี่

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

แทนที่จะแสดงลิงก์สำหรับโซลูชันของคุณโปรดเพิ่มที่นี่เป็นคำตอบ
amarnath

1

ฟังก์ชั่นซึ่งเป็นที่คล้ายกันนี้mysql_connect(),mysql_query()ประเภทเป็นรุ่นก่อนหน้านี้คือ PHP (PHP 4) ฟังก์ชั่นและตอนนี้ไม่ได้อยู่ในการใช้งาน

เหล่านี้จะถูกแทนที่ด้วยmysqli_connect(),mysqli_query()ในทำนองเดียวกันใน PHP5 ล่าสุด

นี่คือเหตุผลที่อยู่เบื้องหลังข้อผิดพลาด


2
PHP 5 ยังไม่ได้รับล่าสุดมา 2 ปีแล้ว
Madara's Ghost

1

MySQL เลิกใช้ใน PHP 5.5.0 และถูกลบใน PHP 7.0.0 สำหรับแอปพลิเคชันขนาดใหญ่และเก่านี่เป็นการยากที่จะค้นหาและแทนที่แต่ละฟังก์ชัน

เราสามารถใช้ฟังก์ชั่น MySQL โดยการสร้างฟังก์ชั่น wrapper สำหรับแต่ละด้านล่างใช้รหัส คลิกที่นี่


-9

mysql_ *ฟังก์ชั่นถูกคัดค้าน (ตั้งแต่PHP 5.5 ) เนื่องจากข้อเท็จจริงที่ว่าฟังก์ชั่นที่ดีขึ้นและโครงสร้างรหัสได้รับการพัฒนา ความจริงที่ว่าฟังก์ชั่นถูกเลิกใช้หมายความว่าไม่มีความพยายามมากขึ้นในการปรับปรุงในแง่ของประสิทธิภาพและความปลอดภัยซึ่งหมายความว่ามันเป็นข้อพิสูจน์ในอนาคตที่น้อยลงซึ่งหมายความว่ามันจะน้อยหลักฐานในอนาคต

หากคุณต้องการเหตุผลเพิ่มเติม:

  • ฟังก์ชันmysql_ *ไม่รองรับคำสั่งที่เตรียมไว้
  • ฟังก์ชั่นmysql_ *ไม่รองรับการเชื่อมพารามิเตอร์
  • mysql_ *ฟังก์ชั่นขาดฟังก์ชั่นสำหรับการเขียนโปรแกรมเชิงวัตถุ
  • รายการไปที่ ...

18
คำตอบนี้ล้าสมัยแล้ว นอกจากนี้จะไม่เพิ่มสิ่งใดที่เป็นประโยชน์ต่อคำตอบที่มีอยู่แล้ว
สามัญสำนึกของคุณ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.