[출처] http://www.hanb.co.kr/network/view.html?bi_id=1383


데이터로서의 코드: PHP의 Reflection(1)


                       

제공 : 한빛 네트워크
저자 : 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)할 수 있는 방법이 없다. 여기에 이러한 역할을 실행하는 함수가 있다.:
<?php

#$Id: parse_source.php,v 1.2 2007/04/23 11:30:14 zkessin Exp $

   $file  = $argv[1];
   $class = $argv[2];

function parse_class($file,$class)
   {
   include_once($file);
   $block   = "n<methods fail='continue' class='$class'>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 .= "  <method  name='$methodName' $static $final>n";
   foreach ($method->getParameters() as $p)
   {
   $default = $p->isDefaultValueAvailable()?"default='".$p->getDefaultValue()."'":'';
   $name    = $p->getName();
   $block .= "    <parameter name='$name' $default/>n";
   }
   
   $block .= "  </method>n";
   $block .= "<!--*****************************************************************-->nn";
   }
   
   $block .= "</methods>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 version="1.0"?>
   <methods fail="continue" class="testClass">
 <method name="execute">
 <parameter name="uid"/>
 <test name="test2"><parameter name="uid"
 value="12345"/><assertion type="exception" value='exception'/></test>
 <test name="test3"><parameter name="uid"
 value="test"/><assertion type='exception' value='exception'/></test>
 <test name="test4"><parameter name="uid"
 value="12345"/><assertion type='value' value='true/></test>

 </method>
 <!--********************************************************************************-->

</methods>
테스트 구축

인터페이스는 사용자가 각각의 테스트에 대해 정의할 수 있도록 구축해야 할 것이다. 여러가지 다양한 형태로 구현할 수 있겟지만, Ajax 오브젝트로 구현하는 것이 가장 좋아 보인다. 사용자는 클래스,메소드 그리고 테스트들과 같은 3종류의 메뉴를 제공받을 수 있을 것이다. 사용자는 만들어진 테스트를 편집하거나 추가할 테스트를 선택할 수 있다. JavaScript 프로토타입 라이브러리를 사용하면 Ajax 어플리케이션을 꽤 간단히 만들 수 있다. 우리는 그저 데이터를 실질적인 형태(이 경우에는 raw HTML)로 인코드할 수 있는 PHP 백엔드를 생성하고, 사용자 인터페이스를 위한 약간의 JavaScript 코드가 필요할 뿐이다.

일단 사용자가 작업할 클래스와 메소드를 선택하면, 사용자는 이미 정의한 테스트들의 목록을 함께 제공받을 것이다. 사용자는 기존의 테스트를 편집하거나 새로운 하나를 만들 것이며, 각각의 경우에 대한 처리는 똑같다. 각 파라미터와 기대한 결과들에 대한 등록을 허용하기 위해 HTML 폼이 제공 될 것이다. 이 데이터는 XML 데이터파일로 기록하기 위해 서버로 보내질 것이다. 원칙적으로, 사용자는 테스트들를 지우거나, 복제하고, 재정리 할 수 있을 것이다. 더 완전환 구현은 사용자가 오브젝트 생성자에 파라미터를 설정하는 것 뿐만 아니라 session, get 그리고 post 옵션들을 설정할 수 있도록 허용 할 것이다.

이 프로그램은 XML 파일을 다루거나 포함될 파일들을 보여주기 위해 간단한 커맨드라인 프로그램을 사용한다. 게다가 프로그래머는 Ajax 기반의 어플리케이션에서 데이터를 가지고 작업 할 수 있다. 사용자 인터페이스는 사용자가 클래스들의 목록으로부터 작업할 클래스를 선택하도록 한 후, 테스트할 메쏘드를 선택하고 테스트 시나리오를 만들 수 있도록 사용자에게 권한을 줄 것이다.

Ajax 프론트엔드는 PHP 백엔드와 소통(communicate)하기 위해 Reflection API를 사용할 것이다. 어떤 경우에는 데이터는 raw HTML 조각들로 보내지고, 또 어떤 경우에는 JSON 구조체로 보내진다. 프론트엔드가 어떤HTML을 대치해야만 하는 경우에는, 그것을 서버쪽에서 생성하고, HTML로 전송하는게 가장 쉽다. JavaScript 프로토타입 라이브러리는 이러한 것을 매우 쉽게 만든다, 왜냐하면 디폴트 모드에서 그것은HTML 엘리먼트 안에 반환된 HTML 안에서의 Ajax 호출을 허용 할 것이기 때문이다.

여기에 프론트엔드 자바스크립트가 있다.:
/* $Id: ajaxInterface.js,v 1.3 2007/04/23 11:30:14 zkessin Exp $ */

function loadClasses()
   {
   new Ajax.Updater('classes',
   'list_classes.php',
   {asynchronous: true, 
   evalScripts:  true, 
   onComplete:   function(request, json){},
   }
   );
   }
   
function loadMethods(className)
   {
   new Ajax.Updater('methods',
   'list_methods.php',
   {asynchronous: true, 
   evalScripts:  true, 
   onComplete:   function(request, json){},
   parameters:   {class: className}});
   }

function loadTests(className,methodName)
   {
   new Ajax.Updater('tests',
   'list_tests.php',
   {asynchronous: true, 
   evalScripts:  true, 
   onComplete:   function(request, json){},
   parameters:   {class: className,method:methodName}});
   }
     
function editTests(className,methodName,testname)
   {
   var target;
   
   target = 'new_test.php';
   new Ajax.Updater('test_form',
   target,
   {asynchronous: true, 
   evalScripts:  true, 
   onComplete:   function(request, json)
   {
   },
   parameters:   {class: className,method:methodName,test:testname}});
   }

function addTest(button) 
   {
   form = button.form;
   console.log(form);
   var className  = form.class.value;
   var methodName = form.method.value;
   new Ajax.Updater('result',
   'add_test.php',
   {asynchronous: true, 
   evalScripts:  true, 
   onComplete:   function(request, json)
   {
   loadTests(className,methodName);
   },
   parameters:   Form.serialize(form)});
   return false;
}
그리고 이것은 파일들의 목록이 있는 XML파일이다.:
 <?xml version="1.0"?>
   <classes>
       <class source="UnitTestBuilder.class.php"/>
   </classes>

역자 유일호님은 현재 어느 중소기업의 소프트웨어 엔지니어로 근무하고 있으며, 잡다한 스킬 덕에 여러 가지 개발관련 일을 맡아서 하고 있습니다. 최근에는 리버스 엔지니어링(Reverse Engineering)에 관심이 많습니다.


경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com






본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
29 How to Call SWI-Prolog from PHP 5 졸리운_곰 2016.05.10 281
28 neural-network by php file 졸리운_곰 2016.03.16 152
27 Learning Library for PHP file 졸리운_곰 2016.03.16 371
26 php 전문가 시스템 php expert system file 졸리운_곰 2016.03.15 56
25 How to Insert JSON Data into MySQL using PHP file 졸리운_곰 2015.12.04 817
24 이클립스(Eclipse) PHP 개발환경 설정. file 졸리운_곰 2015.11.14 216
23 PHP로 만든 달력 file 졸리운_곰 2015.10.27 118
22 라이트 cms 다운로드 ritecms_2.2.1.zip file 졸리운_곰 2015.10.27 24
21 드루팔 다운로드 drupal-7.41.zip file 졸리운_곰 2015.10.27 11
20 도쿠위키 다운로드 dokuwiki-5422200921b.tgz file 졸리운_곰 2015.10.27 47
19 미디어위키 다운로드 mediawiki-1.25.3.tar.gz file 졸리운_곰 2015.10.27 34
18 워드프레스 다운로드 wordpress-4.3.1-ko_KR.zip file 졸리운_곰 2015.10.27 34
17 제로보드 다운로드 XE Core ver. 1.8.13 xe.zip file 졸리운_곰 2015.10.27 30
16 XE 스킨 제작 매뉴얼 v1.1 XE-Skin_Manual-ko(v1.1).pdf file 졸리운_곰 2015.10.26 21
15 제로보드 XE 개발자 가이드 file 졸리운_곰 2015.10.26 23
14 php로 웹 수집 : Basic PHP Web Scraping Script Tutorial 졸리운_곰 2015.09.20 64
13 PHP 와 MYSQL 연동 졸리운_곰 2015.08.11 585
12 PHP 기반의 Micro Frameworks 정리 졸리운_곰 2015.05.15 276
11 PHP의 composer 란 무엇인가?, PHP 의존성 관리도구 졸리운_곰 2015.05.15 372
10 슬림(Slim): 마이크로 프레임워크 [모바일 restful 서버] 졸리운_곰 2015.05.15 243
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED