Post

(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() : 적용된 해싱 알고리즘이 최신인지 확인. 사용하는 것이 좋다.
This post is licensed under CC BY 4.0 by the author.