Hướng dẫn php delusions - ảo tưởng php

  1. Tại sao PDO?
  2. Đang kết nối. DSN
  3. Chạy truy vấn. PDO :: Truy vấn ()
  4. Tuyên bố chuẩn bị. Bảo vệ khỏi tiêm SQL
    • Phương pháp ràng buộc
    • Truy vấn các bộ phận bạn có thể liên kết
  5. Tuyên bố chuẩn bị. Nhiều thực thi
  6. Chạy Chọn Chèn, Cập nhật hoặc Xóa các câu lệnh
  7. Lấy dữ liệu ra khỏi tuyên bố. cho mỗi()
  8. Lấy dữ liệu ra khỏi tuyên bố. tìm về()
    • Các loại trở lại.
  9. Lấy dữ liệu ra khỏi tuyên bố. FetchColumn ()
  10. Lấy dữ liệu ra khỏi tuyên bố trong hàng chục định dạng khác nhau. Fetchall ()
    • Nhận một mảng đơn giản.
    • Nhận một cột.
    • Nhận các cặp giá trị khóa.
    • Nhận các hàng được lập chỉ mục bởi trường duy nhất
    • Nhận các hàng được nhóm theo một số lĩnh vực
  11. Xử lý lỗi. Ngoại lệ
    • Báo cáo lỗi PDO
    • Bắt các ngoại lệ PDO
  12. Nhận được số lượng hàng với PDO
  13. Hàng bị ảnh hưởng và chèn ID
  14. Các tuyên bố chuẩn bị và điều khoản thích
  15. Các tuyên bố chuẩn bị và trong mệnh đề
  16. Bảo vệ bảng và tên trường
  17. Một vấn đề với mệnh đề giới hạn
  18. Giao dịch
  19. Gọi các thủ tục được lưu trữ trong PDO
  20. Chạy nhiều truy vấn với PDO
  21. Chế độ thi đua. PDO :: attr_emulation_prepares
    • Khi chế độ mô phỏng được bật
    • Khi chế độ mô phỏng bị tắt
  22. Mysqlnd và truy vấn đệm. Bộ dữ liệu khổng lồ.
  23. Nhận xét (309)

Có rất nhiều hướng dẫn về PDO, nhưng thật không may, hầu hết trong số họ không giải thích được lợi ích thực sự của PDO, hoặc thậm chí thúc đẩy các thực hành khá xấu. Hai trường hợp ngoại lệ duy nhất là phptherightway.com và hashphp.org, nhưng họ bỏ lỡ rất nhiều thông tin quan trọng. Do đó, một nửa các tính năng của PDO vẫn bị che khuất và hầu như không bao giờ được sử dụng bởi các nhà phát triển PHP, do đó, liên tục cố gắng phát minh lại bánh xe đã tồn tại trong PDO.

Không giống như những điều đó, hướng dẫn này được viết bởi một người đã sử dụng PDO trong nhiều năm, đào qua nó và trả lời hàng ngàn câu hỏi về Stack Overflow (người mang huy hiệu PDO vàng duy nhất). Theo nhiệm vụ của trang web này, bài viết này sẽ bác bỏ các ảo tưởng và thực hành xấu khác nhau, trong khi thay vào đó hiển thị đúng cách.

Mặc dù hướng dẫn này dựa trên trình điều khiển MySQL, thông tin nói chung, có thể áp dụng cho bất kỳ trình điều khiển nào được hỗ trợ.mysql driver, the information, in general, is applicable for any driver supported.

Tại sao PDO?

Điều đầu tiên trước tiên. Tại sao PDO ở tất cả?

PDO là một lớp trừu tượng truy cập cơ sở dữ liệu. Tuy nhiên, sự trừu tượng là hai lần: một được biết đến rộng rãi nhưng ít quan trọng hơn, trong khi một lần khác là mơ hồ nhưng quan trọng nhất.

Mọi người đều biết rằng PDO cung cấp giao diện thống nhất để truy cập nhiều cơ sở dữ liệu khác nhau. Mặc dù tính năng này là tuyệt vời, nhưng nó không tạo ra vấn đề lớn cho ứng dụng cụ thể, trong đó chỉ có một phụ trợ cơ sở dữ liệu được sử dụng. Và, mặc dù có một số tin đồn, không thể chuyển đổi các phụ trợ cơ sở dữ liệu bằng cách thay đổi một dòng duy nhất trong cấu hình PDO - do các hương vị SQL khác nhau (để làm như vậy, người ta cần sử dụng ngôn ngữ truy vấn trung bình như DQL). Do đó, đối với nhà phát triển đèn trung bình, điểm này khá không đáng kể và đối với anh ta, PDO chỉ là một phiên bản phức tạp hơn của chức năng

$sql 'SELECT * FROM users WHERE email = :email AND status=:status';
2 quen thuộc. Tuy nhiên, nó không phải là; Nó là nhiều, nhiều hơn nữa.

Tóm tắt PDO không chỉ là API cơ sở dữ liệu, mà còn các hoạt động cơ bản mà phải được lặp lại hàng trăm lần trong mỗi ứng dụng, làm cho mã của bạn cực kỳ ướt. Không giống như MySQL và MySQLI, cả hai đều là API trần cấp thấp không có ý định được sử dụng trực tiếp (nhưng chỉ là vật liệu xây dựng cho một số lớp trừu tượng cao hơn),

$sql 'SELECT * FROM users WHERE email = :email AND status=:status';
3 là một sự trừu tượng. Tuy nhiên, vẫn chưa đầy đủ, nhưng ít nhất là có thể sử dụng.

Các lợi ích PDO thực sự là:

  • Bảo mật (Báo cáo chuẩn bị có thể sử dụng) (usable prepared statements)
  • Khả năng sử dụng (nhiều chức năng của người trợ giúp để tự động hóa các hoạt động thường xuyên) (many helper functions to automate routine operations)
  • Khả năng tái sử dụng (API hợp nhất để truy cập vào vô số cơ sở dữ liệu, từ SQLite đến Oracle) (unified API to access multitude of databases, from SQLite to Oracle)

Lưu ý rằng mặc dù PDO là trình điều khiển DB bản địa tốt nhất, đối với ứng dụng web hiện đại, hãy xem xét sử dụng ORM với trình xây dựng truy vấn hoặc bất kỳ thư viện trừu tượng cao hơn nào khác, chỉ thỉnh thoảng dự phòng với Vanilla PDO. Orms tốt là học thuyết, hùng hồn, redbean và yii :: ar. Aura.sql là một ví dụ điển hình về trình bao bọc PDO với nhiều tính năng bổ sung.

Dù bằng cách nào, thật tốt khi biết các công cụ cơ bản trước tiên. Vì vậy, hãy bắt đầu:

Đang kết nối. DSN

PDO có một phương thức kết nối ưa thích gọi là DSN. Mặc dù không có gì phức tạp - thay vì một danh sách đơn giản và đơn giản, PDO yêu cầu bạn nhập các chỉ thị cấu hình khác nhau ở ba nơi khác nhau:

  • $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    4,
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    5,
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    6 và
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    7, cũng như ít được sử dụng
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    8 và
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    9 đi vào DSN;
  • $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    0 và
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    1 đi đến nhà xây dựng;
  • Tất cả các tùy chọn khác đi vào Tùy chọn Mảng.

Trong đó DSN là một chuỗi phân phối dấu chấm phẩy, bao gồm các cặp

$stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email$status]);
$user $stmt->fetch();
// or
$stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
$stmt->execute(['email' => $email'status' => $status]);
$user $stmt->fetch();
2, bắt đầu từ tên trình điều khiển và một dấu hai chấm:

      mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4
driver
^    ^ colon         ^param=value pair    ^semicolon  

Lưu ý rằng điều quan trọng là phải tuân theo định dạng thích hợp - không có khoảng trống hoặc trích dẫn hoặc đồ trang trí khác phải được sử dụng trong DSN, nhưng chỉ các tham số, giá trị và phân định, như trong hướng dẫn.no spaces or quotes or other decorations have to be used in DSN, but only parameters, values and delimiters, as shown in the manual.

Đây là một ví dụ cho MySQL:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}

Với tất cả các biến đã nói ở trên, chúng tôi sẽ có phiên bản PDO thích hợp trong biến

$stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email$status]);
$user $stmt->fetch();
// or
$stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
$stmt->execute(['email' => $email'status' => $status]);
$user $stmt->fetch();
3.

Ghi chú quan trọng cho người dùng mở rộng MySQL muộn:

  1. Không giống như các chức năng
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    4 cũ, có thể được sử dụng ở bất cứ đâu trong mã, ví dụ
    $sql 'SELECT * FROM users WHERE email = :email AND status=:status';
    3 được lưu trữ trong một biến thông thường, điều đó có nghĩa là nó có thể không thể truy cập được bên trong các hàm - vì vậy, người ta phải làm cho nó có thể truy cập được, bằng cách truyền nó thông qua các tham số chức năng hoặc Sử dụng các kỹ thuật nâng cao hơn, chẳng hạn như container IOC.
  2. Kết nối chỉ được thực hiện một lần! Không kết nối trong mọi chức năng. Không kết nối trong mỗi hàm tạo lớp. Nếu không, nhiều kết nối sẽ được tạo, cuối cùng sẽ tiêu diệt máy chủ cơ sở dữ liệu của bạn. Do đó, một thể hiện PDO duy nhất phải được tạo và sau đó được sử dụng thông qua toàn bộ thực thi tập lệnh.
  3. Điều rất quan trọng là đặt Charset thông qua DSN - đó là cách duy nhất thích hợp bởi vì nó cho PDO biết rằng Charset sẽ được sử dụng. Do đó, hãy quên việc chạy
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    6 truy vấn theo cách thủ công, thông qua
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    7 hoặc
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    8. Chỉ khi phiên bản PHP của bạn không thể chấp nhận được (cụ thể là dưới 5.3.6), bạn phải sử dụng truy vấn
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    6 và luôn tắt chế độ mô phỏng.set charset through DSN - that's the only proper way because it tells PDO which charset is going to be used. Therefore forget about running
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    6 query manually, either via
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    7 or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    8. Only if your PHP version is unacceptably outdated (namely below 5.3.6), you have to use
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
    $stmt->execute([$email$status]);
    $user $stmt->fetch();
    // or
    $stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
    $stmt->execute(['email' => $email'status' => $status]);
    $user $stmt->fetch();
    6 query and always turn emulation mode off.

Thông tin chi tiết về MySQL có thể được tìm thấy trong chương tương ứng, kết nối với MySQL

Chạy truy vấn. PDO :: Truy vấn ()

Có hai cách để chạy một truy vấn trong PDO. Nếu không có biến nào sẽ được sử dụng trong truy vấn, bạn có thể sử dụng phương thức PDO :: Query (). Nó sẽ chạy truy vấn của bạn và trả về đối tượng đặc biệt của lớp pdostatement có thể được so sánh với tài nguyên, được trả về bởi

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
0, đặc biệt là trong cách bạn có thể lấy các hàng thực tế từ nó:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}

Ngoài ra, phương pháp

$stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email$status]);
$user $stmt->fetch();
// or
$stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
$stmt->execute(['email' => $email'status' => $status]);
$user $stmt->fetch();
7 cho phép chúng tôi sử dụng chuỗi phương thức gọn gàng cho các truy vấn được chọn, sẽ được hiển thị bên dưới.

Tuyên bố chuẩn bị. Bảo vệ khỏi tiêm SQL

Đây là lý do chính và quan trọng duy nhất tại sao bạn bị tước mất chức năng

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
0 yêu dấu của bạn và bị ném vào thế giới khắc nghiệt của các đối tượng dữ liệu: PDO đã chuẩn bị các tuyên bố hỗ trợ ra khỏi hộp. Câu lệnh đã chuẩn bị là cách thích hợp duy nhất để chạy truy vấn, nếu bất kỳ biến nào sẽ được sử dụng trong đó. Lý do tại sao nó rất quan trọng được giải thích chi tiết trong hướng dẫn của Hitchhiker về phòng chống tiêm SQL.the only proper way to run a query, if any variable is going to be used in it. The reason why it is so important is explained in detail in The Hitchhiker's Guide to SQL Injection prevention.

Vì vậy, đối với mỗi truy vấn bạn chạy, nếu ít nhất một biến sẽ được sử dụng, bạn phải thay thế nó bằng trình giữ chỗ, sau đó chuẩn bị truy vấn của bạn, sau đó thực hiện nó, chuyển các biến riêng biệt.placeholder, then prepare your query, and then execute it, passing variables separately.

Câu chuyện dài, nó không khó như nó có vẻ. Trong hầu hết các trường hợp, bạn chỉ cần hai hàm - PREPARE () và EXECUTE ().

Trước hết, bạn phải thay đổi truy vấn của mình, thêm trình giữ chỗ thay cho các biến. Nói, một mã như thế này

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";

sẽ trở thành

$sql 'SELECT * FROM users WHERE email = ? AND status=?';

hoặc

$sql 'SELECT * FROM users WHERE email = :email AND status=:status';

Lưu ý rằng PDO hỗ trợ người giữ chỗ vị trí (

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
3) và được đặt tên (
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
4), sau này luôn bắt đầu từ một dấu hai chấm và chỉ có thể được viết bằng các chữ cái, chữ số và chỉ nhấn mạnh. Cũng lưu ý rằng không có báo giá nào phải được sử dụng xung quanh các khoản giữ chỗ.no quotes have to be ever used around placeholders.

Có một truy vấn với người giữ chỗ, bạn phải chuẩn bị nó, sử dụng phương pháp

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
5. Hàm này sẽ trả về cùng một đối tượng
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
6 mà chúng ta đã nói ở trên, nhưng không có bất kỳ dữ liệu nào được đính kèm với nó.

Cuối cùng, để thực hiện truy vấn, bạn phải chạy phương thức

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7 của đối tượng này, chuyển các biến trong đó, dưới dạng mảng. Và sau đó, bạn sẽ có thể đưa dữ liệu kết quả ra khỏi tuyên bố (nếu có):

$stmt $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email$status]);
$user $stmt->fetch();
// or
$stmt $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
$stmt->execute(['email' => $email'status' => $status]);
$user $stmt->fetch();

Như bạn có thể thấy, đối với các vị trí vị trí, bạn phải cung cấp một mảng thông thường với các giá trị, trong khi đối với các trình giữ chỗ được đặt tên, nó phải là một mảng kết hợp, trong đó các phím phải khớp với tên trình giữ chỗ trong truy vấn. Bạn không thể trộn các vị trí và đặt chỗ được đặt tên trong cùng một truy vấn.

Xin lưu ý rằng các chủ trình vị trí cho phép bạn viết mã ngắn hơn, nhưng nhạy cảm với thứ tự đối số (phải giống hệt như thứ tự của các trình giữ chỗ tương ứng trong truy vấn). Trong khi các chủ sở hữu đặt chỗ làm cho mã của bạn rõ ràng hơn, chúng cho phép thứ tự ràng buộc ngẫu nhiên.

Cũng lưu ý rằng mặc dù có một ảo tưởng rộng rãi, nhưng không cần "

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
8" trong các phím.

Sau khi thực hiện, bạn có thể bắt đầu nhận dữ liệu của mình, sử dụng tất cả các phương thức được hỗ trợ, như được mô tả trong bài viết này.

Nhiều ví dụ có thể được tìm thấy trong bài viết tương ứng.

Phương pháp ràng buộc

Chuyển dữ liệu vào

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7 (như được hiển thị ở trên) nên được coi là phương pháp mặc định và thuận tiện nhất. Khi phương thức này được sử dụng, tất cả các giá trị sẽ bị ràng buộc dưới dạng chuỗi (lưu cho các giá trị
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0, sẽ được gửi đến truy vấn như là, tức là SQL
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0), nhưng hầu hết thời gian đều ổn và sẽ không gây ra vấn đề gì.strings (save for
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0 values, that will be sent to the query as is, i.e. as SQL
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0), but most of time it's all right and won't cause any problem.

Tuy nhiên, đôi khi tốt hơn là đặt kiểu dữ liệu một cách rõ ràng. Các trường hợp có thể là:

  • Điều khoản giới hạn (hoặc bất kỳ mệnh đề SQL nào khác không thể chấp nhận toán hạng chuỗi) nếu chế độ mô phỏng được bật.
  • Các truy vấn phức tạp với kế hoạch truy vấn không tầm thường có thể bị ảnh hưởng bởi loại toán hạng sai
  • Các loại cột đặc biệt, như
    $sql "UPDATE users SET name = ? WHERE id = ?";
    $pdo->prepare($sql)->execute([$name$id]);
    2 hoặc
    $sql "UPDATE users SET name = ? WHERE id = ?";
    $pdo->prepare($sql)->execute([$name$id]);
    3 yêu cầu một toán hạng chính xác bị ràng buộc (lưu ý rằng để liên kết giá trị lớn với pdo :: param_int bạn cần cài đặt dựa trên mysqlnd).

Trong trường hợp liên kết rõ ràng như vậy phải được sử dụng, mà bạn có thể lựa chọn hai hàm, BindValue () và BindParam (). Cái trước phải được ưu tiên, bởi vì, không giống như

$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
4, nó không có tác dụng phụ để đối phó.

Truy vấn các bộ phận bạn có thể liên kết

Điều rất quan trọng là phải hiểu những phần truy vấn nào bạn có thể liên kết bằng cách sử dụng các câu lệnh đã chuẩn bị và bạn không thể. Trên thực tế, danh sách này ngắn một cách quá mức: chỉ có thể bị ràng buộc theo chuỗi và số có thể bị ràng buộc. Vì vậy, bạn có thể nói rằng miễn là dữ liệu của bạn có thể được biểu diễn trong truy vấn dưới dạng số hoặc một chuỗi được trích dẫn theo nghĩa đen - nó có thể bị ràng buộc. Đối với tất cả các trường hợp khác, bạn không thể sử dụng các câu lệnh được chuẩn bị PDO: không phải là mã định danh hoặc danh sách được phân tách bằng dấu phẩy hoặc một phần của chuỗi được trích dẫn theo nghĩa đen hoặc bất kỳ phần truy vấn tùy ý nào khác không thể bị ràng buộc bằng cách sử dụng câu lệnh đã chuẩn bị.

Giải pháp cho các trường hợp sử dụng thường xuyên nhất có thể được tìm thấy trong phần tương ứng của bài viết

Tuyên bố chuẩn bị. Nhiều thực thi

Đôi khi bạn có thể sử dụng các câu lệnh đã chuẩn bị cho việc thực hiện nhiều truy vấn đã chuẩn bị. Nó nhanh hơn một chút so với việc thực hiện cùng một truy vấn nhiều lần, vì nó chỉ truy vấn phân tích cú pháp một lần. Tính năng này sẽ hữu ích hơn nếu có thể thực hiện một câu lệnh được chuẩn bị trong một phiên bản PHP khác. Nhưng than ôi - nó không. Vì vậy, bạn chỉ giới hạn trong việc lặp lại cùng một truy vấn trong cùng một trường hợp, điều này hiếm khi cần thiết trong các tập lệnh PHP thông thường và giới hạn việc sử dụng tính năng này để chèn hoặc cập nhật lặp lại:

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}

Lưu ý rằng tính năng này là một chút được đánh giá cao. Không chỉ cần thiết quá hiếm khi nói đến, mà việc tăng hiệu suất không phải là lớn - phân tích cú pháp truy vấn là thực sự nhanh chóng.

Lưu ý rằng bạn chỉ có thể nhận được lợi thế này khi chế độ mô phỏng bị tắt.off.

Chạy Chọn Chèn, Cập nhật hoặc Xóa các câu lệnh

Cố lên mọi người. Hoàn toàn không có gì đặc biệt trong các truy vấn này. Để pdo tất cả đều giống nhau. Không quan trọng bạn đang chạy truy vấn nào.

Giống như nó đã được hiển thị ở trên, những gì bạn cần là chuẩn bị một truy vấn với người giữ chỗ, và sau đó thực hiện nó, gửi các biến riêng biệt. Hoặc đối với truy vấn

$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
5 và
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
6, quá trình này về cơ bản là giống nhau. Sự khác biệt duy nhất là (vì các truy vấn DML không trả về bất kỳ dữ liệu nào), bạn có thể sử dụng chuỗi phương thức và do đó gọi
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7 ngay cùng với
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
8:

$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);

Tuy nhiên, nếu bạn muốn có được số lượng hàng bị ảnh hưởng, mã sẽ phải giống nhau ba dòng:

$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();

Nhiều ví dụ có thể được tìm thấy trong bài viết tương ứng.

Lấy dữ liệu ra khỏi tuyên bố. cho mỗi()

Cách cơ bản và trực tiếp nhất để có được nhiều hàng từ một câu lệnh sẽ là vòng lặp

$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
9. Nhờ giao diện có thể đi qua,
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
6 có thể được lặp lại bằng cách sử dụng toán tử
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
9:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
0

Lưu ý rằng phương pháp này thân thiện với bộ nhớ, vì nó không tải tất cả các hàng thu được trong bộ nhớ nhưng cung cấp cho chúng từng cái một (mặc dù hãy nhớ rằng vấn đề này).

Lấy dữ liệu ra khỏi tuyên bố. tìm về()

Chúng tôi đã thấy chức năng này rồi, nhưng chúng ta hãy xem xét kỹ hơn. Nó tìm nạp một hàng từ cơ sở dữ liệu và di chuyển con trỏ bên trong trong tập kết quả, do đó, các cuộc gọi cho chức năng này sẽ trả về tất cả các hàng kết quả từng cái một. Điều này làm cho phương pháp này trở thành một tương tự sơ bộ với

$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
2 nhưng nó hoạt động theo một cách hơi khác: thay vì nhiều chức năng riêng biệt (
$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
3,
$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
4, v.v.), chỉ có một chức năng, nhưng hành vi của nó có thể được thay đổi bằng một tham số. Có rất nhiều chế độ tìm nạp trong PDO và chúng tôi sẽ thảo luận sau, nhưng đây là một số ít để bắt đầu:

  • $stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
    $stmt->execute([$cat]);
    $deleted $stmt->rowCount();
    5 Trả về mảng được liệt kê
  • $stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
    $stmt->execute([$cat]);
    $deleted $stmt->rowCount();
    6 returns mảng liên kết
  • $stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
    $stmt->execute([$cat]);
    $deleted $stmt->rowCount();
    7 - cả hai điều trên
  • $stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
    $stmt->execute([$cat]);
    $deleted $stmt->rowCount();
    8 Trả về đối tượng
  • $stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
    $stmt->execute([$cat]);
    $deleted $stmt->rowCount();
    9 cho phép cả ba phương thức (liên kết số và đối tượng) không có chi phí bộ nhớ.

Từ những điều trên, bạn có thể nói rằng chức năng này phải được sử dụng trong hai trường hợp:

  1. Khi chỉ có một hàng được mong đợi - để có được hàng đó. Ví dụ,

    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    1

    Sẽ cung cấp cho bạn một hàng từ câu lệnh, dưới dạng mảng kết hợp.

  2. Khi chúng ta cần xử lý dữ liệu được trả về bằng cách nào đó trước khi sử dụng. Trong trường hợp này, nó phải được chạy qua thông thường trong khi vòng lặp, giống như một vòng hiển thị ở trên.

Một chế độ hữu ích khác là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
00, có thể tạo một đối tượng của lớp cụ thể

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
2

sẽ tạo ra một mảng chứa đầy các đối tượng của lớp tin tức, đặt thuộc tính lớp từ các giá trị được trả về. Lưu ý rằng trong chế độ này

  • Thuộc tính được đặt trước cuộc gọi của hàm tạo
  • Đối với tất cả các thuộc tính không xác định
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    01 Phương pháp ma thuật sẽ được gọi
  • Nếu không có phương thức
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    01 trong lớp, thì thuộc tính mới sẽ được tạo
  • Các thuộc tính riêng tư cũng sẽ được lấp đầy, điều này hơi bất ngờ nhưng khá tiện dụng

Lưu ý rằng chế độ mặc định là

$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
7, nhưng bạn có thể thay đổi nó bằng tùy chọn cấu hình
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
04 như được hiển thị trong ví dụ kết nối. Vì vậy, một khi được đặt, nó có thể được bỏ qua hầu hết thời gian.

Các loại trở lại.

Chỉ khi PDO được xây dựng trên MYSQLND và chế độ mô phỏng bị tắt, thì PDO sẽ trả về các giá trị

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
05 và
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
06 với các loại tương ứng. Nói, nếu chúng ta tạo một bảngoff, then PDO will return
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
05 and
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
06 values with respective types. Say, if we create a table

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
3

Và sau đó truy vấn nó từ PDO dựa trên mysqlnd với việc mô phỏng đã tắt, đầu ra sẽ

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
4

Mặt khác, hành vi

$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
2 quen thuộc sẽ được tuân thủ - tất cả các giá trị được trả về dưới dạng các chuỗi chỉ với
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0 được trả về là
$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
0.

Nếu vì một lý do nào đó, bạn không thích hành vi này và chỉ thích kiểu cũ với các chuỗi và null, thì bạn có thể sử dụng tùy chọn cấu hình sau để ghi đè nó:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
5

Lưu ý rằng đối với loại

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
10, chuỗi luôn được trả về, do bản chất của loại này nhằm giữ lại giá trị chính xác, không giống như các loại nổi và kép không chính xác.

Lấy dữ liệu ra khỏi tuyên bố. FetchColumn ()

Một hàm trợ giúp gọn gàng trả về giá trị của trường đơn của hàng được trả về. Rất tiện dụng khi chúng tôi chỉ chọn một trường:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
6

Lấy dữ liệu ra khỏi tuyên bố trong hàng chục định dạng khác nhau. Fetchall ()

Đó là chức năng thú vị nhất, với hầu hết các tính năng đáng kinh ngạc. Chủ yếu là nhờ sự tồn tại của nó, người ta có thể gọi PDO là một trình bao bọc, vì chức năng này có thể tự động hóa nhiều hoạt động được thực hiện bằng tay.

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
11 trả về một mảng bao gồm tất cả các hàng được trả về bởi truy vấn. Từ thực tế này, chúng tôi có thể đưa ra hai kết luận:all the rows returned by the query. From this fact we can make two conclusions:

  1. Chức năng này không nên được sử dụng nếu nhiều hàng* đã được chọn. Trong trường hợp như vậy, một vòng lặp thông thường nên được sử dụng để tìm nạp từng hàng một thay vì lấy tất cả chúng như một mảng cùng một lúc. * "Nhiều" có nghĩa là nhiều hơn là phù hợp để được hiển thị trên trang web trung bình.
    * "many" means more than is suitable to be shown on the average web page.
  2. Chức năng này chủ yếu hữu ích trong một ứng dụng web hiện đại không bao giờ xuất dữ liệu ngay lập tức trong quá trình tìm nạp, mà thay vào đó chuyển nó sang mẫu.

Bạn sẽ ngạc nhiên, trong bao nhiêu định dạng khác nhau, chức năng này có thể trả về dữ liệu (và mức độ ít người dùng PHP trung bình biết về chúng), tất cả được kiểm soát bởi các biến

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
12. Một số trong số họ là:

Nhận một mảng đơn giản.

Theo mặc định, chức năng này sẽ trả về mảng được liệt kê đơn giản bao gồm tất cả các hàng được trả về. Các hằng số định dạng hàng, chẳng hạn như

$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
5,
$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
6,
$stmt $pdo->prepare("DELETE FROM goods WHERE category = ?");
$stmt->execute([$cat]);
$deleted $stmt->rowCount();
8, vv có thể thay đổi định dạng hàng.

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
7

Nhận một cột.

Nó thường rất tiện dụng để có được mảng một chiều đơn giản ra khỏi truy vấn, nếu chỉ có một cột trong số nhiều hàng được lấy. Bạn đi đây:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
8

Nhận các cặp giá trị khóa.

Cũng định dạng cực kỳ hữu ích, khi chúng ta cần có cùng một cột, nhưng được lập chỉ mục không theo số theo thứ tự mà bởi một trường khác. Đây là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
16 hằng số:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
9

Lưu ý rằng bạn chỉ phải chọn hai cột cho chế độ này, trước tiên phải là duy nhất.

Nhận các hàng được lập chỉ mục bởi trường duy nhất

Tương tự như trên, nhưng không nhận được một cột mà là hàng đầy đủ, nhưng được lập chỉ mục bởi một trường duy nhất, nhờ hằng số

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
17:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
0

Lưu ý rằng cột đầu tiên được chọn phải là duy nhất (trong truy vấn này, người ta cho rằng cột đầu tiên là ID, nhưng để chắc chắn liệt kê rõ ràng hơn nó một cách rõ ràng).

Nhận các hàng được nhóm theo một số lĩnh vực

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
18 sẽ nhóm các hàng vào một mảng lồng nhau, trong đó các chỉ mục sẽ là các giá trị duy nhất từ ​​cột đầu tiên và các giá trị sẽ là các mảng tương tự như các chỉ mục được trả về bởi
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
19 thông thường. Ví dụ, mã sau đây sẽ tách các chàng trai khỏi các cô gái và đưa chúng vào các mảng khác nhau:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
1

Vì vậy, đây là giải pháp lý tưởng cho một nhu cầu phổ biến như "các sự kiện nhóm theo ngày" hoặc "hàng hóa nhóm theo danh mục". Một số trường hợp sử dụng thực tế:

  • Làm thế nào để nhiều kết quả truy vấn để giảm số truy vấn?
  • Danh sách hồ sơ được nhóm theo tên danh mục

Các chế độ khác

Tất nhiên, có một

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
20 cho người hâm mộ lập trình chức năng.

Nhiều chế độ sẽ sớm ra mắt.

Xử lý lỗi. Ngoại lệ

Mặc dù có một số chế độ xử lý lỗi trong PDO, nhưng phương thức duy nhất thích hợp là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
21. Vì vậy, người ta phải luôn luôn đặt nó theo cách này, bằng cách thêm dòng này sau khi tạo phiên bản PDO,

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
2

hoặc như một tùy chọn kết nối, như đã được trình bày trong ví dụ trên. Và đây là tất cả những gì bạn cần cho báo cáo lỗi cơ bản.

Báo cáo lỗi PDO

TL; DR: Mặc dù tất cả những gì các hướng dẫn khác nói, bạn không cần một nhà điều hành

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
22 để báo cáo các lỗi PDO. Bắt một ngoại lệ chỉ khi bạn có một kịch bản xử lý ngoài việc chỉ báo cáo nó. Mặt khác, chỉ cần để nó bong bóng lên một trình xử lý toàn trang web (lưu ý rằng bạn không phải viết một, có một trình xử lý tích hợp cơ bản trong PHP, khá tốt).
Despite what all other tutorials say, you don't need a
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
22 operator to report PDO errors
. Catch an exception only if you have a handling scenario other than just reporting it. Otherwise just let it bubble up to a site-wide handler (note that you don't have to write one, there is a basic built-in handler in PHP, which is quite good).

Ngoại lệ duy nhất (không có ý định chơi chữ) là việc tạo phiên bản PDO, trong trường hợp lỗi có thể tiết lộ thông tin xác thực kết nối (đó sẽ là một phần của dấu vết ngăn xếp). Để ẩn chúng, chúng ta có thể gói mã kết nối vào toán tử

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
22 và sau đó ném một
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
24 mới chỉ chứa thông báo nhưng không phải là thông tin đăng nhập.

Một lời ca ngợi dài về vấn đề:

Mặc dù ảo tưởng rộng rãi, bạn không bao giờ nên bắt lỗi để báo cáo chúng. Một mô -đun (như lớp cơ sở dữ liệu) không nên báo cáo các lỗi của nó. Chức năng này phải được giao cho một trình xử lý toàn ứng dụng. Tất cả những gì chúng ta cần là nêu ra một lỗi (dưới dạng ngoại lệ) - điều mà chúng ta đã làm. Đó là tất cả. Bạn cũng không nên "luôn luôn kết thúc các hoạt động PDO của mình trong một

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
25" như hướng dẫn phổ biến nhất từ ​​Tutsplus khuyến nghị. Hoàn toàn ngược lại, việc bắt một ngoại lệ nên là một trường hợp đặc biệt (ý định chơi chữ).

Trên thực tế, không có gì đặc biệt trong các ngoại lệ PDO - chúng là những lỗi giống nhau. Vì vậy, bạn phải đối xử với chúng giống hệt như các lỗi khác. Nếu bạn đã có một trình xử lý lỗi trước đây, bạn không nên tạo một trình chuyên dụng cho PDO. Nếu bạn không quan tâm - điều đó cũng ổn, vì PHP tốt với việc xử lý lỗi cơ bản và sẽ tiến hành ngoại lệ PDO.

Xử lý ngoại lệ là một trong những vấn đề với hướng dẫn PDO. Được làm quen với các ngoại lệ lần đầu tiên khi bắt đầu với PDO, các tác giả xem xét các ngoại lệ dành riêng cho thư viện này và bắt đầu xử lý các ngoại lệ một cách siêng năng (nhưng không đúng) cho PDO. Đây là hoàn toàn vô nghĩa. Nếu một người không chú ý đặc biệt đến bất kỳ trường hợp ngoại lệ nào trước đây, họ không nên thay đổi thói quen của họ cho PDO. Nếu một người không sử dụng

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
22 trước đây, họ nên giữ điều đó, cuối cùng học cách sử dụng các ngoại lệ và khi nào nó phù hợp để bắt chúng.

Vì vậy, bây giờ bạn có thể nói rằng hướng dẫn sử dụng PHP là sai, nói rằng

Nếu ứng dụng của bạn không bắt được ngoại lệ được ném từ hàm tạo PDO, hành động mặc định được thực hiện bởi động cơ Zend là chấm dứt tập lệnh và hiển thị dấu vết trở lại. Dấu vết trở lại này có thể sẽ tiết lộ các chi tiết kết nối cơ sở dữ liệu đầy đủ, bao gồm tên người dùng và mật khẩu.

Tuy nhiên, không có thứ gọi là "hiển thị dấu vết trở lại"! Những gì Zend Engine thực sự làm chỉ là chuyển đổi một ngoại lệ chưa được thực hiện thành một lỗi nghiêm trọng. Và sau đó, lỗi nghiêm trọng này được xử lý như bất kỳ lỗi nào khác - vì vậy nó sẽ chỉ được hiển thị nếu chỉ thị

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
27 thích hợp được đặt. Do đó, mặc dù bạn có thể hoặc bạn có thể không bắt được một ngoại lệ, nhưng nó hoàn toàn không liên quan gì đến việc hiển thị thông tin nhạy cảm, bởi vì đó là một cài đặt cấu hình hoàn toàn khác để đáp ứng điều này. Vì vậy, đừng bắt các ngoại lệ PDO để báo cáo chúng. Thay vào đó, hãy định cấu hình máy chủ của bạn đúng cách:there is no such thing as "the displaying of a back trace"! What zend engine really does is just convert an uncaught exception into a fatal error. And then this fatal error is treated like any other error - so it will be displayed only if appropriate
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
27 directive is set. Thus, although you may or you may not catch an exception, it has absolutely nothing to do with displaying sensitive information, because it's a totally different configuration setting in response to this. So, do not catch PDO exceptions to report them. Instead, configure your server properly:

Trên máy chủ phát triển chỉ cần bật lỗi hiển thị lỗi:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
3

Trong khi trên máy chủ sản xuất, hãy tắt hiển thị lỗi trong khi lỗi đăng nhập:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
4
  • Hãy nhớ rằng có những lỗi khác không nên được tiết lộ cho người dùng.

Bắt các ngoại lệ PDO

Bạn có thể chỉ muốn bắt lỗi PDO trong hai trường hợp:

  1. Nếu bạn đang viết một trình bao bọc cho PDO và bạn muốn tăng thông tin lỗi với một số dữ liệu bổ sung, như chuỗi truy vấn. Trong trường hợp này, hãy nắm bắt ngoại lệ, thu thập thông tin cần thiết và ném lại một ngoại lệ khác.re-throw another Exception.
  2. Nếu bạn có một kịch bản nhất định để xử lý các lỗi trong phần cụ thể của mã. Một số ví dụ:certain scenario for handling errors in the particular part of code. Some examples are:

    • Nếu lỗi có thể được bỏ qua, bạn có thể sử dụng thử..catch cho việc này. Tuy nhiên, không làm cho nó một thói quen. Bắt trống trong mọi khía cạnh hoạt động như một toán tử triệt tiêu lỗi, và nó cũng không kém phần xấu xa.
    • Nếu có một hành động phải được thực hiện trong trường hợp thất bại, tức là rollback giao dịch.
    • Nếu bạn đang chờ một lỗi cụ thể để xử lý. Trong trường hợp này, hãy nắm bắt ngoại lệ, xem liệu lỗi bạn đang tìm kiếm và sau đó xử lý vấn đề này. Nếu không, chỉ cần ném nó một lần nữa - vì vậy nó sẽ bong bóng lên đến người xử lý theo cách thông thường.

E.g.:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
5

Tuy nhiên, nói chung, không có điều trị dành riêng cho ngoại lệ PDO là cần thiết. Nói tóm lại, để có lỗi PDO được báo cáo đúng:

  1. Đặt PDO ở chế độ ngoại lệ.
  2. Không sử dụng
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    22 để báo cáo lỗi.
  3. Định cấu hình PHP để báo cáo lỗi thích hợp
    • Trên một trang web trực tiếp bộ
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      29 và
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      30live site set
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      29 and
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      30
    • Trên trang web phát triển, bạn có thể muốn đặt
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      31development site, you may want to set
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      31
    • Tất nhiên,
      $host '127.0.0.1';
      $db   'test';
      $user 'root';
      $pass '';
      $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
      $options = [
          
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
          
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          
      PDO::ATTR_EMULATE_PREPARES   => false,
      ];
      try {
           
      $pdo = new PDO($dsn$user$pass$options);
      } catch (\
      PDOException $e) {
           throw new \
      PDOException($e->getMessage(), (int)$e->getCode());
      }
      32 phải được đặt thành e_all trong cả hai trường hợp

Do đó, bạn sẽ luôn được thông báo về tất cả các lỗi cơ sở dữ liệu mà không cần một dòng mã bổ sung nào! Đọc thêm.

Nhận được số lượng hàng với PDO

Bạn không cần nó.

Mặc dù PDO cung cấp một chức năng để trả về số lượng hàng được tìm thấy bởi truy vấn,

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
33, bạn hiếm khi cần nó. Có thật không.

Nếu bạn nghĩ rằng nó kết thúc, bạn sẽ thấy rằng đây là một chức năng bị sử dụng sai nhiều nhất trong web. Hầu hết thời gian nó được sử dụng không phải để đếm bất cứ thứ gì, mà là một lá cờ đơn thuần - chỉ để xem liệu có bất kỳ dữ liệu nào được trả về. Nhưng đối với trường hợp như vậy, bạn có chính dữ liệu! Chỉ cần nhận dữ liệu của bạn, sử dụng

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
34 hoặc
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
19 - và nó sẽ phục vụ như một lá cờ như vậy! Nói, để xem liệu có bất kỳ người dùng nào có tên như vậy không, chỉ cần chọn một hàng:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
6

Chính xác là điều tương tự với việc nhận được một hàng hoặc một mảng có hàng:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
7

Hãy nhớ rằng ở đây bạn không cần số lượng, số lượng hàng thực tế, mà là một lá cờ boolean. Vì vậy, bạn đã nhận được nó.

Chưa kể rằng trường hợp sử dụng phổ biến thứ hai cho chức năng này không bao giờ được sử dụng. Người ta không bao giờ nên sử dụng

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
36 để đếm các hàng trong cơ sở dữ liệu! Thay vào đó, người ta phải yêu cầu một cơ sở dữ liệu để đếm chúng và trả về kết quả trong một hàng duy nhất:single row:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
8

là cách duy nhất thích hợp.

Về bản chất:

  • Nếu bạn cần biết có bao nhiêu hàng trong bảng, hãy sử dụng truy vấn
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    37.
  • Nếu bạn cần biết liệu truy vấn của bạn có trả về bất kỳ dữ liệu nào không - hãy kiểm tra dữ liệu đó.
  • Nếu bạn vẫn cần biết có bao nhiêu hàng đã được trả lại bởi một số truy vấn (mặc dù tôi khó có thể tưởng tượng ra một trường hợp), thì bạn có thể sử dụng
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    36 hoặc gọi đơn giản là
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    39 trên mảng được trả về bởi
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    19 (nếu có).

Do đó, bạn có thể nói rằng câu trả lời hàng đầu cho câu hỏi này trên Stack Overflow về cơ bản là vô nghĩa và có hại - một cuộc gọi đến

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
36 có thể không bao giờ được thay thế bằng truy vấn
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
42 - mục đích của chúng về cơ bản là khác nhau, trong khi chạy thêm một truy vấn để có được số lượng hàng trở lại bởi các truy vấn khác hoàn toàn không có ý nghĩa.

Hàng bị ảnh hưởng và chèn ID

PDO đang sử dụng cùng một chức năng để trả về cả số lượng hàng được trả về bởi câu lệnh chọn và số lượng hàng bị ảnh hưởng bởi các truy vấn DML -

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
33. Do đó, để có được số lượng hàng bị ảnh hưởng, chỉ cần gọi chức năng này sau khi thực hiện truy vấn.

Một câu hỏi thường gặp khác là do thực tế là MySQL sẽ không cập nhật hàng, nếu giá trị mới giống như cũ. Do đó, số lượng hàng bị ảnh hưởng có thể khác với số lượng hàng phù hợp với mệnh đề WHERE. Đôi khi cần phải biết số sau này.

Mặc dù bạn có thể nói

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
36 trả lại số lượng hàng được khớp thay vì các hàng bị ảnh hưởng bằng cách đặt tùy chọn
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
45 thành True, nhưng, vì đây là một tùy chọn chỉ có kết nối và do đó bạn không thể thay đổi hành vi của nó trong thời gian chạy Một chế độ cho ứng dụng, có thể không thuận tiện lắm.

Lưu ý rằng

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
45 không được đảm bảo hoạt động, như được mô tả trong nhận xét dưới đây.

Thật không may, không có đối tác PDO cho hàm

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
47 mà đầu ra có thể dễ dàng phân tích cú pháp và số mong muốn được tìm thấy. Đây là một trong những nhược điểm PDO nhỏ.

Một mã định danh được tạo tự động từ một trường hoặc trường tự động_inclement trong MySQL có thể được lấy từ chức năng PDO :: LastInsertid. Một câu trả lời cho một câu hỏi thường gặp, "Liệu chức năng này có an toàn để sử dụng trong môi trường đồng thời không?" là tích cực: Có, nó là an toàn. Chỉ là một giao diện của mysql c api mysql_insert_id (), nó hoàn toàn an toàn.

Các tuyên bố chuẩn bị và điều khoản thích

Mặc dù PDO dễ sử dụng chung, dù sao cũng có một số gotchas, và tôi sẽ giải thích một số.

Một trong số đó là sử dụng các khoản giữ chỗ với mệnh đề

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
48 SQL. Lúc đầu, người ta sẽ nghĩ rằng một truy vấn như vậy sẽ làm:

$stmt $pdo->query('SELECT name FROM users');
while (
$row $stmt->fetch())
{
    echo 
$row['name'] . "\n";
}
9

Nhưng ngay sau đó họ sẽ học được rằng nó sẽ tạo ra một lỗi. Để hiểu bản chất của nó, người ta phải hiểu rằng, giống như người ta đã nói ở trên, một người giữ chỗ chỉ phải đại diện cho một dữ liệu hoàn chỉnh theo nghĩa đen - một chuỗi hoặc một số là. Và không có nghĩa là nó có thể đại diện cho một phần của một phần SQL theo nghĩa đen hoặc một số SQL tùy ý. Vì vậy, khi làm việc với như thế, chúng tôi phải chuẩn bị hoàn toàn theo nghĩa đen của mình, và sau đó gửi nó đến truy vấn theo cách thông thường:complete literal first, and then send it to the query the usual way:

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
0

Các tuyên bố chuẩn bị và trong mệnh đề

Giống như người ta đã nói ở trên, không thể thay thế một phần truy vấn tùy ý bằng một trình giữ chỗ. Bất kỳ chuỗi nào bạn liên kết thông qua một trình giữ chỗ sẽ được đưa vào truy vấn như một chuỗi đơn nghĩa đen. Ví dụ: chuỗi

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
49 sẽ bị ràng buộc dưới dạng chuỗi, dẫn đến

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
1

Tạo SQL để tìm kiếm chỉ một giá trị.

Để làm cho nó đúng, người ta cần các giá trị tách biệt, để làm cho một truy vấn trông giống như

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
2

Do đó, đối với các giá trị được phân tách bằng dấu phẩy, như đối với toán tử SQL

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
50, người ta phải tạo một bộ
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
3S theo cách thủ công và đưa chúng vào truy vấn:

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
3

Không thuận tiện lắm, nhưng so với MySQLI, nó ngắn gọn một cách tuyệt vời.

Trong trường hợp có những người giữ chỗ khác trong truy vấn, bạn có thể sử dụng hàm

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
52 để tham gia tất cả các biến vào một mảng, thêm các biến khác của bạn dưới dạng mảng, theo thứ tự chúng xuất hiện trong truy vấn của bạn:

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
4

Trong trường hợp bạn đang sử dụng các khoản giữ chỗ được đặt tên, mã sẽ phức tạp hơn một chút, vì bạn phải tạo ra một chuỗi các giữ chỗ được đặt tên, ví dụ:

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
53. Vì vậy, mã sẽ là:

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
5

May mắn thay, đối với những người giữ chỗ được đặt tên, chúng tôi không phải tuân theo thứ tự nghiêm ngặt, vì vậy chúng tôi có thể hợp nhất các mảng của chúng tôi theo bất kỳ thứ tự nào.

Bảo vệ bảng và tên trường

Trên Stack Overflow, tôi đã thấy số lượng người dùng PHP áp đảo triển khai mã PDO gây tử vong nhất, nghĩ rằng chỉ có các giá trị dữ liệu phải được bảo vệ. Nhưng tất nhiên là không.

Thật không may, PDO không có trình giữ chỗ cho số nhận dạng (tên bảng và trường), vì vậy một nhà phát triển phải lọc chúng theo cách thủ công. Một bộ lọc như vậy thường được gọi là "danh sách trắng" (trong đó chúng tôi chỉ liệt kê các giá trị cho phép) trái ngược với "danh sách đen" trong đó chúng tôi liệt kê các giá trị không được phép. Đây là một ví dụ ngắn gọn

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
6

Cách tiếp cận tương tự nên được sử dụng cho hướng, mặc dù mã sẽ đơn giản hơn một chút

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
7

Có hai biến số này theo cách này sẽ giúp chúng an toàn 100%

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
8

Cách tiếp cận tương tự phải được sử dụng mỗi khi bảng tên trường sẽ được sử dụng trong truy vấn.

Một vấn đề với mệnh đề giới hạn

Một vấn đề khác có liên quan đến mệnh đề SQL

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
54. Khi ở chế độ mô phỏng (theo mặc định), PDO thay thế trình giữ chỗ bằng dữ liệu thực tế, thay vì gửi riêng. Và với ràng buộc "lười biếng" (sử dụng mảng trong
$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7), PDO coi mọi tham số dưới dạng chuỗi. Do đó, truy vấn
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
56 đã chuẩn bị trở thành
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
57, đó là cú pháp không hợp lệ khiến truy vấn bị lỗi.

Có hai giải pháp:

Một là tắt thi đua (vì MySQL có thể sắp xếp tất cả các giữ chỗ đúng cách). Để làm như vậy, người ta có thể chạy mã này:

$sql "SELECT * FROM users WHERE email = '$email' AND status='$status'";
9

Và các tham số có thể được giữ trong

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
0

Một cách khác là liên kết các biến này một cách rõ ràng trong khi đặt loại tham số thích hợp:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
1

Một điều kỳ dị về

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
59: Vì một số lý do, nó không thực thi loại đúc. Do đó, sử dụng nó trên một số có loại chuỗi sẽ gây ra lỗi đã nói ở trên:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
2

Nhưng thay đổi

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
60 trong ví dụ thành
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
61 - và mọi thứ sẽ diễn ra suôn sẻ.

Giao dịch

Để chạy thành công một giao dịch, bạn phải đảm bảo rằng chế độ lỗi được đặt thành ngoại lệ và tìm hiểu ba phương thức chính tắc:

  • $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    62 để bắt đầu giao dịch
  • $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    63 để cam kết một
  • $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    64 Để hủy tất cả các thay đổi bạn đã thực hiện kể từ khi giao dịch bắt đầu.

Ngoại lệ là rất cần thiết cho các giao dịch vì chúng có thể bị bắt. Vì vậy, trong trường hợp một trong những truy vấn không thành công, việc thực hiện sẽ bị dừng và di chuyển thẳng đến khối bắt, nơi toàn bộ giao dịch sẽ được quay lại.

Vì vậy, một ví dụ điển hình sẽ giống như

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
3

Xin lưu ý những điều quan trọng sau:

  • Chế độ báo cáo lỗi PDO phải được đặt thành
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    21
  • Bạn đã bắt được một
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    66, không phải
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    67, vì không có vấn đề gì về ngoại lệ cụ thể đã hủy bỏ việc thực thi.
  • Bạn nên ném lại một ngoại lệ sau khi quay lại, để được thông báo về vấn đề theo cách thông thường.
  • Cũng đảm bảo rằng một công cụ bảng hỗ trợ các giao dịch (nghĩa là đối với mysql, nó phải là innodb, không phải myisam)
  • Không có câu lệnh Ngôn ngữ định nghĩa dữ liệu (DDL) xác định hoặc sửa đổi lược đồ cơ sở dữ liệu giữa các truy vấn trong giao dịch của bạn, vì một truy vấn như vậy sẽ gây ra cam kết ngầm

Gọi các thủ tục được lưu trữ trong PDO

Có một điều về các thủ tục được lưu trữ mà bất kỳ lập trình viên nào vấp ngã lúc đầu: mọi quy trình được lưu trữ luôn trả về một tập kết quả bổ sung: một (hoặc nhiều) kết quả với dữ liệu thực tế và một kết quả chỉ trống. Điều đó có nghĩa là nếu bạn cố gắng gọi một thủ tục và sau đó tiến hành truy vấn khác, thì "không thể thực hiện các truy vấn trong khi các truy vấn không bị truy vấn khác đang hoạt động" sẽ xảy ra lỗi, vì bạn phải xóa kết quả trống thêm đó trước. Do đó, sau khi gọi một quy trình được lưu trữ nhằm trả về chỉ một bộ kết quả, chỉ cần gọi

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
68 một lần (tất nhiên sau khi tìm nạp tất cả dữ liệu được trả về từ câu lệnh, hoặc nó sẽ bị loại bỏ):one extra result set: one (or many) results with actual data and one just empty. Which means if you try to call a procedure and then proceed to another query, then "Cannot execute queries while other unbuffered queries are active" error will occur, because you have to clear that extra empty result first. Thus, after calling a stored procedure that is intended to return only one result set, just call
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
68 once (of course after fetching all the returned data from statement, or it will be discarded):

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
4

Trong khi đối với các quy trình được lưu trữ trả về nhiều kết quả, nhưng hành vi sẽ giống như với nhiều truy vấn thực thi:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
5

Tuy nhiên, như bạn có thể thấy ở đây là một thủ thuật khác phải được sử dụng: hãy nhớ rằng bộ kết quả bổ sung đó? Về cơ bản, nó trống rỗng đến mức ngay cả một nỗ lực để tìm nạp từ nó sẽ tạo ra một lỗi. Vì vậy, chúng tôi không thể sử dụng chỉ

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
69. Thay vào đó, chúng tôi cũng phải kiểm tra kết quả trống. Với mục đích đó
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
70 chỉ là tuyệt vời.

Tính năng này là một trong những khác biệt thiết yếu giữa các thư viện MySQL cũ và hiện đại: sau khi gọi một quy trình được lưu trữ với

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
0, không có cách nào để tiếp tục làm việc với cùng một kết nối, bởi vì không có chức năng
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
72 cho
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
73. Người ta phải đóng kết nối và sau đó mở một cái mới một lần nữa để chạy các truy vấn khác sau khi gọi một thủ tục được lưu trữ.

Gọi một thủ tục được lưu trữ là một trường hợp hiếm hoi trong đó việc sử dụng

$sql "UPDATE users SET name = ? WHERE id = ?";
$pdo->prepare($sql)->execute([$name$id]);
4 là hợp lý, vì đó là cách duy nhất để xử lý các tham số
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
75 và
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
76. Ví dụ có thể được tìm thấy trong chương thủ công tương ứng. Tuy nhiên, đối với MySQL, nó không hoạt động. Bạn phải dùng đến một biến SQL và một cuộc gọi thêm.for mysql it doesn't work. You have to resort to an SQL variable and an extra call.

Lưu ý rằng đối với các cơ sở dữ liệu khác nhau, cú pháp cũng có thể khác nhau. Ví dụ: để chạy quy trình được bảo vệ đối với Microsoft SQL Server, hãy sử dụng định dạng sau

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
6

ở đâu ? Dấu hiệu là người giữ chỗ. Lưu ý rằng không nên sử dụng niềng răng trong cuộc gọi.

Chạy nhiều truy vấn với PDO

Lưu ý không có lý do để nhét nhiều truy vấn trong một cuộc gọi và nói chung bạn không cần chức năng này. Chạy các truy vấn từng người một là bằng nhau theo mọi cách để chạy chúng trong một lô. Trường hợp sử dụng duy nhất cho chức năng này tôi có thể nghĩ đến là khi bạn cần thực hiện một bãi rác SQL hiện có và kiểm tra kết quả.

Khi ở chế độ mô phỏng, PDO có thể chạy các truy vấn đột biến trong cùng một câu lệnh, thông qua truy vấn () hoặc

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
77. Để truy cập kết quả của các truy vấn hậu quả, người ta phải sử dụng
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
68:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
7

Trong vòng lặp này, bạn sẽ có thể thu thập tất cả các thông tin liên quan từ mọi truy vấn, như các hàng bị ảnh hưởng, ID hoặc lỗi do tự động tạo ra.

Điều quan trọng là phải hiểu rằng tại điểm

$data = [
    
=> 1000,
    
=>  300,
    
=>  200,
];
$stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach (
$data as $id => $bonus)
{
    
$stmt->execute([$bonus$id]);
}
7 PDO sẽ báo cáo lỗi cho truy vấn đầu tiên. Nhưng nếu xảy ra lỗi tại bất kỳ truy vấn hậu quả nào, để có được lỗi đó, người ta phải lặp lại kết quả. Mặc dù có một số ý kiến ​​không biết gì, PDO không thể và không nên báo cáo tất cả các lỗi cùng một lúc. Một số người chỉ không thể nắm bắt toàn bộ vấn đề và không hiểu thông báo lỗi đó không phải là kết quả duy nhất từ ​​truy vấn. Có thể có một bộ dữ liệu được trả về, hoặc một số siêu dữ liệu như ID chèn. Để có được những thứ này, người ta phải lặp lại các kết quả, từng cái một. Nhưng để có thể ném lỗi ngay lập tức, PDO sẽ phải lặp lại tự động và do đó loại bỏ một số kết quả. Đó sẽ là một điều vô nghĩa rõ ràng.for the first query only. But if error occurred at any of consequent queries, to get that error one has to iterate over results. Despite some ignorant opinions, PDO can not and should not report all the errors at once. Some people just cannot grasp the problem at whole, and don't understand that error message is not the only outcome from the query. There could be a dataset returned, or some metadata like insert id. To get these, one has to iterate over resultsets, one by one. But to be able to throw an error immediately, PDO would have to iterate automatically, and thus discard some results. Which would be a clear nonsense.

Không giống như

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
80 PDO không thực hiện cuộc gọi không đồng bộ, vì vậy bạn không thể "Fire và quên" - gửi phần lớn các truy vấn đến MySQL và kết nối gần, PHP sẽ đợi cho đến khi truy vấn cuối cùng được thực thi.

Chế độ thi đua. PDO :: attr_emulation_prepares

Một trong những tùy chọn cấu hình PDO gây tranh cãi nhất là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
81. Nó làm gì? PDO có thể chạy các truy vấn của bạn theo hai cách:

  1. Nó có thể sử dụng một tuyên bố đã chuẩn bị thực hoặc gốc: Khi Prepare () được gọi, truy vấn của bạn với người giữ chỗ sẽ được gửi đến MySQL như vậy, với tất cả các dấu hỏi bạn đặt (trong trường hợp được sử dụng giữ chỗ, chúng được thay thế bằng? Đồng thời), trong khi dữ liệu thực tế đi sau đó, khi EXECUTE () được gọi.real or native prepared statement:
    When prepare() is called, your query with placeholders gets sent to mysql as is, with all the question marks you put in (in case named placeholders are used, they are substituted with ?s as well), while actual data goes later, when execute() is called.
  2. Nó có thể sử dụng câu lệnh được chuẩn bị được mô phỏng, khi truy vấn của bạn được gửi đến MySQL dưới dạng SQL thích hợp, với tất cả dữ liệu tại chỗ, được định dạng đúng. Trong trường hợp này, chỉ có một vòng tròn đến cơ sở dữ liệu xảy ra, với cuộc gọi
    $data = [
        
    => 1000,
        
    =>  300,
        
    =>  200,
    ];
    $stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
    foreach (
    $data as $id => $bonus)
    {
        
    $stmt->execute([$bonus$id]);
    }
    7. Đối với một số trình điều khiển (bao gồm cả MYSQL) Chế độ mô phỏng được chuyển
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    83 theo mặc định.emulated prepared statement, when your query is sent to mysql as proper SQL, with all the data in place, properly formatted. In this case only one roundtrip to database happens, with
    $data = [
        
    => 1000,
        
    =>  300,
        
    =>  200,
    ];
    $stmt $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
    foreach (
    $data as $id => $bonus)
    {
        
    $stmt->execute([$bonus$id]);
    }
    7 call. For some drivers (including mysql) emulation mode is turned
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    83 by default.

Cả hai phương pháp đều có nhược điểm và lợi thế của chúng, và - tôi phải nhấn mạnh vào nó - cả hai đều an toàn như nhau, nếu được sử dụng đúng cách. Mặc dù giai điệu khá hấp dẫn của bài viết phổ biến trên Stack Overflow, cuối cùng, nếu bạn đang sử dụng các phiên bản được hỗ trợ của PHP và MySQL đúng cách, bạn sẽ an toàn 100%. Tất cả những gì bạn phải làm là đặt mã hóa trong DSN, như nó hiển thị trong ví dụ trên và các câu lệnh được chuẩn bị được mô phỏng của bạn sẽ an toàn như những câu thực tế.equally secure, if used properly. Despite rather appealing tone of the popular article on Stack Overflow, in the end it says that if you are using supported versions of PHP and MySQL properly, you are 100% safe. All you have to do is to set encoding in the DSN, as it shown in the example above, and your emulated prepared statements will be as secure as real ones.

Lưu ý rằng khi chế độ gốc được sử dụng, dữ liệu không bao giờ xuất hiện trong truy vấn, được phân tích cú pháp bởi động cơ, với tất cả các giữ chỗ tại chỗ. Nếu bạn đang xem xét nhật ký truy vấn MySQL cho truy vấn đã chuẩn bị của bạn, bạn phải hiểu rằng đó chỉ là một truy vấn nhân tạo chỉ được tạo ra cho mục đích ghi nhật ký, nhưng không phải là một truy cập thực sự đã được thực hiện.the data is never appears in the query, which is parsed by the engine as is, with all the placeholders in place. If you're looking into Mysql query log for your prepared query, you have to understand that it's just an artificial query that has been created solely for logging purpose, but not a real one that has been executed.

Các vấn đề khác với chế độ mô phỏng như sau:

Khi chế độ mô phỏng được bật

Người ta có thể sử dụng một tính năng tiện dụng của các câu lệnh được chuẩn bị được đặt tên - một trình giữ chỗ có cùng tên có thể được sử dụng bất kỳ số lần nào trong cùng một truy vấn, trong khi biến tương ứng chỉ phải được ràng buộc một lần. Vì một số lý do tối nghĩa, chức năng này bị vô hiệu hóa khi chế độ mô phỏng bị tắt:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
8

Ngoài ra, khi mô phỏng là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
83, PDO có thể chạy nhiều truy vấn trong một câu lệnh đã chuẩn bị.

Ngoài ra, vì các câu lệnh được chuẩn bị gốc chỉ hỗ trợ một số loại truy vấn nhất định, bạn chỉ có thể chạy một số truy vấn với các câu lệnh đã chuẩn bị khi mô phỏng là

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
83. Mã sau sẽ trả về tên bảng trong chế độ mô phỏng và lỗi nếu không:

$sql 'SELECT * FROM users WHERE email = ? AND status=?';
9

Khi chế độ mô phỏng bị tắt

Người ta có thể bận tâm không với các loại tham số, vì MySQL sẽ sắp xếp tất cả các loại đúng cách. Do đó, ngay cả chuỗi có thể được ràng buộc để giới hạn các tham số, như nó đã được ghi nhận trong chương tương ứng.

Ngoài ra, chế độ này sẽ cho phép sử dụng lợi thế của tính năng thực thi-đa dạng đơn.

Thật khó để quyết định chế độ nào phải được ưu tiên, nhưng vì khả năng sử dụng, tôi thà biến nó

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
86, để tránh một rắc rối với mệnh đề
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
54. Các vấn đề khác có thể được coi là không đáng kể khi so sánh.

Mysqlnd và truy vấn đệm. Bộ dữ liệu khổng lồ.

Gần đây, tất cả các tiện ích mở rộng PHP hoạt động với cơ sở dữ liệu MySQL đã được cập nhật dựa trên thư viện cấp thấp có tên

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
88, thay thế máy khách
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
89 cũ. Do đó, một số thay đổi trong hành vi PDO, chủ yếu được mô tả ở trên và một thay đổi theo sau:

Có một thứ gọi là truy vấn đệm. Mặc dù bạn có thể không nhận thấy nó, nhưng bạn đã sử dụng chúng tất cả các cách. Thật không may, đây là tin xấu cho bạn: không giống như các phiên bản PHP cũ, nơi bạn đang sử dụng các truy vấn được đệm hầu như miễn phí, các phiên bản hiện đại được xây dựng trên trình điều khiển MySQLND sẽ không cho phép bạn làm điều đó nữa:

Khi sử dụng libmysqlclient làm giới hạn bộ nhớ của thư viện sẽ không tính bộ nhớ được sử dụng cho các bộ kết quả trừ khi dữ liệu được tìm nạp thành các biến PHP. Với MySQLND, bộ nhớ chiếm sẽ bao gồm bộ kết quả đầy đủ.With mysqlnd the memory accounted for will include the full result set.

Toàn bộ là về một kết quả, là viết tắt của tất cả các dữ liệu được tìm thấy bởi truy vấn.

Khi truy vấn chọn của bạn được thực thi, có hai cách để cung cấp kết quả trong tập lệnh của bạn: được đệm và không bị ảnh hưởng. Khi phương thức được đệm được sử dụng, tất cả dữ liệu được trả về bởi truy vấn sẽ được sao chép trong bộ nhớ của tập lệnh. Trong khi ở chế độ không có máy chủ, máy chủ cơ sở dữ liệu sẽ cung cấp từng hàng được tìm thấy từng hàng.gets copied in the script's memory at once. While in unbuffered mode a database server feeds the found rows one by one.

Vì vậy, bạn có thể nói rằng ở chế độ đệm, một kết quả luôn luôn gây gánh nặng bộ nhớ trên máy chủ ngay cả khi tìm nạp không bắt đầu. Đó là lý do tại sao không nên chọn bộ dữ liệu lớn nếu bạn không cần tất cả dữ liệu từ nó.

Tuy nhiên, khi các máy khách dựa trên LibMysQL cũ được sử dụng, vấn đề này không làm phiền quá nhiều PHP, vì bộ nhớ được tiêu thụ bởi kết quả không được tính trong

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
90 và
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
91.

Nhưng với MySQLND, mọi thứ đã thay đổi và kết quả được trả về bởi truy vấn được đệm sẽ được tính vào cả

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
90 và
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
91, bất kể bạn chọn cách nào để có được kết quả:

$sql 'SELECT * FROM users WHERE email = :email AND status=:status';
0

sẽ cho bạn

$sql 'SELECT * FROM users WHERE email = :email AND status=:status';
1

Điều đó có nghĩa là với truy vấn đệm, bộ nhớ được tiêu thụ ngay cả khi bạn đang tìm nạp từng hàng một!even if you're fetching rows one by one!

Vì vậy, hãy nhớ rằng nếu bạn đang chọn một lượng dữ liệu thực sự lớn, luôn luôn đặt

$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
94 thành
$host '127.0.0.1';
$db   'test';
$user 'root';
$pass '';
$charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    
PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
     
$pdo = new PDO($dsn$user$pass$options);
} catch (\
PDOException $e) {
     throw new \
PDOException($e->getMessage(), (int)$e->getCode());
}
95.

Tất nhiên, có những nhược điểm. Một là khét tiếng

Không thể thực hiện các truy vấn trong khi các truy vấn không bị truy cập khác đang hoạt động

Thông báo lỗi có nghĩa là cho đến khi bạn sẽ không truy xuất tất cả các hàng đã chọn từ truy vấn không bị ảnh hưởng, sẽ không thể chạy bất kỳ truy vấn nào khác so với kết nối cơ sở dữ liệu HTE.

Và một vài cái nhỏ,

  1. Với truy vấn không có nội dung, bạn không thể sử dụng phương thức
    $host '127.0.0.1';
    $db   'test';
    $user 'root';
    $pass '';
    $charset 'utf8mb4';$dsn "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        
    PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    try {
         
    $pdo = new PDO($dsn$user$pass$options);
    } catch (\
    PDOException $e) {
         throw new \
    PDOException($e->getMessage(), (int)$e->getCode());
    }
    36 (điều này là vô dụng, như chúng ta đã biết ở trên)
  2. Di chuyển (tìm kiếm) Con trỏ bên trong kết quả hiện tại qua lại (cũng vô dụng).

Những bài viết liên quan:

  • Chế độ tìm nạp PDO
  • Tìm nạp các đối tượng với PDO
  • Trình bao bọc PDO đơn giản nhưng hiệu quả
  • SQL tiêm chống lại các tuyên bố đã chuẩn bị sẽ không giúp được gì
  • Các vấn đề về khả năng sử dụng của mysqli so với pdo
  • Các bệnh về thời thơ ấu của cơ sở dữ liệu đầu tiên của bạn
  • Xác thực người dùng bằng pdo và password_verify ()
  • MCVE hoặc cách gỡ lỗi các tương tác cơ sở dữ liệu với PDO
  • Một tuyên bố chuẩn bị giáo phái hàng hóa
  • Chức năng trợ giúp trắng