리얼라이져의 마케팅, 개발, 창업 블로그

PHP - PDO 정리 및 적용 본문

IT/PHP

PHP - PDO 정리 및 적용

nadadhl 2020. 6. 27. 20:34
728x90

그동안 php를 사용하는데 있어서, 

나는  아래와 같이 많이 사용했다.

$conn = mysqli_connect("localhost","db아이디","디비패스워드","testdb");
$newpassword=$_POST['Data1'];
$email =$_POST['Data2'];

$update="UPDATE checkemail SET password ='$newpassword' where email ='$email'";//해당학생 메일의  패스워드를 바꿔줌
$result1=mysqli_query($conn,$update);//바꾸기  쿼리 ㄱ






 

 

그런데,  여기서 문제가 될수 있는 점은 다음과 같다.

1. db connection을  mysqli api로 사용했다.  

2. 쿼리문에  입력받은 변수(email)가  바로 대입되어있다.

 

우선,  1번의 경우는, 

mysql 만  사용하게 되면,  상관 없지만 새로운 db(mysql 이  아닌)

사용할때,  해당 db에 맞는  api들로  다시  바꿔줘야 된다..

 

즉, 그동안  db에  종속 된  코드를  썼던 것...

 

2번은 SQL injection  문제가 있다. 

 

SQL Injection이란?

쿼리문에  악의적인  sql문을  주입하여,  개발자가 의도한 바와  다른 결과를

내도록  조작하는 일종의 해킹이다.

 

내가 원래 쓰던 방식 대로 하게 되면, 

해커가  악의적인  sql문을 그대로  내 쿼리문에 적용할수 있는 확률인 높아진다.

 

 

그럼 어떻게 해야 할까??

 

PHP 에서  제공하는  PDO(PHP Data Objects) 를 사용하면 된다. 

 

 

 

PDO란??

 

pdo란  php에서 제공하는  여러db를  같은 방법으로  접근하게 해주는  확장 모듈이다.

(원래는 pdo를  안쓰면,  각가 php는  각각  데이터 베이스 별로  확장 모듈을 가지고 있음)

여러 종류의  데이터 베이스를  같은 방식으로  사용할수 있게 해주고,

PDO Statement 라는  일종의  prepare statement를  제공해  데이터 바인딩을 지원한다.

이 데이터 바인딩을  통해  sql 인젝션을  막고,   쿼리문에 대하여,  캐시가 되기 때문에  성능 향상에도 도움이된다.

 

 

그럼 기본적으로  pdo의  사용법을  알아보자.!

 

우선 db connection부분이다.

$mysql_hostname = 'localhost';
$mysql_username = 'root';
$mysql_password = 'sssddd456852';
$mysql_db_name = 'used_market_project_db';
$mysql_charset = 'utf8';

$dsn = 'mysql:host='.$mysql_hostname.';dbname='.$mysql_db_name.';charset='.$mysql_charset;

try{

    $pdo = new PDO($dsn, $mysql_username, $mysql_password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "Connected successfully </br>";

} catch (PDOException $e) {

    echo 'Connect failed : ' . $e->getMessage() . '';
    
}


 

이  dsn 부분에서는  현재 사용중인  db를  명시하고  사용하면, 된다.

현재 사용중인 db는 mysql 이기 때문에 "mysql:"를 적어줌.

 

기존에  각 db별  새로운 api를  사용하는 거에 비해  매우  간단해짐을 알수 있다.

$dsn = 'mysql:host='.$mysql_hostname.';dbname='.$mysql_db_name.';charset='.$mysql_charset;

 

 

 

그리고 이렇게  try catch부분으로  exception관리를 할수 있음.

try{

    $pdo = new PDO($dsn, $mysql_username, $mysql_password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "Connected successfully </br>";

} catch (PDOException $e) {

    echo 'Connect failed : ' . $e->getMessage() . '';
    
}

 

 

PDOstatement를 사용해서 데이터 바인딩 가능

 

//PDO db 연결 파일
include('DbConnection/dbcon.php');

$name="shawn";
$select_data_stmt = $pdo->prepare('SELECT * FROM test_table WHERE name= :name');
$select_data_stmt->bindValue(':name',$name,PDO::PARAM_STR);
$select_data_stmt->execute();
$result = $select_data_stmt->fetch(PDO::FETCH_ASSOC);
   
print_r($result);

 

기존에 는  select 구문을 쓸때

 

select*from test_table where name='$name'; 으로 사용 했다면, 

현재는  prepare ()로  'SELECT * FROM test_table WHERE name= :name'  쿼리문을  정의하고,

 

나중에 아래처럼 값을  대입해준다. 

$select_data_stmt->bindValue(':name',$name,PDO::PARAM_STR);

 

이렇게 하면, sql 문과  데이터를  명확히  구분할수 있어서,  sql 문에  해커가  따로 쿼리문을  적을수가 없다.

sql 인젝션 방어 가능

 

그리고, 준비 구문이  정의되고  데이터만  바뀌면서  여러번 실행할수 있기 때문에 

클라이언트,  서버 양쪽에서  쿼리 계획(sql문) 및  메타 정보(데이터)를  캐시할수 있어  

성능이  향상 된다.

 

 

PDO 사용법  간단 정리

 

1. 이렇게 데이터 베이스  접속 하려면,  PDO 객체를  생성해야됨.

 

아래처럼  dsn부분에 각 db 관련  정보 입력후  ,  그뒤  모든 db  마찬가지로  db 유저명과  패스워드를  쓰면 됨.

<?php

      $dsn = "mysql:host={$dbHost};dbname={$dbName};charset={$dbChar}";

?>

<?php

      $pdo = new PDO($dsn$dbUser$dbPass);

?>

 

 

2.   prepare (이 안에  sql 문  정의)

      sql문에는 왠만하면,  ? 보단 이름 사용하자.

      ?로하면   순서를 사용해서  바인딩 해야되는데,  값이 많아지면,  더  비교 어려워짐.

      이름( ex -> :name )이  명시적이고 좋음.


<?php

      $name = "이름";

      $email = "이메일";



      // 이름 붙인 파라미터를 사용할 때

      $pdoStatement = $pdo -> prepare("SELECT * FROM member WHERE name = :name OR email = :email");

      $pdoStatement = bindValue(":name", $name);

      $pdoStatement = bindValue(":email", $email);



      // 물음표를 사용할때 파라미터가 많아지면 물음표(?)의 경우 순서를 파악하기 어려워 진다.

      $pdoStatement = $pdo -> prepare("SELECT * FROM member WHERE name = ? OR email = ?");

      $pdoStatement = bindValue(1, $name);

      $pdoStatement = bindValue(2, $email);





?>

 

 

 

3.  bindValue  vs  bindParam

 

pdo statement 중에  데이터 바인딩을  위한 메소드가 있는데,

bindValue 와  bindParam이다.

 

이둘의  차이점은  다음과 같다.

 

bindValue의 경우는  그 값이 그대로 들어간다. 

 

예를들어

$name= "이동훈";  

$stmt -> bindValue(":name", $name);

이런 코드가 있다고 할때, 

 

$name에  들어간  "이동훈"이라는 값이   대입된다. 

그래서 데이터 바인딩 후, 변수에 다른 값을  넣었을때,  그 값은  바뀌지 않는다.

 

ex)

$name= "이동훈"; 

$stmt -> bindValue(":name", $name);

$name="차길호";

 $stmt -> execute();//쿼리문 실행

 

이때, 이동훈이라는 값이 바인딩 되었으므로,  아무리  변수값을 바꿔도  바인딩된  값으로 "이동훈"이 들어가  

쿼리가 실행된다.

 

반면, bindParam의 경우는  그 변수가 바인딩 된다. 

ex)

$name= "이동훈"; 

$stmt -> bindParam(":name", $name);

$name="차길호";

$stmt -> execute();//쿼리문 실행

 

이때,  $name이라는 변수가  binding 된것이므로,  실행전  변수값이 바뀌면,  

바뀐  값이  쿼리문에 실행된다. 

 

 

4. 결과 값  가져오기

 

fetch() vs  fetchAll()

 

fetch의 경우는  가장  처번째  행을 가지고온다.

반복문을 사용해서 모든 행 가져오는 처리 가능.

 

fetchAll()의  경우는 모든 행을 가지고 올수 있음.

-이거 조회수가 많을때 쓰면, 서버 메모리 부족 올수도 있음. 

 

5. 모드 지정

 

fetch()나  fetchAll() 에 사용하는 예약 상수들이 있음.

 

default mode -> PDO::FETCH_BOTH => 숫자 인덱스를 배열의 키로반환  + 컬럼 명도  배열의 키로 반환

그러면, 이제  $result['컬럼명'] or  $result[index]  둘다 되는 거임.

 

 

PDO::FETCH_ASSOC => 컬럼명으로만  배열의  키로 반환 -> $result['컬럼명']만 사용이 가능하다. 

 

PDO::FETCH_NUM => 숫자 인덱스로  배열의 키를 반환한다. -> $result[0];

 

PDO::FETCH_OBJ =>  객체로 반환  =>  $result -> name 이렇게 

 

PDO::FETCH_CLASS =>  지정한 클래스의  객체로   -> 이건  나중에 쓸거 같아서  예제 코드 아래 첨부

 


<?php

	class iDol {

		private $idx;

		private $name;

		private $position;

		private $group;

		private $company;

		private $birthDay;



		public function getIdx() {

			return $this -> idx;

		}



		public function getName() {

			return $this -> name;

		}



		public function getPosition() {

			return $this -> position;

		}



		public function getGroup() {

			return $this -> group_name;

		}



		public function getCompany() {

			return $this -> company;

		}



		public function getBirthDay() {

			return $this -> birthDay;

		}

	}

?>

 


<?php

        $dbHost = "localhost";

        $dbName = "test_db";
        $dbUser = "tester";
        $dbPass = "1q2w3e";
        $dbChar = "utf8";

        $dsn = "mysql:host={$dbHost};dbname={$dbName};charset={$dbChar}";
        $pdo = new PDO($dsn, $dbUser, $dbPass);

        $stmt = $pdo -> prepare("SELECT * FROM girl_group WHERE name = :name");
        $stmt -> bindValue(":name", "다현");
        $stmt -> execute();
    

	$stmt -> setFetchMode(PDO::FETCH_CLASS, "iDol");

	$row = $stmt -> fetch();



	echo $row -> getGroup();

	echo "<br/>";

	echo $row -> getName();

	echo "<br/>";

	echo $row -> getPosition();

	echo "<br/>";

	echo $row -> getCompany();

?>


 

 

 

이런식으로

 

 

 

 

이렇게  PDO 정리 끝~  

나중에 추가할거 있으면 추가하기

 

 

아 추가적으로 DB연결 할때 

$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
이거  -> PDO::ATTR_ERRMODE->  PDO객체가 에러 처리 방식 결정한다. 

PDO::ERRMODE_EXCEPTION -> ECEPTION을 던지는걸로 처리하겠다 -> try catch 구문 사용. 

 

 

그리고

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false); 

 

이거 있는데,  이거 true일경우  서버로  sql문 이렇게 가짐. 

"INSERT into Customers (CustomerName, ContactName) VALUES ('Cardinal', 'Tom B. Erichsen')"

 

하지만, false 인경우

"INSERT into Customers (CustomerName, ContactName) VALUES (:cus_name, :con_name)"

 

 즉,  true인경우  매개 변수가 있는 쿼리의 보안이  적용되지 않아 보임. 

false를 해주는게 낳을듯

 

하지만,  sql server에서  몇몇  지원하지 않는  매개 변수를 넘길때는  true를  해주면,  무시 할수 있다고함. 

이때는 sql 코드에  악성  코드가 있는지 확인 해야됨

 

 

 

[PHP] PDO 사용법 정리

참고 : 바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서 ■ PDO(PHP Data Objects) 란?  -. PDO(PHP Data Objects)는 여러 종류의 데이터베이스를 같은 방식으로 사용할 수 있게 해준다.  -. 그리고 PDOStat..

magic.wickedmiso.com

 

 

php 에서 PDO vs MySQLi vs MySQL

1. PDO 대 MySQLi 대 MySQL 우리 모두 알고 있듯이, MySQL은 SQL (Structured Query ...

blog.naver.com

 

Comments