일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 27 | 28 | 29 | 30 | 31 |
- 이클립스 코틀린
- 도즈모드
- Android
- access modifier
- utf8 인코딩
- kotlin plugin
- nginx 한글 깨짐
- gitemoji
- was server
- 안드로이드
- doze mode
- git commit 해쉬
- 에뮬레이터 에러
- git 안드로이드 스튜디오 연동
- aws 리전 변경
- E212: Can't open file for writing
- 안드로이드 스튜디오 에러
- error while loading state for instance 0x0 of device 'goldfish_pipe
- aws 느림
- toastmessage
- 코틀린 플러그인
- AWS
- 자바
- BuildConfig
- ppk to pem
- git
- git 저장소
- nextInt()
- 탄력적 ip
- basic toast
- Today
- Total
리얼라이져의 마케팅, 개발, 창업 블로그
PHP - PDO 정리 및 적용 본문
그동안 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 코드에 악성 코드가 있는지 확인 해야됨