摘要:是什么首先思考為什么選擇是一個(gè)數(shù)據(jù)訪(fǎng)問(wèn)抽象層抽象是雙重的一個(gè)是眾所周知但不太重要的另一個(gè)是模糊的但是是最重要的眾所周知為不同的數(shù)據(jù)庫(kù)提供了統(tǒng)一的接口雖然這個(gè)功能本身很龐大但是對(duì)于固定程序來(lái)說(shuō)不是過(guò)于重要的事情基本所有的程序都是使用統(tǒng)一的后端
PDO是什么
首先思考, 為什么選擇PDO
PDO 是一個(gè)數(shù)據(jù)訪(fǎng)問(wèn)抽象層(Database Access Abstraction Layer). 抽象是雙重的: 一個(gè)是眾所周知但不太重要的. 另一個(gè)是模糊的但是是最重要的.
眾所周知 PDO 為不同的數(shù)據(jù)庫(kù)提供了統(tǒng)一的接口. 雖然這個(gè)功能本身很龐大, 但是對(duì)于固定程序來(lái)說(shuō)不是過(guò)于重要的事情, 基本所有的程序都是使用統(tǒng)一的后端數(shù)據(jù)庫(kù). 盡管有一些謠言, 但是通過(guò)改變單行 PDO 配置來(lái)切換后端數(shù)據(jù)庫(kù)是不可能的-由于不同的 SQL 風(fēng)格(為此, 需要使用像 DQL 這樣的平均查詢(xún)語(yǔ)言). 因此對(duì)于普通的 LAMP 開(kāi)發(fā)者來(lái)說(shuō), 這一點(diǎn)是微不足道的, 并且對(duì)他而言, PDO只是熟悉的 mysql(i)_query() 函數(shù)的另一個(gè)更復(fù)雜版本. 但實(shí)際上它不是, 它有豐富的其他功能.
PDO 不僅抽象了數(shù)據(jù)庫(kù)API, 還抽象了基本操作, 否則必須在每個(gè)應(yīng)用程序中重復(fù)數(shù)百次, 使您的代碼非常WET. 不同于 mysql 和 mysqli , 兩個(gè)都不能直接使用低級(jí)裸 APIs(但僅作為某些更高級(jí)別抽象層的構(gòu)建材料), PDO就是這樣的抽象. 雖然仍是不完整的, 但是至少可用.
真正的PDO好處是:
安全性 (可用的準(zhǔn)備語(yǔ)句)
可用性 (許多輔助函數(shù)可以自動(dòng)執(zhí)行日常操作)
可重用性 (用于訪(fǎng)問(wèn)大量數(shù)據(jù)庫(kù)的統(tǒng)一API, 從SQLITE到oracle)
請(qǐng)注意, 盡管 PDO 是原生數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序中最好的, 但對(duì)于現(xiàn)代WEB應(yīng)用程序來(lái)說(shuō), 請(qǐng)考慮將使用有查詢(xún)構(gòu)建器的 ORM 或者與其他更高抽象級(jí)別的庫(kù)一起使用, 只是偶爾使用原生的PDO. 好的ORM比如 Doctrine, Eloquent, RedBean和 Yii::AR. Aura.SQL 是具有很多附加功能的使用PDO包裝器的一個(gè)很好的例子.
無(wú)論哪種方式, 首先要了解基本工具是件好事. 那么, 讓我們開(kāi)始吧:
PDO有一個(gè)叫 DSN 的預(yù)想接方式. 它并不復(fù)雜-PDO需要你在三個(gè)不同的位置輸入不同的配置, 而不是一個(gè)簡(jiǎn)單的選項(xiàng)列表.
database driver, host, db(schema) name 和 charset, 以及不常使用的 port 和 unix_socket 設(shè)置 DSN
user_name 和 password 設(shè)置構(gòu)造方法
其他所有的配置在options數(shù)組
其中 DSN 是以分號(hào)分隔的字符串, 由 param=value 鍵值對(duì)組成, 從驅(qū)動(dòng)程序名稱(chēng)和冒號(hào)開(kāi)始:
mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4 driver^ ^colon ^param=value pair ^semicolon
注意, 遵循正確的格式是非常重要的- DSN中不能使用 空格, 引號(hào), 和其他的符號(hào), 只能使用參數(shù), 值和定界符. 就像手冊(cè)上展示的.
這里有一個(gè)例子:
$host = "127.0.0.1"; $db = "test"; $pass = "root"; $charset = "utf8mb4"; $dsn = "mysql:host={$host};dbnamej={$db};charset={$charset}"; $options = [ PDO::ATR_ERRMODE => PDO::ERRMODE_EXECPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new PDO($dsn, $user, $pass, $options); } catch (PDOException $e) { }
設(shè)置了所有上述變量屬性, 我們將在 $pdo 變量中得到一個(gè)正確的 PDO 實(shí)例.
使用舊mysql擴(kuò)展用戶(hù)重要通知
不同于 mysql_* 函數(shù), 可以在代碼的任意位置使用, pdo 實(shí)例被存儲(chǔ)在一個(gè)變量中, 那就意味著只能在函數(shù)內(nèi)部進(jìn)行訪(fǎng)問(wèn). 因此, 必須通過(guò)函數(shù)參數(shù)傳遞或使用更高級(jí)的技術(shù), 比如IOC容器.
連接只用創(chuàng)建一次. 不要在函數(shù), 類(lèi)構(gòu)造函數(shù)創(chuàng)建連接, 否則, 會(huì)創(chuàng)建多個(gè)連接, 最終導(dǎo)致數(shù)據(jù)庫(kù)服務(wù)宕機(jī). 因此必須創(chuàng)建唯一的 PDO 實(shí)例, 讓整個(gè)腳本使用.(適用于FPM模式)
通過(guò)DSN設(shè)置字符集是非常重要的-這是唯一正確的方式因?yàn)樗鼤?huì)告訴PDO哪個(gè)字符集會(huì)被使用. 因此, 忘記通過(guò) Query 運(yùn)行SET NAMES 或者通過(guò) PDO::MYSQL_ATTR_INIT_COMMAND. 只有當(dāng)PHP版本過(guò)低時(shí)(低于5.3.6), 才可以使用 SET NAMES 查詢(xún), 并且關(guān)閉仿真模式.
更多關(guān)于連接的內(nèi)容可以在 連接MySQL查看
運(yùn)行查詢(xún) PDO::query()使用 PDO 有兩種方式運(yùn)行查詢(xún). 如果查詢(xún)中沒(méi)有使用變量, 可以使用 PDO::query 方法. 它會(huì)運(yùn)行查詢(xún)并返回一個(gè) PDOStatement 類(lèi)的對(duì)象, 該類(lèi)與 mysql_query 返回的資源大致相同, 特別時(shí)從中獲取實(shí)際記錄的操作:
$stmt = $pdo->query("SELECT name FROM users"); while ($row = $stmt->fetch()) { echo $row["name"] . " "; }
并且 query() 方法允許我們使用一個(gè)整潔的方法連接 SELECT 查詢(xún), 如下所示.
預(yù)處理, 防止SQL注入放棄熟悉的 mysql_query()函數(shù) 并進(jìn)入嚴(yán)格數(shù)據(jù)對(duì)象領(lǐng)域的主要原因是 PDO 已經(jīng)準(zhǔn)備好了開(kāi)箱即用的預(yù)處理語(yǔ)句. 如果要在語(yǔ)句中使用變量, 預(yù)處理語(yǔ)句是唯一正確運(yùn)行的方式. 它如此重要的原因在 The Hitchhiker"s Guide to SQL Injection prevention.有詳細(xì)的解釋.
對(duì)于運(yùn)行的查詢(xún), 如果至少使用一個(gè)變量, 你必須使用占位符替換它. 準(zhǔn)備執(zhí)行語(yǔ)句, 然后分別傳入變量執(zhí)行.
長(zhǎng)話(huà)短說(shuō), 它不像感覺(jué)的那么困難. 在大多數(shù)例子中, 你只需要使用函數(shù) prepare 和 execute .
首先, 需要修改查詢(xún), 在使用變量的位置添加占位符, 就像這樣
$sql = "SELECT * FROM users WHERE email = "{$email}" AND status = "{$status}"";
改為
$sql = "SELECT * FROM users where email = ? and status = ?";
或者
$sql = "SELECT * FROM users where email = :email AND status = :status";
注意 PDO 支持位置(?)和命名(:email)占位符, 后者始終以冒號(hào)開(kāi)始,并且只能使用字母, 數(shù)字和下劃線(xiàn). 還需要注意 占位符周?chē)荒苁褂靡?hào) .
一個(gè)查詢(xún)使用了占位符, 就必須使用PDO::prepare()方法預(yù)處理. 這個(gè)方法返回一個(gè)和我們上邊討論的相同的 PDOStatement 對(duì)象, 但是沒(méi)有綁定任何數(shù)據(jù).
最后, 必須使用 PDOStatement 對(duì)象的 execute() 方法執(zhí)行查詢(xún), 并且通過(guò)數(shù)組形式傳遞參數(shù). 之后, 就可以從語(yǔ)句中得到結(jié)果數(shù)據(jù)(如果適用).
$stmt = $pdo->prepare("SELECT * FROM users WHERES 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();
可以看到, 位置占位符, 你需要提供一個(gè)索引數(shù)組. 命名占位符, 需要提供一個(gè)關(guān)聯(lián)數(shù)組, 并且鍵要匹配查詢(xún)中的占位符. 同一個(gè)查詢(xún)中不能混合位置占位符和命名占位符.
位置占位符可以讓你寫(xiě)更簡(jiǎn)短的代碼, 但是對(duì)參數(shù)順序是敏感的(必須于查詢(xún)中參數(shù)的順序一致). 雖然命名占位符使代碼更冗長(zhǎng), 但是允許隨機(jī)參數(shù)綁定.
另外需要注意, 雖然存在普遍的誤解, 但是數(shù)組鍵中 : 不是必須的.
執(zhí)行后就可以使用支持的方法獲取結(jié)果.
更多的例子可以查看(respective article)[https://phpdelusions.net/pdo_...].
參數(shù)綁定將數(shù)據(jù)傳入 execute() (如上所示)方法中應(yīng)被視為默認(rèn)的最方便的方式. 如果使用這個(gè)方法, 所有參數(shù)都將會(huì)綁定為字符串(如果使用NULL值, 將會(huì)使用SQL NULL發(fā)送給查詢(xún)), 大多數(shù)時(shí)候都沒(méi)有問(wèn)題.
但是, 有時(shí)候最好明確設(shè)置類(lèi)型. 可能情況如下:
開(kāi)啟仿真模式的 LIMIT 子句(或者其他不能接受字符串操作數(shù)的 SQL 子句)
可能受到錯(cuò)誤操作數(shù)類(lèi)型影響具有特殊查詢(xún)計(jì)劃的復(fù)雜特殊查詢(xún)
特有的列類(lèi)型, 像 bigint boolean 必須綁定精確的操作數(shù)(為了將 BIGINT 綁定為 PDO::PARAM_INT 需要基于 mysqlnd)
這種情況下, 必須使用顯式綁定, 可以從 bindvalue() 和 bindParam() 兩個(gè)函數(shù)中選擇一個(gè). 前者是推薦使用的, 它不像 bindParam()具有一定的副作用.
查詢(xún)可以綁定的部分了解哪些查詢(xún)部分可以使用參數(shù)綁定哪些部分不能使用是非常重要的. 事實(shí)上, 這個(gè)列表是非常短的: 只有字符串和數(shù)字字面量可以被綁定. 只要你的數(shù)據(jù)在查詢(xún)中能被表示為數(shù)字或者帶引號(hào)的字符串, 就可以被綁定. 其他所有情況你不能使用 PDO 預(yù)處理語(yǔ)句: 既不是標(biāo)識(shí)符也不是逗號(hào)分隔列表, 或者是引用的文字字符串的一部分, 或者其他任意查詢(xún)部分都不能使用預(yù)準(zhǔn)備語(yǔ)句綁定
最常見(jiàn)的用例解決方案可以在[本章的響應(yīng)部分查看]()
有時(shí)候你可以使用預(yù)處理多次執(zhí)行準(zhǔn)備好的查詢(xún), 比一次又一次執(zhí)行相同的查詢(xún)快一點(diǎn), 因?yàn)樗唤馕霾樵?xún)一次. 如果可以執(zhí)行另一個(gè)PHP實(shí)例中的預(yù)處理語(yǔ)句, 這個(gè)功能就是非常有用的, 但是事實(shí)并非如此. 只會(huì)在同一個(gè)實(shí)例中重復(fù)相同的查詢(xún), 這在常規(guī)的PHP腳本中很少使用到, 并限制了此功能用于重復(fù)插入和更新.
$data = [ 1 => 1000, 2 => 200, 3 => 200, ]; $stmt = $pdo->prepare("UPDATE users SET bonus = bonus + ? where id = ?"); foreach ($data as $id => $bonus) { $stmt->execute([$bonus, $id]); }
注意這個(gè)功能有點(diǎn)被高估了. 不僅需要討論, 而且性能提升也不是很大 - 查詢(xún)解析有時(shí)候是
很快的. 而且只有在關(guān)閉仿真模式的時(shí)候才能帶來(lái)性能提升.
這些查詢(xún)沒(méi)有什么特別之處, 對(duì)PDO來(lái)說(shuō)他們都是一樣的. 運(yùn)行哪個(gè)查詢(xún)并不重要.
如上所示, 需要準(zhǔn)備帶有占位符的預(yù)處理查詢(xún), 傳入變量并執(zhí)行. DELETE 和 SELECT 的處理過(guò)程是基本相同的. 僅有的不同點(diǎn)是( DML查詢(xún)不會(huì)返回任何數(shù)據(jù)), 你可以使用鏈?zhǔn)椒椒? 調(diào)用 execute() 和 prepare().
$sql = "UPDATE users SET name = ? where id = ?"; $pdo->prepare($sql)->execute([$name, $id]);
然而, 你像獲得影響行數(shù), 代碼將和無(wú)聊的三行代碼相同:
$stmt = $pdo->prepare("DELETE FROM goods where category = ?"); $stmt->execute([$cat]); $deleted = $stmt->rowCount();
更多的例子可以在respective article.找到.
從statement獲取數(shù)據(jù) foreach我們已經(jīng)見(jiàn)過(guò)這個(gè)函數(shù)了, 現(xiàn)在讓我們仔細(xì)看看. 它從數(shù)據(jù)庫(kù)獲取單行數(shù)據(jù), 在結(jié)果集中移動(dòng)內(nèi)部指針, 因此, 對(duì)函數(shù)的后續(xù)調(diào)用將逐個(gè)返回所有行. 這個(gè)方法和 mysql_fetch_array() 大致相同但在工作模式稍微有點(diǎn)不同: 代替很多不同函數(shù)( mysql_fetch_assoc() mysql_fetch_row), 這個(gè)只有一個(gè)方法, 但是它的行為可以通過(guò)一個(gè)參數(shù)改變. 在 PDO 中有很多的獲取模式, 稍后我們?cè)敿?xì)討論, 這里有一些簡(jiǎn)單的實(shí)例:
PDO::FETCH_NUM 返回索引數(shù)組
PDO::FETCH_ASSOC 返回關(guān)聯(lián)數(shù)組
PDO::FETCH_BOTH 以上兩者都包含
PDO::FETCH_OBJ 返回對(duì)象
PDO::FETCH_LAZY 允許三個(gè)(索引數(shù)組, 關(guān)聯(lián)數(shù)組, 對(duì)象)方法沒(méi)有內(nèi)存開(kāi)銷(xiāo).
從上面可以看出, 這個(gè)必須在兩種情況下使用:
當(dāng)只需要一行時(shí), 只獲取一行
$row = $stmt->fetch(PDO::FETCH_ASSOC);
將以關(guān)聯(lián)數(shù)組的方式從語(yǔ)句中獲取一行
當(dāng)我們需要在使用之前處理返回?cái)?shù)據(jù). 在這種情況下, 必須通過(guò)while循環(huán)運(yùn)行, 如上所示.
另一種有用的模式是 PDO::FETCH_CLASS 可以創(chuàng)建一個(gè)特定類(lèi)的對(duì)象
$news = $pdo->query("select * from news")->fetchAll(PDO::FETCH_CLASS, "News");
將生成一個(gè)News類(lèi)對(duì)象的數(shù)組, 并且通過(guò)返回值設(shè)置類(lèi)屬性. 注意這個(gè)模式下:
屬性會(huì)在構(gòu)造方法之前設(shè)置
所有未定義的屬性都會(huì)調(diào)用 __set魔術(shù)方法
如果沒(méi)有 __set方法, 將會(huì)創(chuàng)建新屬性
私有屬性也會(huì)被設(shè)置, 這有點(diǎn)意外但是非常方便
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31055.html
摘要:早在年我就開(kāi)始寫(xiě)這個(gè)類(lèi)庫(kù)了,平時(shí)在工作中,將一些好的想法,一些問(wèn)題的解決方法等融合進(jìn)來(lái),歷時(shí)兩年多,經(jīng)過(guò)不斷的實(shí)踐,我感覺(jué)它已經(jīng)很成熟了,于是它來(lái)到了你面前我的倉(cāng)庫(kù)地址這里假設(shè)有一個(gè)數(shù)據(jù)表,其中為主鍵。 早在2015年我就開(kāi)始寫(xiě)這個(gè)PHP類(lèi)庫(kù)了,平時(shí)在工作中,將一些好的想法,一些問(wèn)題的解決方法等融合進(jìn)來(lái),歷時(shí)兩年多,經(jīng)過(guò)不斷的實(shí)踐,我感覺(jué)它已經(jīng)很成熟了,于是它來(lái)到了你面前! 我的倉(cāng)庫(kù)地...
摘要:原文使用和開(kāi)發(fā)網(wǎng)站應(yīng)用在領(lǐng)域目前看來(lái)新浪云走的比較早,也比較成熟。新浪云需要使用新浪微博的帳號(hào)才能登錄和使用。目前,新浪云需要進(jìn)行實(shí)名認(rèn)證才能創(chuàng)建個(gè)以上的應(yīng)用,所以推薦進(jìn)行實(shí)名認(rèn)證。 原文:使用SAE和Gitcafe開(kāi)發(fā)網(wǎng)站應(yīng)用 在PaaS領(lǐng)域目前看來(lái)新浪云走的比較早,也比較成熟。相比IaaS,PaaS更能為企業(yè)或個(gè)人帶來(lái)成本上的節(jié)約。本文以php為例,記錄了如何在新浪云上注冊(cè)創(chuàng)建自...
摘要:本文主要介紹內(nèi)置函數(shù)的使用,在擴(kuò)展開(kāi)發(fā)中,會(huì)經(jīng)常用到這些內(nèi)置函數(shù),的封裝,使得調(diào)用這些函數(shù)像代碼一樣簡(jiǎn)單。的使用方法與語(yǔ)言的是完全一致的。包括的超全局變量和其他代碼使用關(guān)鍵詞聲明的全局變量。 本文主要介紹PHP-X內(nèi)置函數(shù)的使用,在PHP擴(kuò)展開(kāi)發(fā)中,會(huì)經(jīng)常用到這些內(nèi)置函數(shù),PHP-X的封裝,使得調(diào)用這些函數(shù)像PHP代碼一樣簡(jiǎn)單。 echo 在擴(kuò)展中需要輸出一些內(nèi)容,可以使用echo函數(shù)...
摘要:好啦,我們看看在框架的不同版本中是怎么處理攻擊,注入等問(wèn)題的。那要是,又是怎樣處理的喃考慮目前國(guó)內(nèi)網(wǎng)站大部分采集文章十分頻繁,更有甚者不注明原文出處,原作者更希望看客們查看原文,以防有任何問(wèn)題不能更新所有文章,避免誤導(dǎo)繼續(xù)閱讀 作者:白狼 出處:http://www.manks.top/yii2_filter_xss_code_or_safe_to_database.html 本文版權(quán)...
摘要:而在項(xiàng)目開(kāi)發(fā)中,我們想要的是一個(gè)更好用的可維護(hù)的工具,此時(shí),對(duì)代碼的封裝模塊化就顯得尤為重要,于是出現(xiàn)了兩種方案查詢(xún)構(gòu)造器,對(duì)象關(guān)系映射。典型環(huán)境下按照一般的查詢(xún)構(gòu)造器處理就行。 文章目錄 寫(xiě)一個(gè)特殊的查詢(xún)構(gòu)造器 - (前言) 寫(xiě)一個(gè)特殊的查詢(xún)構(gòu)造器 - (一、程序結(jié)構(gòu),基礎(chǔ)封裝) 寫(xiě)一個(gè)特殊的查詢(xún)構(gòu)造器 - (二、第一條語(yǔ)句) 寫(xiě)一個(gè)特殊的查詢(xún)構(gòu)造器 - (三、條件查詢(xún)) 寫(xiě)一個(gè)特殊...
閱讀 2027·2019-08-30 15:52
閱讀 2990·2019-08-29 16:09
閱讀 1333·2019-08-28 18:30
閱讀 2464·2019-08-26 12:24
閱讀 1109·2019-08-26 12:12
閱讀 2284·2019-08-26 10:45
閱讀 580·2019-08-23 17:52
閱讀 845·2019-08-23 16:03