(PHP) mysqli, PDO / password\_\*
MySQL 접속에 사용할 수 있는 API는 mysql, mysqli, PDO
세 가지다. mysql
보다는 오브젝트로 관리할 수 있는 mysqli, PDO
를 사용하는 것이 좋다. * query 결과가 저장된 변수를 그냥 print $result
하면 안되고, 함수를 사용해야 한다.
mysqli
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
$mysqli = new mysqli('localhost', 'sample', '123qw', 'sampledb');
if($mysqli-> connect\_error){
print "<br><br>===================<br><br>";
die('Connect Error:('.$mysqli->connect\_errno.') '.$mysqli->connect\_error);
}
print 'Connect Success';
$sql = "SELECT \* FROM blind\_test WHERE id= '$id' and pw = '$pw'"; // 반드시 ''로 감싸줄 것.
$result = $mysqli->query($sql);
// $result->fetch\_array();
if (!$result){
echo $mysqli->error; // 프로덕션 환경에서는 주석처리한다.
die();
}
else {
echo "<pre>";
print\_r($result->fetch\_all());
echo "</pre>";
}
$result->free();
$mysqli->close();
PDO
PDO(PHP Data Object)는 C로 개발된 확장 모듈이다. 안정성이 좋으며 쿼리를 캐싱하기 때문에 같은 쿼리를 반복하는 경우 빠르다. 이식성에서도 이점이 있는데, PDO는 드라이버를 변경할 수 있기 때문에 PostgreSQL, SQLite 등 여러 데이터베이스에 사용할 수 있다. 인수는 DNS(Data Source Name) 형태로 넘긴다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
$pdo = new PDO('mysql:host=localhost;dbname=sampledb;charset=utf8', 'sample', '123qwe');
$pdo->setAttribute(PDO::ATTR\_ERRMODE, PDO::ERRMODE\_EXCEPTION); // #1
$pdo->setAttribute(PDO::ATTR\_EMULATE\_PREPARES, false); // #2
print('PDO Connection Success<br>');
} catch (PDOException $eException) {
die('Connection Error : '.$Exception->getMessage());
}
$pdo = null; // disconnect
PDO 객체를 생성하며 DB에 접속할 때 실패하면 exception을 던지기 때문에 try-catch
와 함께 사용하는 편이 좋다.
#1
DB 접속과 이후 작업을 한꺼번에 try-catch
에서 처리하기 위해 에러가 발생했을 때 exception을 던지도록 설정을 변경하는 것이 좋다. $pdo->setAttribute(PDO::ATTR\_ERRMODE, PDO::ERRMODE\_EXCEPTION);
#2 prepared statement
https://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not 읽어볼 것.
이는 DB가 native prepared statement를 지원할 때만 사용할 수 있으며 지원한다면 사용하는 것이 좋다. $pdo->setAttribute(PDO::ATTR\_EMULATE\_PREPARES, false);
PDO::ATTR\_EMULATE\_PREPARES
기본 옵션은 php true
인데, PDO가 알아서 쿼리 내부의 변수를 escape하고, concatenate하게 된다. 즉, prepared statement가 동작하는 것 처럼 모방한다. 그러나 prepared statement가 동작하는 것과는 근본적으로 다르기 때문에, native prepared statement를 사용하려면 옵션을 false
로 변경해야 한다. prepared statement를 모방하도록 두면(true
) 특정 환경에서 SQL injection 가능성이 존재한다. e.g., artificially constructed multi-byte connection encoding situations
보통 prepared statement를 안쓰는 경우 SQL에 변수를 포함하려면, 이런 식으로 작성하게 된다.
1
2
$sql = "SELECT ... WHERE foo = '$bar' and pw = '$pw'";
이 때 $bar, $pw
는 반드시 필터링된 데이터여야 하며 mysqli\_real\_escape\_string
를 사용해 적절히 escape시켜줘야 한다.
prepared statement를 사용하면 완전히 다른 방식으로 dynamic value를 query에 포함시킬 수 있다. 옵션에 false
를 주고 이런 식으로 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$sql = 'SELECT ... WHERE foo = :bar and pw = :pw';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':bar', $bar);
$stmt->bindValue(':pw', $pw);
$stmt->execute();
또는
$stmt->execute(array(':bar' => $bar)); 도 가능하다.
while (($result = $stmt->fetch(PDO::FETCH\_ASSOC)) !== false) {
echo $result['email'];
}
이렇게 $sql
의 형식을 미리 지정해 놓고, 추후 대입되는 값을 placeholder로 처리하기 때문에 주석처리 등이 불가능해 상대적으로 injection 등에 안전하다.
named parameter는 :name
unspecified parameter는 ?
이며 ?
는 bindValue()
시 순서대로 1, 2, 3, ...
을 넣으면 된다. 변수 bind에는 bindValue() / bindParam()
을 사용한다.( link )
PDO를 사용하면 트랜잭션도 간단히 구성 및 실행할 수 있다. * 트랜잭션은 원자성을 보장하는 작업 단위다. 즉 전체가 성공적으로 실행되지 않는 한 아무것도 실행되지 않음을 보장하는 SQL 모음이라고 할 수 있다.
password_*
password\_hash()
: 패스워드 해시. ` PASSWORD_DEFAULT를 지정하면 bcrypt 해싱 알고리즘을 사용하게 된다. 또는 직접
crypt()`를 사용해도 된다.password\_verify()
: 패스워드 검증.password\_needs\_rehash()
: 적용된 해싱 알고리즘이 최신인지 확인. 사용하는 것이 좋다.