PHP cung cấp ba API khác nhau để kết nối với MySQL. Đây là mysql
(loại bỏ kể từ PHP 7) mysqli
và các PDO
phần mở rộng.
Các mysql_*
chức năng được sử dụng rất phổ biến, nhưng việc sử dụng chúng không được khuyến khích nữa. Nhóm tài liệu đang thảo luận về tình hình bảo mật cơ sở dữ liệu và giáo dục người dùng tránh xa tiện ích mở rộng ext / mysql thường được sử dụng là một phần của điều này (kiểm tra php.i INTERNals: deprecating ext / mysql ).
Và nhóm phát triển PHP sau đã đưa ra những quyết định để tạo ra E_DEPRECATED
lỗi khi người dùng kết nối với MySQL, dù là thông qua mysql_connect()
, mysql_pconnect()
hoặc các chức năng kết nối ngầm xây dựng trong ext/mysql
.
ext/mysql
đã chính thức bị từ chối kể từ phiên bản PHP 5.5 và đã bị xóa kể từ phiên bản PHP 7 .
Thấy hộp đỏ không?
Khi bạn vào bất kỳ mysql_*
trang hướng dẫn chức năng nào , bạn sẽ thấy một hộp màu đỏ, giải thích nó không nên được sử dụng nữa.
Tại sao
Di chuyển khỏi ext/mysql
không chỉ là về bảo mật, mà còn về việc có quyền truy cập vào tất cả các tính năng của cơ sở dữ liệu MySQL.
ext/mysql
được xây dựng cho MySQL 3.23 và chỉ có rất ít bổ sung kể từ đó trong khi chủ yếu giữ khả năng tương thích với phiên bản cũ này khiến cho mã khó bảo trì hơn một chút. Thiếu các tính năng không được hỗ trợ bởi ext/mysql
bao gồm: ( từ hướng dẫn sử dụng PHP ).
Lý do không sử dụng mysql_*
chức năng :
- Không phát triển tích cực
- Đã xóa kể từ PHP 7
- Thiếu giao diện OO
- Không hỗ trợ các truy vấn không đồng bộ, không đồng bộ
- Không hỗ trợ các câu lệnh được chuẩn bị hoặc các truy vấn được tham số hóa
- Không hỗ trợ các thủ tục được lưu trữ
- Không hỗ trợ nhiều báo cáo
- Không hỗ trợ giao dịch
- Không hỗ trợ tất cả các chức năng trong MySQL 5.1
Điểm trên trích dẫn từ câu trả lời của Quentin
Thiếu hỗ trợ cho các câu lệnh được chuẩn bị đặc biệt quan trọng vì chúng cung cấp một phương thức thoát và trích dẫn dữ liệu bên ngoài rõ ràng hơn, ít lỗi hơn so với thoát thủ công bằng một lệnh gọi hàm riêng biệt.
Xem so sánh các phần mở rộng SQL .
Ức chế cảnh báo khấu hao
Trong khi mã đang được chuyển đổi thành MySQLi
/ PDO
, các E_DEPRECATED
lỗi có thể được loại bỏ bằng cách cài đặt error_reporting
trong php.ini để loại trừE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Lưu ý rằng điều này cũng sẽ ẩn các cảnh báo khấu hao khác , tuy nhiên, có thể dành cho những thứ khác ngoài MySQL. ( từ hướng dẫn sử dụng PHP )
Bài viết PDO so với MySQLi: Bạn nên sử dụng cái nào? bởi Dejan Marjanovic sẽ giúp bạn lựa chọn.
Và một cách tốt hơn là PDO
, và bây giờ tôi đang viết một PDO
hướng dẫn đơn giản .
Hướng dẫn PDO đơn giản và ngắn gọn
Q. Câu hỏi đầu tiên trong đầu tôi là: `PDO` là gì?
A. tôn PDO - Đối tượng dữ liệu PHP - là lớp truy cập cơ sở dữ liệu cung cấp một phương thức truy cập thống nhất vào nhiều cơ sở dữ liệu.
Kết nối với MySQL
Với mysql_*
chức năng hoặc chúng ta có thể nói theo cách cũ (không dùng nữa trong PHP 5.5 trở lên)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Với PDO
: Tất cả những gì bạn cần làm là tạo một PDO
đối tượng mới . Hàm tạo chấp nhận các tham số để chỉ định hàm tạo của cơ sở dữ liệu PDO
chủ yếu lấy bốn tham số là DSN
(tên nguồn dữ liệu) và tùy chọn username
, password
.
Ở đây tôi nghĩ bạn quen thuộc với tất cả ngoại trừ DSN
; đây là mới trong PDO
. A DSN
về cơ bản là một chuỗi các tùy chọn cho biết PDO
trình điều khiển nào sẽ sử dụng và chi tiết kết nối. Để tham khảo thêm, hãy kiểm tra PDO MySQL DSN .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Lưu ý: bạn cũng có thể sử dụng charset=UTF-8
, nhưng đôi khi nó gây ra lỗi, vì vậy tốt hơn là sử dụng utf8
.
Nếu có bất kỳ lỗi kết nối, nó sẽ ném một PDOException
đối tượng có thể bị bắt để xử lý Exception
thêm.
Đọc tốt : Kết nối và quản lý kết nối
Bạn cũng có thể chuyển trong một số tùy chọn trình điều khiển dưới dạng một mảng cho tham số thứ tư. Tôi khuyên bạn nên chuyển tham số đưa PDO
vào chế độ ngoại lệ. Bởi vì một số PDO
trình điều khiển không hỗ trợ các câu lệnh chuẩn bị sẵn, nên PDO
thực hiện mô phỏng chuẩn bị. Nó cũng cho phép bạn tự kích hoạt mô phỏng này. Để sử dụng các câu lệnh được chuẩn bị phía máy chủ riêng, bạn nên đặt nó một cách rõ ràng false
.
Cách khác là tắt mô phỏng chuẩn bị được bật trong MySQL
trình điều khiển theo mặc định, nhưng nên tắt chế độ mô phỏng để sử dụng PDO
một cách an toàn.
Sau này tôi sẽ giải thích tại sao nên chuẩn bị thi đua. Để tìm lý do xin vui lòng kiểm tra bài này .
Nó chỉ có thể sử dụng được nếu bạn đang sử dụng một phiên bản cũ MySQL
mà tôi không khuyến nghị.
Dưới đây là một ví dụ về cách bạn có thể làm điều đó:
$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));
Chúng ta có thể thiết lập các thuộc tính sau khi xây dựng PDO không?
Có , chúng tôi cũng có thể đặt một số thuộc tính sau khi xây dựng PDO bằng setAttribute
phương thức:
$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);
Xử lý lỗi
Xử lý lỗi dễ dàng PDO
hơn nhiều so với mysql_*
.
Một thực tế phổ biến khi sử dụng mysql_*
là:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
không phải là một cách tốt để xử lý lỗi vì chúng tôi không thể xử lý sự cố die
. Nó sẽ chỉ kết thúc tập lệnh đột ngột và sau đó báo lỗi cho màn hình mà bạn thường KHÔNG muốn hiển thị cho người dùng cuối của mình và để tin tặc đẫm máu khám phá lược đồ của bạn. Thay phiên, các giá trị trả về củamysql_*
hàm thường có thể được sử dụng cùng với mysql_error () để xử lý lỗi.
PDO
cung cấp một giải pháp tốt hơn: ngoại lệ. Bất cứ điều gì chúng tôi làm với PDO
nên được bọc trong một try
- catch
khối. Chúng ta có thể ép buộcPDO
vào một trong ba chế độ lỗi bằng cách đặt thuộc tính chế độ lỗi. Ba chế độ xử lý lỗi dưới đây.
PDO::ERRMODE_SILENT
. Nó chỉ đặt mã lỗi và hoạt động tương tự nhưmysql_*
nơi bạn phải kiểm tra từng kết quả và sau đó xem xét$db->errorInfo();
để nhận chi tiết lỗi.
PDO::ERRMODE_WARNING
Nâng cao E_WARNING
. (Cảnh báo thời gian chạy (lỗi không nghiêm trọng). Việc thực thi tập lệnh không bị dừng lại.)
PDO::ERRMODE_EXCEPTION
: Ném ngoại lệ. Nó đại diện cho một lỗi được nêu ra bởi PDO. Bạn không nên ném PDOException
từ mã của riêng bạn. Xem Ngoại lệ để biết thêm thông tin về các ngoại lệ trong PHP. Nó hoạt động rất giống như or die(mysql_error());
khi nó không bị bắt. Nhưng không giống như or die()
, PDOException
có thể được bắt và xử lý một cách duyên dáng nếu bạn chọn làm như vậy.
Đọc tốt :
Giống:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
Và bạn có thể gói nó trong try
- catch
, như dưới đây:
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());
}
Bạn không phải xử lý với try
- catch
ngay bây giờ. Bạn có thể bắt nó bất cứ lúc nào thích hợp, nhưng tôi thực sự khuyên bạn nên sử dụng try
- catch
. Ngoài ra, nó có thể có ý nghĩa hơn để bắt nó ở bên ngoài chức năng gọi các PDO
công cụ:
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.
}
Ngoài ra, bạn có thể xử lý bằng or die()
hoặc chúng tôi có thể nói như thế mysql_*
, nhưng nó sẽ thực sự đa dạng. Bạn có thể ẩn các thông báo lỗi nguy hiểm trong sản xuất bằng cách xoay display_errors off
và chỉ đọc nhật ký lỗi của bạn.
Bây giờ, sau khi đọc tất cả những điều trên, bạn có thể nghĩ: những gì heck là khi tôi chỉ muốn bắt đầu nghiêng đơn giản SELECT
, INSERT
, UPDATE
, hoặc DELETE
báo cáo? Đừng lo lắng, chúng tôi đi đây:
Chọn dữ liệu
Vì vậy, những gì bạn đang làm mysql_*
là:
<?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'];
}
Bây giờ PDO
, bạn có thể làm điều này như sau:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
Hoặc là
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Lưu ý : Nếu bạn đang sử dụng phương thức như dưới đây ( query()
), phương thức này trả về một PDOStatement
đối tượng. Vì vậy, nếu bạn muốn lấy kết quả, hãy sử dụng nó như trên.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
Trong dữ liệu PDO, nó được lấy thông qua ->fetch()
, một phương thức xử lý câu lệnh của bạn. Trước khi gọi tìm nạp, cách tiếp cận tốt nhất sẽ là nói với PDO về cách bạn muốn dữ liệu được tìm nạp. Trong phần dưới đây tôi đang giải thích điều này.
Chế độ tìm nạp
Lưu ý việc sử dụng PDO::FETCH_ASSOC
trong fetch()
và fetchAll()
mã ở trên. Điều này nóiPDO
để trả về các hàng dưới dạng một mảng kết hợp với tên trường là các khóa. Có nhiều chế độ tìm nạp khác mà tôi sẽ giải thích từng cái một.
Trước hết, tôi giải thích cách chọn chế độ tìm nạp:
$stmt->fetch(PDO::FETCH_ASSOC)
Ở trên, tôi đã được sử dụng fetch()
. Bạn cũng có thể dùng:
Bây giờ tôi đến chế độ tìm nạp:
PDO::FETCH_ASSOC
: trả về một mảng được lập chỉ mục theo tên cột như được trả về trong tập kết quả của bạn
PDO::FETCH_BOTH
(mặc định): trả về một mảng được lập chỉ mục bởi cả tên cột và số cột được lập chỉ mục 0 như được trả về trong tập kết quả của bạn
Thậm chí còn có nhiều sự lựa chọn hơn! Đọc về tất cả chúng trong PDOStatement
tài liệu Fetch. .
Lấy số hàng :
Thay vì sử dụng mysql_num_rows
để lấy số lượng hàng trả về, bạn có thể lấy PDOStatement
và thực hiện rowCount()
, như:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Lấy ID được chèn lần cuối
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Chèn và cập nhật hoặc xóa câu lệnh
Những gì chúng ta đang làm trong mysql_*
chức năng là:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
Và trong pdo, điều tương tự có thể được thực hiện bằng cách:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
Trong truy vấn trên PDO::exec
thực hiện một câu lệnh SQL và trả về số lượng các hàng bị ảnh hưởng.
Chèn và xóa sẽ được đề cập sau.
Phương pháp trên chỉ hữu ích khi bạn không sử dụng biến trong truy vấn. Nhưng khi bạn cần sử dụng một biến trong truy vấn, đừng bao giờ thử như trên và ở đó cho câu lệnh chuẩn bị hoặc câu lệnh được tham số hóa .
Báo cáo chuẩn bị
H : Tuyên bố được chuẩn bị là gì và tại sao tôi cần chúng?
A. Một câu lệnh được chuẩn bị là một câu lệnh SQL được biên dịch trước có thể được thực thi nhiều lần bằng cách chỉ gửi dữ liệu đến máy chủ.
Quy trình công việc điển hình của việc sử dụng một tuyên bố đã chuẩn bị như sau ( trích từ Wikipedia ba 3 điểm ):
Chuẩn bị : Mẫu câu lệnh được tạo bởi ứng dụng và được gửi đến hệ thống quản lý cơ sở dữ liệu (DBMS). Một số giá trị nhất định không được chỉ định, được gọi là tham số, giữ chỗ hoặc biến liên kết (được gắn nhãn ?
bên dưới):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMS phân tích cú pháp, biên dịch và thực hiện tối ưu hóa truy vấn trên mẫu câu lệnh và lưu trữ kết quả mà không thực hiện nó.
- Thực thi : Sau đó, ứng dụng cung cấp các giá trị (hoặc liên kết) cho các tham số và DBMS thực thi câu lệnh (có thể trả về kết quả). Ứng dụng có thể thực thi câu lệnh bao nhiêu lần tùy ý với các giá trị khác nhau. Trong ví dụ này, nó có thể cung cấp 'Bánh mì' cho tham số đầu tiên và
1.00
cho tham số thứ hai.
Bạn có thể sử dụng một câu lệnh được chuẩn bị bằng cách bao gồm các trình giữ chỗ trong SQL của bạn. Về cơ bản, có ba cái không có chỗ dành sẵn (đừng thử cái này với biến ở trên), một cái có giữ chỗ không tên và một cái có giữ chỗ được đặt tên.
H: Vậy bây giờ, tên giữ chỗ được đặt tên là gì và làm cách nào để sử dụng chúng?
A. Đặt tên được giữ chỗ. Sử dụng tên mô tả đứng trước dấu hai chấm, thay vì dấu chấm hỏi. Chúng tôi không quan tâm đến vị trí / thứ tự của giá trị trong tên người giữ tên:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Bạn cũng có thể liên kết bằng cách sử dụng một mảng thực thi:
<?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);
Một tính năng hay khác cho OOP
bạn bè là trình giữ chỗ có tên có khả năng chèn các đối tượng trực tiếp vào cơ sở dữ liệu của bạn, giả sử các thuộc tính khớp với các trường được đặt tên. Ví dụ:
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);
H: Vậy bây giờ, giữ chỗ không tên là gì và làm cách nào để sử dụng chúng?
A. Hãy có một ví dụ:
<?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();
và
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
Ở trên, bạn có thể thấy những cái ?
đó thay vì một cái tên như trong một chủ sở hữu tên. Bây giờ trong ví dụ đầu tiên, chúng ta gán các biến cho các trình giữ chỗ khác nhau ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Sau đó, chúng tôi gán giá trị cho các trình giữ chỗ đó và thực hiện câu lệnh. Trong ví dụ thứ hai, phần tử mảng thứ nhất chuyển sang phần thứ nhất ?
và phần thứ hai sang phần thứ hai?
.
LƯU Ý : Trong các phần giữ chỗ chưa được đặt tên, chúng ta phải quan tâm đến thứ tự đúng của các phần tử trong mảng mà chúng ta đang truyền cho PDOStatement::execute()
phương thức.
SELECT
, INSERT
, UPDATE
, DELETE
Chuẩn bị các truy vấn
SELECT
:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
GHI CHÚ:
Tuy nhiên PDO
và / hoặc MySQLi
không hoàn toàn an toàn. Kiểm tra câu trả lời Các câu lệnh được chuẩn bị PDO có đủ để ngăn chặn việc tiêm SQL không? bởi ircmaxell . Ngoài ra, tôi đang trích dẫn một số phần từ câu trả lời của anh ấy:
$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 /*"));