개발시 디버깅을 위해 필수적으로 필요한 것이 로그이다. 이는 개발시 뿐만 아니라 운영중에 오류가 발생했을 때도 오류의 원인을 찾는데 커다란 역할을 하는 중요한 개발 요소 중의 하나이다.

PHP에서 개발자의 로그를 어떻게 남기는지 몰라서 계속해서 var_dump($variable);로 브라우저 화면에 로그를 남겨서 본 뒤에 그 로그 출력 코드를 삭제해 버리는 방식으로 작업을 했다.
대다수의 PHP 개발자들이 var_dump(), print_r() 함수등을 이용해서 위와 같은 방식으로 로그를 기록해서 디버깅을 하는 것 같다. 하지만 이는 매우 좋지 못한 방식이다.

 * 로그를 출력 한 뒤에 로그 출력 코드를 항상 지워야만 한다. 그렇지 않으면 로그가 브라우저를 통해 사용자에게 전달 돼 버리므로.
 * 이로인해 매우 개발이 귀찮기 짝이 없는 작업이 된다.
 * 실제 운영 시간에 발생한 오류에 대해 에러 메시지만을 얻을 수 있을 뿐 그 당시 상태에 관한 로그를 전혀 얻을 수가 없다.


그러다가, 너무 짜증이나서 간단한 로깅 함수를 만들어서 바꾸었다.
그러나 여전히 맘에 안든다. 로그 레벨 지정이나, 로그 파일 날짜별로 돌리기 등 기능이 너무 부족하다.

PHP에서 로깅 전략에 대한 글을 찾아보니 우리나라에서는 거의 없는거 같고, devshed에 Logging With PHP라는 좋은 글이 있었다.
그 중에서도 PEAR Log Package를 이용하기로 하였다. PEAR에는 여러 좋은 패키지들이 많이 깔끔하게 정리되어 있는 것 같다. 근데, 우리나라 PHP 개발자들은 아직 잘 안쓰는건가? 관련 글을 찾아보기가 쉽지 않네.

그 외에도 Log4PHP가 있는데, 사실 이걸 쓰고 싶은 생각이 정말 많이 들었었다. Apache에서 만든 것이고, 이미 내게 익숙한 Log4J의 PHP 포팅이기 때문이다. 게다가 설정 파일을 통해 언제든지 로깅 방식을 변경할 수도 있다(Log4J처럼). 하지만, 참은 이유는.... 2003년 이후 버전업이 전혀 안되고 있기 때문이다. 4년씩이나 버전 업이 안되는 건 좀 이해가 안간다.

PEAR::Log 사용법과 로그 레벨 지정, 날짜별 파일로 로그 남기기, 로그를 남길 때마다 어떤 PHP파일의 몇 번째 줄에서 로그를 남겼는지 출력하기, PHP의 오류 메시지를 PEAR::Log를 이용해 로그로 남기기 등의 기법을 정리해 본다. 대부분의 내용은 PEAR::Log의 매뉴얼을 정리한 것이다.

설치


 

$ pear install log



 

로깅 객체 얻기


 

require_once 'Log.php';

$logger = &Log::singletone('file', 'C:/temp/test.log', 'Test'); # 로깅을 위한 객체 얻기
$logger->log('이래 저래 에러가 났잖아욧!'); # 로그 메시지 출력하기


싱글턴으로 객체를 가져옴으로써 불필요한 객체 생성과, 객체가 소유하고 있는 리소스의 중복 정의를 방지한다.

&Log::singleton($handler, $name, $ident, $conf, $maxLevel);


 * $handler : 로그 핸들러 타입. console, file 등 로그 출력을 처리하는 방식을 지정한다.$name : 파일 이름과 같이, 로그를 기록할 자원을 지정한다. 핸들러 구현체에 따라서 이 파라미터가 뭘로 사용될지 결정된다.
 * $name : 로그 파일명 등을 의미한다. $handler에 따라 의미가 달라진다.
 * $ident : 로그 구분자이다. Java의 Log4j에서 클래스 FQCN과 같은 역할을 한다.
 * $conf : 연관 배열로 핸들러에 사용자 정의 설정 정보를 넘겨준다.
 * $maxLevel : 최대 로그 레벨을 결정한다. 기본값은 PEAR_LOG_DEBUG(최대값)이다. 이 레벨보다 낮아야 로그가 기록된다.

$logger->log("로그메시지", PEAR_LOG_NOTICE);와 같은 방식으로 로그를 기록한다.

로그 메시지 부분에는 문자열 뿐만 아니라, 일반 객체를 지정해도 된다. 객체가 getString(), toString() 혹은 __toString() 메소드를 구현하고 있다면 그 메소드를 실행해서 메시지를 기록한다. 모두 없다면 객체를 직렬화한 값을 출력할 것이다.

만약 배열과 같이 복잡한 타입의 변수 값을 제대로 찍고 싶다면 $logger->log($variable) 처럼, 문자열로 바꾸지 말고, 변수를 그대로 인자로 넘겨주면, var_dump($variable) 한 것처럼 변수 내용을 찍어준다.

로그 레벨


로그 레벨 옆의 메소드명은 log() 메소드에 로그 레벨을 지정하는 대신, 해당 메소드를 직접 호출하면, 그 로그 레벨로 로그가 남게 됨을 의미한다.
즉, $logger->log("로그메시지", PEAR_LOG_DEBUG)$logger->debug(" 로그메시지") 와 동일한 역할을 한다.
 * PEAR_LOG_EMERG emerg() 시스템이 사용 불가 상태에 빠졌다.
 * PEAR_LOG_ALERT alert() 즉시 처리가 필요하다.
 * PEAR_LOG_CRIT crit() 심각한 상태이다.
 * PEAR_LOG_ERR err() 오류
 * PEAR_LOG_WARNING warning() 경고
 * PEAR_LOG_NOTICE notice() 주의
 * PEAR_LOG_INFO info() 정보
 * PEAR_LOG_DEBUG debug() 디버그 메시지

로그 레벨 마스크(mask)


특정 로그 레벨의 로그 메시지만 출력하도록 지정한다.

$mask = Log::UPTO(PEAR_LOG_INFO);
$logger->setMask($mask);


최소 PEAR_LOG_INFO 까지 로그 메시지만 출력한다. 즉, PEAR_LOG_EMER ~ PEAR_LOG_INFO 까지 메시지만 로그로 남긴다.

$mask = Log::MASK(PEAR_LOG_NOTICE) | Log::MASK(PEAR_LOG_DEBUG);


PEAR_LOG_NOTICE와 PEAR_LOG_DEBUG 레벨의 메시지만을 로그로 남긴다.
 * PEAR_LOG_ALL 모든 로그 레벨을 포함하고 있는 마스크
 * PEAR_LOG_NONE 어떤 로그 레벨도 포함하지 않고 있는 마스크

$mask =PEAR_LOG_ALL ^ Log::MASK(PEAR_LOG_NOTICE);


PEAR_LOG_NOTICE만 제외하고 로그를 남긴다.

$logger->getMask();


현재 로거의 로그 마스크를 가져온다.

로그 이벤트 내보내기


몇몇 로그 핸들러는 로그 메시지를 버퍼링 한다. 버퍼링된 로그 메시지를 모두 출력하도록 하려면,

$logger->flush();
혹은,
$logger->close();


로거를 닫아도 flush가 된다.

로그 핸들러


로그 핸들러는 로그를 어떤식으로 처리할지를 결정하는 것이다. 모든 로거는 로그 핸들러를 지정해야 한다.
로그 객체를가져올 때 'console' 하는 식으로 지정하는 것이다. 구체적인 사용법은 매뉴얼을 참조한다.
 * console : 화면상에 로그를 출력한다.
 * display : 웹 브라우저에 로그를 출력한다.
 * error_log : PHP의 error_log 함수를 사용해서 로그를 출력한다.
 * file : 파일로 로그를 출력한다.
 * mail : 이메일로 로그를 전송한다.
 * null : 로그를 무시한다.
 * sql : DB에 로그를 저장한다.
 * sqlite : SQLite DB에 로그를 저장한다.
 * syslog : PHP의 syslog() 함수를 이용해서 로그를 저장한다.
 * win : 브라우저의 새창으로 로그를 출력한다.
 * composite : 여러 핸들러를 함께 사용할 수 있게 해준다.

$console = &Log::singleton('console', '', 'TEST');
$file = &Log::singletone('file', 'out.log', 'TEST');
$composite = &Log::singleton('composite');
$composite->addChild(&console);
$composite->addChild($file);
$composite->log('blah blah blah...');
$composite->removeChild($file); # 로그 핸들러 삭제





 

File 로그 핸들러


file 로그 핸들러를 제일 많이 사용할 것이기 때문에, 이에대해 정리한다.
 * 설정값 : 설정키(Type, 기본값)
  o append(boolean, true) : true이면 기존의 로그파일에 계속 추가해서 쓰고, false이면 기존 파일을 삭제하고 새로 쓴다.
  o mode(integer, 0644) : Unix 계열에서 파일의 허가권 지정
  o eol(string, OS default) : 줄끝 문자
  o lineFormat(string ,%1$s %2$s [%3$s] %4$s]) 로그 출력 형태 지정
  o timeFormat(string, %b %d %H:%M:%S) : 시간 출력 형태 지정(strftime 함수로 시간을 출력한다.)
 * 설정값의 lineFormat
  o %1$s : 날짜와 시간
  o %2$s : $ident
  o %3$s : 로그 레벨
  o %4$s : 로그 메시지
  o %5$s : log() 메소드를 호출한 파일명
  o %6$s : log()메소드를 호출한 줄 번호
  o %7$s : log()메소드를 호출한 함수명
 * 예제

$conf = array('mode'=>0600, 'timeFormat'=>'%X %x');
$logger = &Log::singletone('file', 'out.log', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $1");
}



표준 오류 출력을 PEAR::Log 패키지로 위임하기


PHP 사용중 발생하는 예외와 오류를 PEAR::Log 패키지를 이용해 처리하도록 지정할 수 있다.
이것은 set_error_handler() 함수를 이용해서 가능하다.

function errorHandler($code, $message, $file, $line)
{
    global $logger;
    /* Map the PHP error to a Log priority. */
    switch ($code) {
        case E_WARNING:
        case E_USER_WARNING:
            $priority = PEAR_LOG_WARNING;
            break;

        case E_NOTICE:
        case E_USER_NOTICE:
            $priority=PEAR_LOG_NOTICE;
            break;

        case E_ERROR:
        case E_USER_ERROR:
            $priority = PEAR_LOG_ERR;
            break;

        default:
            $priority = PEAR_LOG_INFO;
    } 
    $logger->log($message . ' in ' . $file . ' at line ' . $line, $priority);
} // end of function

set_error_handler('errorHandler');

# test
trigger_error('This is an information log message.', E_USER_NOTICE);



나는 이렇게 사용한다


강력한 설정기능 등이 Log4php에 비해 많이 딸리는 편인데, 어쨌든, 나는 아래 처럼 사용한다.
아래에서 정의한 logger() 함수는 PEAR::Log를 이용해서, 날짜별로 로그 파일을 남기는 것이다.
그리고, 로그를 남길 때는 항상 로그를 찍은 소스 파일명과 로그를 찍은 코드의 줄 번호도 함께 출력하도록 하였다.
파일 이름은 logger.php라고 치자.


위 logger()메소드는 다음 처럼 간단히 활용할 수 있다.

<?php
// 로그레벨 설정 상수가 Log.php에 들어 있기 때문에
// 로그 레벨 설정과 logger.php보다 Log.php를 먼저 require 해줘야 한다.
require_once 'Log.php';

define('LOG_FILENAME', 'C:/Temp/test.log');
define('LOG_LEVEL', PEAR_LOG_DEBUG);
define('PHP_LOG_LEVEL', PEAR_LOG_WARNING);

require_once 'logger.php';

echo "Why?<br/>";

logger()->log("하하..", PEAR_LOG_INFO);
logger()->debug("허허.. 이건 DEBUG레벨");
logger()->alert("경고야!!");
$arr = array("key" => "value",
    "array-" => "var_export_str 함수를 이용해서 감싸서 로그를 남기세요."
);
logger()->info("객체나 배열같은 변수를 로깅 하는 방법은 두가지가 있다.");
logger()->info($arr); # 이처럼 변수를 그냥 넘기는 방법과
logger()->info("배열 : " . var_export_str($arr)); # 이렇게 var_export_str()로 감싸는 방법
?>


위를 실행하면 C:\Temp\test.log.오늘날짜 파일로 다음과 같이 로그가 남는다.

[info] GlobalLogger 20070207 18:38:03 (C:\test\test.php 14 (none)) 하하..
[debug] GlobalLogger 20070207 18:38:03 (C:\test\test.php 15 (none)) 허허.. 이건 DEBUG레벨
[alert] GlobalLogger 20070207 18:38:03 (C:\test\test.php 16 (none)) 경고야!!
[info] GlobalLogger 20070207 18:38:03 (C:\test\test.php 20 (none)) 객체나 배열같은 변수를 로깅 하는 방법은 두가지가 있다.
[info] GlobalLogger 20070207 18:38:03 (C:\test\test.php 21 (none)) Array
(
    [key] => value
    [array-] => var_export_str 함수를 이용해서 감싸서 로그를 남기세요.
)

[info] GlobalLogger 20070207 18:38:03 (E:\Workspace\ServiceCSTool2\test.php 22 (none)) 배열 : array (
  'key' => 'value',
  'array-' => 'var_export_str 함수를 이용해서 감싸서 로그를 남기세요.',
)


출처 : http://kwon37xi.egloos.com/2971666

vbs파일을 생성해 스케줄작업을 하는 경우가 꽤 된다.
vbs에서는 기본적으로 외부파일에 대해서 include 방식이 제공되지 않아서 상당히 불편하다.
이때, 공통으로 쓸 변수, 함수 들을 따로 파일로 만들어 놓고 쓸수 있는 방법일 찾다 아래 함수를 찾게 되었다.

'--vbs에서 외부 vbs파일을 inclue 하기 위해서 사용한다.
Sub Include(sInstFile)
On Error Resume Next

Dim oFSO, f, s

Set oFSO = CreateObject("Scripting.FileSystemObject")
If oFSO.FileExists(sInstFile) Then
Set f = oFSO.OpenTextFile(sInstFile)
s = f.ReadAll
f.Close
ExecuteGlobal s
End If

Set oFSO = Nothing
Set f = Nothing
End Sub

위 함수를 기본 vbs파일에 넣어주고 include할 파일을 위 함수를 이용해 호출하여 사용하면된다.


예제.
 A.vbs에서 b.vbs파일 include하기 위해서는
A.vbs에서
Call Include("b.vbs")
해주면 된다..
이때..a.vbs와  b.vbs가 같은 디렉토리에 있을경우에는 파일명만 써도 되지만, 서로 다른 디렉토리에 있다면, include시에 b.vbs의 전체 경로를 써주면된다.

출처 : http://dev.blog.serve.co.kr/

'Dev > wsh' 카테고리의 다른 글

exe, vbs 파일등을 실행 시키는 WMI 메소드  (0) 2011.05.24

안녕하세요... 멍멍이입니다.

원격지에 있는 컴퓨터의 exe, vbs 파일등을 실행 시키는 WMI 메소드가 있어
소개해 드릴까 합니다... soap 같이 원격지에서도 아주 잘 되는군요... 서버의
파일을 실행시키는 상용컴포넌트도 있던데... 장사가 안될것 같네요. ^^;;


서버에서 프로그램을 실행해야 하는 경우 유용하게 쓰일것 같네요...
로컬 (IIS와 같은 컴퓨터)에서는 예전에 Han님이 소개해 주신 HanWSC.Object 를
사용해야 합니다. 아래링크를 참조하시길...


1.exec.asp 파일을 만든다.
2. c:\ 에 makefolder.vbs 폴더를 만든다.


윈도우 작업관리자로 확인하면 프로그램 실행여부를 알수 있습니다.


--------1. exec.asp -------------


<%
 Dim ProcID


 bClose = Request("bClose")
 uip = Request("uip")
 uid = Request("uid")
 upw = Request("upw")
 cmd = Request("cmd")


 if Len(cmd) > 0 then
  call ExecProgram()
 end if


 if bClose = "1" then
  call TerminateProgram(ProcID)
 end if


function GetLoc()
 NSpace = "root\CIMV2"


 Set oLoc = CreateObject("WbemScripting.SWbemLocator")
 Set wn = CreateObject("WScript.NetWork")
  sn = wn.ComputerName
 Set wn = Nothing
 if (uip="") or (uid="") or (upw="") then
  Set oh = CreateObject("HanWSC.Object")
  Set oSvc = oh.GetHObject("winmgmts:\\" & sn & "\" & NSpace)
  Set oh = Nothing
 else
  Set oSvc = oLoc.ConnectServer(uip,NSpace,uid,upw)
 end if
 Set GetLoc = oSvc
end function


sub ExecProgram()
  Set obj = GetLoc()
  Set oProc = obj.Get("Win32_Process")
  rs = oProc.Create(cmd,null,ot,ProcID)
  Set oProc = Nothing
  Set obj = Nothing
end sub


sub TerminateProgram(p_id)
 Set obj = GetLoc()


 Set p = obj.ExecQuery("Select * from " _
 & " Win32_Process where ProcessId='" & p_id & "'")


 if (p.count) > 0 then
  for each t1 in p
   iv = t1.Terminate(0)
  next
 end if
 Set p = Nothing
 Set obj = Nothing
end sub


if Len(ProcID) > 0 then Response.write ProcID & "<br>"


%>


<html>


<head>
<script language="javascript">
function chgSel(){
 var frm = document.form1;
 var sel = document.form1.selCmd;
 var idx = sel.selectedIndex;
 frm.cmd.value = sel.options[idx].value;

 

}


</script>
</head>

<body>


<form name="form1" method="post">


<input type="checkbox" value="1" name="bClose">실행후 프로그램 종료
<br><br>
<select name="selCmd" onchange="chgSel();">
<option value="">명령 선택</option>
<option value="calc.exe">calc.exe</option>
<option value="wscript c:\makefolder.vbs">makefolder.vbs</option>
<option value="notepad.exe">notepad.exe</option>
</select>


명령줄 : <input type="test" name="cmd" size="50">
<br><br>
<hr>
<br><br>
다른 컴퓨터에는 아래의 값을 모두 입력 <br><br>
IP Addr : <input type="text" name="uip"><br>
User ID :<input type="text" name="uid" value="Administrator"><br>
User PW : <input type="text" name="upw"><br>


<br><br>
<input type="submit" value="실행">


</form>


</body>


</html>


-------- 2.makefolder.vbs ----------------


Set fso = CreateObject("Scripting.FileSystemObject")


 Set f = fso.CreateFolder("c:\aspnewsgroup")
 Set f = Nothing


Set fso = Nothing

출처 : http://blog.paran.com

'Dev > wsh' 카테고리의 다른 글

vbs 파일 작업시 외부 vbs파일 include 하는 방법  (0) 2011.05.24


1. 오픈 소스

사용자가 OS를 해킹하더라도 이를 막거나 하지 않고, 마켓플레이스 조차 제제하지 않는다.

2. 멀티 태스킹

아이폰도 멀티태스킹을 지원하기는 하지만 모든 앱에서 지원하지 않는다.

3. 어도비 플래시 지원

특히 우리나라의 웹사이트를 사용하는 스마트폰 유저라면 플레시를 지원이 더 원할한 웹서핑을 할 수 있도록 해준다.

4. 다양한 기기 선택의 자유

5. 다양한 통신사 선택의 자유

6. ITunes 같은 프로그램을 통해 동기화를 할 필요가 없다.

안드로이드 단순 USB 처럼 파일을 폰으로 복사 할 수 있다.

7. 동영상을 인코딩 없이 볼 수 있다.

아이폰은 MP4 동영상 파일만 지원하지만 안드로이드는 제한이 없다.

8. 위젯 기능

아이폰은 바탕화면이 단순한 아이콘만 있지만 안드로이드는 사용자가 원하는 위젯을 마음대로 위치 시킬 수 있다.

9. 쉬운 개발 환경 구성

Eclipse 라는 개발툴로 무료로 개발 환경을 구성 할 수 있을 뿐만 아니라, Java 를 익힌 프로그래머라면 쉽게 안드로이드 프로그래밍을 익힐 수 있다.

출처 : http://f7key.tistory.com/

'Dev > android SDK' 카테고리의 다른 글

구글맵 api key 얻어오기  (0) 2011.05.17

PHP 입력/출력(input/output) 스트림

PHP 3.0.13 이후의, php://output PHP 4.3.0 이후의 php://input, PHP 5.0.0 이후의 php://filter

  • php://stdin

  • php://stdout

  • php://stderr

  • php://output

  • php://input

  • php://filter

php://stdin, php://stdout php://stderr은 PHP 프로세스의 입력이나 출력 스트림에 대한 접속 권한을 허용한다.

php://outputprint()echo() 같은 출력 버퍼 메카니즘에 대한 쓰기 권한을 허용한다.

php://input은 raw POST 데이터를 읽을수 있는 권한을 허용한다. $HTTP_RAW_POST_DATA에 대한 메모리 집적에 대한 차선책이고, 특별한 php.ini 디렉티브 설정이 필요치 않다.

php://stdinphp://input 은 읽기 전용, 반면에 php://stdout, php://stderrphp://output 쓰기 전용이다.

php://filter는 스트림을 열때 필터의 응용을 허용하도록 설계된 메타-래퍼(meta-wrapper)의 한 종류이다. 컨텐츠를 읽기전에 스트림에 대한 필터를 적용할 기회가 전혀 없는 readfile(), file(), file_get_contents() 같은 올-인-원(all-in-one) 함수에 대한 가용성을 갖는다.

php://filter 타겟은 그 'path'의 부분으로서 다음 'parameters'를 취한다.

  • /resource=<필터링되는 스트림> (필수) 이 인자는 php://filter 사양의 끝부분에 위치해야 하고 필터링을 원하는 스트림을 가리켜야 한다.

    <?php
    /* This is equivalent to simply:
       readfile("http://www.example.com");
       since no filters are actually specified */

    readfile("php://filter/resource=http://www.example.com");
    ?>

  • /read=<읽기 체인에 적용되는 필터 리스트> (선택적) 이 인자는 | 파이프 문자로 구분되는 하나이상의 필터명이 적용된다.

    <?php
    /* This will output the contents of
       www.example.com entirely in uppercase */
    readfile("php://filter/read=string.toupper/resource=http://www.example.com");

    /* This will do the same as above
       but will also ROT13 encode it */
    readfile("php://filter/read=string.toupper|string.rot13/resource=http://www.example.com");
    ?>

  • /write=<쓰기 체인에 적용되는 필터 리스트> (선택적) 이 인자는 | 파이프 문자로 구분하는 하나이상의 필터명을 적용합니다.

    <?php
    /* This will filter the string "Hello World"
       through the rot13 filter, then write to
       example.txt in the current directory */
    file_set_contents("php://filter/write=string.rot13/resource=example.txt","Hello World");
    ?>

  • <양쪽 체인에 적용되는 필터 리스트> (선택적) read=write=에 선행하지 않는 모든 필터 목록은 읽기와 쓰기 양쪽 모두의 체인에 (적절하게) 적용합니다.

표 J-6. 래퍼(Wrapper) 요약(php://filter에 대한, 필터링 되는 래퍼의 요약으로 참조됨.)

속성 지원
allow_url_fopen으로 제한 No
읽기 허용 php://stdin, php://input 전용.
쓰기 허용 php://stdout, php://stderr, php://output 전용.
추가 허용 php://stdout, php://stderr, php://output only. (쓰기와 동일)
동시 읽기/쓰기 허용 No. 이 래퍼는 방향성을 갖지 않는다.
stat() 지원 No
unlink() 지원 No
rename() 지원 No
mkdir() 지원 No
rmdir() 지원

'Dev > android SDK' 카테고리의 다른 글

아이폰보다 안드로이드폰이 나은 이유  (0) 2011.05.21

[펌] [cyberuls.springnote.com]

 

php4에서는 단일 상속과 몇가지에 대한 지원밖에는 해주지 못했지만.. php5에서는 자바의 객체지향 모델을 가지고 옴으로써 여러가지를 지원해주게 됩니다.

 

  1. 상속

여전히 상속은 단일 상속만을 지원합니다.

하지만 interface 개념을 도입함으로써 여러가지로 작동하는 원리로 만들어줍니다. interface는 다중상속을 위해서 나온 것은 아닙니다. 그러니 다중상속 개념은 없다고 보는것이 맞겠지요...

 

2. 인터페이스  

     php5에서는 interface 개념을 지원합니다. interface는 다른 객체와 잘 부합할 수 있도록 객체의 표준을 마련하는 것이라 보면 쉽겠습니다.

 

3.  추상 클래스

    인터페이스와 비슷한 개념으로 추상클래스를 지원합니다. 하지만 추상클래스와 인터페이스의 용도는 좀 다르죠.. 일단 클래스이기 때문에 상속은 단일 상속밖에 할 수 없습니다. 그리고 추상메소드를 정의를 해야하고 abstract 라는 키워드를 써서 추상클래스임을 명시해야합니다. 일단 설계하는 클래스가 여러가지로 몇가지 기능을 빼고는 다 같은 방식으로 작동이 된다면 추상클래스는 아주 좋은 선택이 될 수도 있습니다.

 

  •  php5에서는 추상 클래스에서 인터페이스를 구현(implements) 하면 인터페이스에 속해 있는 메소드들은 무조건 실제 작동하는 메소드로 생성이 되어야 합니다. 하지만 자바에서는 인터페이스 메소드들도 그대로 추상메소드로 되어질 수 있습니다. 이것이 조금 번거로울 수도 있겠네요.

예를 들어서   java 는

interface A { public void a();}

abstract class B implements A { abstract public void a(); }

 

php는

 

interface A { public function a(); }

abstract class B implements A { public function a() { echo "aaa"; } }

 

위의 예제와 같이 java는 interface의 메소드(추상메소드이지요)는 추상클래스내에서 다시 추상메소드가 될 수 있지만 php는 반드시 구현되어야 하는 메소드가 되버립니다.

 

4.  오버로딩(Overloading), 오버라이딩 (Overriding)

오버로딩은 같은 클래스 내에서 이미 정의해 놓은 메소드를 같은 의미이지만 다른 매개변수를 사용 할 때 같은 이름의 메소드를 정의 할 수있게 해주는 것을 말합니다.

오버라이딩은 상속관계에서 같은 메소드이지만 다른 내용으로 만들고 싶을 때 메소드를 재정의 하는 것을 말합니다.

  php5 에서 사용하는 방법은 조금 신기합니다. ㅋㅋ

     일단 오버라이딩은 자동으로 됩니다... php에서는 같은 이름의 함수가 있으면 가장 마지막에 선언이 되어진 함수로 실행을 하기 때문에 오버라이딩은 자동으로 되게 되어있습니다. 그렇다면 오버로딩은 어떨까요?

 

     php5 에서는 몇가지 매직함수를 지원하는데요.. 그중에서 __call 이라는 놈이 재미난 놈입니다...

     이놈은 선언 되어지지 않은 메소드가 실행이 될때 실행되어지는 메소드 입니다.

 

     예를 들어서

 

     class AAA   {

          function run() {

 echo "뛰어";

          }

}

 

$a = new AAA();

$a->run();

 

   일반적으로는 이렇게 실행을 하는데요....

 

   $a->run_to("진호");

 

      이런식으로 선언되어지지 않은 메소드를 사용하면 __call 이 자동으로 불리어집니다. ㅋㅋ 오~~ 여기서 부터 재밌습니다.

      그럼 __call은 어떻게 쓰느냐... ㅋ

 

     class AAA   {

          function run() {

 echo "뛰어";

          }

 

function __call($func, $args) {

     echo "함수 : ", $func, ", 매개변수 리스트 ", implode(",", $args) ;

   }

}

    

      이런식으로 __call 은 메소드 이름과 매개변수 리스트를 배열로 받아들인다. ㅋㅋ  자 이간단한 개념을 가지고 살짝 재밌는 기능을 구상해 보자.

 

     요즘 웹프레임워크 중에 제일 잘 나가고 있는 루비의 일부 기능을 흉내내보겠당... 루비는 메타프로그래밍이 가능한 아주 유연한 언어이다.

    실행중에 클래스 메소드 자체를 마음대로 바꾸어 줄 수 있다. 그런 기능을 이용해서 레일스에서는 액티브 레코드에 여러가지 기능을 넣어두었다.

 

    예를 들어

 

  obj.find_by_name("진호")     --- name 필드가 진호인 것을 찾는다.

 

     여기서 주의깊게 봐야할 부분이 name 부분인데 이것은 필드 이름인데 언제 어디서든 바뀔 수가 있답니다. 그렇다면 메소드가 동적으로바뀐다는 말이되겠군요..

     어! 메소드가 동적으로 바뀌네 하면 php에서는 __call 을 떠올리세요. ㅋㅋ

 

    function __call($func, $args) {

       if (strpos($func, "find_by_") == 0) {

             // 자 이제 메소드에서 나머지 부분만 때어 볼까요?

            $arr = explode("find_by_", $func);

         $field = $arr[1];      // 이렇게 하면 필드 이름이 나오겠네요.. 오호~~

            $value = $args[0];   // 이렇게 하면 검색 하는 값이 나오겠네요. .오.. 정말..

 

           //  오 쉽게 select 구문이 되네요.. ㅋ

           $sql = "select * from 테이블 where {$field} = '{$value}' ";

 

           // 나머지는 상상에 맡길게요.. ㅋ

       }

    }

 

    제가 여기서 말하고자 하는 것은 php5 오면서 상당히 유연해졌다는 것입니다. 그리고 pecl에 있는 라이브러리 중에서는 실행시간 중에도 php의 메소드를 바꿔주는 메소드도 있습니다.. 즉 메타프로그래밍이 된다는 거죠.... ^^

 

    앞으로도 php는 발전 가능성이 너무 많네요.... ㅋ

 

5. 다형성

    다형성이라는 말은 여러가지로 해석이 될 수 있는데요,,,  하나의 자료형에 여러가지 다른 자료형을 넣을 수 있느냐는 것과 서로 유사한 클래스들을 하나의 인터페이스로서 제어 할 수 있느냐는 것도 다형성 중에 하나의 요소가 됩니다.   이것에 대해서는 php는 왕이죠.. 거의..  (필자가 생각하기에는.. ㅋㅋ )

 

    array  하나로 모든 것을 저장하고 내 뱉고 검색 할 수 있답니다.. 심지어는 array_multi_sort 라는 함수를 이용하면 db 에 있는 데이타를 가지고 와서 배열에 담아두고 그 자체로 다시 정렬 할 수 있습니다. 그래서 php는 기본적으로 다형성을 가지고 시작을 합니다. 모든 자료형은 동적으로 생성이 됩니다. 스크립트 언어들의 거의 기본적인 특징이죠... 그래서 좀 더 유연하고 쉽고 빠르게 프로그래밍을 배울 수 있답니다. 하지만 쉽다는 것이 곧 아무나 한다는 의미는 아니니 명심하세요...

 

6. 동적 바인딩

 동적 바인딩은  실행시간에 실제 그 객체의 메소드를 정확하게 찾아서 메소드를 실행 시키는 방법입니다. 이 말은 C++이나 자바에서 많이 접할 수 있는데요...

 C++이나 자바는 미리 클래스를 선언해주고 변수에 객체를 할당합니다. 그래서 그 클래스 변수에는 딱 정해진 자료형으로만 사용되어지는데요...

 그래서 인터페이스나 추상클래스를 두고 실제 움직이는 메소드에 대해서는 다른 클래스에 맡겨 두는 방식이 됩니다. 그렇게 되면 여러 클래스들이 인터페이스나 추상클래스 앞으로 모일 수가 있답니다. 즉 인터페이스와 추상클래스를 가지고 다른 클래스들을 공통적인 방법으로 실행하지만 실제 실행되는건 인터페이스나 추상클래스가 아닌 실제 그것을 구현한 클래스에 들어있는 메소드라는 것입니다.. 자기 자신을 아는 것이죠...

 

   하지만 php는 애초에 자료형을 지정해줄 필요가 없답니다. 그래서 지금 내가 할당되어진 객체 그 자체로 실행을 하기 때문에 언제든 동적 바인딩이 된다고 볼수가 있답니다. 그러니 좀 더 편하게 프로그래밍을 할 수 있답니다.


+ Recent posts