瀏覽天

1 12 月, 2017

PHP PDO

1.
簡介
簡述:PHP 資料物件 (PDO) 擴展為PHP存取資料庫定義了一個輕量級的一致接口
特色:PDO 提供了一個 資料存取 抽象層,不管使用哪種資料庫,都可以用相同的函數(方法)來查詢和存取資料
2.
連結與管理
連接到 MySQL
1
$dbh new PDO('mysql:host=localhost;dbname=test'$user$pass);
處理連結錯誤
1
2
3
4
5
6
7
8
9
10
try {
    $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
    foreach($dbh->query('SELECT * from FOO') as $row) {
        print_r($row);
    }
    $dbh = null;
} catch (PDOException $e) {
    print "Error!: " . $e->getMessage() . "<br/>";
    die();
}
關閉一個連結
1
$dbh = null;
持續連線
優點:無需每次建立一個新的連結,讓web application 更快
1
2
3
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
    PDO::ATTR_PERSISTENT => true
));
3.
交易與自動提交
四大特性
原子性(Atomicity)
一致性(Consistency)
隔離性(Isolation)
持久性(Durability)
假如資料庫不支援Transactions可運行的程式碼
啟動
1
PDO::beginTransaction()
提交
1
PDO::commit()
倒回
1
PDO::rollBack()
一個交易處理例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try {
  $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
      array(PDO::ATTR_PERSISTENT => true));
  echo "Connected\n";
} catch (Exception $e) {
  die("Unable to connect: " . $e->getMessage());
}
try
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $dbh->beginTransaction();
  $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
  $dbh->exec("insert into salarychange (id, amount, changedate)
      values (23, 50000, NOW())");
  $dbh->commit();
  
} catch (Exception $e) {
  $dbh->rollBack();
  echo "Failed: " . $e->getMessage();
}
4.
預先語句與儲存過程
預先語句的插入
1
2
3
4
5
6
7
8
9
10
11
12
13
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// 插入一行
$name = 'one';
$value = 1;
$stmt->execute();
//  用不同的值插入另一行
$name = 'two';
$value = 2;
$stmt->execute();
預防 SQL 注入攻擊
1
2
3
4
5
6
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
  while ($row = $stmt->fetch()) {
    print_r($row);
  }
}
設定 限制 輸入與輸出 型態
1
2
3
4
5
6
7
8
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");
$value = 'hello';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
// 调用存储过程
$stmt->execute();
print "procedure returned $value\n";
正確的LIKE使用
1
2
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
5.
錯誤與處理
PDO::ERRMODE_SILENT
1
PDO::ERRMODE_SILENT

簡單設置錯誤碼,可使用

1
2
PDO::errorCode()
PDO::errorInfo()
檢查語句與資料庫物件
PDO::ERRMODE_WARNING
主要功能:發出E_WARNING資訊,不中斷應用程式
使用時機:測試期
PDO::ERRMODE_EXCEPTION
主要功能:設置錯誤碼、丟出PDOException異常類別,設置他的屬性來反映錯誤程式碼與錯誤訊息
一個簡單的pdo錯誤處理
1
2
3
4
5
6
7
8
9
10
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
    $dbh = new PDO($dsn, $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
6.
Large Objects
主要用於大量資料,通常是4kb以上,可使用PDO::PARAM_LOB處理
使用PDO::PARAM_LOB顯示一張照片
1
2
3
4
5
6
7
8
9
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET['id']));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
fpassthru($lob);
使用PDO::PARAM_LOB插入一張照片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // 调用某个函数来分配一个新 ID
// 假设处理一个文件上传
// 可以在 PHP 文档中找到更多的信息
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES['file']['type']);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$db->beginTransaction();
$stmt->execute();
$db->commit();
7.

PDO

PDO::beginTransaction

一個transactions簡單的例子
1
2
3
4
5
6
7
8
9
10
11
12
/* 開始一個Transaction,關閉自動提交 */
$dbh->beginTransaction();
/* 更改資料庫架構及資料 */
$sth = $dbh->exec("DROP TABLE fruit");
$sth = $dbh->exec("UPDATE dessert
    SET name = 'hamburger'");
/* 識別出錯誤並倒回更改 */
$dbh->rollBack();
/* 資料庫連接現在返回到自動提交模式 */
PDO::commit
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 開始一個Transaction,關閉自動提交 */
$dbh->beginTransaction();
/* 在全有或全無的基礎上插入多行記錄(全部插入,全部不插入) */
$sql = 'INSERT INTO fruit
    (name, colour, calories)
    VALUES (?, ?, ?)';
$sth = $dbh->prepare($sql);
foreach ($fruits as $fruit) {
    $sth->execute(array(
        $fruit->name,
        $fruit->colour,
        $fruit->calories,
    ));
}
/* 提交更改 */
$dbh->commit();
/* 現在資料庫連接返回到自動提交模式 */

PDO::__construct

建立一個資料庫連結的PDO實例
一個簡單的例子
1
2
3
4
5
6
7
8
9
10
/* Connect to an ODBC database using driver invocation */
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
PDO::errorCode
一個簡單的範例
1
2
3
4
5
/* 引發一个錯誤 -- BONES 資料表不存在 */
$dbh->exec("INSERT INTO bones(skull) VALUES ('lucy')");
echo "\nPDO::errorCode(): ";
print $dbh->errorCode();// 輸出PDO::errorCode(): 42S02
PDO::errorInfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
    echo "\nPDO::errorInfo():\n";
    print_r($dbh->errorInfo());
}
/*
PDO::errorInfo():
Array
(
    [0] => HY000
    [1] => 1
    [2] => near "bogus": syntax error
)
*/
PDO::getAttribute
取回一個資料庫連結的屬性
  1. PDO::ATTR_AUTOCOMMIT
  2. PDO::ATTR_CASE
  3. PDO::ATTR_CLIENT_VERSION
  4. PDO::ATTR_CONNECTION_STATUS
  5. PDO::ATTR_DRIVER_NAME
  6. PDO::ATTR_ERRMODE
  7. PDO::ATTR_ORACLE_NULLS
  8. PDO::ATTR_PERSISTENT
  9. PDO::ATTR_PREFETCH
  10. PDO::ATTR_SERVER_INFO
  11. PDO::ATTR_SERVER_VERSION
  12. PDO::ATTR_TIMEOUT
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
$conn = new PDO('odbc:sample', 'db2inst1', 'ibmdb2');
$attributes = array(
    "AUTOCOMMIT", "ERRMODE", "CASE", "CLIENT_VERSION", "CONNECTION_STATUS",
    "ORACLE_NULLS", "PERSISTENT", "PREFETCH", "SERVER_INFO", "SERVER_VERSION",
    "TIMEOUT"
);
foreach ($attributes as $val) {
    echo "PDO::ATTR_$val: ";
    echo $conn->getAttribute(constant("PDO::ATTR_$val")) . "\n";
}
PDO::getAvailableDrivers
回傳一個可使用的驅動
一個簡單的範例
1
print_r(PDO::getAvailableDrivers());
PDO::inTransaction
檢查是否在一個Transactions內
PDO::lastInsertId
回傳最後插入的id或序列值
PDO::prepare
預先準備語句 回傳一個語句的物件
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$sql = 'SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
$red = $sth->fetchAll();
$sth->execute(array(':calories' => 175, ':colour' => 'yellow'));
$yellow = $sth->fetchAll();
// 執行重複語句
/* Execute a prepared statement by passing an array of values */
$sth $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->execute(array(150, 'red'));
$red $sth->fetchAll();
$sth->execute(array(175, 'yellow'));
$yellow $sth->fetchAll();

PDO::query

執行SQL語句並回傳結果
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getFruit($conn) {
    $sql = 'SELECT name, color, calories FROM fruit ORDER BY name';
    foreach ($conn->query($sql) as $row) {
        print $row['name'] . "\t";
        print $row['color'] . "\t";
        print $row['calories'] . "\n";
    }
/*
輸出
apple   red     150
banana  yellow  250
kiwi    brown   75
lemon   yellow  25
orange  orange  300
pear    green   150
watermelon      pink    90
/*
}
8.
PDOStatement
PDOStatement::bindColumn
使用方法:PDOStatement::bindColumn ( mixed $column , mixed &$param [, int $type [, int $maxlen [, mixed $driverdata ]]] )
參數說明:
  • column:欄位序號(從1開始索引)或欄位名稱。如果使用欄位名稱,注意名稱應該與由驅動回傳的列名大小寫保持一致。
  • param:將綁定到列的 PHP 變數名稱
  • type:通過 PDO::PARAM_* 常數指定的參數的資料類型。
  • maxlen:預分配提示。
  • driverdata:驅動的可選參數。
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function readData($dbh) {
  $sql 'SELECT name, colour, calories FROM fruit';
  try {
    $stmt $dbh->prepare($sql);
    $stmt->execute();
    /* Bind by column number */
    $stmt->bindColumn(1, $name);
    $stmt->bindColumn(2, $colour);
    
    /* Bind by column name */
    $stmt->bindColumn('calories'$cals);
    while ($row $stmt->fetch(PDO::FETCH_BOUND)) {
      $data $name "\t" $colour "\t" $cals "\n";
      print $data;
    }
  }
  catch (PDOException $e) {
    print $e->getMessage();
  }
}
readData($dbh);
PDOStatement::bindParam
绑定一个參數到指定的變數名稱
使用方法:
bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type = PDO::PARAM_STR [, int $length [, mixed $driver_options ]]] )
參數說明:
  • parameter:參數標識符號。對於使用命名佔位符的預處理語句,應是類似 :name 形式的參數名。對於使用問號佔位符的預處理語句,應是以1開始索引的參數位置。
  • variable:綁定到 SQL 語句參數的 PHP 變量名。
  • data_type:使用 PDO::PARAM_* 常數明確地指定參數的類型。
  • length:資料類型的長度。為表明參數是一個存儲過程的 OUT 參數,必須明確地設置此長度。
  • driver_options
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
// 對應的
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour 'red';
$sth $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->bindParam(1, $calories, PDO::PARAM_INT);
$sth->bindParam(2, $colour, PDO::PARAM_STR, 12);
$sth->execute();
PDOStatement::bindValue
參數說明:
  • parameter:參數標識符。對於使用命名佔位符的預處理語句,應是類似 :name 形式的參數名。對於使用問號佔位符的預處理語句,應是以1開始索引的參數位置。
  • value:綁定到參數的值
  • data_type:使用 PDO::PARAM_* 常數明確地指定參數的類型。
差異 bindParamm 與 bindValue
bindParam會將一個PHP變數與SQL綁定,當變數變化時,SQL也會變動,bindValue則不會,只會執行最初綁定的值
PDOStatement::columnCount
回傳執行結果用了幾個欄位
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$dbh = new PDO('odbc:sample', 'db2inst1', 'ibmdb2');
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
/* Count the number of columns in the (non-existent) result set */
$colcount = $sth->columnCount();
print("Before execute(), result set has $colcount columns (should be 0)\n");
$sth->execute();
/* Count the number of columns in the result set */
$colcount = $sth->columnCount();
print("After execute(), result set has $colcount columns (should be 2)\n");
/*
Before execute(), result set has 0 columns (should be 0)
After execute(), result set has 2 columns (should be 2)
*/
PDOStatement::debugDumpParams
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindValue(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
$sth->debugDumpParams();
/*
SQL: [96] SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour
Params:  2
Key: Name: [9] :calories
paramno=-1
name=[9] ":calories"
is_param=1
param_type=1
Key: Name: [7] :colour
paramno=-1
name=[7] ":colour"
is_param=1
param_type=2
*/
PDOStatement::errorCode
一個簡單的範例
1
2
3
4
5
6
/* Provoke an error -- the BONES table does not exist */
$err = $dbh->prepare('SELECT skull FROM bones');
$err->execute();
echo "\nPDOStatement::errorCode(): ";
print $err->errorCode(); // PDOStatement::errorCode(): 42S02
PDOStatement::errorInfo
回傳值
0    SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard).
1    Driver specific error code.
2    Driver specific error message.
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Provoke an error -- the BONES table does not exist */
$sth = $dbh->prepare('SELECT skull FROM bones');
$sth->execute();
echo "\nPDOStatement::errorInfo():\n";
$arr = $sth->errorInfo();
print_r($arr);
/*
PDOStatement::errorInfo():
Array
(
    [0] => 42S02
    [1] => -204
    [2] => [IBM][CLI Driver][DB2/LINUX] SQL0204N  "DANIELS.BONES" is an undefined name.  SQLSTATE=42704
)
*/
PDOStatement::execute
執行一個預備語句
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Execute a prepared statement by binding a variable and value */
$calories = 150;
$colour = 'gre';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour LIKE :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindValue(':colour', "%{$colour}%");
$sth->execute();
// 以陣列執行
$calories = 150;
$colour 'red';
$sth $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories':colour' => $colour));
//使用?執行
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour 'red';
$sth $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->execute(array($calories$colour));
使用in語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Execute a prepared statement using an array of values for an IN clause */
$params = array(1, 21, 63, 171);
/* Create a string for the parameter placeholders filled to the number of params */
$place_holders = implode(',', array_fill(0, count($params), '?'));
/*
    This prepares the statement with enough unnamed placeholders for every value
    in our $params array. The values of the $params array are then bound to the
    placeholders in the prepared statement when the statement is executed.
    This is not the same thing as using PDOStatement::bindParam() since this
    requires a reference to the variable. PDOStatement::execute() only binds
    by value instead.
*/
$sth = $dbh->prepare("SELECT id, name FROM contacts WHERE id IN ($place_holders)");
$sth->execute($params);
PDOStatement::fetch
模式
  1. PDO::FETCH_ASSOC
  2. PDO::FETCH_BOTH
  3. PDO::FETCH_BOUND
  4. PDO::FETCH_CLASS
  5. PDO::FETCH_INTO
  6. PDO::FETCH_LAZY
  7. PDO::FETCH_NUM
  8. PDO::FETCH_OBJ
一個簡單的比較
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
/* 使用 PDOStatement::fetch 風格 */
print("PDO::FETCH_ASSOC: ");
print("Return next row as an array indexed by column name\n");
$result = $sth->fetch(PDO::FETCH_ASSOC);
print_r($result);
print("\n");
print("PDO::FETCH_BOTH: ");
print("Return next row as an array indexed by both column name and number\n");
$result = $sth->fetch(PDO::FETCH_BOTH);
print_r($result);
print("\n");
print("PDO::FETCH_LAZY: ");
print("Return next row as an anonymous object with column names as properties\n");
$result = $sth->fetch(PDO::FETCH_LAZY);
print_r($result);
print("\n");
print("PDO::FETCH_OBJ: ");
print("Return next row as an anonymous object with column names as properties\n");
$result = $sth->fetch(PDO::FETCH_OBJ);
print $result->NAME;
print("\n");
/*
PDO::FETCH_ASSOC: Return next row as an array indexed by column name
Array
(
    [name] => apple
    [colour] => red
)
PDO::FETCH_BOTH: Return next row as an array indexed by both column name and number
Array
(
    [name] => banana
    [0] => banana
    [colour] => yellow
    [1] => yellow
)
PDO::FETCH_LAZY: Return next row as an anonymous object with column names as properties
PDORow Object
(
    [name] => orange
    [colour] => orange
)
PDO::FETCH_OBJ: Return next row as an anonymous object with column names as properties
kiwi
*/
PDOStatement::fetchAll
取得所有的資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
/* Fetch all of the remaining rows in the result set */
print("Fetch all of the remaining rows in the result set:\n");
$result = $sth->fetchAll();
print_r($result);
/*
Array
(
    [0] => Array
        (
            [name] => pear
            [0] => pear
            [colour] => green
            [1] => green
        )
    [1] => Array
        (
            [name] => watermelon
            [0] => watermelon
            [colour] => pink
            [1] => pink
        )
)
*/
fetch 與 fetchAll 差異
抓取單筆資料用fetch 多筆使用fetchAll
PDOStatement::fetchColumn
取得欄位名稱
一個簡單的範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
print("Fetch the first column from the first row in the result set:\n");
$result = $sth->fetchColumn();
print("name = $result\n");
print("Fetch the second column from the second row in the result set:\n");
$result = $sth->fetchColumn(1);
print("colour = $result\n");
/*
Fetch the first column from the first row in the result set:
name = lemon
Fetch the second column from the second row in the result set:
colour = red
*/
PDOStatement::nextRowset
advances to the next rowset in a multi-rowset statement handle
一個簡單的用途
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$sql = 'CALL multiple_rowsets()';
$stmt = $conn->query($sql);
$i = 1;
do {
    $rowset = $stmt->fetchAll(PDO::FETCH_NUM);
    if ($rowset) {
        printResultSet($rowset, $i);
    }
    $i++;
} while ($stmt->nextRowset());
function printResultSet(&$rowset, $i) {
    print "Result set $i:\n";
    foreach ($rowset as $row) {
        foreach ($row as $col) {
            print $col . "\t";
        }
        print "\n";
    }
    print "\n";
}
/*
Result set 1:
apple    red
banana   yellow
Result set 2:
orange   orange    150
banana   yellow    175
Result set 3:
lime     green
apple    red
banana   yellow
*/
PDOStatement::rowCount
回傳受到影響的資料有幾筆
一個刪除影響幾筆資料的簡單範例
1
2
3
4
5
6
7
8
9
10
11
12
/* Delete all rows from the FRUIT table */
$del = $dbh->prepare('DELETE FROM fruit');
$del->execute();
/* Return number of rows that were deleted */
print("Return number of rows that were deleted:\n");
$count = $del->rowCount();
print("Deleted $count rows.\n");
/*
Return number of rows that were deleted:
Deleted 9 rows.
*/
也可以藉由query 來顯示每筆受影響的資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$sql = "SELECT COUNT(*) FROM fruit WHERE calories > 100";
if ($res = $conn->query($sql)) {
    /* Check the number of rows that match the SELECT statement */
  if ($res->fetchColumn() > 0) {
        /* Issue the real SELECT statement and work with the results */
         $sql = "SELECT name FROM fruit WHERE calories > 100";
       foreach ($conn->query($sql) as $row) {
           print "Name: " $row['NAME'] . "\n";
         }
    }
    /* No rows matched -- do something else */
  else {
      print "No rows matched the query.";
    }
}
$res = null;
$conn = null;
/*
apple
banana
orange
pear
*/
PDOStatement::setFetchMode
設定抓取資料的預設模式
一個設為PDO::FETCH_NUM模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$sql = 'SELECT name, colour, calories FROM fruit';
try {
  $stmt = $dbh->query($sql);
  $result = $stmt->setFetchMode(PDO::FETCH_NUM);
  while ($row = $stmt->fetch()) {
    print $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n";
  }
}
catch (PDOException $e) {
  print $e->getMessage();
}
/*
apple   red     150
banana  yellow  250
orange  orange  300
kiwi    brown   75
lemon   yellow  25
pear    green   150
watermelon      pink    90
*/
9.
PDOException
顯示pdo產生的錯誤
類別摘要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PDOException extends RuntimeException {
/* 屬性 */
public array $errorInfo ;
protected string $message ;
protected string $code ;
/* 繼承的方法 */
final public string Exception::getMessage ( void )
final public Exception Exception::getPrevious ( void )
final public int Exception::getCode ( void )
final public string Exception::getFile ( void )
final public int Exception::getLine ( void )
final public array Exception::getTrace ( void )
final public string Exception::getTraceAsString ( void )
public string Exception::__toString ( void )
final private void Exception::__clone ( void )
}
屬性
  • errorInfo:相當於PDO::errorInfo() 或 PDOStatement::errorInfo()
  • message:文本錯誤訊息。用 Exception::getMessage() 來存取。
  • code:SQLSTATE 錯誤碼。用Exception::getCode() 來存取。

文本直送科技新聞: 寶可夢、Hello Kitty聯手代表日本前進2025年大阪世博

首圖 以動漫聞名的日本在2025年世界博覽會中將以近年最具話題性的寶可夢Go(Pokemon Go)的吉祥物皮卡丘以及Hello Kitty,做為代表日本大阪市的吉祥物。 皮卡丘跟Hello Kitty將選為2025年世博代表人物 雖然2020年由杜拜主辦的世博還沒開始,但大阪已經透露將有意願參選2025年的世博主辦城市,稍早日本外交大使和野太郎在自己的T…


色碼表/RGB Color Code

Hex網頁色碼表&顏色代碼選擇器。

#000000 #2F0000 #600030 #460046 #28004D
#272727 #4D0000 #820041 #5E005E #3A006F
#3C3C3C #600000 #9F0050 #750075 #4B0091
#4F4F4F #750000 #BF0060 #930093 #5B00AE
#5B5B5B #930000 #D9006C #AE00AE #6F00D2
#6C6C6C #AE0000 #F00078 #D200D2 #8600FF
#7B7B7B #CE0000 #FF0080 #E800E8 #921AFF
#8E8E8E #EA0000 #FF359A #FF00FF #9F35FF
#9D9D9D #FF0000 #FF60AF #FF44FF #B15BFF
#ADADAD #FF2D2D #FF79BC #FF77FF #BE77FF
#BEBEBE #FF5151 #FF95CA #FF8EFF #CA8EFF
#d0d0d0 #ff7575 #ffaad5 #ffa6ff #d3a4ff
#E0E0E0 #FF9797 #FFC1E0 #FFBFFF #DCB5FF
#F0F0F0 #FFB5B5 #FFD9EC #FFD0FF #E6CAFF
#FCFCFC #FFD2D2 #FFECF5 #FFE6FF #F1E1FF
#FFFFFF #FFECEC #FFF7FB #FFF7FF #FAF4FF
#000079 #000079 #003E3E #006030 #006000
#000093 #003D79 #005757 #01814A #007500
#0000C6 #004B97 #007979 #019858 #009100
#0000C6 #005AB5 #009393 #01B468 #00A600
#0000E3 #0066CC #00AEAE #02C874 #00BB00
#2828FF #0072E3 #00CACA #02DF82 #00DB00
#4A4AFF #0080FF #00E3E3 #02F78E #00EC00
#6A6AFF #2894FF #00FFFF #1AFD9C #28FF28
#7D7DFF #46A3FF #4DFFFF #4EFEB3 #53FF53
#9393FF #66B3FF #80FFFF #7AFEC6 #79FF79
#AAAAFF #84C1FF #A6FFFF #96FED1 #93FF93
#B9B9FF #97CBFF #BBFFFF #ADFEDC #A6FFA6
#CECEFF #ACD6FF #CAFFFF #C1FFE4 #BBFFBB
#DDDDFF #C4E1FF #D9FFFF #D7FFEE #CEFFCE
#ECECFF #D2E9FF #ECFFFF #E8FFF5 #DFFFDF
#FBFBFF #ECF5FF #FDFFFF #FBFFFD #F0FFF0
#467500 #424200 #5B4B00 #844200 #642100
#548C00 #5B5B00 #796400 #9F5000 #842B00
#64A600 #737300 #977C00 #BB5E00 #A23400
#73BF00 #8C8C00 #AE8F00 #D26900 #BB3D00
#82D900 #A6A600 #C6A300 #EA7500 #D94600
#8CEA00 #C4C400 #D9B300 #FF8000 #F75000
#9AFF02 #E1E100 #EAC100 #FF9224 #FF5809
#A8FF24 #F9F900 #FFD306 #FFA042 #FF8040
#B7FF4A #FFFF37 #FFDC35 #FFAF60 #FF8F59
#C2FF68 #FFFF6F #FFE153 #FFBB77 #FF9D6F
#CCFF80 #FFFF93 #FFE66F #FFC78E #FFAD86
#D3FF93 #FFFFAA #FFED97 #FFD1A4 #FFBD9D
#DEFFAC #FFFFB9 #FFF0AC #FFDCB9 #FFCBB3
#E8FFC4 #FFFFCE #FFF4C1 #FFE4CA #FFDAC8
#EFFFD7 #FFFFDF #FFF8D7 #FFEEDD #FFE6D9
#F5FFE8 #FFFFF4 #FFFCEC #FFFAF4 #FFF3EE
#613030 #616130 #336666 #484891 #6C3365
#743A3A #707038 #3D7878 #5151A2 #7E3D76
#804040 #808040 #408080 #5A5AAD #8F4586
#984B4B #949449 #4F9D9D #7373B9 #9F4D95
#AD5A5A #A5A552 #5CADAD #8080C0 #AE57A4
#B87070 #AFAF61 #6FB7B7 #9999CC #B766AD
#C48888 #B9B973 #81C0C0 #A6A6D2 #C07AB8
#CF9E9E #C2C287 #95CACA #B8B8DC #CA8EC2
#D9B3B3 #CDCD9A #A3D1D1 #C7C7E2 #D2A2CC
#E1C4C4 #D6D6AD #B3D9D9 #D8D8EB #DAB1D5
#EBD6D6 #DEDEBE #C4E1E1 #E6E6F2 #E2C2DE
#F2E6E6 #E8E8D0 #D1E9E9 #F3F3FA #EBD3E8
My label0
Reset
Saturation: 100%Slider thumb shadowSlider thumb
Luminance: 50%Slider thumb shadowSlider thumb
Hexadecimal:
RGB:
HSL:

法人看淡 2018 年第 1 季 iPhone X 出貨量,蘋概股供應鏈面臨壓力

首圖 本土法人 1 日出具最新研究報告指出,對於 2018 年第 1 季的蘋果 iPhone X 出貨量將會較 2017 年第 4 季減少 10% 的情況,加上 2018 年下半年將推出的新款 iPhone 的規格設計將近似 iPhone X,造成整體利多有限,恐將延長換機周期的情況下,該法人調降蘋果供應鏈中大立光的評等至「持有」,並且保守看待其他供應鏈廠商包括鴻海、和碩、業成、…


災情持續擴大,全球每天新增300個挖礦網站】黑色產業覬覦瀏覽器挖礦,5億訪客不知電腦變礦工

「天啊!上線8天,用量從每秒10萬次,暴增到每秒1,350萬次。」足足是135倍的爆量成長。瀏覽器挖礦服務Coinhive團隊在官網第一周營運報告中,毫不隱瞞自己的驚訝,也向數百封抱怨伺服器滿載的使用者投訴信,統一致歉。

9月14日,Coinhive剛推出了一項新型態的虛擬貨幣採礦平臺Coinhive,這是一個可在瀏覽器環境,用JavaScript執行球門羅幣(Monero,代號XMR)挖礦計算的服務。上線第一天,每秒只有10萬次挖礦雜奏運算(Hash)的API呼叫,但是,到了第8天,Coinhive平臺累計的挖礦雜奏運算呼叫次數,就爆衝到每秒1,350萬次呼叫,占了門羅幣這個全球第六大虛擬貨幣整體區塊Hash值計算量的5%,同時連上Coinhive的WebSocket連線數,多達220萬個,若以預設值每臺電腦用3個WebSocket連線數估算,相當有73萬臺電腦成了挖礦機。

原本Coinhive平臺所有系統,都部署在單一臺伺服器上,Coinhive也緊急在幾天內擴充到38臺主機規模的分散式叢集(28臺WebSocket代理伺服器、6臺Web伺服器、2臺資料庫伺服器和2臺維運用VPS),才撐住百倍爆量的需求。

但,Coinhive團隊的驚喜,旋即成了全球網民的痛苦。因為許多挖礦電腦的擁有者,並不知道自己的筆電、PC,或是手機,都成了暗中幫陌生人賺錢的挖礦機,CPU運算滿載,系統效能遲緩,甚至連打開文書處理軟體要寫份報告,都要等上好久。

9月15日,全球最大BT種子網站的使用者最先傳出災情,發現每次瀏覽海盜灣首頁,CPU使用率都會突然衝破80%,直到關閉網頁才停止。分析網頁程式碼後發現,海盜灣暗中執行Coinhive程式來挖礦,但沒有坦白告知而引用戶不滿,甚至鬧上媒體。

海盜灣事件讓Coinhive這個瀏覽器挖礦程式聲名大噪,廣大引起的關注,甚至引來了黑色產業的覬覦。趨勢科技研發部資深工程師王博榮指出,看上挖礦程式可以無風險地賺取虛擬貨幣利益,開始有黑色產業濫用Coinhive挖礦程式,將Coinhive挖礦程式惡意植入他人網頁。像趨勢科技就在9月19日時首次發現,惡名昭彰的惡意廣告組織ElTest,展開了植入Coinhive程式偷挖礦的活動,追查之下,早在9月14日,趨勢全球用戶已有人連上了暗藏Coinhive挖礦程式的網頁,最高峰是9月20日,一天內高達7萬次連線。

王博榮表示,觀測到9月底,嵌入Coinhive挖礦程式的網站,45%是英文語系,又以盜版影片網站和部落格為大宗,在臺灣也有幾個部落格上榜,不過多數網站都會首頁告知,一旦連上該網頁,就會用使用者的CPU來挖擴,若不願意提供CPU資源也可關閉,讓使用者選擇。

不過,依照趨勢科技的統計,全球每天至少增加300個藏有Coinhive挖礦程式的網站,甚至,95%以上的網站都未經用戶同意或告知,就開始偷偷挖礦。

依照趨勢的監控,黑色產業最初利用Coinhive的事件是發生在9月19日,EITest利用技術詐騙網頁的方式,讓用戶在不知情的情況下,淪為礦工,王博榮指出,這類的攻擊手法,是透過網站伺服器上執行的軟體系統,辨識造訪網頁用戶的電腦軟體漏洞,並藉機植入惡意程式。

舉例來說,駭客會發送偽裝成微軟技術解決的提醒視窗,提醒用戶的電腦系統,已經被惡意軟體侵害,但是用戶卻無法關閉該視窗,接著,該網頁就就會從Coinhive的伺服器上,下載JavaScript的挖礦腳本,並偷偷將用戶的電腦,變成一個礦工。

黑色產業開始覬覦,3周災情就蔓延全球

黑色產業覬覦瀏覽器挖礦,災情越演越烈。知名廣告過濾服務AdGuard展開了一項全球性的網站清查,緊急在10月12日公布了結果。AdGuard發現,海盜灣網站不是特例,Alexa排名前10萬的網站,有220個網站,暗藏了挖礦程式,其中最大者是在法國排名70大的檔案分享平臺Uptobox,每月訪客數多達6,000萬人次,臺灣也有4個網站上榜。這批網站每月平均訪客,累計多達5億人次,若不考慮一人造訪多個網站的重複情況,可以說,全球這5億名訪客的電腦,都成了挖礦機,在瀏覽這批網站時,偷偷地暗中挖礦。

這些網站大多沒有告知使用者,就像是偷用使用者的瀏覽器來挖礦,像Uptobox技術長緊急出面解釋,暗中執行挖礦程式而未告知,是為了測試新的營收模式,事件曝光後,也承諾會改回原來的網路廣告模式。

綁架10萬臺PC的瀏覽器挖礦,能獲利多少?

根據Check Point估計,目前每天約可以挖出720個門羅幣區塊,獎勵金合計有4,464個門羅幣。而一般PC的算力,啟用4個瀏覽器執行緒時,每秒可以計算出45個Hash值,相較於全球門羅幣的算力是每秒2億6,612.2萬個Hash,按算力比例,一臺PC執行一天可以賺到0.000754個門羅幣。以10月底門羅幣每顆約88.46美元的幣值,挖礦網站若能綁架10萬臺PC,連續執行1天挖礦程式,就可以不勞而獲,賺到相當於臺幣20萬元的收入。

暗中測試這種靠用戶挖礦獲利的網站,不只Uptobox,AdGuard後來還發現,美國知名CBS媒體集團旗下,有百萬用戶的影音串流服務Showtime.com網站也藏了Coinhive程式,直到事件曝光後就移除了,但Showtime拒絕說明這是主動測試還是網站遭駭。

挖礦綁架植入手法開始多元化

這種綁架使用者瀏覽器暗中挖礦的行為,開始出現一個專有名詞「挖礦綁架」(Cryptojacking)來形容,成了專家資安威脅清單上最新一項威脅,趨勢科技、Fortinet、Check Point等資安業者相繼投入研究,甚至開始攔截,或列入要阻擋的惡意威脅清單。

如廣告過濾軟體如AdGuard開始提供阻擋機制,而防毒軟體Avast則是當用戶瀏覽這類網站時,直接阻擋挖礦程式碼。Check Point也將這類挖礦綁架的網頁視為重大威脅,在自家安全閘道器產品中列入封鎖名單。更有電信業者,如中華電信開始將Coinhive網址列入企業資安黑名單,禁止用戶瀏覽。CDN業者Cloudflare也開始封鎖那些擅自利用使用者電腦資源採礦的帳號與服務。Coinhive官方更因產品遭濫用而出面道歉,並推出了會強制跳出告知說明的新版JavaScript挖礦程式,但,舊版本仍持續服務。

不過,瀏覽器挖礦技術還在持續變化,而挖礦綁架手法也開始進化,一來開始出現更多種類的挖礦程式,除了Coinhive,還有JSEcoin、CryptoLoot、MineMyTraffic,以及10月底出現的Papoto,有意用瀏覽器挖礦者開始有更多選擇,而資安黑名單要封鎖的挖礦程式網址,也越來越多,甚至防不勝防。

挖礦腳本程式植入手法越來越多元,先前大多是藏在網頁頁尾程式碼中,但最近,網站安全公司Sucuri就發現了好幾種新手法,一來是將Coinhive程式碼加密,降低被發現的機率,另一種是直接植入知名部落格平臺WordPress核心網頁,如管理模組標頭網頁admin-header.php,或通用範本檔general-template.php中,第三種Sucuri發現的新手法則更難偵測,挖礦腳本程式直接寫入了開源電商平臺Magento的資料庫,當你打開購物車程式,從資料庫取出的不是訂單資訊,而是要來綁架瀏覽器的挖礦程式。而資安公司Check Point則發現,有影音網站跳出的Flash Video Player安裝提醒視窗中,也會暗中執行挖礦程式。甚至,WordPress已有一項挖礦免費擴充套件,直接安裝就可以啟用瀏覽器挖礦功能,來偷用用戶的CPU資源。王博榮表示,這款套件預設是不開啟使用者告知機制。趨勢科技甚至發現了多款含有採礦能力的行動App,利用內建Webview動態載入JavaScript與原生程式碼注入來閃避偵測,其中有兩款App用的就是Coinhive的挖礦程式。

如何避免自家電腦淪為礦工?

一般用戶可以開啟系統監控CPU使用率的工具,是否連上特定網頁時,CPU使用率突然飆高,關掉網頁後卻又恢復正常,就可能藏有挖礦程式。或使用Chrome瀏覽器時,按下F12打開開發者工具,查看Network功能分頁,若開啟的網頁藏有挖礦程式,會出現異常網路流量,或有不尋常的wasm檔案。

而在企業端,王博榮建議,因挖礦網站須連回Coinhive伺服器才能進行挖礦作業,因此,企業只要封鎖Coinhive網址的連線行為,就能阻止。目前也有廣告過濾程式如AdBlock Plus和AdGuard可以直接阻擋Coinhive的JavaScript程式,Chrome瀏覽器也出現了幾款專門封鎖挖礦行為的外掛,如AntiMiner、No Coin、MinerBlock等。

而對網站開發人員而言,王博榮提醒,需注意所用的第三方套件,最好用程式碼掃描工具,過濾挖礦程式的API,或是避免出現挖礦程式函式庫的連結,也要避用剛推出而未有大量使用者的套件,尤其是整合多功能的複雜套件,應更謹慎。文☉王宏仁、何維涓