엄범


MySQL 접속에 사용할 수 있는 API는 ``php mysql, mysqli, PDO`` 세 가지다.

`` mysql``보다는 오브젝트로 관리할 수 있는 ``php mysqli, PDO``를 사용하는 것이 좋다.

* query 결과가 저장된 변수를 그냥 ``php print $result``하면 안되고, 함수를 사용해야 한다.


mysqli

```php

$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()`` : 적용된 해싱 알고리즘이 최신인지 확인. 사용하는 것이 좋다.