본문 바로가기

프로그래밍 공부/PHP

PHP - 메일 수신, 외부 명령 실행 그리고 클래스

메일 수신

웹 페이지에서 메일 서버에 접속하여 메일을 수신하려면 PHP에 imap기능을 사용해야 한다.

IMAP

IMAP은 Internet Message Access Protocol의 약자로 메일 서버에 메일을 수신하기 위한 프로토콜이다. PHP에서 imap을 사용하려면 php.ini 파일에 imap기능을 열어두어야 한다. Mac과 Linux에 경우는 imap 기능이 기본적으로 사용하도록 되어 있다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<html>
<head>
    <title>PHP 테스트</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
    매일 수신:
    <?php
        $username = "username@gmail.com";
        $password = "password";
        $mailserver = "imap.gmail.com";
        
        // POP3 서버
        //$mailbox = @imap_open("{" . $mailserver . ":110/pop3}INBOX", $username, $password);
        
        // IMAP 서버
        //$mailbox = @imap_open("{" . $mailserver . ":143/imap/novalidate-cert}INBOX", $username, $password);
        
        // Gmail 서버
        $mailbox = imap_open("{" . $mailserver . ":993/imap/novalidate-cert/ssl}INBOX"$username$password);
        
        if($mailbox){
            $mails = imap_check($mailbox);
            $count = $mails->Nmsgs;
            if($count >= 1){
                ?>
                메일이 <?=$count?>건 있습니다.<br>
                <table border=1>
                    <tr>
                        <td>No</td>
                        <td>제목</td>
                        <td>날짜</td>
                        <td>발신자</td>
                        <td>크기</td>
                    </tr>
                    <?php
                        for($num = 1$num <= $count$num ++){
                            $head = imap_header($mailboxk$num);
                            $body = imap_body($mailbox$num, FT_INTERNAL);
                            ?>
                            <tr>
                                <td><?=$num?></td>
                                <td nowrap><?=htmlspecialchars(mb_decode_mimeheader($head->subject))?></td>
                                <td nowrap><?=$head->date?></td>
                                <td nowrap><?=htmlspecialchars(mb_decode_mimeheader($head->fromaddress))?></td>
                                <td nowrap><?=$head->Size?></td>
                            </tr>
                            <?php
                        }
                    ?>
                </table>
                <?php
            }else{
                ?>
                새로운 메일이 없습니다.<br>
                <?php
            }
            imap_close($mailbox);
        }else{
            ?>
            사용자명 또는 패스워드가 틀립니다.
            <?php
        }
    ?>
</body>
</html>
cs

메일 서버에 접속하려면 접속할 메일 서버명과 사용자명, 패스워드를 imap_open(); 함수에 지정한다. imap_open(); 함수는 메일 서버에 접속하기 위한 함수이다. 서버는 POP3, IMAP 그리고 Google의 Gmail 서버가 있는데 Gmail은 말그대로 구글의 메일 서버를 의미하고 POP3의 POP는 Post Office Protocol의 약자로 메일 소프트웨어 이다. IMAP과의 차이점으로 POP는 메일을 한꺼번에 내려받을 수 있지만 IMAP에서는 제목과 발신인을 확인하여 수신 여부를 결정할 수 있다.

imap_open(); 함수 앞에 @는 오류 메세지를 무효로 처리하여 경고 문구가 표시되지 않게 한다. pop3앞에 110은 접속 포트 번호로 pop3 서버에 접속할 수 있다. INBOX는 사용자 개인의 메일 박스를 의미한다. 접속에 성공하면 $mailbox에 IMAP 스트림이 할당된다. imap_check(); 함수는 메일 박스의 정보를 객체(object)로 돌려준다. 

객체를 $mails에 할당하여 $mail->Nmsgs를 작성하면 메일 박스 안에 있는 메세지의 개수를 참조할 수 있다. 이때 이 메세지의 개수를 $count에 할당하고 if 조건문으로 $count >= 1의 조건으로 메세지가 1개 이상있으면 블록안에 처리를 실행시킨다.

다음으로 for 반복문으로 $count(메세지의 개수)를 반복처리한다. imap_header(); 함수는 제목이나 날짜와 같은 메일의 정보를 넣어둔 객체를 돌려준다. $head에 객체를 할당하고 $head->subject로 작성하면 제목을 참조할 수 있다.

메일의 제목은 MIME 인코드(암호화)되어 있기 때문에 mb_decode_mimeheader(); 함수에 넣어 제목을 디코드(복호화)해야 한다. htmlspecialchars(); 함수로 제목에 포함된 HTML 태그를 무효로 처리할 수 있다. 다음으로 $head->data로 날짜로 $head->Size로 메일의 크기를 참조한다.

마지막으로 메세지의 개수만큼 처리를 끝내면 imap_close(); 함수로 IMAP 스트림을 닫는다.

접속 포트

인터넷에서 IP 주소가 네트워크 상의 주소가 된다. 일반적으로 하나의 IP 주소에서 여러 대의 컴퓨터와 동시에 통신하기 위해서 포트라틑 번호를 사용한다. POP3 서버와 접속하려면 110번 포트, FTP 서버와 접속하려면 21번 포트, 웹 서버는 80 포트로 정해져 있다. IP 주소와 포트를 조합한 것을 소켓이라 부르고 데이터 송수신은 소켓 단위로 한다.

외부 명령 실행

PHP에서 시스템의 내장된 명령을 실행하려면 system(); 함수와 exec(); 함수 등을 사용한다.

exec(); 함수

1
2
3
4
5
6
7
8
9
<?php
    // Windows
    $result = exec("dir");
    print mb_convert_encoding($result"UTF-8""EUC-KR");
    
    // Mac, Linux
    //$result = exec("ls");
    //print $result;
?>
cs

exec(); 함수를 사용하면 시스템의 명령을 실행할 수 있다. exec(); 함수로 명령을 실행하면 실행결과의 마지막 한 행만 문자열로 돌려준다. 여기서 dir을 실행(dir은 파일 목록 표기)하면 실행 결과의 마지막 한 행을 $result에 할당(Mac과 Linux에 경우 ls 명령으로 실행)한다. Windows는 출력된 결과가 EUC-KR이기 때문에 mb_convert_encodeing(); 함수로 EUC-KR에 UTF-8로 인코드한다.

여기서 말하는 외부명령이란 명령 프롬프트나 터미널에서 실행할 수 있는 프로그램을 말한다. Windows에 경우 dir(파일 목록), ren(파일명 변경), data/t(오늘 날짜 표시) 등이 있다.

system(); 함수

1
2
3
4
5
6
7
8
9
<?php
    header('Content-Type: text/html; charset=EUC-KR');
    // Windows
    $files = system("dir");
    // Mac, Linux
    //$files = system("ls");
    print "<br>";
    print $files;
?>
cs

system(); 함수에 경우 실행 시에 결과를 모두 데이터로 출력한다. 반환 값을 받는 변수에는 결과 데이터의 마지막 한 행만 할당이 된다. system(); 함수에 ls(Windows는 dir)를 지정하여 실행하면 PHP 파일을 실행하고 있는 디렉터리 안에 있는 파일명이 출력이 된다. system(); 함수 결과를 $files에 넣어두고 print문에서 출력한다. 

` 연산자

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
    <title>PHP 테스트</title>
</head>
<body>
    <pre>
        <?php
            $ls = `/bin/ls -l /etc`;
            print $ls;
        ?>
    </pre>
</body>
</html>
cs

backtick 연산자는 exec(); 함수나 system(); 함수와 달리 명령 실행 결과를 모두 변수에 할당하는 기능을 한다. 위의 코드(/bin/ls)는 Linux에서 명령으로 /etc 디렉터리 안의 파일 목록을 표시하는 명령이다. Windows에서는 /usr/bin/php 이다.

명령행에서 PHP 실행

메일 데이터를 저장하는 작업과 같이 정해진 시간에 프로그램을 동작시키려면 명령행에서 PHP 파일을 실행한다. 호스팅 서버에서 작업할 때를 예를 들면 PHP 파일을 실행하려면 PHP 명령의 전체 경로가 필요하다. SSH를 이용할 수 있으면 which 명령으로 찾을 수 있다. 

1
2
3
4
5
6
7
8
<?php
    $result = "/usr/bin/php /path/test.php";
    if($result){
        "/usr/bin/php /path/test1.php";
    }else{
        "/usr/bin/php /path/test2.php";
    }
?>
cs

PHP 명령의 전체 경로를 알았다면 명령행에서 실행하고 싶은 프로그램을 실제로 실행한다. backtick 연산자(/usr/bin/php)뒤에 path 부분은 실제 경로를 입력한다. 

보안

명령의 인수나 폼에서 사용자가 입력한 데이터를 사용하는 처리에서는 반드시 보안에 신경 써야한다. ;를 이용해 여러개의 명령을 연결할 수 있어 명령에 덧붙여 파일을 삭제하거나 암호로 변경하는 명령을 간단하게 실행할 수 있다. ;과 같은 명령을 실행하는 셸에서 의미가 있는 문자열은 escapeshellcmd(); 함수로 이스케이프(무효화)하여 사용한다. 비슷한 함수로 escapeshellarg(); 함수가 존재한다.

클래스

객체지향 언어는 소프트웨어를 개발하는 경우 개발 대상을 물리적 또는 추상적 대상에 비유하여 생각한다. 이러한 객체지향은 대부분의 프로그래밍 언어에 적용되어 있다. Java와 PHP는 이런 객체지향 언어중 클래스(class)를 기반으로 하는 객체지향 언어이고, JavaScript에서는 ECMAScript 6 버전부터 클래스를 지원하지만 기본적으로 프로토타입(prototype)을 기반으로 하는 객체지향 언어이다.

PHP에서 처리의 양이 많아질 때 JavaScript와 마찬가지로 function(){}을 사용한다. 

객체지향이 필요한 이유는 페이지의 수가 늘어나고 사용하는 함수도 많아져 프로그램의 크기가 커지면 일일히 함수의 이름을 붙이기도 어려워지고 각 함수의 공통 변수(데이터)를 여기저기 가지고 다니는 것도 번거롭게 되기 때문이다. 객체는 처리와 데이터가 설정되기 때문에 유지 보수가 편해진다. 클래스의 내부에서는 처리와 데이터를 정의한다. 객체지향에서는 처리를 메서드, 내부의데티어를 멤버라고 부른다.

멤버(내부 데이터)
메서드 1(처리 1)
메서드 2(처리 2)
메서드 3(처리 3)

실제로 클래스를 컴퓨터의 메모리로 읽어 들이는 모습은 위의 표와 같다.

1
2
3
4
5
6
7
8
<?php
    class 클래스명{
        public $변수 = "문자열";
        public function 함수명(){
            // 처리할 내용을 기술한다.
        }
    }
?>
cs

PHP에서 클래스를 정의하려면 class 키워드를 사용한뒤 클래스명을 지정해야 한다. 위의 예시가 클래스를 선언하는 방법이다. 클래스명에는 PHP의 예약어를 제외한 모든 이름을 사용할 수 있다. 다음으로 클래스 안에 멤버나 메서드를 정의한다. 멤버는 변수, 메서드는 함수라고 생각하면 편하다.

여기서 멤버나 메서드를 public, private, protected와 같이 정의 한다.

여기서 말하는 예약어란 foreach나 function 등 PHP에서 의미를 가진 키워드나 $POST와 같이 특별한 변수명으로 변수, 함수명 그리고 클래스명에서는 사용할 수 없다.

명칭(또는 함수 이름) 형태 설명
imap_open(POP3) $변수 = imap_open("{yourmailserver:110/pop3}INBOX", user_id, password); POP3 110번 포트로 접속
imap_open(IMAP) $변수 = imap_open("{yourmailserver:143/imap/novalidate-cert}INBOX", user_id, password); IMAP 서버 143번 포트로 접속
imap_open(Google) $변수 = imap_open("{yourmailserver:993/imap/novalidate-cert/ssl}INBOX", user_id, password); Google의 imap.gmail.com의 993 포트로 접속
imap_check imap_check($변수); 메일 박스의 정보를 객체로 리턴
imap_header imap_header($변수, $변수); 제목, 날짜 등 메일 정보 객체 리턴
mb_decode_mimeheader mb_decode_mimeheader($변수->참조할 정보); MIME 인코드(암호화)를 디코드(복호화)한다.
htmlspecialchars htmlspecialchars($변수); HTML 태그를 무효로 처리
imap_close imap_close($변수); IMAP 스트림 종료
exec exec("외부 명령"); 외부 명령을 실행
mb_convert_encoding mb_convert_encoding($변수, 바뀔 인코드, 바꿀 인코드); 출력될 charset을 바꿈
system system("외부 명령"); 실행 결과를 받음
backtick $변수 = `/bin/ls -l /파일 경로`; Mac, Linux 에서 실행 결과를 모두 변수에 할당
  $변수 = "/usr/bin/php 파일 경로"; Windows 에서 실행 결과를 모두 변수에 할당
escapeshellcmd $변수 = escapeshellcmd("문자열"); ;과 같은 명령을 실행하는 셸에서 의미가 있는 문자열을 무효화시킴
escapeshellarg $변수 = escapeshellarg("문자열"); "로 묶인 문자열에 "가 있으면 삭제하고 단일 인수만 명령으로 넘김
class class 클래스명{} 클래스(class) 선언
function function 함수명(){처리할 내용} JavaScript의 함수와 동일