其他語言:
- English documentation
- Русская документация
- Documentation française
- Deutsche Dokumentation
- Documentazione italiana
- 日本語ドキュメント
- Documentación en español
- 한국어 문서
- 简体中文文档
- Dokumentasi Bahasa Indonesia
- Documentação em Português (BR)
- हिंदी दस्तावेज़
- التوثيق بالعربية
- Türkçe Dokümantasyon
- Tài liệu tiếng Việt
您可以下載壓縮檔,從此站點克隆或透過composer安裝(packagist.org連結):
composer require krugozor/database
krugozor/database是一個PHP >= 8.0類別函式庫,用於使用PHP擴展mysqli簡單、方便、快速和安全地處理MySQL資料庫。
PHP中所有MySQL資料庫工作函式庫的主要缺點是:
- 冗長性
- 為了防止SQL注入,開發人員有兩種方法:
- 使用預處理語句。
- 手動轉義進入SQL查詢主體的參數。字串參數透過mysqli_real_escape_string,並將預期的數字參數轉換為相應的類型 —
int和float。
- 兩種方法都有巨大的缺點:
- 預處理語句非常冗長。使用「開箱即用」的PDO抽象或mysqli擴展,而不聚合從DBMS獲取資料的所有方法是根本不可能的 — 要從表中獲取值,必須至少編寫5行程式碼!每個查詢都是如此!
- 手動轉義進入SQL查詢主體的參數 — 甚至不討論。好的程式設計師是懶惰的程式設計師。一切都應該最大程度地自動化。
- 為了防止SQL注入,開發人員有兩種方法:
- 無法獲取SQL查詢進行除錯
- 要了解為什麼程式中的SQL查詢不工作,需要除錯它 — 找到邏輯或語法錯誤。要找到錯誤,需要「看到」資料庫「抱怨」的SQL查詢本身,其主體中替換了參數。即,擁有完全形成的SQL。 如果開發人員使用帶有預處理語句的PDO,這是...不可能的!本機函式庫中沒有提供最大方便的機制。只能扭曲或查看資料庫日誌。
- 消除冗長 — 使用「本機」函式庫執行一個查詢需要3行或更多程式碼,而您只需編寫一行。
- 根據指定的佔位符類型轉義進入查詢主體的所有參數 — 可靠地防止SQL注入。
- 不替換「本機」mysqli適配器的功能,而只是補充它。
- 可擴展。本質上,該函式庫僅提供解析器和具有保證防止SQL注入的SQL查詢執行。您可以從函式庫的任何類別繼承,並使用函式庫的機制和
mysqli和mysqli_result的機制建立所需的方法。
大多數用於各種資料庫驅動程式的包裝器都是具有可怕架構的無用程式碼堆積。它們的作者自己不了解包裝器的實際目的,將它們轉變為查詢建構器、ActiveRecord函式庫和其他ORM解決方案。
krugozor/database函式庫不是這些中的任何一個。這只是一個在MySQL DBMS框架內處理普通SQL的便捷工具 — 僅此而已!
Placeholders(佔位符)是*在SQL查詢字串中*代替顯式值(查詢參數)編寫的特殊類型化標記。值本身「稍後」作為執行SQL查詢的主要方法的後續參數傳遞:
$result = $db->query(
"SELECT * FROM `users` WHERE `name` = '?s' AND `age` = ?i",
"達太安", 41
);通過placeholders系統的SQL查詢參數由特殊的轉義機制處理,具體取決於佔位符類型。也就是說,現在不再需要像以前那樣將變數包含在mysqli_real_escape_string()等轉義函式中或將其轉換為數字類型:
<?php
// 以前,在每次查詢DBMS之前,我們都做
// 大約這樣(許多人仍然不做「這個」):
$id = (int) $_POST['id'];
$value = mysqli_real_escape_string($mysql, $_POST['value']);
$result = mysqli_query($mysql, "SELECT * FROM `t` WHERE `f1` = '$value' AND `f2` = $id");現在編寫查詢變得簡單、快速,最重要的是krugozor/database函式庫完全防止所有可能的SQL注入。
佔位符的類型及其用途如下所述。在熟悉佔位符類型之前,需要了解函式庫的機制如何工作。
PHP是一種弱類型語言,在開發此函式庫期間出現了意識形態困境。 想像一下,我們有一個具有以下結構的表:
`name` varchar not null
`flag` tinyint not null並且函式庫必須(由於某種原因,可能不依賴於開發人員)執行以下查詢:
$db->query(
"INSERT INTO `t` SET `name` = '?s', `flag` = ?i",
null, false
);在這個例子中,嘗試將null值寫入文字not null欄位name,
並將布林類型false寫入數字欄位flag。在這種情況下該怎麼辦?
- 誰負責驗證查詢參數 - 客戶端程式碼還是函式庫?
- 在這種情況下是否需要中斷程式執行,或者可能應該應用一些操作以便將資料寫入資料庫?
- 我們可以將
tinyint欄的false值解釋為0值,將name欄的null解釋為空字串嗎? - 我們如何在程式碼中簡化或標準化這個問題?
考慮到提出的問題,決定在此函式庫中實現兩種操作模式。
- Mysql::MODE_STRICT — 佔位符類型和參數類型的嚴格匹配模式。
在
Mysql::MODE_STRICT模式下,參數類型必須與佔位符類型匹配。例如,嘗試為整數類型佔位符?i傳遞55.5或'55.5'值作為參數將引發異常:
// 設定嚴格模式
$db->setTypeMode(Mysql::MODE_STRICT);
// 此表達式不會執行,將引發異常:
// 嘗試在查詢範本「SELECT ?i」中為「integer」類型的佔位符指定「double」類型的值
$db->query('SELECT ?i', 55.5);- Mysql::MODE_TRANSFORM — 當佔位符類型和參數類型不匹配時將參數轉換為佔位符類型的模式。
Mysql::MODE_TRANSFORM模式預設設定,是「寬容」模式 — 當佔位符類型和參數類型不匹配時不產生異常,而是嘗試使用PHP語言本身將參數轉換為所需的佔位符類型。順便說一句,我作為函式庫的作者,總是使用這種模式,嚴格模式(Mysql::MODE_STRICT)在實際工作中從未使用過,但也許您需要它。
Mysql::MODE_TRANSFORM模式允許以下轉換:
- 轉換為
int類型(佔位符?i)- 以
string類型和double類型表示的浮點數 boolTRUE轉換為int(1),FALSE轉換為int(0)null轉換為int(0)
- 以
- 轉換為
double類型(佔位符?d)- 以
string類型和int類型表示的整數 boolTRUE轉換為float(1),FALSE轉換為float(0)null轉換為float(0)
- 以
- 轉換為
string類型(佔位符?s)boolTRUE轉換為string(1) "1",FALSE轉換為string(1) "0"。這種行為與PHP中bool到int的類型轉換不同,因為在實踐中,布林類型通常在MySQL中恰好作為數字寫入。numeric類型的值根據PHP轉換規則轉換為字串null轉換為string(0) ""
- 轉換為
null類型(佔位符?n)- 任何參數。
- 不允許對陣列、物件和資源進行轉換。
$db->query(
'SELECT * FROM `users` WHERE `id` = ?i', 123
);範本轉換後的SQL查詢:
SELECT * FROM `users` WHERE `id` = 123注意! 如果您使用超過PHP_INT_MAX的數字,那麼:
- 在程式中僅將它們作為字串使用。
- 不要使用此佔位符,使用字串佔位符
?s(見下文)。問題是,超過PHP_INT_MAX的數字,PHP會將其解釋為浮點數。函式庫的解析器將嘗試將參數轉換為int類型,結果«結果將是未定義的,因為float沒有足夠的精度來返回正確的結果。在這種情況下,不會輸出警告,甚至不會輸出通知!» — php.net。
$db->query(
'SELECT * FROM `prices` WHERE `cost` IN (?d, ?d)',
12.56, '12.33'
);範本轉換後的SQL查詢:
SELECT * FROM `prices` WHERE `cost` IN (12.56, 12.33)注意! 如果您使用函式庫處理double資料類型,請設定適當的區域設定,以便PHP級別和DBMS級別的整數和小數部分之間的分隔符相同。
參數值使用mysqli::real_escape_string()方法轉義:
$db->query(
'SELECT "?s"',
"你們都是傻瓜,而我是達太安!"
);範本轉換後的SQL查詢:
SELECT "你們都是傻瓜,而我是達太安!"參數值使用mysqli::real_escape_string()方法轉義 + 轉義LIKE運算子中使用的特殊字元(%和_):
$db->query('SELECT "?S"', '% _');範本轉換後的SQL查詢:
SELECT "\% \_"任何參數的值都被忽略,佔位符在SQL查詢中被替換為字串NULL:
$db->query('SELECT ?n', 123);範本轉換後的SQL查詢:
SELECT NULL其中符號*是以下佔位符之一:
i(整數佔位符)d(浮點數佔位符)s(字串類型佔位符)
轉換和轉義規則與上述單個標量類型相同。範例:
$db->query(
'INSERT INTO `test` SET ?Ai',
['first' => '123', 'second' => 456]
);範本轉換後的SQL查詢:
INSERT INTO `test` SET `first` = "123", `second` = "456"其中*是以下類型之一:
i(整數佔位符)d(浮點數佔位符)s(字串類型佔位符)
轉換和轉義規則與上述單個標量類型相同。範例:
$db->query(
'SELECT * FROM `test` WHERE `id` IN (?ai)',
[123, 456]
);範本轉換後的SQL查詢:
SELECT * FROM `test` WHERE `id` IN ("123", "456")範例:
$db->query(
'INSERT INTO `users` SET ?A[?i, "?s"]',
['age' => 41, 'name' => "達太安"]
);範本轉換後的SQL查詢:
INSERT INTO `users` SET `age` = 41,`name` = "達太安"範例:
$db->query(
'SELECT * FROM `users` WHERE `name` IN (?a["?s", "?s"])',
["侯爵達爾基安", "達太安"]
);範本轉換後的SQL查詢:
SELECT * FROM `users` WHERE `name` IN ("侯爵達爾基安", "達太安")此佔位符用於在查詢中透過參數傳遞表名或欄位名的情況。欄位和表名用「反引號」符號括起來:
$db->query(
'SELECT ?f FROM ?f',
'name',
'database.table_name'
);範本轉換後的SQL查詢:
SELECT `name` FROM `database`.`table_name`函式庫要求程式設計師遵守SQL語法。 這意味著以下查詢將不起作用:
$db->query(
'SELECT CONCAT("Hello, ", ?s, "!")',
'world'
);— 佔位符?s必須用單引號或雙引號括起來:
$db->query(
'SELECT concat("Hello, ", "?s", "!")',
'world'
);範本轉換後的SQL查詢:
SELECT concat("Hello, ", "world", "!")對於習慣使用PDO的人來說,這可能看起來很奇怪,但實現一個機制來確定在一種情況下是否需要用引號括起佔位符值是一項非常不平凡的任務,需要編寫完整的解析器。
請參閱檔案../console/tests.php