제공 : 한빛 네트워크
저자 : Zachary Kessin
역자 : 유일호
원문 : Code As Data: Reflection in PHP
프로그래머들이 그렇듯이, 우리들 대다수는 본능적으로 우리가 만들고 동작하는 프로그램과 처리할 수 있는 의미를 가진 데이터 사이를 분명히 구분한다. 이것이 종종 유익한 것인 한편, 한가지 중요한 사실이 숨겨지도록 하는 경향이 있다: 프로그램들 그 자신도 잘 정의된 데이터 없이는 아무것도 아니다. 프로그램을 실행하기 위해, 어떤 프로그램은 반드시 데이터를 분석하고 실행 가능한 형태로 변환 해야만 한다. 이것은 컴파일러나, 인터프리터, 또는 다른 어떤 도구일수도 있지만, 그것들도 어디까지나 프로그램일 뿐이다. 그러나 많은 사람들에게 있어 실제로 코드를 컴파일하거나 실행하는 것 이외에 텍스트 편집기 안의 코드를 컬러화하는 하이라이터(highlighter)가 그들이 사용하는 유일한 파서이다.
프로그램을 사용하여 C 그리고 그와 비슷한 언어로 코드를 작성하는 것을 자동화하기 위한 긴 역사가 있다(Lex, yacc, lint, ctags 등등을 포함하여). C프로그래머들에게는, 1970년대 이후로 어떤 형태로든 이러한 도구들이 상당수 존재 해왔고, 이미 커뮤니티를 통해서 잘 알려져 있다. 그러나, PHP, Perl 및 Python(“P”언어들)으로 코딩하는 법을 배운 이들을 위해서는, 이것과 같은 도구들은 결코 그들의 범주 안에 들어 가지 못할지도 모른다. 이것은 부당하다. 내가 여기서 보여줄 것처럼 코드생성과 분석을 위한 견고한 툴체인은 보다 나은 결과물을 이끌어 낼 수 있고 프로그래머의 능률을 향상시키기 때문이다.
일단 우리가 도구를 사용해 코드를 분석 할 수 있다는 것을 증명하려면, 두 가지 질문이 주어지게 된다.: 어떻게 해야 하는지 그리고 그 후엔 정보를 어떻게 처리해야 되는지. 코드를 분석 하는데 정규식을 사용 하는 것도 가능하다. 예를 들자면, 이것은 Emacs나 다른 에디터들이 가지고 있는 구문강조 방법과 유사하다. 그러나, 정규표현식을 사용하는 것은 매우 빨리 어려움에 직면할 수 있다. 현대 “P” 언어들은 대개 아주 복잡하다. 그리고, PHP나 Perl을 설명할 수 있는(to describe) 정규표현식 고정셋(a solid set of regular expressions)을 생성하는 것은 매우 어려울 것이다. 많은 구문 강조기들(syntax highlighters)은 이와 같거나 다른 특별한 경우들과 같이 서로 연관된 문제를 가지고 있다. 언어가 자체적으로 내장하고 있는 파서가 이미 자신을 분석(parse)하는 법을 알고 있다면 그것으로 하게끔 어려운 부분(hard part)을 책임지게 하는 것이 좋을 것이다. PHP에서는 (5버전이나 그 이후의 버전), 이런 것들을 하기 위해서 Reflection API를 사용할 수 있다.
Reflection은 PHP 버전 5에 추가되어 프로그램이 스스로 자신의 코드를 검사할 수 있도록 한다. Reflection API는 함수나 오브젝트, 그것이 어디에 정의되어 있는지(파일과 줄번호의 범위)를 포함하여, 파라미터 목록, 함수이름, 도큐먼트, 주석 등등에 관한 많은 정보를 우리에게 알려줄 것이다.
Reflection을 사용하기 위해서는 우선 검사할 프로그램 코드를 삽입 해야만 한다. 반드시 include 문을 사용하되 require 문은 사용하지 말라, require 문 같은 경우에는 포함된 코드안에서 에러가 발생할 경우 프로그램을 종료하게 된다. Reflection은 코드를 검사하기 위해 PHP 자신의 파서를 사용한다, 그래서 파일안에 포함된 함수나 클래스 내부에 있지 않은 모든 코드가 실행될 것이다. 이와 같은 이유로, 신뢰성이 없는(untrusted) 코드에 Reflection을 사용한다는 것은 좋은 생각이 아니다. 그러나 이것은 그다지 문제가 안될 것이다. 왜냐하면 일반적으로 이런 형태의 도구는 그것을 원하는 프로그래머나 그의 팀에 의해서 만들어 질것이기 때문이다. 그래서 목적 코드에 버그가 있음직함에도 불구하고, 실제로는 크게 작용하지 않을 수도 있다. 이 테스팅 엔진은 신뢰성이 없는 코드에는 사용하지 말아야 할것이다.
일단 파일이 포함 되면(included), 우리는 그 안의 클래스들이나 함수들을 검사하기 위해 Reflection을 사용할 수 있다. 프로그램은 WSDL 파일, 또는 단위 테스트들 혹은 다른 wrapper들과 같이 코드기반에 의지하는 다양한 구조물들을 구축하기 위해서 이 정보를 사용할 수 있다.
사용자는 검사할 클래스나 클래스들이 정의되어 있는 파일을 제공해야만 할 것이다. get_declared_classes() 함수는 PHP가 알고 있는 클래스들의 모든 목록을 리턴할 것이다. 이것은 사용자에게 메뉴를 제공하기 위해 사용될 수 있을 것이다.
일단 사용자가 작업할 클래스를 선택하게 되면, Reflection은 그 클래스를 검사할 수 있게 될 것이다. 이 예제에서, 나는 클래스와 오브젝트들을 다룰 것이다. 그러나, 함수들을 위한 Reflection 인터페이스는 클래스 메소드들들 위한 것과 비슷하다. Reflection은 메소드들의 목록과 클래스 프로퍼티들을 만들 수 있으며, 그로부터 우리는 테스트 모음을 구축할 수 있다. 우리의 테스트 모음은 private나 protect속성을 가진 클래스의 함수들은 호출할 수 없을 것이니 주의해야 하며, Reflection에서는 오브젝트의 접근 보호(access protection)를 오버라이드(override)할 수 있는 방법이 없다. 여기에 이러한 역할을 실행하는 함수가 있다.:
n";
$rf = new ReflectionClass($class);
$methods = $rf->getMethods();
foreach($methods as $method)
{
if($method->isPublic() == false)
continue;
$static = $method->isStatic()?"static="true"":"";
$final = $method->isFinal()?"final="true"":"";
$final = $method->isAbstract()?"abstract="true"":"";
$methodName = $method->getName();
$className = $method->getDeclaringClass()->getName();
$block .= " n";
foreach ($method->getParameters() as $p)
{
$default = $p->isDefaultValueAvailable()?"default="".$p->getDefaultValue().""":"";
$name = $p->getName();
$block .= " n";
}
$block .= " n";
$block .= "nn";
}
$block .= "n";
return $block;
}
file_put_contents($file . ".test.xml",parse_class($file,$class));
?>
이 예제는 테스트 모음의 다소 제한된 경우로, Reflection의 개념을 설명하기 위한 목적으로 작성되었다. 테스트 모음은 하나 혹은 여러 개의 테스트 세트로 구성 되어질 것이다. 이상적으로(Idealy) PHP모듈안의 각 함수나 메소드는 코드에서 발생할지도 모르는 다양한 모든 조건을 체크하기 위해서 여러가지 테스트를 치뤄야 한다.
하나의 클래스가 선택되면, 프로그램은 클래스안의 모든 메소드들을 찾기 위해 Reflection을 사용할 것이다. 그러면 사용자는 클래스의 다양한 메소드들 상에서 테스트들을 생성할 수 있는 기회를 가지게 될 것이다. 각 테스트 마다, 사용자는 메소드에 대한 각 파라미터 뿐만 아니라 클래스 생성자에 대한 각 파라미터를 위해서 값을 입력할 수 있을것이다. 테스트들이 정의된 후에는, XML 데이터파일로 기록되고, 그것은 실제 테스트들을 실행할 PHP코드를 생성하기 위해서 사용될 수 있을 것이다..
데이터 저장
각 PHP 클래스 마다, 코드는 데이터를 저장하기 위해 XML파일을 만들어 낸다. XML은 그것이 텍스트 포맷을 가지고 있다는 것에 장점이 있다. 그래서 만약에 사용자들이 인터페이스에 등록되어 있지 않은 뭔가를 원한다면 데이터를 직접 편집할 수 있다. 그것은 또한 CVS나 다른 소스코드 컨트롤 시스템에 체크인(checked into)할 수 있을 것 이다. 어쨌거나 각 public 메소드 마다, 우리는 각각의 디폴트 값과 함께 파라미터 이름들을 저장한다. 그밖에 우리는 필요한 만큼 테스트를 저장한다. 각각의 테스트는 이름과, 어떤 셋업 코드나, 정의된 파라미터들을 이용한 메소드 호출, 그리고 assertion 을 가진다. Assertion은 리턴 값이나 예외를 테스트할 수 있다. 만약에 assertion이 충족되게 된다면 테스트는 통과한다. 코드는 첫번째 테스트 실패 후에 정지되거나, 실행을 유지할 수 있도록 프로그래머의 의도에 따르도록 만들 수 있다.
그리고 이것은 파일들의 목록이 있는 XML파일이다.:
역자 유일호님은 현재 어느 중소기업의 소프트웨어 엔지니어로 근무하고 있으며, 잡다한 스킬 덕에 여러 가지 개발관련 일을 맡아서 하고 있습니다. 최근에는 리버스 엔지니어링(Reverse Engineering)에 관심이 많습니다.