[PHP] mysqli, PDO / password_*
MySQL 접속에 사용할 수 있는 API는 ``php mysql, mysqli, PDO`` 세 가지다.
`` mysql``보다는 오브젝트로 관리할 수 있는 ``php mysqli, PDO``를 사용하는 것이 좋다.
* query 결과가 저장된 변수를 그냥 ``php print $result``하면 안되고, 함수를 사용해야 한다.
mysqli
$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) 형태로 넘긴다.
```php
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을 던지기 때문에 ``php try-catch``와 함께 사용하는 편이 좋다.
#1
DB 접속과 이후 작업을 한꺼번에 ``php try-catch``에서 처리하기 위해 에러가 발생했을 때 exception을 던지도록 설정을 변경하는 것이 좋다.
``php $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를 지원할 때만 사용할 수 있으며 지원한다면 사용하는 것이 좋다.
``php $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);``
``php PDO::ATTR_EMULATE_PREPARES`` 기본 옵션은 ``php true``인데, PDO가 알아서 쿼리 내부의 변수를 escape하고, concatenate하게 된다. 즉, prepared statement가 동작하는 것 처럼 모방한다.
그러나 prepared statement가 동작하는 것과는 근본적으로 다르기 때문에, native prepared statement를 사용하려면 옵션을 ``php false``로 변경해야 한다.
prepared statement를 모방하도록 두면(``php true``) 특정 환경에서 SQL injection 가능성이 존재한다.
e.g., artificially constructed multi-byte connection encoding situations
보통 prepared statement를 안쓰는 경우 SQL에 변수를 포함하려면, 이런 식으로 작성하게 된다.
```php
$sql = "SELECT ... WHERE foo = '$bar' and pw = '$pw'";
```
이 때 ``php $bar, $pw``는 반드시 필터링된 데이터여야 하며 `` mysqli_real_escape_string``를 사용해 적절히 escape시켜줘야 한다.
prepared statement를 사용하면 완전히 다른 방식으로 dynamic value를 query에 포함시킬 수 있다.
옵션에 ``php false``를 주고 이런 식으로 사용한다.
```php
$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'];
}
```
이렇게 ``php $sql``의 형식을 미리 지정해 놓고, 추후 대입되는 값을 placeholder로 처리하기 때문에 주석처리 등이 불가능해 상대적으로 injection 등에 안전하다.
named parameter는 `` :name``
unspecified parameter는 `` ?``이며 `` ?``는 ``php bindValue()``시 순서대로 `` 1, 2, 3, ...``을 넣으면 된다.
변수 bind에는 ``php bindValue() / bindParam()``을 사용한다. ( link )
PDO를 사용하면 트랜잭션도 간단히 구성 및 실행할 수 있다.
* 트랜잭션은 원자성을 보장하는 작업 단위다. 즉 전체가 성공적으로 실행되지 않는 한 아무것도 실행되지 않음을 보장하는 SQL 모음이라고 할 수 있다.
password_*
- ``php password_hash()`` : 패스워드 해시. `` PASSWORD_DEFAULT``를 지정하면 bcrypt 해싱 알고리즘을 사용하게 된다. 또는 직접 ``php crypt()``를 사용해도 된다.
- ``php password_verify()`` : 패스워드 검증.
- ``php password_needs_rehash()`` : 적용된 해싱 알고리즘이 최신인지 확인. 사용하는 것이 좋다.
'Languages & Frameworks > PHP' 카테고리의 다른 글
[PHP] File IO (0) | 2017.11.11 |
---|---|
[PHP] SQL Escape & Bypass (0) | 2017.10.23 |
[PHP] 함수, 클래스, 객체, 상속, 트레이트 (0) | 2017.06.10 |
[PHP] form tag, GET POST / cookie, session (0) | 2017.06.10 |
[PHP] HTTP response header 수정 ( redirect, Download Dialog ) (0) | 2017.06.08 |