სწორი PDO კავშირი MySQL-თან ხუთი ხაზია: DSN ჰოსტით, პორტით, ბაზითა და charset=utf8mb4-ით, მომხმარებლის სახელი და პაროლი გარემოდან და სამი პარამეტრი - exception-ები შეცდომისას, ნაგულისხმევად ასოციაციური მასივები და ნატიური prepared statement-ები. ყველაფერი დანარჩენი არის prepared statement-ების გამოყენება ყოველი მნიშვნელობისთვის, რომელიც კოდის გარედან მოდის, მრავალბრძანებიანი ცვლილებების ტრანზაქციებში მოქცევა და იმის ცოდნა, რომელი შეცდომის კოდი რას ნიშნავს. ეს პოსტი თითოეულ ნაწილს განიხილავს, მათ შორის ნაგულისხმევ მნიშვნელობებს, რომლებიც PHP 8-ში შეიცვალა, და კონსტანტების სახელებს, რომლებიც PHP 8.4-სა და 8.5-ში გადაადგილდა, რომ დღეს დაწერილმა კოდმა მომავალ წელს deprecation გაფრთხილებები არ ისროლოს.
DSN და კავშირი, რომელიც პირველივე ჯერზე სწორია#
PDO უკავშირდება data source name სტრიქონით, მომხმარებლის სახელითა და პაროლით. MySQL-ისთვის DSN-ის პრეფიქსია mysql:, ხოლო პარამეტრები წერტილმძიმით გამოიყოფა.
<?php$dsn = sprintf( 'mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', getenv('DB_HOST'), (int) getenv('DB_PORT'), getenv('DB_NAME'));$pdo = new PDO($dsn, getenv('DB_USER'), getenv('DB_PASSWORD'), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false,]);DSN-ის პარამეტრები MySQL-ისთვის:
| პარამეტრი | მაგალითი | შენიშვნა |
|---|---|---|
host | db.example.com | localhost ნიშნავს Unix socket-ს და არა TCP-ს - იხილე ქვემოთ |
port | 3306 | იგნორირდება socket-ით დაკავშირებისას |
dbname | app | არჩევითია; მის გარეშე ბაზა USE-ით უნდა აირჩიო |
charset | utf8mb4 | კავშირის სიმბოლოების ნაკრები. ყოველთვის დააყენე |
unix_socket | /run/mysqld/mysqld.sock | მხოლოდ მაშინ, როცა ბაზა იმავე მანქანაზეა |
localhost-ის მახეში ერთხელ ყველა ვარდება. MySQL-ის კლიენტური ბიბლიოთეკა ჰოსტის სახელ localhost-ს ლოკალური Unix socket-ის გამოყენების მოთხოვნად აღიქვამს, ამიტომ host=localhost;port=3307 პორტს უგულებელყოფს და ვარდება SQLSTATE[HY000] [2002] No such file or directory-ით, როცა ლოკალური სერვერი არ არის. თუ ბაზა სხვა მანქანაზეა - ჰოსტინგის ბაზა ყოველთვის ასეა - გამოიყენე მისი ნამდვილი ჰოსტის სახელი ან მისამართი. თუ იმავე მანქანაზეა, მაგრამ TCP-ზე უსმენს, გამოიყენე 127.0.0.1.
მონაცემები ფაილის გარეთ შეინახე. წაიკითხე ისინი გარემოს ცვლადებიდან ან კონფიგურაციის ფაილიდან web root-ის გარეთ; db.php პაროლით შიგნით ერთი არასწორად მორგებული ვებ სერვერის მოშორებითაა იქამდე, რომ ტექსტად გაიცეს. გარემოს ცვლადები და საიდუმლოებები განიხილავს, სად უნდა ინახებოდეს ისინი.
ერთი კავშირი თითო მოთხოვნაზე და ხანგრძლივი პროცესების გამონაკლისი
ჩვეულებრივ PHP-ში PHP-FPM-ის უკან შექმენი PDO ობიექტი ერთხელ თითო მოთხოვნაზე - პატარა factory ფუნქციაში ან შენი framework-ის container-ში - და გადაეცი ყველაფერს, რასაც სჭირდება. ყოველ ფუნქციაში, რომელიც query-ს უშვებს, ახალი კავშირის გახსნა handshake-ებსა და კავშირებს უსარგებლოდ ამრავლებს; გვერდმა, რომელიც ოც helper-ს იძახებს, ოცი კავშირი არ უნდა გახსნას. როცა მოთხოვნა მთავრდება, PHP ობიექტს ანადგურებს და კავშირი იხურება. სამართავი pool არ არის, და ერთდროული კავშირების რაოდენობა უბრალოდ დაკავებული FPM worker-ების რაოდენობაა.
ხანგრძლივად გაშვებული PHP სხვაა. რიგის worker-ი, WebSocket სერვერი, დაგეგმილი სკრიპტი, რომელსაც batch-ებს შორის სძინავს, ან აპლიკაციის სერვერი, როგორიცაა RoadRunner, Swoole ან FrankenPHP worker რეჟიმში, ერთ PDO ობიექტს საათობით ცოცხლად ინახავს. MySQL უმოქმედო კავშირებს wait_timeout წამის შემდეგ ხურავს - ნაგულისხმევად 28,800, გაზიარებულ სერვერებზე ხშირად ნაკლები - და მოძველებულ ობიექტზე შემდეგი query ვარდება 2006 MySQL server has gone away-ით. PDO თავისით ხელახლა არ უკავშირდება. გამოსავალია, ეს შეცდომა ყოველი job-ის საზღვარზე დაიჭირო, PDO ობიექტი გადააგდო, ახალი შექმნა და job ერთხელ გაიმეორო:
function db(bool $fresh = false): PDO{ static $pdo = null; if ($fresh || $pdo === null) { $pdo = new PDO($GLOBALS['dsn'], getenv('DB_USER'), getenv('DB_PASSWORD'), $GLOBALS['opts']); } return $pdo;}try { handleJob(db(), $job);} catch (PDOException $e) { if (!in_array($e->errorInfo[1] ?? 0, [2006, 2013], true)) { throw $e; } handleJob(db(true), $job); // reconnect once and retry}გაიმეორე მხოლოდ სამუშაო, რომლის ორჯერ გაშვებაც უსაფრთხოა, ან რომელიც ტრანზაქციაში იყო და დაკარგულმა კავშირმა უკან დააბრუნა. Laravel-ის რიგის worker-ი ამის ერთ ვერსიას შენთვის აკეთებს, და ესაა ერთ-ერთი მიზეზი, რის გამოც framework-ის worker-ები პერიოდულად --max-jobs-ით ან --max-time-ით გადაიტვირთება.
პარამეტრები, რომლებიც ღირს დაყენება, და მათი ნაგულისხმევი მნიშვნელობები#
PDO-ს ნაგულისხმევი მნიშვნელობები გაუმჯობესდა, მაგრამ ყველა არ უხდება MySQL-ს.
| ატრიბუტი | ნაგულისხმევი | რეკომენდებული | რატომ |
|---|---|---|---|
ATTR_ERRMODE | ERRMODE_EXCEPTION (PHP 8.0+) | იგივე, ცხადად დაყენებული | 8.0-მდე ნაგულისხმევი ჩუმი ჩავარდნა იყო |
ATTR_DEFAULT_FETCH_MODE | FETCH_BOTH | FETCH_ASSOC | FETCH_BOTH ყოველ სვეტს ორჯერ აბრუნებს |
ATTR_EMULATE_PREPARES | true MySQL-ისთვის | false | ნამდვილი სერვერული prepare-ები, ტიპიზებული შედეგები |
ATTR_PERSISTENT | false | false | მუდმივ კავშირებს მდგომარეობა მოთხოვნებს შორის გადააქვთ |
ATTR_STRINGIFY_FETCHES | false | false | true ყოველ რიცხვს სტრიქონად აქცევს |
MYSQL_ATTR_FOUND_ROWS | false | დამოკიდებულია | true-ით rowCount() დამთხვეულ ხაზებს ითვლის და არა შეცვლილს |
MYSQL_ATTR_USE_BUFFERED_QUERY | true | true | false მხოლოდ უზარმაზარი შედეგების ნაკადად წასაკითხად |
შეცდომის რეჟიმი ცხადად დააყენე PHP 8-ზეც, რადგან კოდი ძველი პარამეტრების მქონე პროექტებში კოპირდება და რადგან ეს განზრახვას ადასტურებს. ჩუმი რეჟიმი - სადაც execute() false-ს აბრუნებს და მოსალოდნელია, რომ შეამოწმებ - ასე იქცევა ბაგები, რომლებიც ხმამაღლა უნდა გამოჩენილიყო, დაკარგულ სტრიქონებად.
კონსტანტები PHP 8.4-სა და 8.5-ში გადაადგილდა
PHP 8.4-მა დაამატა დრაივერის სპეციფიკური ქვეკლასები: Pdo\Mysql, Pdo\Pgsql, Pdo\Sqlite. PDO::connect($dsn, $user, $password, $options) DSN-ის შესაბამის ქვეკლასს აბრუნებს, და MySQL-ის სპეციფიკური ატრიბუტები ახლა მასზეა, როგორც Pdo\Mysql::ATTR_SSL_CA, Pdo\Mysql::ATTR_INIT_COMMAND, Pdo\Mysql::ATTR_FOUND_ROWS და ასე შემდეგ. PHP 8.5 ძველ PDO::MYSQL_ATTR_* დაწერილობებს მოძველებულად აცხადებს. ისინი ჯერ კიდევ მუშაობს, მაგრამ deprecation შეტყობინებას აგდებს, რაც ტესტების ნაკრებებს ამტვრევს, რომლებიც შეტყობინებებს ჩავარდნად თვლიან - რამდენიმე framework ზუსტად ამას წააწყდა PDO::MYSQL_ATTR_SSL_CA-ით თავის ნაგულისხმევ კონფიგურაციაში. თუ PHP 8.4-ზე ძველს უჭერ მხარს, ძველი სახელები დატოვე; თუ შენი მინიმუმი 8.4-ია, ახლავე გადადი.
Prepared statement-ები#
prepared statement SQL-ს placeholder-ებით და მნიშვნელობებს ცალ-ცალკე აგზავნის, ამიტომ მნიშვნელობას query-ის სტრუქტურის შეცვლა არასოდეს შეუძლია. ეს არის SQL injection-ისგან მთელი დაცვა, და ის მხოლოდ მაშინ მუშაობს, თუ კოდის გარედან მოსული ყოველი მნიშვნელობა placeholder-ზე გადის.
// Positional placeholders$stmt = $pdo->prepare('SELECT id, email FROM users WHERE status = ? AND created_at > ?');$stmt->execute(['active', '2026-01-01']);$users = $stmt->fetchAll();// Named placeholders$stmt = $pdo->prepare('UPDATE users SET email = :email WHERE id = :id');$stmt->execute(['email' => $email, 'id' => $id]);echo $stmt->rowCount(); // rows actually changedexecute()-ზე გადაცემული მნიშვნელობები ყველა სტრიქონებად მიება, რომლებსაც MySQL საჭიროებისამებრ გარდაქმნის. როცა ტიპს მნიშვნელობა აქვს, ცხადად მიაბი:
$stmt = $pdo->prepare('SELECT id, title FROM posts ORDER BY id DESC LIMIT :limit OFFSET :offset');$stmt->bindValue('limit', $perPage, PDO::PARAM_INT);$stmt->bindValue('offset', ($page - 1) * $perPage, PDO::PARAM_INT);$stmt->execute();LIMIT კლასიკური შემთხვევაა. ემულირებული prepare-ებით ლიმიტის execute()-ით მიბმა LIMIT '10'-ს იძლევა, რაც სინტაქსური შეცდომაა. ნატიური prepare-ებით ან PARAM_INT-ით მუშაობს.
რისი გაკეთება არ შეუძლია placeholder-ებს
placeholder-ები მნიშვნელობებს ცვლის და არასოდეს იდენტიფიკატორებს ან საკვანძო სიტყვებს. ცხრილის სახელის, ORDER BY-ში სვეტის სახელის ან ASC/DESC-ის მიბმა შეუძლებელია. მათთვის მომხმარებლის შეყვანა ფიქსირებულ სიაზე დაიყვანე:
$sortable = ['created_at' => 'created_at', 'title' => 'title'];$column = $sortable[$_GET['sort'] ?? ''] ?? 'created_at';$direction = ($_GET['dir'] ?? '') === 'asc' ? 'ASC' : 'DESC';$stmt = $pdo->query("SELECT id, title FROM posts ORDER BY $column $direction");IN (...) სიას თითო მნიშვნელობაზე ერთი placeholder სჭირდება, მასივიდან აგებული:
$ids = array_map('intval', $ids);$marks = implode(',', array_fill(0, count($ids), '?'));$stmt = $pdo->prepare("SELECT * FROM products WHERE id IN ($marks)");$stmt->execute($ids);ამამდე ცარიელი მასივისგან დაიცავი თავი; IN () სინტაქსური შეცდომაა.
ემულირებული და ნატიური prepare-ები#
ნაგულისხმევად MySQL-ის დრაივერი სერვერულ prepared statement-ებს საერთოდ არ იყენებს. ჩართული ATTR_EMULATE_PREPARES-ით PDO ყოველ მნიშვნელობას თავად ახდენს escape-ს და ერთ დასრულებულ query სტრიქონს აგზავნის. ეს უსაფრთხოა, სანამ კლიენტმა კავშირის სიმბოლოების ნაკრები იცის - სწორედ ამიტომ ეკუთვნის charset=utf8mb4 DSN-ს და არა შემდგომ SET NAMES query-ს: PDO-მ მხოლოდ იმ charset-ის შესახებ იცის, რომელიც DSN-ში უთხრეს, ხოლო charset-ის არასწორი წარმოდგენით escape ძველი მრავალბაიტიანი injection ხრიკების ფესვია.
განსხვავებები, რომლებიც პრაქტიკაში ჩანს:
- ტიპები. ნატიური prepare-ები MySQL-ის ბინარულ პროტოკოლს იყენებენ, ამიტომ მთელი რიცხვები PHP-ის მთელ რიცხვებად ბრუნდება, ხოლო float-ები float-ებად. ემულირებული prepare-ები ყველაფერს სტრიქონებად აბრუნებდნენ PHP 8.1-მდე, რომელმაც ისინიც ნატიური მთელი რიცხვებისა და float-ების დაბრუნებაზე გადაიყვანა. ძველ კოდში ეს ცვლილება ღირს ცოდნად, როცა მკაცრი შედარება, როგორიცაა
$row['id'] === '5', განახლების შემდეგ უცებ ვარდება. - განმეორებული სახელიანი placeholder-ები. ემულაცია საშუალებას გაძლევს,
:termერთ query-ში ორჯერ გამოიყენო. ნატიური prepare-ები - არა: მიიღებSQLSTATE[HY093]: Invalid parameter number-ს. გამოიყენე:term1და:term2. - Round trip-ები. ნატიური prepare execute-მდე სერვერთან ცალკე მოთხოვნაა. ერთხელ გაშვებული ბრძანებისთვის ეს ერთი დამატებითი round trip-ია; ციკლში გაშვებულისთვის ერთხელ prepare და ბევრჯერ execute უფრო სწრაფია.
- შეცდომები. ნატიური prepare-ებით სინტაქსური შეცდომა
prepare()-ზე ჩნდება. ემულაციით -execute()-ზე.
სწორად გამოყენებისას ორივე რეჟიმი უსაფრთხოა. ნატიური უკეთესი ნაგულისხმევია, რადგან ტიპები სწორია და შეცდომები ადრე მოდის; ემულაცია ჩართე მხოლოდ მაშინ, თუ განმეორებულ სახელიან placeholder-ებს ეყრდნობი ან დამატებითი round trip გაზომე.
სიმბოლოების ნაკრებები: utf8mb4 ბოლომდე#
charset=utf8mb4 კავშირის სიმბოლოების ნაკრებს აყენებს - რას აგზავნის PHP და რას ელის უკან. ცხრილებიც utf8mb4 უნდა იყოს, რაც MySQL 8.4-ში სერვერის ნაგულისხმევია utf8mb4_0900_ai_ci collation-ით. ძველი utf8 (utf8mb3-ის ფსევდონიმი) სიმბოლოზე მაქსიმუმ სამ ბაიტს ინახავს და emoji-სა და ზოგ CJK სიმბოლოს უარყოფს შეცდომით 1366, Incorrect string value: '\xF0\x9F\x98\x80'. თუ ამას ხედავ, რომელიმე სვეტი ან ცხრილი ჯერ კიდევ utf8mb3-ია; MySQL-ის utf8mb4 და collation-ები მის გარდაქმნას განიხილავს.
თუ კავშირისთვის კონკრეტული collation გჭირდება, დააყენე ის init ბრძანებით, რომელიც თითო კავშირზე ერთხელ ეშვება:
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci",// PHP 8.4+: Pdo\Mysql::ATTR_INIT_COMMANDიგივე პარამეტრი არის ადგილი session-ის დროის სარტყლის (SET time_zone = '+00:00') ან sql_mode-ის დასაყენებლად, თუ აპლიკაცია მასზეა დამოკიდებული.
შედეგების წამოღება და დიდების ნაკადად წაკითხვა#
fetch მეთოდები fetchAll()-ის მიღმაც ღირს ცოდნად:
| გამოძახება | აბრუნებს |
|---|---|
fetch() | ერთ ხაზს, ან false-ს, როცა მეტი აღარ არის |
fetchAll() | ყველა ხაზს მასივად |
fetchColumn() | შემდეგი ხაზის პირველ სვეტს - იდეალურია COUNT(*)-ისთვის |
fetchAll(PDO::FETCH_COLUMN) | ერთი სვეტის ბრტყელ სიას |
fetchAll(PDO::FETCH_KEY_PAIR) | [col1 => col2] ორსვეტიანი query-დან |
fetchAll(PDO::FETCH_GROUP) | ხაზებს, დაჯგუფებულს პირველი სვეტით |
fetchObject(Product::class) | ერთ ხაზს, კლასში ჩატვირთულს |
ნაგულისხმევად PDO მთელ შედეგს PHP-ის მეხსიერებაში ბუფერავს, სანამ შენი ციკლი პირველ ხაზს დაინახავს. ეს არის ის, რაც ჩვეულებრივი გვერდებისთვის გინდა, და ის, რის გამოც 2-მილიონხაზიან ექსპორტს memory_limit ამოეწურება. ექსპორტისთვის ბუფერიზაცია ამ ერთი query-სთვის გამორთე და გაიარე ციკლით:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);$stmt = $pdo->query('SELECT id, email, created_at FROM users');$out = fopen('php://output', 'w');foreach ($stmt as $row) { fputcsv($out, $row);}$stmt->closeCursor();$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);სანამ არაბუფერიზებული შედეგი ღიაა, კავშირს სხვა query-ის გაშვება არ შეუძლია - მიიღებ SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active-ს. ჯერ ციკლი დაასრულე და closeCursor() გამოიძახე. PHP ini-ის მნიშვნელოვანი პარამეტრები memory_limit-სა და max_execution_time-ს განიხილავს იმ job-ებისთვის, რომლებსაც სჭირდებათ.
ტრანზაქციები, insert ID-ები და ხაზების რაოდენობა#
ყველაფერი, რაც ერთზე მეტ ხაზს ერთ ერთეულად ცვლის, ტრანზაქციას ეკუთვნის:
try { $pdo->beginTransaction(); $stmt = $pdo->prepare('INSERT INTO orders (user_id, total) VALUES (?, ?)'); $stmt->execute([$userId, $total]); $orderId = (int) $pdo->lastInsertId(); $line = $pdo->prepare('INSERT INTO order_lines (order_id, sku, qty) VALUES (?, ?, ?)'); foreach ($items as $item) { $line->execute([$orderId, $item['sku'], $item['qty']]); } $pdo->commit();} catch (Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $e;}ამ მაგალითში სამი დეტალი ადვილად შეიძლება არასწორად გაკეთდეს:
lastInsertId()ამ კავშირზე ბოლო insert-ისAUTO_INCREMENTმნიშვნელობას აბრუნებს, სტრიქონად. ის კავშირზეა მიბმული, ამიტომ ერთდროული მოთხოვნები ერთმანეთის ID-ებს ვერ ხედავენ.rowCount()UPDATE-ის შემდეგ რეალურად შეცვლილ ხაზებს აბრუნებს. ხაზის განახლება იმავე მნიშვნელობებზე, რაც უკვე აქვს, ნულად ითვლება, რაც აკვირვებს კოდს, რომელიც რაოდენობით წყვეტს, არსებობს თუ არა ხაზი.MYSQL_ATTR_FOUND_ROWS => trueმას დამთხვეულ ხაზებზე ცვლის.- DDL ბრძანებები, როგორიცაა
CREATE TABLEანALTER TABLE, MySQL-ში იმპლიციტურად commit-ს აკეთებს. migration-ის, რომელიც DDL-ს ტრანზაქციაში ურევს, უკან დაბრუნება შეუძლებელია, და PHP 8-ზე შემდგომიcommit()აგდებსThere is no active transaction-ს.
Deadlock-ები (შეცდომა 1213) მთელ ტრანზაქციას აბრუნებს და უნდა გამეორდეს; lock wait timeout-ები (1205) ნაგულისხმევად მხოლოდ ბრძანებას აბრუნებს. MySQL-ის ტრანზაქციები, locking და deadlock-ები შეიცავს გამეორების wrapper-ს და მიზეზებს, რის გამოც თითოეული ხდება.
შეცდომები და რას ნიშნავს ისინი#
PDOException SQLSTATE-ს getCode()-ში ატარებს, ხოლო MySQL-ის შეცდომის ნომერს $e->errorInfo[1]-ში. განშტოება MySQL-ის ნომრით გააკეთე; SQLSTATE HY000 ათობით ურთიერთდაუკავშირებელ შეცდომას ფარავს.
try { $stmt->execute([$email]);} catch (PDOException $e) { if (($e->errorInfo[1] ?? null) === 1062) { return 'That email is already registered.'; } throw $e;}| შეტყობინების ფრაგმენტი | MySQL კოდი | ჩვეულებრივი მიზეზი |
|---|---|---|
[2002] Connection refused | 2002 | არასწორი ჰოსტი ან პორტი, ან firewall |
[2002] No such file or directory | 2002 | host=localhost ლოკალური socket-ის გარეშე |
[1045] Access denied for user | 1045 | არასწორი პაროლი, ან მომხმარებელს ამ ჰოსტიდან შესვლა არ აქვს ნებადართული |
[1049] Unknown database | 1049 | არასწორი dbname, ან მომხმარებელი მას ვერ ხედავს |
2006 MySQL server has gone away | 2006 | უმოქმედო კავშირი დაიხურა wait_timeout-ით, ან პაკეტი ზედმეტად დიდია |
Duplicate entry ... for key | 1062 | unique constraint - ხშირად მომხმარებლის ლეგიტიმური შეცდომა |
Cannot add or update a child row | 1452 | foreign key-ის სამიზნე არ არსებობს |
Incorrect string value | 1366 | სვეტი არ არის utf8mb4 |
Too many connections | 1040 | კავშირების ლიმიტი მიღწეულია |
1045-ისთვის გახსოვდეს, რომ MySQL ანგარიში მომხმარებელი და ჰოსტის შაბლონია ერთად: 'app'@'localhost' და 'app'@'%' სხვადასხვა ანგარიშებია სხვადასხვა პაროლით. MySQL-ის მომხმარებლები და პრივილეგიები დამთხვევის წესებს ხსნის, ხოლო MySQL-ის კავშირების ლიმიტები და pooling 1040-სა და 2006-ს განიხილავს.
production-ში $e->getMessage() ვიზიტორებს არასოდეს აჩვენო. ის შეიძლება შეიცავდეს ჰოსტის სახელს, მომხმარებლის სახელს და query-ის ნაწილებს. ჩაწერე log-ში და აჩვენე ზოგადი შეცდომა.
დაშიფრული კავშირები#
MySQL 8.4 სერვერზე TLS-ს ნაგულისხმევად რთავს, მაგრამ PDO მას არ ითხოვს, სანამ SSL პარამეტრს არ დააყენებ. ინტერნეტის გავლით მოითხოვე:
$pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_SSL_CA => '/etc/ssl/certs/db-ca.pem', PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,]);თუ სერვერი იყენებს სერტიფიკატს, რომელიც თავისთვის თავად დააგენერირა, და მისი CA ფაილი არ გაქვს, მაინც შეგიძლია დაშიფვრა, თუ მასივში SSL პარამეტრს დატოვებ და MYSQL_ATTR_SSL_VERIFY_SERVER_CERT-ს false-ზე დააყენებ - ეს ტრაფიკს შიფრავს, მაგრამ არაფერს ამტკიცებს იმაზე, ვინ გიპასუხა. ეს უბრალო ტექსტზე უკეთესია და ვერიფიკაციაზე უარესი. PHP-ის მხრიდან შეამოწმე SHOW SESSION STATUS LIKE 'Ssl_cipher': ცარიელი მნიშვნელობა ნიშნავს, რომ კავშირი დაშიფრული არ არის.
FAQ#
PDO თუ mysqli?
PDO, კოდის უმეტესობისთვის. მას აქვს სახელიანი placeholder-ები, იგივე API სხვა ბაზებისთვის და უფრო სუფთა exception-ები. mysqli რამდენიმე MySQL-ის სპეციფიკურ შესაძლებლობას ავლენს, რომელიც PDO-ს არ აქვს, მაგალითად ასინქრონულ query-ებს და მრავალბრძანებიანი შედეგების დამუშავებას. ორივე მხარდაჭერილია და ორივე უსაფრთხოა prepared statement-ებით.
quote()-ით escape ისეთივე კარგია, როგორც prepared statement?
ის სწორად ახდენს escape-ს, როცა კავშირის charset DSN-შია დაყენებული, მაგრამ ერთხელ დავიწყება ადვილია, და ერთხელ დავიწყებაა სწორედ მოწყვლადობა. prepared statement-ები უსაფრთხო გზას ნაგულისხმევად აქცევს. quote() გამოიყენე მხოლოდ იქ, სადაც placeholder-ები ნამდვილად ვერ იმუშავებს.
გამოვიყენო მუდმივი კავშირები?
ჩვეულებრივ არა. ისინი თითო PHP-FPM worker-ზე ერთ ღია კავშირს ინახავენ, რაც თითო მოთხოვნაზე handshake-ის რამდენიმე მილიწამს ზოგავს, მაგრამ დროებით ცხრილებს, session-ის ცვლადებს და ზოგჯერ ნახევრად დასრულებულ ტრანზაქციებს ამ worker-ზე შემდეგ მოთხოვნას უტოვებს. ჯერ handshake გაზომე; მოკლე ქსელურ გზაზე ის პატარაა.
რატომ ბრუნდება ჩემი მთელი რიცხვები სტრიქონებად?
ან PHP 8.1-ზე ძველზე ხარ ემულირებული prepare-ებით, ან ATTR_STRINGIFY_FETCHES ჩართულია. დააყენე ATTR_EMULATE_PREPARES false-ზე და ბინარული პროტოკოლი ნატიურ მთელ რიცხვებსა და float-ებს დააბრუნებს. DECIMAL სვეტები მაინც სტრიქონებად ბრუნდება განზრახ, მცურავი წერტილის დამრგვალების თავიდან ასაცილებლად.
როგორ ჩავრთო TLS MySQL-ისთვის Laravel-ში?
Laravel-ის config/database.php mysql კავშირისთვის PDO-ს options მასივს გადასცემს, და ნაგულისხმევი ფაილი უკვე კითხულობს CA-ის გზას MYSQL_ATTR_SSL_CA გარემოს ცვლადიდან SSL CA ატრიბუტში. დააყენე ეს ცვლადი CA ფაილის გზაზე .env-ში. Laravel-ის ახალი ვერსიები PHP 8.5-ზე Pdo\Mysql კონსტანტას ირჩევენ deprecation შეტყობინების თავიდან ასაცილებლად; ძველ კონფიგურაციის ფაილში ეს ხაზი განაახლე, როცა 8.5-ზე გადახვალ.
როგორ ვნახო query, რომელიც PDO-მ რეალურად გაუშვა?
$stmt->debugDumpParams() ბეჭდავს SQL-სა და მიბმულ პარამეტრებს, და PHP 7.2-დან გაშლილ query-საც შეიცავს, როცა prepare-ები ემულირებულია. სერვერის მხარეს general query log ან slow query log ზუსტად აჩვენებს, რა მივიდა.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.