******* 存取步驟與常用功能介紹 *******

官網資料:http://tw2.php.net/manual/en/book.pdo.php

  1. 先確定系統是否已安裝資料庫對應的函式庫(Unix 為 .so 檔,Windows 為 .dll 檔),
    例如 mysql 為 php_mysqli_libmysql.sophp_mysqli_libmysql.dll 檔,
    (或者舊的 php_mysql_libmysql.so 或 php_mysql_libmysql.dll)
    PostgreSQL 為 php_pgsql.sophp_pgsql.dll
    除了基本的函式庫之外,還要確認是否也安裝了 pdo 函式庫
    例如 mysql 為 php_pdo_mysql_libmysql.sophp_pdo_mysql_libmysql.dll
    PostgreSQL 為 php_pdo_pgsql.sophp_pdo_pgsql.dll
    這些函式庫位於 php.ini 裡面所設定的 extension_dir 目錄內;
    另外也要將 php.ini 內要使用資料庫函式庫的 extension 部份的註解取消,
    也就是將 ;extension=php_*.so 左邊的分號(;)拿掉
    如果是在已啟動 Apache 的狀態下更動 php.ini 檔,記得重新啟動 Apache 才會作用。
  2. DSN 格式為 <協定>:host=<主機>;dbname=<資料庫名稱>
    建立 Connection 物件,
    傳入引數 1 為 DSN,引數 2 為使用者帳號,引數 3 為使用者密碼,如下:
    $conn=new PDO(DSN,user_account,password);
  3. 資料庫處理時可以包在 try 中,發生問題時可以丟給 catch 處理(或更細部處理)
    catch(PDOException $e){},
    PDOException 繼承 Exception 可呼叫一般 method,
    例如 getCode()getMessage()getLine()getFile()
    還有等同於 PDO::errorInfo() method 的 errorInfo 「屬性」可用
    ($e->errorInfo)
    errorInfo 為 array,必須用 array 的操作取出。
  4. 要執行 SQL 指令有三種方式:
    $conn->exec()$conn->query()$conn->prepare()
    若要快速執行 SQL 指令,可以呼叫 $conn->exec(SQL)。會傳回執行成功的筆數
    如果 INSERT,通常是 1,若是 DELETE 或 UPDATE 或 SELECT,筆數可能更多。
    $conn->exec 較適合用來快速下 INSERT、DELETE、UPDATE 指令(不需回傳資料)。
  5. 若要快速執行 SELECT 的 SQL 指令取出 ResultSet,
    可以呼叫 $conn->query(SQL)
    會傳回 ResultSet 物件,然後再自行讀出 ResultSet 裡的個別 row 資料。

    若要用 query() 取出 ResultSet,為了防止 SQL Injection 攻擊產生錯亂,
    最好先將 SQL 指令用 $conn->quote(SQL) 處理過。
    quote() 會處理引號字串相關事宜;
    但是若是要下 INSERT、DELETE、UPDATE 之類的 SQL 指令,
    建議最好還是用 $conn->prepare() 來產生 PreparedStatement。

  6. $conn->prepare(arg1,arg2) 可以執行預儲指令執行增刪修查等四種含變數的 SQL,

    arg1(String)
    傳入要執行的 SQL 指令(字串),但是可以設定變數,在執行期才決定變數的值,
    變數可以用 :name? 兩種 placeholder 來代表,
    常用於 INSERTUPDATEDELETE 等需要多次進行資料處理的 SQL 上;
    要注意的是,SQL 中不可將 ColumnTable Name 等用 placeholder 代替,
    否則會出現「所給的資料數量與 placeholder 數量不符」的奇怪錯誤,
    不管是 MySQL 或 PostgreSQL 都一樣。

    arg2(Hash)
    傳入 key=>value 關聯式陣列型態的 Driver 選項。
    一般會傳入 array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL);
    表示使用 scrollable cursor 以任意移動 ResultSet 的 cursor 位置;
    如果進行 MANIPULATE 的動作(亦即 INSERTUPDATEDELETE)時,
    本參數設定錯誤的話(例如設定為上例的 PDO::ATTR_CURSOR ),
    MANIPULATE 的動作將不會成功(不設定反而可以正常運作)。
  7. $conn->prepare 得到一個 PDOStatement 物件(PreparedStatement),
    如果要指定 SQL 中的變數值,可以透過以下三種方式:

    1. bindParam(param,var,type,length):

      param(String)
      指定 SQL 中變數的名稱,
      如果是 :name 之類的 placeholder,則直接輸入 :name 「字串」,
      字串前面的 “:" 可以省略,加或不加都可以正常執行。
      例如 bindParam(“:room",…) 或 bindParam(“room",…) 都可以,
      “:" 放在 SQL 中是為了代表「這是一個 placeholder」;
      如果是 ? 之類的 placeholder,則是輸入「數值」,第一個 ? 是 1。
      var(&$var)
      指定一個「變數」的值給此 SQL 中的 placeholder。
      一定要指定變數而不能指定 literal 實字,否則會發生錯誤。
      若要指定實字,則使用 bindValue() 指定。
      type(int)
      設定此變數的型態。
      數值型態是 PDO::PARAM_INT字串型態是 PDO::PARAM_STR
      預設為 PDO::PARAM_STR。可參考 PHP 官網 PDO 說明頁的 Constants。
      http://tw2.php.net/manual/en/pdo.constants.php
      即使變數型態非此處所設定的型態,還是可以正常傳入 SQL 中。
      可能用於 Stored Procedure。
      length(int)
      設定此變數的長度。即使傳入的變數長度超過此限,資料還是可以正常傳入 SQL。
      可能用於 Stored Procedure。

      一般使用 bindParam() 只需要傳入前兩個引數就可以了。

    2. bindValue(param,value,type):

      param(String)
      同 bindParam,指定 SQL 中變數的名稱,
      如果是 :name 之類的 placeholder,則直接輸入 :name 「字串」,
      字串前面的 “:" 可以省略,加或不加都可以正常執行。
      例如 bindValue(“:room",…) 或 bindValue(“room",…) 都可以,
      “:" 放在 SQL 中是為了代表「這是一個 placeholder」;
      如果是 ? 之類的 placeholder,則是輸入「數值」,第一個 ? 是 1。
      value(String or int etc.)
      傳入一個 literal 實字,即使傳入變數也可以,
      而 bindParam 第二引數是 by reference (&$var),所以不可以傳入實字。
      type(int)
      設定此變數的型態。
      數值型態是 PDO::PARAM_INT字串型態是 PDO::PARAM_STR
      預設為 PDO::PARAM_STR。可以參考 PHP 官網 PDO 說明頁的 Constants。
      http://tw2.php.net/manual/en/pdo.constants.php
      即使變數型態非此處所設定的型態,還是可以正常傳入 SQL 中。
      可能用於 Stored Procedure。
    3. 設定 array 資料:

      如果是 :name 型態的 placeholder,
      則建立 array(key=>value) 類的關聯式陣列
      如果是 ? 型態的 placeholder,
      則建立 array(value,value,..) 類的索引式陣列
      關聯式陣列內容可以不照順序,但是索引式陣列必須依照 placeholder 順序放置。

  8. $conn->prepare() 設定 SQL,並指定相對於 placeholder 的值之後,
    最後以 $stat->execute() 執行 SQL 指令。
    如果資料是以 bindParam()bindValue() 設定,
    則 execute() 可以不用傳入引數;
    如果將資料包成 array,則把 array 傳入 execute() 中。

    如果有多項指令要執行,可以將資料放到 array 中(含多項 array 型態內容 的 array),
    然後用 foreach 逐項用 bindParam() 或 bindValue() 設定值之後再 execute(),
    或將每一個 array 丟入 $stat->execute() 當引數。

  9. 執行 $stat->execute()
    如果成功,得到的回傳值是 TRUE;如果失敗,則是 FALSE。
    如果執行的是 SELECT 的查詢,則會將查到的資料存入原來的 $stat 中,
    不需要另外用一個變數來接收 ResultSet (也不能這麼做)。
  10. 透過 $stat->prepare() 或 $conn->query(),
    執行 SELECT SQL 所得到的 ResultSet,
    可以用 fetch()fetchAll()fetchColumn() 等取出資料
    (fetchObject() 用於 ORM)

    1. fetch(style,ori,offset):用於取出單一筆資料。

      style(int)
      決定取出 row 成為哪一種型態。一般常用索引型陣列、關聯式陣列、物件三種。

      PDO::FETCH_NUM 可以用索引取出欄位資料如 $row[0]、$row[1] …
      PDO::FETCH_ASSOC 可以用字串取出欄位資料如 $row[“room"]、$row[“name"] …
      PDO::FETCH_BOTH 可以用索引或欄位名稱的字串取出欄位資料
      PDO::FETCH_OBJ 可以用物件表示法取出資料如 $row->room、$row->name …
      PDO::FETCH_LAZY 可以用索引、欄位名稱的字串、物件表示法取出資料

      以上 BOTH 等於 NUMASSOC(association) 的合體,
      LAZY 等於 BOTHOBJ 的合體。如果用 print_r 將資料 dump 出來,
      可以發現 BOTH 會存有兩份相同的資料,只是取用方式不同。

      若用 style 設定為複合式,則同一筆 row 可以用不同方式取出資料,例如:
      print “姓名:{$row[‘name’]},年齡:{$row[1]},電話:".$row->tel;

      ori(int)
      決定下一筆資料的位置。預設為 PDO::FETCH_ORI_NEXT,也就是取出下一筆。
      如果要讓 cursor 可以任意移動,
      則設定為 PDO::FETCH_ORI_ABS(absolute),
      或者 PDO::FETCH_ORI_REL( relative 相對於前一筆),
      前面 prepare() 的 arg2 要傳入
      array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL)。
      可以參考 http://tw2.php.net/manual/en/pdo.constants.php 網址中,
      PDO::FETCH_ORI_* 的部份。
      offset(int)
      如果 prepare() 的 arg2 傳入
      array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL)
      然後第二引數設定為 PDO::FETCH_ORI_ABSPDO::FETCH_ORI_REL
      則可以設定這一引數以任意移動 cursor
      如果是 REL 還可以設定為負數往前移動。

      如果 Driver 不支援 scrollable cursor(如 mysql pdo 不支援),
      則沒有辦法任意移動 cursor 來指定要取出哪一筆 row。(PostgreSQL 支援)

    2. fetchAll(style,arg):用於取出所有 ResultSet 的資料,存成陣列型陣列或物件陣列。

      style(int)
      同 fetch() 的第一引數,決定取出 row 成為哪一種型態。
      雖然可以設定成複合型態 BOTH 或物件型態 OBJ,但是沒辦法設定為 LAZY
      除此之外有個不同於 fetch() 可用的型態 PDO::FETCH_COLUMN
      設定為 COLUMN 的話,可以將 ResultSet 某一個欄位的所有資料存成索引式陣列
      arg(int)
      如果 style 設定為 PDO::FETCH_COLUMN 的話,
      此引數可以設定要取出哪一個欄位。
      欄位索引從 0 開始,第一欄為 0
    3. fetchColumn(col):用於取出「下一筆」資料中某個欄位的值。

      col(int)
      指定要取出的欄位編號,欄位索引從 0 開始。如果沒有傳入值,則表示取第一欄。
      (fetchColumn() 等同於 fetchColumn(0))
      回傳字串形態的資料。如果引數數值超過欄位數範圍,則回傳 null(找不到)。
  11. 只要是透過 PreparedStatement 處理的 INSERT、DELETE、UPDATE、SELECT,
    都可以利用 $stat->rowCount() 取得執行成功的筆數
    (類似 $conn->exec() 的回傳值)。
    (由 $conn->query() 執行 SELECT 取得的 $stat 也可用 rowCount() 得到查詢筆數)
    一般資料庫系統都可以透過 rowCount() 得到 INSERT、DELETE、UPDATE 執行成功的筆數,
    不過有些資料庫系統不支援 rowCount() 取得 SELECT 的執行結果。
    (MySQL 支援 SELECT 的 rowCount(),但是 PostgreSQL 不支援)
  12. 如果在 PDO 物件(Connection)操作中發生錯誤(exec()、query()、prepare() 等),
    可以呼叫 PDO 物件errorCode()errorInfo() 取得錯誤訊息;
    若是在 PDOStatement 物件操作中發生錯誤,
    (bindParam()、execute()、fetch() 等)
    則可以呼叫 PDOStatement 物件errorCode()errorInfo() 取得錯誤訊息。

    errorInfo() 回傳一個錯誤訊息堆疊的 array,
    也可以在 catch 中用 PDOException->errorInfo 取得。
    (為 property 而非 method)。