728x90

대체 MVC가 뭐지?

MVC(Model-View-Controller)는 소프트웨어 공학에서 사용하는 아키텍쳐 패턴이다.

PHP 코드를 작성할 때 Data 와 Design, Controller 를 분리시켜 만드는 것이다.

개발하는데 복잡하고 어려워도 확장성이 좋고 수정/유지보수하기가 편하다.

 

Framework(프레임워크)는 Web 애플리케이션을 개발할 때 많이 사용하는 기능이나 일상적인 구조를 정리한 것이다. 대부분의 프레임워크는 MVC의 구조를 채용한다.

 

뭐 대충 이렇게 정의하고 있다.

- Model : application 의 정보(데이터) → 데이터의 생성, 접근, 수정, 삭제하는 부분

             데이터의 값과 상태 및 각종 기능을 처리하는 Business Logic 부분과 사용되는 데이터 부분으로 구성

             Controller 에서 건네준 요청을 처리하고 다시 돌려준다.

             모델은 데이터의 가공이나 저장, 비즈니스 로직 등의 프로그램 처리를 담당한다.

- View : User Interface (HTML, CSS 로 만든 정적인 페이지 부분)

            화면 UI 부분이며 사용자게게 응답 처리 결과를 보여준다.

            뷰는 필요에 따라 모델의 데이터 내용을 참조하고 사용자에게 화면을 보여준다.

- Controller : Data 와 Business Logic 사이의 상호동작을 관리한다.

                   Model 과 View 사이에서 request 와 response를 관리하며 동작한다.

                   컨트롤러는 사용자가 조작하면 모델이나 뷰에 필요한 지시를 보낸다.

 

 

PHP MVC Frmaework 의 대표적인 것이 Laravel(라라벨), CodeIgniter(코드이그나이터) 라는데 아직 사용해본 적은 없다.

그누보드, 제로보드, KIMSQRB 는 프레임웍이라기 보다는 CMS툴에 가깝다고 봐야 한다.

 

프레임웍을 만들정도까지 실력을 키우려면 한참 배워야 한다.

이제 그런 구조(패턴)을 고려해서 PHP 코드 작성을 해보고 있는 중이다.

 

게시글 http://link2me.tistory.com/1398 , http://link2me.tistory.com/1399 에 첨부된 파일과 비교해서 수정된 부분이 어떤 것인지 살펴보면 도움될 것이다.

본 코드는 테스트용도이므로 Class를 더 세분화해서 분리하고 Class 이름을 정해서 추가해나가면 된다.

 

아래 코드와 login.php, register.php 파일이 포함된 압축파일을 첨부한다.

pdo.zip
다운로드

 

 

 

 

 

<?php
//****************
// DB 정보 입력
//****************
define('_DBHOST','localhost');
define('_DBUSER','android'); // DB 사용자
define('_DBPASS', '!android@'); // DB 패스워드
define('_DBNAME','android'); // DB 명
define('_DBTYPE','mysql'); // 데이터베이스 종류
define('_DSN',_DBTYPE.':host='._DBHOST.';dbname='._DBNAME.';charset=utf8');

//*****************************
// 파일 디렉토리
//*****************************
define('_CONFIG_DIR',__DIR__);
define('_CLASS_DIR',_CONFIG_DIR.'/Class/');

//***************************
//클래스 파일 불러오기
//***************************
require_once _CLASS_DIR.'DBConClass.php'; // DB 연결 클래스
require_once _CLASS_DIR.'MemberClass.php';

?>

<?php
class DBConClass{
    protected $db; // 변수를 선언한 클래스와 상속받은 클래스에서 참조할 수 있다.

    public function __construct(){
        $this->dbConnect();

        // construct 메소드는 객체가 생성(인스턴스화)될 때 자동으로 실행되는 특수한 메소드다.

    }

    private function dbConnect() {
        try{
            // MySQL PDO 객체 생성
            $this->db = new PDO(_DSN, _DBUSER, _DBPASS);
            $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
        } catch(PDOException $ex){
            die("오류 : ".$ex->getMessage());
        }
    }
}
?>

<?php
class MemberClass extends DBConClass {
    // 회원 정보 신규 입력
    public function storeUser($userID, $userNM, $email, $password, $telNO, $mobileNO) {
        $hash = $this->hashSSHA($password);
        $encrypted_password = $hash['encrypted']; // encrypted password
        $salt = $hash['salt']; // salt

        try{
            $this->db->beginTransaction();
            $sql = "INSERT INTO members(userID, userNM, email, passwd, salt, telNO,mobileNO,created_at) VALUES(:userID,:userNM,:email,:passwd,:salt,:telNO,:mobileNO,:created_at)";
            $stmt = $this->db->prepare($sql);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $stmt->bindValue(':userNM',$userNM,PDO::PARAM_STR);
            $stmt->bindValue(':email',$email,PDO::PARAM_STR);
            $stmt->bindValue(':passwd',$encrypted_password,PDO::PARAM_STR);
            $stmt->bindValue(':salt',$salt,PDO::PARAM_STR);
            $stmt->bindValue(':telNO',$telNO,PDO::PARAM_STR);
            $stmt->bindValue(':mobileNO',$mobileNO,PDO::PARAM_STR);
            $stmt->bindValue(':created_at',date("YmdHis"));
            $result = $stmt->execute();
            $this->db->commit();
        } catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
        // check for successful store
        if ($result) {
            $stmt = $this->db->prepare("SELECT * FROM members WHERE userID = :userID");
            $stmt->bindValue(':userID', $userID, PDO::PARAM_STR);
            $stmt->execute();
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            return $user;
        } else {
            return false;
        }
    }

    // 로그인 체크
    public function getUser($userID, $password) {
        $sql = "SELECT * FROM members WHERE userID=:userID";
        $stmt = $this->db->prepare($sql);
        $stmt->bindValue(':userID', $userID);
        $stmt->execute();

        if ($user=$stmt->fetch()) {
            // verifying user password
            $salt = $user['salt'];
            $encrypted_password = $user['passwd'];
            $hash = $this->checkhashSSHA($salt, $password);
            if ($encrypted_password == $hash) {
                // user authentication details are correct
                return $user;
            }
        } else {
            return NULL;
        }
    }

    // 안드로이드/아이폰 로그인 체크
    public function LoginUserChk($userID,$password,$deviceID){
        if(empty($userID) || empty($password)){
            return 0;
        } else {
            $user = $this->getUser($userID, $password);
            if($user['idx']>0){
                // 장치 일련번호 체크
                if($user['phoneSE'] == NULL){
                    // 신규 장비번호 입력(최초 로그인)
                    $this->LoginUserEquipInput($userID,$deviceID);
                    return $user['idx'];
                } else {
                    if($user['phoneSE'] === $deviceID){
                        return 1; // 일련번호 일치
                    } else {
                        return -1; //일련번호 불일치
                    }
                }
            } else {
                return 0; //계정오류
            }
        }

    }

    // 장치번호 업데이트
    public function LoginUserEquipInput($userID,$deviceID){
        if(strlen($deviceID)>0 && is_numeric($deviceID)){ // 안드로이드폰
            $ostype = 2;
        } else if(strlen($deviceID)>30){ // 아이폰
            $ostype = 1;
        } else { // 기타
            $ostype = 0;
        }

        try{
            $this->db->beginTransaction();
            $sql='update members set phoneSE=:phoneSE, OStype=:OStype where userID=:userID';
            $stmt = $this->db->prepare($sql);
            $stmt->bindValue(':phoneSE',$deviceID,PDO::PARAM_STR);
            $stmt->bindValue(':OStype',$ostype,PDO::PARAM_INT);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $status = $stmt->execute();
            $this->db->commit();
            if($status == true){
                return 1;
            } else {
                return 0;
            }
        }catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }//end

    // 장치번호 초기화
    public function EquipReset($userID){
        try{
            $this->db->beginTransaction();
            $ostype = 0;
            $sql='update members set phoneSE=NULL,OStype=:OStype where userID=:userID';
            $stmt = $this->db->prepare($sql);
            $stmt->bindValue(':OStype',$ostype,PDO::PARAM_INT);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $status = $stmt->execute();
            $this->db->commit();
            if($status == true){
                return 1;
            } else {
                return 0;
            }
        } catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }//end

    // 회원 가입 여부 체크
    public function isUserExisted($userID) {
        $stmt = $this->db->prepare("SELECT userID from members WHERE userID=:userID");
        $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
        $stmt->execute();
        if ($stmt->rowCount() > 0) {
            return true;
        } else {
            return false;
        }
    }

    // 회원 정보 삭제
    public function deleteUser($userID){
        try{
            $this->db->beginTransaction();
            $stmt = $this->db->prepare("delete FROM members WHERE userID=:userID");
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $stmt->execute();
            $this->db->commit();
        }catch (PDOException $pex) {
            $this->db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }

    public function hashSSHA($password) {
        $salt = sha1(rand());
        $salt = substr($salt, 0, 10);
        $encrypted = base64_encode(sha1($password . $salt, true) . $salt);
        $hash = array("salt" => $salt, "encrypted" => $encrypted);
        return $hash;
    }

    public function checkhashSSHA($salt, $password) {
        $hash = base64_encode(sha1($password . $salt, true) . $salt);
        return $hash;
    }
}
?>

 

728x90
블로그 이미지

Link2Me

,
728x90

윈도우 기반 AutoSet9 에서 테스트를 하다보니 계속 driver 를 못찾는다는 에러 메시지를 출력한다.

원인은 PDO(PHP Data Object) 설정이 안되어 있어서다.

 

PHP에는 다양한 데이터베이스를 쉽게 이용할 수 있도록 PDO라는 확장모듈이 포함되어 있다.

PDO(PHP Data Object) 동작이 되도록 설정하는 방법은 간단하다.

 

1. conf/php.ini 파일을 열어서 아래와 같이 주석을 제거한다.

- php_pdo_mysql.dll 만 주석제거 해도 되는데 PostgreSQL 도 사용하게 될지 몰라서 같이 주석 제거를 했다.

 

 

 

2. php.ini 수정사항을 반영하기 위해서는 Apache Web서버를 재기동해야 한다.

 

 

웹서버 멈춤을 눌러서 웹서버를 종료시켰다가 웹서버 시작을 눌러서 재실행되도록 한다.

 

 

3. phpinfo(); 로 설정된 상태가 반영되었는지 확인한다.

 

이렇게 해주면 PDO 방식으로 코딩 테스트를 할 수 있다.

 

AutoSet10 (PHP 7.0 + MariaDB) 도 동일하게 설정하면 된다.

728x90
블로그 이미지

Link2Me

,
728x90

<?php
// 절차지향 방식
$sql = "SELECT * FROM table WHERE field1 = $field1 AND field2 = '$field2' ORDER BY id";
$stmt = mysqli_query($db,$sql);

// PDO (정석대로)
$sql = "SELECT * FROM table WHERE field1 = ? AND field2 = ? ORDER BY id";
$stmt = $db->prepare($sql);
$stmt->bindParam(1, $field1, PDO::PARAM_INT);
$stmt->bindParam(2, $field2, PDO::PARAM_STR);
$stmt->execute();

// PDO (그나마 좀 간소화시킨 문법)
$sql = "SELECT * FROM table WHERE field1 = ? AND field2 = ? ORDER BY id";
$stmt = $db->prepare($sql);
$stmt->execute(array($field1, $field2));
?>

 

PDO(PHP Data Object) 방식 함수를 사용하여 만든 MemberClass 예제다.

<?php
class MemberClass {
    // 회원 정보 신규 입력
    public function storeUser($userID$userNM$email$password$telNO$mobileNO) {
        global $db;
 
        $hash = $this->hashSSHA($password);
        $encrypted_password = $hash['encrypted']; // encrypted password
        $salt = $hash['salt']; // salt
 
        try{
            $db->beginTransaction();
            $sql = "INSERT INTO members(userID, userNM, email, passwd, salt, telNO,mobileNO,created_at) VALUES(:userID,:userNM,:email,:passwd,:salt,:telNO,:mobileNO,:created_at)";
            $stmt = $db->prepare($sql);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $stmt->bindValue(':userNM',$userNM,PDO::PARAM_STR);
            $stmt->bindValue(':email',$email,PDO::PARAM_STR);
            $stmt->bindValue(':passwd',$encrypted_password,PDO::PARAM_STR);
            $stmt->bindValue(':salt',$salt,PDO::PARAM_STR);
            $stmt->bindValue(':telNO',$telNO,PDO::PARAM_STR);
            $stmt->bindValue(':mobileNO',$mobileNO,PDO::PARAM_STR);
            $stmt->bindValue(':created_at',date("YmdHis"));
            $result = $stmt->execute();
            $db->commit();
        } catch (PDOException $pex) {
            $db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
        // check for successful store
        if ($result) {
            $stmt = $db->prepare("SELECT * FROM members WHERE userID = :userID");
            $stmt->bindValue(':userID'$userID, PDO::PARAM_STR);
            $stmt->execute();
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
 
            return $user;
        } else {
            return false;
        }
    }
 
    // 로그인 체크
    public function getUser($userID$password) {
        global $db;
        $sql = "SELECT * FROM members WHERE userID=:userID";
        $stmt = $db->prepare($sql);
        $stmt->bindValue(':userID'$userID);
        $stmt->execute();
 
        if ($user=$stmt->fetch()) {
            // verifying user password
            $salt = $user['salt'];
            $encrypted_password = $user['passwd'];
            $hash = $this->checkhashSSHA($salt$password);
            if ($encrypted_password == $hash) {
                // user authentication details are correct
                return $user;
            }
        } else {
            return NULL;
        }
    }
 
    // 안드로이드/아이폰 로그인 체크
    public function LoginUserChk($userID,$password,$deviceID){
        global $db;
        if(empty($userID|| empty($password)){
            return 0;
        } else {
            $user = $this->getUser($userID$password);
            if($user['idx']>0){
                // 장치 일련번호 체크
                if($user['phoneSE'== NULL){
                    // 신규 장치번호 입력(최초 로그인)
                    $this->LoginUserEquipInput($userID,$deviceID);
                    return $user['idx'];
                } else {
                    if($user['phoneSE'=== $deviceID){
                        return 1// 일련번호 일치
                    } else {
                        return -1//일련번호 불일치
                    }
                }
            } else {
                return 0//계정오류
            }
        }
 
    }
 
    // 장치번호 업데이트
    public function LoginUserEquipInput($userID,$deviceID){
        global $db;
        if(strlen($deviceID)>0 && is_numeric($deviceID)){ // 안드로이드폰
            $ostype = 2;
        } else if(strlen($deviceID)>30){ // 아이폰
            $ostype = 1;
        } else { // 기타
            $ostype = 0;
        }
 
        try{
            $db->beginTransaction();
            $sql='update members set phoneSE=:phoneSE, OStype=:OStype where userID=:userID';
            $stmt = $db->prepare($sql);
            $stmt->bindValue(':phoneSE',$deviceID,PDO::PARAM_STR);
            $stmt->bindValue(':OStype',$ostype,PDO::PARAM_INT);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $status = $stmt->execute();
            $db->commit();
            if($status == true){
                return 1;
            } else {
                return 0;
            }
        }catch (PDOException $pex) {
            $db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }//end
 
    // 장치번호 초기화
    public function EquipReset($userID){
        global $db;
        try{
            $db->beginTransaction();
            $ostype = 0;
            $sql='update members set phoneSE=NULL,OStype=:OStype where userID=:userID';
            $stmt = $db->prepare($sql);
            $stmt->bindValue(':OStype',$ostype,PDO::PARAM_INT);
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $status = $stmt->execute();
            $db->commit();
            if($status == true){
                return 1;
            } else {
                return 0;
            }
        } catch (PDOException $pex) {
            $db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }//end
 
    // 회원 가입 여부 체크
    public function isUserExisted($userID) {
        global $db;
        $stmt = $db->prepare("SELECT userID from members WHERE userID=:userID");
        $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
        $stmt->execute();
        if ($stmt->rowCount() > 0) {
            return true;
        } else {
            return false;
        }
    }
 
    // 회원 정보 삭제
    public function deleteUser($userID){
        global $db;
        try{
            $db->beginTransaction();
            $stmt = $db->prepare("delete FROM members WHERE userID=:userID");
            $stmt->bindValue(':userID',$userID,PDO::PARAM_STR);
            $stmt->execute();
            $db->commit();
        }catch (PDOException $pex) {
            $db->rollBack();
            echo "에러 : ".$pex->getMessage();
        }
    }
 
    public function hashSSHA($password) {
        $salt = sha1(rand());
        $salt = substr($salt010);
        $encrypted = base64_encode(sha1($password . $salttrue) . $salt);
        $hash = array("salt" => $salt"encrypted" => $encrypted);
        return $hash;
    }
 
    public function checkhashSSHA($salt$password) {
        $hash = base64_encode(sha1($password . $salttrue) . $salt);
        return $hash;
    }
}
?>

 

 

안드로이드와 연동하는 login.php, register.php 파일 예제는 첨부파일을 참조하면 된다.

pdo.zip
다운로드

 

조만간에 부트스트랩 registerForm.php 와 loginForm.php 파일을 간단하게 만들고, 관련 처리 기능을 만들 예정이다.

 

 

PHP 7.0 에서 mysql 기존방식은 지원을 아예 안하고 mysqli  및 pdo를 지원하므로 PDO 방식으로 코딩하는 방법을 계속 연습해두고 기능을 하나 하나 익혀두는게 나을거 같아서 정리를 해보는 중이다.

 

수정사항(2017.11.30)

회원정보 삭제 테스트를 하다보니 원하는 결과가 나오지 않아서 로직을 수정 보완했다.

// 회원 가입 여부 체크
public function isUserExisted($userID) {
    $stmt = $this -> db -> prepare("SELECT count(userID) from members WHERE userID=:userID");
    $stmt -> bindValue(':userID', $userID, PDO::PARAM_STR);
    $stmt -> execute();
    if ($row = $stmt -> fetch()) {
        return $row[0];
        // 미가입이면 0 반환, 가입이면 1 반환
    } else {
        return -1;
    }
}

// 회원 정보 삭제 : Move
public function deleteUser($userID) {
    if ($this -> isUserExisted($userID) > 0) {
        try {
            $this -> db -> beginTransaction();
            $stmt = $this -> db -> prepare("delete FROM members WHERE userID=:userID");
            $stmt -> bindValue(':userID', $userID, PDO::PARAM_STR);
            $stmt -> execute(); // 삭제할 데이터의 존재 유무에 상관없이 실행한다.
            $this -> db -> commit();
        } catch (PDOException $pex) {
            $this -> db -> rollBack();
            echo "에러 : " . $pex -> getMessage();
        }
        return 1; // 삭제 성공
    } else {
        return 0; // 삭제할 데이터가 없음
    }
}

 

728x90
블로그 이미지

Link2Me

,
728x90

PDO(PHP Data Object) 방식으로 DB을 연결하는 방법이다.

- PDO는 여러가지 DBMS를 제어하는 방법을 표준화시켜 다양한 DB(Oracle, PostgrSQL, MySQL 등)를 동일한 방법으로 제어할 수 있다.

- PHP 7.0 에서는 MySQL API 지원 중단, MySQLi 절차식 API 및 객체 API 제공, PDO 객체 API 제공


사용법

require_once 'DBController.php';
require_once 'loginClass.php';
$c=new LoginClass;

LoginClass 만 객체 생성해주면 부모클래스인 DBController 클래스는 자동으로 인식된다.


<?php
//****************
// DB 정보 입력
// define('상수명', '값', true);
//****************
define('_DBHOST','localhost');
define('_DBUSER','address_root'); // DB 사용자
define('_DBPASS', 'autoset'); // DB 패스워드
define('_DBNAME','address'); // DB 명
define('_DBTYPE','mysql'); // 데이터베이스 종류
define('_DSN',_DBTYPE.':host='._DBHOST.';dbname='._DBNAME.';charset=utf8');
?>

 <?php
class DBController {
    protected $db;

    public function __construct() {
        $this->db = $this -> dbConnect();
        // construct 메소드는 객체가 생성(인스턴스화)될 때 자동으로 실행되는 특수한 메소드다.
    }

    private function dbConnect() {
        require_once 'config.php';
        try { // MySQL PDO 객체 생성
            $conn = new PDO(_DSN, _DBUSER, _DBPASS);
            $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
        } catch(PDOException $ex) {
            die("오류 : " . $ex -> getMessage());
        }
        return $conn;
    }
?>

 <?php
class LoginClass extends DBController {
    // 회원 정보 신규 입력
    public function storeUser($userID, $userNM, $password, $mobileNO) {
        $hash = $this -> hashSSHA($password);
        $encrypted_password = $hash['encrypted']; // encrypted password
        $salt = $hash['salt']; // salt
        $regdate = date("YmdHis");

        $key = "userID, userNM, passwd, salt, regdate";
        $val = "$userID,$userNM,$encrypted_password,$salt,$regdate";
        $result = $this -> getDbInsert('members', $key, $val); //
DBController에서 별도 생성 함수
        // check for successful store
        if ($result) {
            $user = $this -> getUser($userID, $password);
            $rs = $this -> storeUserDetail($user['idx'], $userNM, $userID, $mobileNO);
            if ($rs == 1) {
                return $user;
            } else {
                return -1;
            }
        } else {
            return -1; // 0
        }
    }

    // 회원 세부 정보 입력
    public function storeUserDetail($relatedidx, $userNM, $email, $mobileNO) {
        $regdate = date("YmdHis");
        $key = 'relatedidx,name,email,mobileNO,modifydate'; // 칼럼 정확한지 체크
        $val = "$relatedidx,$userNM,$email,$mobileNO,$regdate";
        $this -> getDbInsert('member_data', $key, $val);
    }

    // 회원정보 반환
    public function getUser($userID, $password) {
        $sql = "SELECT * FROM members WHERE userID=:userID";
        $stmt = $this -> db -> prepare($sql);
        $stmt -> bindValue(':userID', $userID);
        $stmt -> execute();

        if ($user = $stmt -> fetch()) {
            // verifying user password
            $salt = $user['salt'];
            $encrypted_password = $user['passwd'];
            $hash = $this -> checkhashSSHA($salt, $password);
            if ($encrypted_password == $hash) {
                // user authentication details are correct
                return $user;
            }
        } else {
            return NULL;
        }
    }

    // 회원 가입 여부 체크
    public function isUserExisted($userID) {
        $stmt = $this -> db -> prepare("SELECT count(userID) from members WHERE userID=:userID");
        $stmt -> bindValue(':userID', $userID, PDO::PARAM_STR);
        $stmt -> execute();
        if ($row = $stmt -> fetch()) {
            return $row[0];
            // 미가입이면 0 반환, 가입이면 1 반환
        } else {
            return -1;
        }
    }

    // 회원정보 수정
    public function updateUser($idx, $userNM, $mobileNO) {
        if(isset($idx) && !empty($idx)){
            $this->getDbUpdate('members', "userNM=?", array($userNM), "idx=".$idx);
            $set = "name=?,mobileNO=?";
            $params = array($userNM,$mobileNO);
            $this->getDbUpdate('member_data', $set, $params, 'relatedidx='.$idx);
            return 1;
        } else {
            return 0;
        }
    }

    // 회원 정보 삭제 : Move
    public function deleteUser($userID) {
        if ($this -> isUserExisted($userID) > 0) {
            try {
                $this -> db -> beginTransaction();
                $stmt = $this -> db -> prepare("delete FROM members WHERE userID=:userID");
                $stmt -> bindValue(':userID', $userID, PDO::PARAM_STR);
                $stmt -> execute(); // 삭제할 데이터의 존재 유무에 상관없이 실행한다.
                $this -> db -> commit();
            } catch (PDOException $pex) {
                $this -> db -> rollBack();
                echo "에러 : " . $pex -> getMessage();
            }
            return 1; // 삭제 성공
        } else {
            return 0; // 삭제할 데이터가 없음
        }
    }

    // 안드로이드/아이폰 로그인 체크
    public function LoginUserChk($userID, $password, $deviceID) {
        if (empty($userID) || empty($password)) {
            return 0;
        } else {
            $user = $this -> getUser($userID, $password);
            if ($user['idx'] > 0) {
                // 장치 일련번호 체크
                if ($user['phoneSE'] == NULL) {
                    // 신규 장비번호 입력(최초 로그인)
                    $this -> LoginUserEquipInput($userID, $deviceID);
                    return $user['idx'];
                } else {
                    if ($user['phoneSE'] === $deviceID) {
                        return 1;
                        // 일련번호 일치
                    } else {
                        return -1;
                        //일련번호 불일치
                    }
                }
            } else {
                return 0;
                //계정오류
            }
        }

    }

    // 장치번호 업데이트
    public function LoginUserEquipInput($userID, $deviceID) {
        if (strlen($deviceID) > 0 && is_numeric($deviceID)) {// 안드로이드폰
            $ostype = 2;
        } else if (strlen($deviceID) > 30) {// 아이폰
            $ostype = 1;
        } else {// 기타
            $ostype = 0;
        }

        try {
            $this -> db -> beginTransaction();
            $sql = 'update members set phoneSE=:phoneSE, OStype=:OStype where userID=:userID';
            $stmt = $this -> db -> prepare($sql);
            $stmt -> bindValue(':phoneSE', $deviceID, PDO::PARAM_STR);
            $stmt -> bindValue(':OStype', $ostype, PDO::PARAM_INT);
            $stmt -> bindValue(':userID', $userID, PDO::PARAM_STR);
            $status = $stmt -> execute();
            if ($status == true) {
                $this -> db -> commit();
                return 1;
            } else {
                return 0;
            }
        } catch (PDOException $pex) {
            $this -> db -> rollBack();
            echo "에러 : " . $pex -> getMessage();
        }
    }//end

}
?>


추가 (2018.04.09)

검색하다보니 https://gist.github.com/kijin/5947026 에 ezPDO 라는 Class 파일을 만든 고수분이 있네.


728x90
블로그 이미지

Link2Me

,
728x90

PHP에서 구분자로 문자열 자르기를 하려고 하는데 구분자가 문자열에 여러개 나올 경우가 있다.

구분자를 뒤에서부터 찾는 방법을 알아보려고 찾아보고 적어둔다.


stripos 는 대상 문자열을 앞에서 부터 검색하여 찾고자 하는 문자열이 몇번째 위치에 있는지를 리턴하는 함수
stripos(대상 문자열, 조건 문자열);

strripos 는 대상 문자열을 뒤에서 부터 검색하여 찾고자 하는 문자열이 몇번째 위치에 있는지를 리턴하는 함수
strripos(대상 문자열, 조건 문자열);


$curdir = substr($dir,1,strlen($dir)-1); // 현재 폴더 정보

$filePath = substr($curdir,0,strripos($curdir, "/")); // 상위폴더

$updir = substr($filePath,0,strripos($filePath, "/")); // 차상위폴더


728x90

'Web 프로그램 > PHP 문법' 카테고리의 다른 글

HTML Entity List (특수문자 용어)  (0) 2021.11.27
[PHP] 배열에만 동작하는 foreach 문  (0) 2017.01.26
PHP Class 개념 이해 ★★  (0) 2017.01.10
PHP isset 과 empty 설명  (1) 2016.04.27
PHP 대체 문법  (0) 2016.04.05
블로그 이미지

Link2Me

,
728x90

PHP Connect to MySQL

- MySQL (procedural) : MySQL DB에서만 동작하며, PHP 7.0 이상에선 동작안됨. PHP 5.5에서 deprecated

- MySQLi (procedural) : MySQL DB에서만 동작한다.

- MySQLi (object-oriented) : MySQL DB에서만 동작한다.
- PDO(PHP Data Objects) :
Oracle, PostgreSQL, SQLite, MySQL 등 12가지의 DB에서 동작한다.


MySQLi 기반 Prepared Statements PHP 연동 방식이 SQL 인젝션에 강하다고 되어 있다.

그러면 절차지향 방식의 MySQLi 방식에서는 어떻게 보강하면 될까?

$userid = mysqli_real_escape_string($db, $userid);

와 같이 mysqli_real_escape_string 를 써주면 된다.

해킹 대비를 위해서는 INPUT 해킹 시도(SQL Injection), 출력 해킹 시도(Cross Site Scripting) 둘다 신경써야 한다.


기존 사용하던 MySQLi (procedural) 대신에 MySQLi (object-oriented) 로 연습을 해봤다.


https://www.androidhive.info/2012/01/android-login-and-registration-with-php-mysql-and-sqlite 에서 파일을 다운로드하여 좀 수정했다.


테스트 환경

- Windows10 기반 Autoset9 (PHP 5.6.0, MySQL 5.6.20) : 정상

- Windows10 기반 Autoset10(Apache 2.4.17, PHP 7.0, MariaDB 10.1.9 ) : 정상


리눅스 기반 PHP 5.6.30, MySQL 5.6.30 에서 테스트를 하다보니 일부 함수가 문제가 발생하는 걸 알았다.

mysqlnd 가 설치되지 않아서였더라.

phpize로 추가 설치하는 방법을 검색해봤는데 아직 해결책을 못찾았다.

정 안되면 APM(Apache + PHP + MySQL) 소스 설치를 다시 해볼 생각이다.


- SHA1 패스워드를 적용한 알고리즘이 좋더라. 그래서 이걸 수정/추가해서 코드를 보완

- 아이폰/안드로이드폰에서 접속시 폰의 고유장치번호를 인식하여 최초 등록 단말이 아닌 경우 로그인 불가처리

- 테이블명을 변경하고 칼럼을 추가하고 user 권한을 설정하는 걸 추가


테이블 구조

- phpMyAdmin SQL에서 아래코드를 붙여넣기 하면 자동으로 DB명과 테이블을 생성한다.

- DB명과 user 권한을 수정해서 이용하면 된다.


DB_Functions 클래스 및 DB 접속정보는 첨부파일 참조

- DB_Functions 클래스가 핵심 내용이다.

 <?php

class DB_Functions {
    private $conn;

    // 생성자
    function __construct() {
        require_once 'dbconnect.php';
        // DB 연결
        $db = new Db_Connect();
        $this->conn = $db->connect();
    }

    // 소멸자(destructor)
    function __destruct() {

    }

    // 회원 정보 신규 입력
    public function storeUser($userID, $name, $email, $password, $telNO, $mobileNO) {
        $hash = $this->hashSSHA($password);
        $encrypted_password = $hash['encrypted']; // encrypted password
        $salt = $hash['salt']; // salt

        $stmt = $this->conn->prepare("INSERT INTO members(userID, userNM, email, passwd, salt, telNO,mobileNO,created_at) VALUES(?, ?, ?, ?, ?, ?, ?, NOW())");
        $stmt->bind_param("sssssss", $userID, $name, $email, $encrypted_password, $salt, $telNO, $mobileNO);
        $result = $stmt->execute();
        $stmt->close();

        // check for successful store
        if ($result) {
            $stmt = $this->conn->prepare("SELECT * FROM members WHERE userID = ?");
            $stmt->bind_param("s", $userID);
            $stmt->execute();
            $user = $stmt->get_result()->fetch_assoc();
            $stmt->close();

            return $user;
        } else {
            return false;
        }
    }

    // 로그인 체크
    public function getUser($userID, $password) {
        $stmt = $this->conn->prepare("SELECT * FROM members WHERE userID = ?");
        $stmt->bind_param("s", $userID);

        if ($stmt->execute()) {
            $user = $stmt->get_result()->fetch_assoc();
            $stmt->close();

            // verifying user password
            $salt = $user['salt'];
            $encrypted_password = $user['passwd'];
            $hash = $this->checkhashSSHA($salt, $password);
            // check for password equality
            if ($encrypted_password == $hash) {
                // user authentication details are correct
                return $user;
            }
        } else {
            return NULL;
        }
    }

    // 안드로이드/아이폰 로그인 체크
    public function LoginUserChk($userID,$password,$deviceID){
        if(empty($userID) || empty($password)){
            return 0;
        } else {
            $user = $this->getUser($userID, $password);
            if($user['idx']>0){
                // 장치 일련번호 체크
                if($user['phoneSE'] == NULL){
                    // 신규 장비번호 입력(최초 로그인)
                    $this->LoginUserEquipInput($userID,$deviceID);
                    return $user['idx'];
                } else {
                    if($user['phoneSE'] === $deviceID){
                        return 1; // 일련번호 일치
                    } else {
                        return -1; //일련번호 불일치
                    }
                }
            } else {
                return 0; //계정오류
            }
        }

    }

    // 장치번호 업데이트
    public function LoginUserEquipInput($userID,$deviceID){
        if(strlen($deviceID)>0 && is_numeric($deviceID)){ // 안드로이드폰
            $ostype = 2;
        } else if(strlen($deviceID)>30){ // 아이폰
            $ostype = 1;
        } else { // 기타
            $ostype = 0;
        }

        $sql='update members set phoneSE=?, OStype=? where userID=?';
        $stmt = $this->conn->prepare($sql);
        $stmt->bind_param("sss", $deviceID, $ostype, $userID);
        $status = $stmt->execute();
        if($status == true){
            return 1;
        } else {
            return 0;
        }
    }//end

    // 장치번호 초기화
    public function EquipReset($userID){
        $ostype = 0;
        $sql='update members set phoneSE=NULL, OStype=? where userID=?';
        $stmt = $this->conn->prepare($sql);
        $stmt->bind_param("ss", $ostype, $userID);
        $status = $stmt->execute();
        if($status == true){
            return 1;
        } else {
            return 0;
        }
    }//end

    // 회원 가입 여부 체크
    public function isUserExisted($userID) {
        $stmt = $this->conn->prepare("SELECT userID from members WHERE userID = ?");

        $stmt->bind_param("s", $userID);
        $stmt->execute();
        //$stmt->store_result();
        $result = $stmt->get_result();

        if ($result->num_rows > 0) {
            // user existed
            $stmt->free_result();
            $stmt->close();
            return true;
        } else {
            // user not existed
            $stmt->free_result();
            $stmt->close();
            return false;
        }
    }

    // 회원 정보 삭제
    public function deleteUser($userID){
        $stmt = $this->conn->prepare("delete FROM members WHERE userID = ?");
        $stmt->bind_param("s", $userID);
        $stmt->execute();
        $stmt->close();
    }

    public function hashSSHA($password) {

        $salt = sha1(rand());
        $salt = substr($salt, 0, 10);
        $encrypted = base64_encode(sha1($password . $salt, true) . $salt);
        $hash = array("salt" => $salt, "encrypted" => $encrypted);
        return $hash;
    }

    public function checkhashSSHA($salt, $password) {
        $hash = base64_encode(sha1($password . $salt, true) . $salt);
        return $hash;
    }

}

?>


- user 권한 및 패스워드 설정을 다르게 한 경우에는 해당 파일을 수정해야 한다.


register.php

- 안드로이드에서 전송된 데이터라고 가정하고 값을 직접 입력하여 DB에 잘 저장되는지 테스트했다.


json 으로 넘길 데이터 형태는 개발자가 변경하고 싶은데로 변경하여 사용하면 된다.


login.php


등록된 폰과 다를 경우 관리자에게 문의하여 장치번호를 초기화하고 새로운 폰으로 다시 등록해야만 로그인이 가능해진다.


안드로이드 코드 부분은 다음에 기회되면 작성할 예정이다.

이미 기존에 로그인 처리하는 코드 예제가 있으므로 그걸 수정해서 활용하거나 PHP 코드를 일부 수정해서 사용해도 된다.


androidPHP_Login_and_Registration.zip



PDO 개념 및  사용법 : http://link2me.tistory.com/1332

PDO DB 연결방법 : http://link2me.tistory.com/1398

PDO MemberClass : http://link2me.tistory.com/1399

MVC 패턴 고려한 PDO Class 만들기 : http://link2me.tistory.com/1401

728x90
블로그 이미지

Link2Me

,
728x90

회원가입시 필요한 member Class 함수다.

적용하는 사이트에 따라 칼럼(필드)가 추가로 있기도 하고 없을 수도 있는 걸 고려한 회원 등록, 회원 정보 수정 기능이 포함되어 있다.

패스워드 저장방식은 원하는 걸로 수정하면 된다.


 <?php
class MemberClass {
    //회원 신규 입력
    function MemberRegister($arr){
        date_default_timezone_set('Asia/Seoul');
        $rs=$this->MemberIDCheck($arr['idx'],$arr['userID']); //아이디중복검사
        if($rs<0){ //아이디 중복
            return -1;
            exit;
        } else if($rs==1){ //아이디 입력가능
            $sql="insert into member (idx,userID,passwd,userNM,telNO,mobileNO,email,Cate1,Cate2,smart,";
            if(isset($arr['positionNM']))
                $sql.="positionNM,";
            $sql.="allowLogin,regdate) values(NULL,";
            $sql.="'".$arr['userID']."',";
            $sql.="md5('".substr($arr['mobileNO'],-4)."'),";
            $sql.="'".$arr['userNM']."',";
            $sql.="'".$this->SplitRemove($arr['telNO'])."',";
            $sql.="'".$this->SplitRemove($arr['mobileNO'])."',";
            $sql.="'".$arr['email']."',";
            $sql.="'".$arr['Cate1']."',";
            $sql.="'".$arr['Cate2']."',";
            $sql.="'".$arr['smart']."',";
            if(isset($arr['positionNM'])) // 변수가 있으면 추가하라
                $sql.="'".$arr['positionNM']."',";
            $sql.="'".$arr['allowLogin']."',";
            $sql.="'".date("Ymd")."'"; // 등록일자
            if($result=mysql_query($sql)){
                return mysql_insert_id();
            } else {
                return $sql;
            }

        } else {//알수없는 Db오류
            return $sql;
        }
    }

    //회원정보 수정
    function MemberModify($arr){
        global $db;
        date_default_timezone_set('Asia/Seoul');

        $rs=$this->MemberIDCheck($arr['idx'],$arr['userID']);
        if($rs<0){ //아이디 중복
            return -1;
            exit;
        } else if($rs==1){ //아이디 입력가능
            $sql="update member set ";
            $sql.="userID='".$arr['userID']."',";
            if(isset($arr['passwd'])){
                $sql.="passwd=md5('".$arr['passwd']."'),";
            }
            $sql.="userNM='".$arr['userNM']."',";
            $sql.="telNO='".$this->SplitRemove($arr['telNO'])."',";
            $sql.="mobileNO='".$this->SplitRemove($arr['mobileNO'])."',";
            $sql.="email='".$arr['email']."',";
            $sql.="Cate1='".$arr['Cate1']."',";
            $sql.="Cate2='".$arr['Cate2']."',";
            $sql.="smart='".$arr['smart']."',";
            if(isset($arr['positionNM'])){
                $sql.="positionNM='".$arr['positionNM']."',";  
            }
            if(isset($arr['allowLogin'])){
                $sql.="allowLogin='".$arr['allowLogin']."',";
            }
            if(isset($arr['hidden'])){
                $sql.="hidden='".$arr['hidden']."',";
            }
            $sql.="regdate='".date("Ymd")."'"; // 수정일자
            $sql.=" where idx=".$arr['idx'];
            if($result=mysql_query($sql)){
                return $arr['idx'];
            } else {
                return $sql;
            }

        } else {//알수없는 Db오류
            return 0;
        }

    } //end function


    //회원정보 삭제
    function
MemberDelete($idx){
        global $db;
        $sql="delete from member where idx=".$idx;
        if($result=mysql_query($sql)){
            return 1;
        } else {
            return $sql;
        }
    }

    //변경전 로그인ID 유일성체크
    function MemberIDCheck($idx,$userID){
        global $db;
        $sql="select idx,count(*) from member where userID='".$userID."'";
        if($result=mysql_query($sql)){
            $row=mysql_fetch_row($result);
            if($row[1]==1){ //id존재
                if($row[0]==$idx){ // id변경없음
                    return 1;
                } else { //id변경시도, 다른 id존재, 변경불가
                    return -1;
                }
            } else { //변경시도, 다른id없음, 변경가능
                return 1;
            }
        } else {
            return 0;
        }
    }

}
?>


사용법

<?php
require_once 'memberClass.php';
$m = new MemberClass(); // 객체 생성

// 회원가입시 입력 form 에서 넘겨받은 배열 정보를 넣는다.
$rs = $m->MemberRegister($_POST);

// 회원 정보 수정시 입력 form 에서 넘겨받은 배열 정보를 넣는다.
$rs = $m->MemberModify($_POST);
echo $rs; // Ajax 통신인 경우 결과값을 반환하여 데이터 수정 성공/실패 팝업 메시지를 띄운다.
?>

728x90
블로그 이미지

Link2Me

,
728x90

DB 결과가 숫자로 0, 1로 되어 있을 경우 화면 출력과 수정할 수 있게 하는 코드


    echo "<div data-role='fieldcontain'>";
    echo "<label>&nbsp;개인정보&nbsp;: </label>";
        $personInfo=array('비공개','공개동의');
        foreach($personInfo as $k=>$v){
            if($row['smart']==$k){
                echo "<label>".$v."</label>";
            }
        }
    echo "</div>";


   echo "<div data-role='fieldcontain'>";
    echo "<table>";
    echo "<tr>";
    echo "<td><label>개인정보&nbsp;: </label></td>";
    echo "<td><select class='modistaff' name='smart'>";
        $personInfo=array('비공개','공개동의');
        foreach($personInfo as $k=>$v){
            if($row['smart']==$k){
                echo "<option value='".$k."' selected>".$v."</option>";
            } else {
                echo "<option value='".$k."'>".$v."</option>";
            }
        }
    echo "</select></td>";
    echo "</tr>";
    echo "</table>";
    echo "</div>";


foreach(배열 as $key => $value){


}


여기서 $key 는 배열의 나열순서 0, 1을 의미하며, 배열의 크기가 5라면 0,1,2,3,4

DB에서 가져온 $row['smart'] 값과 key값을 비교하여 같으면 해당 value 를 출력하라는 의미다.

728x90
블로그 이미지

Link2Me

,
728x90

네이버지식인에 자바스크립트 값을 PHP로 넘기는 것에 대해 올라와서 의구심이 들었다.

이런 건 해본적이 없는거 같기도 하고 요즈음 안드로이드 배운다고 jQuery 배우다 중단해서 많이 잊어버려 기억도 안나고 해서 테스트를 해봤다.


<?php
include_once 'dbconnect.php'; // db접속
$width = '<script> document.write(window.screen.width); </script>';
echo 'width : '.$width.'<br />'; // 화면으로 보이는 건 숫자로 보인다. 소스보기로 하면 숫자가 아니다.
echo gettype ($width) . "<br />"; // 타입검사를 하면 string 으로 나온다.
$price = intval($width);  // 정수형으로 변환을 하면 숫자로 나올까??

$sql = "SELECT * FROM table_items WHERE Price > {$price} ";
echo $sql.'<br />';
$result=mysqli_query($dbconn,$sql);
while($row=mysqli_fetch_row($result)){
    echo $row[1].' '.$row[2];
    echo '<br />';
}

?>


$width ="1440";
echo intval($width);
와 같이 직접 입력한 String은 정수형으로 형변환(casting)을 잘 한다.


하지만 자바스크립트에서 받은 값을 직접 DB에 넣어봤더니 숫자로 인식하지 못한다.

육안으로 보이는 <script> document.write(window.screen.width); </script> 는 실제 숫자가 아니라 형변환(intval(string))을 하면 0을 반환한다.


좀 더 명확하게 하기 위해서

include_once 'dbconnect.php'; // db접속
$width = '<script> document.write(window.screen.width); </script>';
echo 'width : '.$width.'<br />';
echo gettype ($width) . "<br />"; //Returns string
$price = intval($width);

$sql = "SELECT * FROM table_items WHERE Price > {$width} ";
echo $sql.'<br />';
$sql = "SELECT * FROM table_items WHERE Price > {$price} ";
echo $sql.'<br />';
이렇게 출력을 해봤다.

화면상으로는

SELECT * FROM table_items WHERE Price > 1920
SELECT * FROM table_items WHERE Price > 0

이렇게 보인다.


브라우저 소스보기로 보면

SELECT * FROM table_items WHERE Price > <script> document.write(window.screen.width); </script>
SELECT * FROM table_items WHERE Price > 0
로 보인다.


https://stackoverflow.com/questions/9715231/unable-to-convert-string-to-integer-in-php

에 PHP 와 JavaScript 언어의 차이점을 간략하게 설명하면서 잘못하고 있다는 걸 답변달아주고 있다.


서버 코드와 Client 코드를 혼용하는 것은 로직이 잘못된 거다.


예전에 "자바스크립트와 PHP 값 전달 이해" 라고 정리해둔 http://link2me.tistory.com/1124 를 보면 조금 도움될 수 있다.

아직 자바스크립트에 대해 완벽하게 이해를 한 것이 아니라서 기회가 될 때마다 수정보강할 예정이다.

728x90
블로그 이미지

Link2Me

,
728x90

요일과 접속시간 설정에 따라 접속 허용/불허하는 간단한 로직이다.

공휴일 접속체크 이런 것은 적용되지 않았다.


<?php
$day = date('N');// 요일: 1(월) ~ 7(일)
$time = date('H');// 시간
$week = array("일", "월", "화", "수", "목", "금", "토");
$access = FALSE;// 접근 불가능

if ($day <= 5) {// 월~금
    if ($time >= 08 && $time < 18) {
        $access = TRUE;
    } else {
        $access = FALSE;
    }
} else { // 토, 일
    $access = FALSE;
}

if ($access === TRUE) {
    echo '오늘 '.date('Y-m-d').', '.$week[date('w')].'요일, 현재 시간 '.date('H:i:d').'<br />';
    echo "접속 허용 ";
} else {
    echo '오늘 '.date('Y-m-d').', '.$week[date('w')].'요일, 현재 시간 '.date('H:i:d').'<br />';
    echo "접속 차단";
}

?>

728x90
블로그 이미지

Link2Me

,
728x90

자주 사용해본 방식이 아니라서 자료 찾으려면 시간도 걸리고 해서 MySQLi 방식(Prepared Statements in MySQLi
)으로 된 예제를 적어둔다.


<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// prepare and bind
$stmt = $conn->prepare("INSERT INTO member (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);

// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();

echo "New records created successfully";

$stmt->close();
$conn->close();
?>
 


$stmt->bind_result($cnt); // POD 방식에서는 에러 발생
$stmt->close(); //POD 방식에서는 에러 발생

출처 : https://www.w3schools.com/PhP/php_mysql_prepared_statements.asp

Prepared Statements in PDO 과 Prepared Statements in MySQLi

두 방식 예제 모두가 나와 있으므로 참고하면 도움된다.


위 예제로는 부족한 점이 있어서 추가로 예제를  만들어서 테스트를 하고 적어둔다.


테이블 만들기

CREATE TABLE IF NOT EXISTS `NOTICE` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `noticeContent` varchar(100) NOT NULL,
  `noticeName` varchar(20) NOT NULL,
  `noticeDate` datetime NOT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; 


<?php
@extract($_POST); // POST 전송으로 전달받은 값 처리
// $_POST['noticeContent']라고 쓰지 않고, $noticeContent 라고 써도 인식하게 함

// 테스트 목적으로 직접 값을 입력하여 테스트 시도
$noticeContent="공지사항 등록 테스트";
$noticeName="관리자";
$noticeDate=date("Y-m-d H:i:s");

// DB 접속 테스트
$servername = "localhost";
$username = "root";
$password = "autoset";
$dbname = "address";

// Create connection
$con = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($con->connect_error) {
    die("Connection failed: " . $con->connect_error);
}

// DB에 자료가 있는지 검사
$stmt = $con->prepare("SELECT count(*) FROM NOTICE WHERE noticeContent=? and noticeName=?");
$stmt->bind_param("ss", $noticeContent, $noticeName);
$stmt->execute(); // execute query
$stmt->bind_result($cnt); // bind result variables
if($stmt->fetch()){
    //echo $cnt;
    if($cnt == 0){ // 중복 자료가 없으면
        $stmt->close(); // close statement
        $stmt = $con->prepare("INSERT INTO NOTICE(noticeContent, noticeName, noticeDate) VALUES(?, ?, ?)");
        $stmt->bind_param("sss", $noticeContent, $noticeName, $noticeDate);
        $stmt->execute();
        
        $response = array();
        $response["success"] = true;
        echo json_encode($response);
        $stmt->close(); // close statement
    } else {
        $response = array();
        $response["success"] = false;
        echo json_encode($response);
    }
}
$con->close();
?>


types
일치하는 bind 변수의 type을 가리키는 문자열
i : integer
d : double
s : string
b : blob

728x90
블로그 이미지

Link2Me

,
728x90

PDO 방식으로 MySQL 과 연동하여 데이터를 가져오는 걸 테스트했다.

사용자 정의 함수를 만들어서 연동하는 방법이다.

사용자 함수 CustomFunction($ItemName,$Quantity) 의 결과가 있을 경우에는 데이터를 반환하고, 없으면 false 로 결과 반환을 한다.

true, fasle 로 처리했으므로 if($result = CustomFunction($ItemName,$Quantity)) 문으로 처리하면 원하는 결과를 얻을 수 있다.


<?php
require_once 'pdoconn.php';
// SELECT
$ItemName ='수박';
$Quantity = 500;
if($result = CustomFunction($ItemName,$Quantity)){
    // Fetch 모드를 설정 및 1 row 씩 가져오기
    while($row= $result->fetch(PDO::FETCH_BOTH)) {
        echo $row[0].' | ';
        echo $row[1].' | ';
        echo $row[2].' | ';
        echo $row[3].'<br />';
    }
}
$db = null;

function CustomFunction($ItemName,$Quantity){
    global $db;
    $sql='select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName and Quantity=:Quantity';
    $stmt = $db->prepare($sql);
    $stmt->bindParam(':ItemName', $ItemName);
    $stmt->bindParam(':Quantity', $Quantity);
    $stmt->execute();
    if($stmt->fetch(PDO::FETCH_BOTH)){
        return $stmt;
    } else {
        return false;
    }
}
?>


함수의 개수가 많아지면 함수명이 중복될 수도 있고 관리가 어려워질 수 있다.

이제 Class 화를 해보자.

<?php
class TestClass
{
    function CustomFunction($ItemName,$Quantity){
        global $db;
        $sql='select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName and Quantity=:Quantity';
        $stmt = $db->prepare($sql);
        $stmt->bindParam(':ItemName', $ItemName);
        $stmt->bindParam(':Quantity', $Quantity);
        $stmt->execute();
        if($stmt->fetch(PDO::FETCH_BOTH)){
            return $stmt;
        } else {
            return false;
        }
    }
}
?> 

bind_param 은 mysqli_stmt 방식이고, bindParam은 PDOStatement 방식이다.


TestClass 에 있는 함수를 이용하는 예제다.

처음에 연동했던 방식과 비교해서 어떤 부분이 달라졌는지만 살펴보면 알 수 있다.

<?php
require_once 'pdoconn.php';
require_once 'pdoClass.php';
$test = new TestClass;

$ItemName ='수박';
$Quantity = 500;
if($result = $test->CustomFunction($ItemName,$Quantity)){
    // Fetch 모드를 설정 및 1 row 씩 가져오기
    while($row= $result->fetch(PDO::FETCH_BOTH)) {
        echo $row[0].' | ';
        echo $row[1].' | ';
        echo $row[2].' | ';
        echo $row[3].'<br />';
    }
}
$db = null;
?> 


위 방식에는 문제가 있더라.

결과 개수가 다르게 나온다.

제대로 처리하기 위해서는 ....

<?php
include_once 'dbinfo.php';
try{
    // MySQL PDO 객체 생성
    $db = new PDO('mysql:host='.$db['host'].';dbname='.$db['name'].';charset=utf8', $db['user'], $db['pass']);
    // 에러 출력
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e) {
    echo 'Failed to obtain database handle : '.$e->getMessage();
}
?>

<?php
class TestClass
{
    function CustomFunction($ItemName,$Quantity){
        global $db;
        $sql='select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName and Quantity=:Quantity';
        $stmt = $db->prepare($sql);
        $stmt->bindParam(':ItemName', $ItemName);
        $stmt->bindParam(':Quantity', $Quantity);
        $stmt->execute();
        if($this->CustomFunctionCount($ItemName,$Quantity)>0){
            return $stmt;
        } else {
            return false;
        }
    }

    function CustomFunctionCount($ItemName,$Quantity){
        global $db;
        $sql='select count(*) from table_items where ItemName=:ItemName and Quantity=:Quantity';
        $stmt = $db->prepare($sql);
        $stmt->bindParam(':ItemName', $ItemName);
        $stmt->bindParam(':Quantity', $Quantity);
        $stmt->execute();
        return $stmt->fetchColumn();
    }

}
?>

<?php
require_once 'pdoconn.php';
require_once 'pdoClass.php';
$test = new TestClass;

$ItemName ='수박';
$Quantity = 500;

//echo $test->CustomFunctionCount($ItemName,$Quantity).'<br />';
if($result = $test->CustomFunction($ItemName,$Quantity)){
    // Fetch 모드를 설정 및 1 row 씩 가져오기
    while($row= $result->fetch(PDO::FETCH_BOTH)) {
        echo $row[0].' | ';
        echo $row[1].' | ';
        echo $row[2].' | ';
        echo $row[3].'<br />';
    }
}
$db = null;
?>


728x90

'Web 프로그램 > PDO' 카테고리의 다른 글

[PHP] PDO MemberClass  (0) 2017.11.16
[PHP] PDO DB 연결방법  (0) 2017.11.16
MySQL 접속방식 PDO 접속방식으로 변환  (0) 2017.07.28
PDO(PHP Data Objects) 개념 및 사용법  (0) 2017.07.25
[리눅스] PDO-MYSQL 모듈 설치  (0) 2017.06.12
블로그 이미지

Link2Me

,
728x90

PDO 접속방식으로 변경하기 위해서 기존 MySQL 방식으로 만든 함수를 PDO 접속형식으로 변경하는 방법이다.


MySQL 

 PDO

 $result=mysql_query($sql);

 $stmt = $db->prepare($sql);

 $stmt->execute();

 $row=mysql_fetch_row($result)

 $row= $stmt->fetch(PDO::FETCH_NUM)

 $row=mysql_fetch_array($result)

 $row= $stmt->fetch(PDO::FETCH_BOTH)

 mysql_num_rows($result)

 $sql='select count(*) from tableName order by uid';
 $stmt = $db->query($sql);
 $num_rows = $stmt->fetchColumn();

 if($result=mysql_query($sql)){
     $row=mysql_fetch_row($result);
     return $row[0];
 } else {
     return 0;
 }

 $stmt = $db->prepare($sql);
 $stmt->execute();
 if($row=$stmt->fetch()){
     return $row[0];
 } else {
     return 0;
 }

 if($result = mysql_query($sql)){
     $row = mysql_fetch_row($result);
     if($row[0] == NULL){
         return 0;
     } else {
         return $row[0];
     }
 } else {
     return 0;
 }

 $stmt = $db->prepare($sql);
 $stmt->execute();
 if($row=$stmt->fetch()){
     if($row[0] == NULL){
         return 0;
     } else {
         return $row[0];
     }
 } else {
     return 0;
 }



728x90
블로그 이미지

Link2Me

,
728x90

mysql 확장 기능은 마침내 PHP 7에서 제거되었다.
PHP는 급격하게 성장하며 더 나은 프로그래밍 언어가 되고 있다.
PDO를 사용하는 것은 객체지향과 재사용 가능한 애플리케이션의 데이터베이스 층을 만드는 첫단계이다.

PDO(PHP Data Objects)란 여러가지 데이터베이스를 제어하는 방법을 표준화시킨 것이다.
데이터베이스는 다양한 종류가 있다. PDO 를 사용하면 동일한 방법으로 데이터베이스를 제어할 수 있다.

 

PDO를 이용하기 위한 준비사항

윈도우 Autoset9 (Apache + PHP + MySQL) 에서 PDO를 이용하려면 php.ini 파일에서 주석처리된 것을 없앤다. extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll

 

Autoset9을 다시 실행하면 phpinfo() 에서 설치된 것을 확인할 수 있다.

 

PDO 를 이용하기 위한 준비가 되었다.

PHP 5.5 이상에서 이용 가능하다. AutoSet9 을 설치하면 PHP 5.6.0 버전을 이용한다.

CentOS 6.6 에서 APM 소스 설치방법으로 하는 방법도 가능하더라. 소스 설치 스크립트를 만들어 두면 편리하다.

 

PDO 기본 사용법

$variable = new PDO(DSN, 사용자명, PW);
$variable = null; // PDO 접속 종료
DSN : 접두사:host=localhost;dbname=DB명;charset=utf8;
접두사 : DB의 종류(MySQL, Pgsql, Sqlite)

 

=== dbinfo.php ===

<?php
$db['host'] = "localhost";
$db['name'] = "address";
$db['user'] = "address_root";
$db['pass'] = "autoset";
$db['port'] = "3306";

//use mysql;
//create user address_root@localhost identified by 'autoset';
//grant all privileges on address.* to address_root@localhost;  -- 사용자 권한 부여
//flush privileges;    -- // 변경된 내용을 메모리에 반영(권한 적용)
?>

 

객체 생성 : try catch 문에서 생성한다.

===  pdoconn.php ===

<?php
include_once 'dbinfo.php';
try{
    // MySQL PDO 객체 생성
    $dbconn = new PDO('mysql:host='.$db['host'].';dbname='.$db['name'].';charset=utf8', $db['user'], $db['pass']);

    // 에러 출력
    $dbconn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e) {
    echo 'Failed to obtain database handle : '.$e->getMessage();
}
?>

 

new PDO DSN 입력할 때 실수하면 에러가 발생한다.

 

 

PDO SQL 실행

 

<?php
require_once 'pdoconn.php';
// SELECT
$sql="select uid,ItemName,Price,Quantity from table_items";
$stmt = $dbconn->prepare($sql);
$stmt->execute();

// Fetch 모드를 설정
$stmt->setFetchMode(PDO::FETCH_BOTH); // PDO::FETCH_ASSOC, PDO::FETCH_NUM
// 1 row 씩 가져오기
while($row= $stmt->fetch()) {
    echo $row[0].' | ';
    echo $row[1].' | ';
    echo $row[2].' | ';
    echo $row[3].'<br />';
}
?>

 

검색조건을 추가해보자.

위의 내용과 비교해서 달라진 점이 무엇인지 확인해보면

검색조건이 where ItemName=:ItemName 로 되어 있고, $stmt->bindParam(':ItemName', $ItemName); 한줄을 추가했고, 실제 검색어 Value 인 $ItemName ='사과'; 을 한줄 추가했다.

<?php
require_once 'pdoconn.php';
// SELECT
$sql="select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName";
$stmt = $dbconn->prepare($sql);
$stmt->bindParam(':ItemName', $ItemName); // 변수에 바인딩하기 위해 bindParam을 사용
$ItemName ='사과';
$stmt->execute();

// Fetch 모드를 설정 및 1 row 씩 가져오기
while($row= $stmt->fetch(PDO::FETCH_BOTH)) {
    echo $row[0].' | ';
    echo $row[1].' | ';
    echo $row[2].' | ';
    echo $row[3].'<br />';
}
?>
 

 

$stmt->bindValue(':id', '1', PDO::PARAM_STR);

$stmt->bindValue(':ItemName', '사과');

'1', '사과' 처럼 값을 직접 넣을 때는 bindValue 를 사용해야 한다.

 

$stmt->bindParam(':ItemName', '사과'); // bindParam 을 사용하면 에러가 발생한다.

 

 

이제 bindParam 과 bindValue 차이를 확인할 수 있는 예제를 한번 보자.

아래의 첫번째 예제는 '사과'에 대한 결과를 반환하고, 아래의 두번째 예제는 '복숭아'에 대한 결과를 반환한다.

<?php
require_once 'pdoconn.php';
// SELECT
$ItemName ='사과';
$sql="select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName";
$stmt = $dbconn->prepare($sql);
$stmt->bindValue(':ItemName', $ItemName); // 변수에 바인딩하기 위해 bindParam을 사용
$ItemName ='복숭아'; // 이건 실행안함. Value 로 이미 인식되어서 인듯....
$stmt->execute();

// Fetch 모드를 설정 및 1 row 씩 가져오기
while($row= $stmt->fetch(PDO::FETCH_BOTH)) {
    echo $row[0].' | ';
    echo $row[1].' | ';
    echo $row[2].' | ';
    echo $row[3].'<br />';
}
$dbconn = null; // DB접속 종료
?>

<?php
require_once 'pdoconn.php';
// SELECT
$ItemName ='사과';
$sql="select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName";
$stmt = $dbconn->prepare($sql);
$stmt->bindParam(':ItemName', $ItemName); // 변수에 바인딩하기 위해 bindParam을 사용
$ItemName ='복숭아';
$stmt->execute();

// Fetch 모드를 설정 및 1 row 씩 가져오기
while($row= $stmt->fetch(PDO::FETCH_BOTH)) {
    echo $row[0].' | ';
    echo $row[1].' | ';
    echo $row[2].' | ';
    echo $row[3].'<br />';
}
$dbconn = null;
?>

 

이렇게 실행해야 하는 경우가 있는지는 모르지만 결과는 두개를 반환한다.

<?php
require_once 'pdoconn.php';
// SELECT
$sql="select uid,ItemName,Price,Quantity from table_items where ItemName=:ItemName";
$stmt = $dbconn->prepare($sql);
$stmt->bindParam(':ItemName', $ItemName); // 변수에 바인딩하기 위해 bindParam을 사용
$ItemNames = array('사과','복숭아');
foreach($ItemNames as $ItemName){
    $stmt->execute();
    // Fetch 모드를 설정 및 1 row 씩 가져오기
    while($row= $stmt->fetch(PDO::FETCH_BOTH)) {
        echo $row[0].' | ';
        echo $row[1].' | ';
        echo $row[2].' | ';
        echo $row[3].'<br />';
    }
}
$dbconn = null;
?>

 

IN 으로 받은 걸 처리하는게 올바른 것인지 궁금하여 Stakoverflow 에서 검색하고 테스트 해본 결과를 적어둔다.

<?php
require_once 'pdoconn.php';
// SELECT
$ItemNames = array('사과','복숭아');
$total_items = count($ItemNames);
$question_marks = array_fill(0, $total_items, '?');
$sql='select uid,ItemName,Price,Quantity from table_items where ItemName IN ('.implode(',', $question_marks). ')';
$stmt = $dbconn->prepare($sql);
$stmt->execute(array_values($ItemNames));

// Fetch 모드를 설정 및 1 row 씩 가져오기
while($row= $stmt->fetch(PDO::FETCH_BOTH)) {
    echo $row[0].' | ';
    echo $row[1].' | ';
    echo $row[2].' | ';
    echo $row[3].'<br />';
}
$dbconn = null;
?>

 

이제 배우는 중이라 그런지 MySQLi 에 비해 불편해보이는 것도 있는거 같다.

익숙해지면 장점이 보일지도.....

 

LIKE 검색어 예제

$likeString = '%' . $string . '%';
 $stmt = $dbconn->prepare("SELECT * FROM tableName WHERE columnName LIKE ?");
 $stmt->bind_param('s', $likeString);
 $stmt->execute();

 

 

Insert 예제

 $stmt = $dbconn->prepare("INSERT INTO db_fruit (id, type, colour) VALUES (:id, :name, :color)");
 $stmt->execute(array('id' => $newId, 'name' => $name, 'color' => $color));

 $stmt = $dbconn->prepare("INSERT INTO db_fruit (id, type, colour) VALUES (? ,? ,?)");
 $stmt->execute(array($newId, $name, $color));

 $stmt = $dbconn->prepare("INSERT INTO db_fruit (`id`, `type`, `colour`) VALUES (:id, :name, :colour)");
 $stmt->bindParam(':id', $newId, PDO::PARAM_INT);
 $stmt->bindParam(':type', $type, PDO::PARAM_INT);
 $stmt->bindParam(':colour', $colour, PDO::PARAM_STR);
 $stmt->execute();

 

https://www.w3schools.com/php/php_mysql_prepared_statements.asp 게시글도 읽어보니 도움 많이 된다.

 

728x90
블로그 이미지

Link2Me

,
728x90

require문, include문 : 자주 사용하는 PHP 코드를 다른 파일에 모아 두고 필요할 때 파일을 읽어들여서 사용할 수 있도록 하는 명령


require "파일명";

- 만일 지정된 파일이 존재하지 않을 경우에는 php.ini에 설정된 include_path에서 파일을 찾는다.

- include_path에도 파일이 존재하지 않는다면 require문을 작성한 현재 파일이 있는 디렉토리에서 찾는다.

- 현재 디렉토리에도 없다면 오류표시하고 정지된다.



require와 include의 차이점

- require문은 오류가 발생했을 경우에 Fatal Error가 되어 정지가 되어버린다.

- require_once() : 지정한 파일을 한 번 읽어 들이면 같은 처리 중에는 다시 읽어 들일 수 없다.

- include문은 오류가 발생했을 경우에 Warning을 출력하고 처리코드를 수행한다.

- include_once() : 지정된 파일을 이미 읽어 들어 경우에는 다시 파일을 읽어 들이지 않는다.



728x90
블로그 이미지

Link2Me

,
728x90

https://www.apachefriends.org/index.html 에서 XAMPP 를 다운로드한다.

설치를 했더니 바로 설치가 안되고 에러가 발생하면서 안된다.


원인은 443 포트를 VMware 사용하고 있어서라고 에러 메시지가 나온다.

AutoSet9 은 강제로 서비스를 OFF 시킨 상태다.

처음 설치하는 PC에서는 이상없이 80, 443 포트가 설치될 것이다.




https://stackoverflow.com/questions/18300377/xampp-apache-error-apache-shutdown-unexpectedly

에 나온 내용을 참조해서 443 포트를 4433으로 변경했다.



좀 더 직관적으로 환경설정 파일을 설정할 수 있도록 되어 있는 점은 상당히 편리한 거 같다.

보통 APM(Apache + PHP + MySQL)을 배울 때 환경설정하는 부분을 잘 모르는 경우가 많은데 이 툴은 이런 부분을 세부적으로 Control 할 수 있게 만든 것은 마음에 든다.


Config 에서 Editor 도 editplus.exe 로 경로를 변경해주었더니 EditPlus 로 연결된다.



httpd.conf 파일 내용을 아래와 같이 수정했다.

기존에 설치했던 경로를 그대로 이용하기 위해서다.


DocumentRoot "C:/AutoSet9/public_html/workspace"
<Directory "C:/AutoSet9/public_html/workspace">
    Options FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

#DocumentRoot "C:/xampp/htdocs"
#<Directory "C:/xampp/htdocs">
#    Options FollowSymLinks Includes ExecCGI
#    AllowOverride All
#    Require all granted
#</Directory>



IDE 개발툴은 https://netbeans.org/downloads/ 에서 PHP x64 버전을 다운로드 받는다.



5번에서 workspace 는 지워야 한다. 왜냐하면 default path 가 workspcae 까지 이기 때문이다.




여기까지 하면 툴 사용에 대한 기본 설정은 한 셈이다.

728x90
블로그 이미지

Link2Me

,
728x90

Feature differences between mysqli and PDO:

Feature PDO MySQLi
Database support 12 different drivers MySQL and MariaDB
Interface OOP OOP and procedural
Named parameters Yes No
Object mapping Yes Yes
Prepared statements Yes Yes
Non-blocking, asynchronous queries with mysqlnd support No Yes
Multiple Statements support Most Yes
MySQL 5.1+ functionality support Most All

 

MySQL PDO 연결을 시도했더니 에러가 발생한다. 확인해보니 PDO 설치가 되어 있지 않다.

 

 

# 설치여부 확인
/usr/local/php/bin/php -i | grep PDO

 

# 설치한 PHP와 동일한 버전의 소스 다운로드 후 압축해제
# 소스파일에서 설치할 확장모듈과 동일한 이름의 경로로 이동
cd /usr/local/APM/php-5.6.30/ext/pdo_mysql
# phpize 파일을 실행한다.
/usr/local/php/bin/phpize
# configure 및 compile
./configure --with-php-config=/usr/local/php/bin/php-config --with-pdo-mysql=/usr/local/mysql
make
# make 명령어를 실행후 cd modules/ 하고 ll 하여 pdo_mysql.so 파일이 생성되었는지 확인한다.
cd ..
cp -arp modules/pdo_mysql.so /usr/local/php/lib/php/extensions/no-debug-zts-20131226
cd /usr/local/php/lib/php/extensions/no-debug-zts-20131226

# php.ini 파일을 열어 extension=확장모듈.so 파일을 추가한다.
extension=/usr/local/php/lib/php/extensions/no-debug-zts-20131226/pdo_mysql.so

# 아파치(Apache) 재시작한다.
/usr/local/apache/bin/apachectl restart

파일이 설치되었는지 확인한다.
확인방법은
/usr/local/php/bin/php -i | grep PDO
또는 phpinfo()함수로 확인한다.

 

 

728x90
블로그 이미지

Link2Me

,
728x90

APM(Apache + PHP + MySQL) 소스설치 방식으로 PHP 버전은 5.6.30 버전을 설치했더니

Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead  메시지가 뜨면서 접속이 제대로 안된다.


기존 모든 소스를 수정해야 하나, APM 소스 설치를 다시 해야 하나 고민되어 검색을 했더니 이렇게 하라고 나온다.


$link = @mysql_connect($db_server,$db_user_name,$db_password);

@ 를 앞에 붙이라고 나온다.

오류 제어 연산자(@) : PHP의 코드가 오류가 있을 때 오류 메시지를 무시하도록 하는 연산자

오류 메시지를 어떤 상황에서든 출력하지 않도록 하고자 할 때 사용하는 연산자이다.


<?php
error_reporting(E_ALL ^ E_DEPRECATED);

이렇게 추가하라고 알려주기도 한다.


원인이 The mysql_* functions has been deprecated as of PHP 5.5.0 버전 이상부터 나오는 현상이란다.


근본적으로는 mysqli_* 방식이나 PDO 방식으로 변경해야 되는데 전부 수정하기에는 엄두가 나지 않는다.

일단 앞에 오류제어 연산자 @를 붙이는 방법으로 임시 조치하여 사용해보고 있다.


PHP 5.4.6 에서 정상 동작하는 코드가 PHP 5.6.30 에서 동작되지 않는 것이 있었는데 코드를 세부적으로 살펴보니 낮은 버전에서는 MySQL 연결 문법을 대략 생략해도 동작되던게 동작되지 않는 증상이라 수정처리했더니 잘 동작한다.


APM 소스 설치시 옵션에 따라서 약간씩 설치가 되는 사이트도 있고 아닌 곳도 있어서 설치하면서 고생을 좀 했다.

phpize 를 이용해서 미처 설치하지 못한 옵션을 추가하는 방법도 알게되고 난 후에는 여러모로 APM 소스 설치가 편리하기는 하더라.

PHP 5.6.30 에서 속도가 좀 더 개선(?)된 느낌이 들기는 한다.


다시 정리하자면

기존 코드 수정을 최소화하면서 이용하는 방법은

mysql_connect( $this->host, $this->user, $this->pass ) or die('error connection');

@mysql_connect( $this->host, $this->user, $this->pass ) or die('error connection');

로 해주면 없어진다.

mysql_connect( $this->host, $this->user, $this->pass ) or die('error connection');

@mysql_connect( $this->host, $this->user, $this->pass ) or die('error connection');

로 해주면 없어진다.



출처: http://link2me.tistory.com/1167 [소소한 일상 및 업무TIP 다루기]


제대로 변경하려면

연동에 사용되는 함수를 찾아서 전부 변경해줘야 한다.

$connect = mysqli_connect('localhost', 'user', 'password', 'dbname');


replace all mysql_* functions into mysqli_* functions


//PHP 5.4 o earlier (DEPRECATED)
$con = mysql_connect($host,$user,$password) or exit("Connection Error");
$conn = mysql_select_db($db, $con);

mysql_query("set names utf8");


//PHP 5.5 (New method)
$conn =  mysqli_connect($host,$user,$password,$db);



PDO (PHP Data Objects) 방식으로 연동

- 연동방식으로 만든 함수를 많이 수정해야만 정상동작되는거 같다. 기존 함수를 변환시 주의할 점이 좀 있다.

$conn = new PDO($hostDb, $user, $password);
또는

$conn = new PDO('mysql:host=localhost;dbname={$dbname};charset=utf8', $user, $password);

로 한다.


728x90
블로그 이미지

Link2Me

,
728x90

네이버 지식인에 문의사항이 있길래 테이블 생성하는 방법을 적는다.


테이블 설계는 보통 phpMyAdmin 상에서 하면 편리하므로 굳이 PHP 코드 상에서 할 필요성을 느끼지 못해서 적어두지 않았다.


테이블 생성은 간단하다.


==== dbinfo.php ===

<?php
$db['host'] = "localhost";
$db['name'] = "csharp";
$db['user'] = "root";  // 원래는 root 사용하면 안되는데 연습용인지라...
$db['pass'] = "autoset";
$db['port'] = "3306";
?>


==== dbconnect.php ===

<?php
include_once 'dbinfo.php';
$connect = isConnectDb($db);

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;
    }
}
?>


==== tablecreate.php ====

<?php
include_once 'dbconnect.php'; // DB 연결

$sql ="
CREATE TABLE IF NOT EXISTS member (
no int(11) not null auto_increment,
id varchar(15) NOT NULL,
user_id varchar(15) NOT NULL,
name varchar(15) NOT NULL,
nick_name varchar(15),
birth varchar(8),
sex varchar(6),
tel varchar(8),
email varchar(40),
pw varchar(32) NOT NULL,
addr_1 varchar(100),
addr_2 varchar(100),
level int,
regdate char(20),
ip varchar(20),
PRIMARY KEY(no)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
";

$result=mysqli_query($dbconn,$sql);
if($result) {
    echo "DB table created !!!";
} else {
    echo "Error creating table : " . mysqli_error($dbconn);
}

mysqli_close($dbconn);
?>


여기서 한가지 알아야 할 사항은 테이블 생성시 CREATE TABLE IF NOT EXISTS member 를 하면 결과는 OK 로만 나온다. 이미 테이블이 생성되어 있으면 에러 메시지를 보이도록 하려면 CREATE TABLE member 로 수정해주면 된다.


테스트에 사용된 코드 첨부


tablecreate.zip




728x90
블로그 이미지

Link2Me

,
728x90

오늘 Android Studio 기반 어플을 테스트 하는데 PHP Array 를 잘못 구현해서 삽질을 한참 했다.


 // 사무실번호, 휴대폰번호, 이름, 소속 등의 정보를 추출
$sql = "select userNM,mobileNO,telNO,sosok from Person ";
$sql .= "where mobileNO='".$search."'";

$R = array(); // 결과 담을 변수 생성
$result = mysqli_query($dbconn,$sql);
while($row = mysqli_fetch_object($result)) {
    array_push($R, $row);
}
echo json_encode(array("result"=>$R));


모든 정보를 select 문으로 가져와서 array_push 에 담은 경우에는 아무런 문제가 되지 않는다.


그런데 sosok 정보를 서브 쿼리로도 가져오기가 힘든 경우가 생겼다.

그래서 함수를 만들어서 해당 정보를 가져와서 array_push 에 저장할 때 아무 생각없이 코딩을 했더니 원하는 결과가 나오지 않고 엉뚱한 결과가 나온다.


$search = add_hyphen_telNo($search);
$sosok = Phone2Dept($search);

// 사무실번호, 휴대폰번호, 이름, 소속 등의 정보를 추출
$sql = "select userNM,mobileNO,telNO from SYS_MEMBER ";
$sql.= "where mobileNO='".$search."' or telNO='".$search."' ";

$R = array(); // 결과 담을 변수 생성
$result = mysqli_query($dbconn,$sql);
while($row = mysqli_fetch_object($result)) {
    array_push($R, $row);

    array_push($R, array("sosok"=>$sosok));

}

echo json_encode(array("result"=>$R));
 


잘못된 결과가 나온다.


아래와 같이 풀어서 해결했다. 풀어서 array_push 에 담으면 암호화된 코드를 추가하기도 편하다.


$sosok = Phone2Dept($search);

// 사무실번호, 휴대폰번호, 이름, 소속 등의 정보를 추출
$sql = "select userNM,mobileNO,telNO from SYS_MEMBER ";
$sql.= "where mobileNO='".$search."' or telNO='".$search."' ";

$R = array(); // 결과 담을 변수 생성
$result = mysqli_query($dbconn,$sql);
while($row = mysqli_fetch_array($result)) {
    array_push($R, array("userNM"=>$row[0],"mobileNO"=>$row[1],"telNO"=>$row[2],"sosok"=>$sosok));
}
echo json_encode(array("result"=>$R));


select 문 하나로 해결이 안되는 걸 json 방식으로 안드로이드폰과 통신하려면 위와 같은 방법으로 데이터를 추가하면 해결될 수 있다.


참고 : How to Use JSON Data with PHP or JavaScript

https://www.taniarascia.com/how-to-use-json-data-with-php-or-javascript/


Android 코드에서는

    int responseCode = conn.getResponseCode();
    //System.out.println("GET Response Code : " + responseCode);
    if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
        bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String json;
        while((json = bufferedReader.readLine())!= null){
            sb.append(json + "\n");
        }
    }
    bufferedReader.close();
}
System.out.println("PHP Comm Out : " + sb.toString());


로 로그 결과를 확인해서 보낸 데이터가 원하는 형태로 넘어오는지 확인을 꼭 하는게 좋다.

              

728x90
블로그 이미지

Link2Me

,