오픈 소스 프로젝트 - XE 개발 포럼
전의 datetime과 관련하여 zbxe 2.0에 앞서 event / filter(현재 있는 form filter와는 다른) 기능이 있어야 한다고 생각합니다.
현재의 애드온은 매우 제한된 기능만을 행하고 있어서, 데이터를 중간에 가로채어 변형하는 filter 기능이나 특별한 event 추적을 할 수 없는 상태입니다. 그렇기에, 해당 기능들의 추가가 되어야 할 것입니다.
예를 들어, 현재는 board 모듈에서 글이 등록되는 순간을 캡쳐하거나 글이 등록될 때 그 데이터를 조작할 수 없습니다. 하지만 event나 filter 기능이 만들어진다면 외부에서 이 데이터를 감시/조작하는, 예를 들어 “욕설 필터”나 “핑백”등의 addon을 쉽게 만들 수 있을 것입니다.
애드온에서는 EventHandler::addEventProc('module:event_name', 'callback_function_name', 100 /*priority*/)같이 호출하여 함수를 등록하고 모듈 이벤트 발생 시에는 EventHandler::callEvent('module:event_name', $event_param1, $event_param2)같이 호출하면 될 것입니다. filter도 비슷한 식으로 하면 될 것이고요.
다른 분들의 생각은 어떠하신가요?
이런… trigger라는 것이 있었군요. 헌데, 현재 trigger를 간단하게 사용하기에는 너무 무겁지 않나 싶습니다.
예를 들어, insertDocument 시에 어떤 문자열을 찾아서 바꾸기만 하면 되는 기능이 있는데, trigger는 호출 함수 대상을 무조건 module로 한정함으로써 addon과 (실제로는 쓰이지 않을)module을 동시에 코딩하도록 하는 듯 합니다.
그러므로, trigger에 addon의 lib에 있는 함수 또한 부를 수 있게 하는 것이 좋을 것 같습니다.
현재까지는 그걸 보완하는 것이 addon 즉 후킹하는 시스템이구요.
혹시 구현하시려는 이벤트나 필터링 종류와 용도에 대해서 간단하게나마 말씀해 주실 수 있으시면 좋겠습니다.
기존의 시스템에서 무리 없이 구현이 될지에 대해서 논의해보면 좋을 것 같구요 그렇지 않다면 새로운 전역 기능을 추가해야 할 것 같습니다.
보다 가볍고 무리없이 전체 Context에 관여하는 것의 의미라면 이번 기회에 만들어져도 좋을 것 같네요.
예를 들어 글에도 나와 있습니다만 핑백 보내기 기능을 만든다고 생각해 봅시다.
핑백이란 어느 글에 링크가 생성되어 등록되면 트랙백처럼, 그러나 트랙백보다 간편하게 그 링크된 사이트로 링크된 사실을 알려주는 기능입니다.
이는 별다른 module을 구현할 필요도 없고, addon만을 이용해서도 처리할 수 있을 정도의 코드입니다. 글에서 정규 표현식으로 a 링크만을 추출하여 XMLRPC로 해당 사이트에 보내기만 하면 되니까요.
헌데 이 시점은 trigger에도 등록되어 있는 document.insertDocument의 성공 후가 적당합니다. 글이 삽입된 후 이벤트가 생성되어 해당 글의 내용을 전달받아 처리하면 되니까요. 의미 상으로도 가장 적절한 방법이라고 생각합니다.
물론, after_module_proc 후 현재 module과 act를 조사하여 db에서 현재 등록된 글을 찾아 내용을 얻어올 수도 있습니다만, 상당히 부조리한 방식인데다 act명이 바뀌거나 다른 모듈에서도 insertDocument 동작을 할 때 등의 상황 변화가 발생할 수 있고, db에서 엉뚱한 글을 읽어서 처리할 수도 있습니다.
그렇기에, addon에서도 trigger 함수를 등록하여 사용할 수 있어야 한다고 생각합니다.
조건은 다음과 같으면 될 것 같습니다.
1. called_position == after_module_proc : 모듈 실행후 시점
2. 모듈 실행 결과 == $this->toBool() : ./classes/module/ModuleObject.class.php에 after_module_proc call이 일어나는 proc() method를 보시면 됩니다.
3. 호출 대상 == Context::get('act') eq 'procBoardInsertDocument'
일단 위의 경우는 게시판모듈에서 글이 등록되거나 수정될 경우입니다. (act = 'procBoardInsertDocument')
그리고 해당 action이 정상적으로 실행된 후에 처리하도록 할 수가 있구요.
procBordInsertDocument 의 경우 $this->get('document_srl')로 등록된 글의 대상 번호를 알 수 있습니다.
즉 대충 코드를 짜면 다음과 같겠네요.
<?php
if(Context::get('act')=='procBoardInsertDocument' && $called_position == 'after_module_proc' && $this->toBool()) {
$document_srl = $this->get('document_srl');
$oDocumentModel = &getModel('document');
$oDocument = $oDocumentModel->getDocument($document_srl);
if($oDocument->isExists()) {
$content = $oDocument->getContent(false, false, false)
// 핑백 보냄..
}
}
?>위와 같이 하여 글이 등록 또는 수정 이후에 글 번호를 구하고 그 글번호로 글객체를 구해서 내용을 찾아 처리하면 될 것 같습니다.
zbXE에서 addon은 4가지 called position에 대해서 native code를 삽입하는 개념으로 보시면 됩니다.
이 4가지 called position이 대부분의 입출력 및 처리 루틴의 중요 포인트라 애드온을 이용할 경우 일반적으로 모든 동작의 추가가 가능합니다.
다만 애드온은 action 기반으로 실행되는 process 에 개입하는 것이라서 특정 method 단위로 동작을 제어하는게 까다롭습니다.
method단위로 개입하여 제어를 하는것이 trigger이고 이 trigger는 구조상 addon에서 가능하도록 하는것은 살짝 어려워 보입니다.
(물론 방법을 찾아서 위젯이나 애드온단위까지 올라가는 것도 좋겠죠)
아무튼 위와 같은 방법이 HNO3님이 구현하시려는 기능에 부합한지 아닌지 알려주시면 좀더 논의를 해보면 될 것 같습니다. ^^
전의 본문에도 나와있지만, 그렇게 구현하는 것은 물론 가능합니다. 하지만, 저렇게 구현하는 것이 프로그램 논리적으로 합당한 것인가에 대해서는 저는 아니라고 생각합니다.
document.insertDocument의 기능을 하는 module은 board만 있는 것이 아닙니다. 물론 현재에는 맞을 지도 모르지만, 차후에 사제 module에서도 말썽 없이 동작을 해야 할 것이고, 이것저것 확장성 등을 고려해 볼 때 저런 코드를 짜는 것은 별로 좋지 않다고 생각합니다. event라는 것은 그래서 있는 것이고요.
또한 “추적”뿐이라면 상관 없겠지만, 예를 들어서 “변형”이 필요할 경우, 윗 단락에서 제시한 다른 module에서의 처리 문제와 더불어 db에 insert시킨 후 그 내용을 다시 update 시키는 쿼리 낭비 또한 발생합니다. 그렇기에, statement 단위에서 데이터를 조작하여 처리하는 trigger를 이용하는 것이 바람직하고, 프로그램의 논리 상으로도 맞다고 생각합니다.
결론은, trigger를 db에 등록시키기 보다는 프로그램이 기동되는 선두에 trigger들에 대한 함수 포인터(php에서는 그냥 string입니다만, 일단 개념적으로 이렇게 서술하겠습니다)를 등록시켜 사용하는 것이 좋을 것이라고 생각합니다. 프로그램 구조가 크게 변경되는 것이 문제입니다만…
이를테면 예전 basic의 goto와 비슷하게까지 저는 생각하고 있습니다.
하지만 native code 처럼 동작할 수 있는게 매력이죠.
현 zbXE의 trigger는 모든 method가 아닌 미리 정의가 된, 즉 호출을 위한 코드가 작성된 method에서만 쓸 수 있습니다.
trigger call을 하기 위한 정의와 called trigger에 catch되기 위한 정의까지 모두 해놔야해서 실제로 쓰기 위해서 까다로운 부분이 있습니다.
(결과적인 동작은 매우 만족스러운 면이 있지만요)
말씀하신것처럼 php에서 함수포인터를 유연하게 쓸수 있는 방법이 있다면 설계를 바꾸어서라도 변경하는 것은 옳다고 생각합니다.
그런데 특정 이벤트를 발생시키는 것과 발생된 이벤트에 대한 함수 포인터를 호출하는 동작을 현 php에서 할 수 있을까요?
예를 들어 document.insertDocument method에 대해 이벤트를 걸고 이벤트 발생시 xxxx.addon.method가 call 되도록 하는 방법이 없는 것으로 알고 있습니다.
그래서 부득이하게 각 trigger 를 사용할 것들을 미리 등록해 놓고 그 등록된 trigger key 를 명시적으로 호출함으로서 실제 동작이 되도록 되어 있습니다.
또한 한가지 문제가 각 method마다 입출력되는 변수의 값이 다르고 이 변수들이 통일된 형식을 가지기가 어려운데 event발생시 특정 method를 호출한다하더라도 어떤 변수가 필요할지 미리 정의하고 전달하고 받을 수 있을가요? ㅡ.ㅜ
HNO3님의 의견에 대해서는 저도 100% 찬성하고 구조적인 변경이 필요하다면 그렇게 하는것이 맞다고 생각하는데 구현할 수가 있을지 의문입니다. ㅡ.ㅜ
- 각각의 trigger에서 함수 호출: 전역 배열
$trigger가 있다고 치면,$trigger[$trigger_name]을 foreach로 돌면서 각 value를 호출합니다. 예를 들어, a.php가 require된 후 abcdef라는 함수를 부르고 싶다면, 일단$trigger[$trigger_name][] = 'abcdef'라고 string을 넣어 준 후(배열에 추가하는 루틴은 특별한 함수에서 관리해야겠지요)foreach($trigger[$trigger_name] as $value)구문 안에서$value($event_arg)라고 호출하면 될 것입니다. - 각각의 trigger의 인자 문제: 이는 각각의 trigger마다 reference를 정의해서 이에 따르도록 하면 될 것입니다. 예를 들어, “document.insertDocument trigger는 $title, $content, $date 등의 인자를 주어 호출하니 callback 함수에서도 이를 따르라”는 식으로 구현하면 아무 문제가 없을 것입니다. trigger를 발생시키는 총괄 함수에서는 가변 인자로 넘겨주어 이를 처리하게 하면 될 것이고요(이는 func_get_args, eval의 조합으로 처리가 가능합니다).
위의 방식은 실제로 wordpress에서 쓰이고 있는 방식이고(add_filter('edit_page_form', 'keywords_edit_form') 같은 식입니다), 충분히 php4 범위에서 구현할 수 있습니다.
또한, 클래스 함수는 $func = array($class_instance, 'function_name'); $func();같이 처리가 가능합니다.
간단한 함수 호출 예제:
<?php
require_once 'test_lib.php';
$func = 'test_lib_func';
$func('string arg', 10);
?>
<?php
function test_lib_func($arg1, $arg2)
{
echo 'It works! ' . $arg1 . ' / ' . $arg2;
}
?>triggerCall을 할때마다 memory에 등록된 대상이 아닌 DB(실제동작은 캐시파일)에 static으로 등록된 대상만 호출하는 것을 확장해야 한다는 것에 대해서 동의하고 진행하면 될 것 같습니다.
다음과 같이 triggerCall에 대한 기능을 변경하면 될 것 같습니다.
기존에 등록된 모든 trigger 호출에 대해서 zbXE구동시에 메모리에 전역변수로 등록을 하고 애드온등에서는 그 전역변수에 원하는 함수 포인터를 등록해서 사용하도록 하면 큰 구조 변경없이 동작 가능할 것 같습니다.
일단 event handling등에 대한건은 위와 같이 trigger 동작의 확장변경을 통해서 하면 되겠죠?
그런데 트리거를 이용하기 위한 등록은 위와 같은 WP처럼 하든 zbXE처럼 하든 다양한 방법이 존재하는데 실제 호출할때 좀더 좋은 방법이 없을까요?
현재는 documentController.insertDocument에서 명시적인 코드를 통해서 trigger 대상을 call하도록 되어 있습니다.
즉 이 trigger라는 것은 method단위이고 php에서 method가 실행되기 전/후를 기반으로 조작이 불가능해서 고정된 method에 trigger call을 하도록 되어 있는데요..
예를 들어 document.insertDocument 에서 호출되길 원하는 트리거를 코드 추가 없이 자동으로 할수 있을까요?
document.controller.php 의 insertDocument() 에서 대상을 꼭 호출해주어야 하는데.. 이게 사실 개인적으로는 안 좋아보이지만 어쩔 수 없이 쓰고 있거든요.
예를 들어 실제 다음과 같은 코드가 존재하는데요,
function insertDocument($obj, $manual_inserted = false) {
// trigger ?몄텧 (before)
$output = ModuleHandler::triggerCall('document.insertDocument', 'before', $obj);
if(!$output->toBool()) return $output;
...
}
위에서 제가 궁금하다고 한것은 위의 ModuleHandler::triggerCall(트리거이름,시점,전달변수) 와 같은 실제 호출하는 코드를 모든 method에 코드 추가 없이 호출할 수 있을까 하는 것입니다.
물론 함수별 trigger 자동 호출은 불가능합니다. 기본적으로 함수 호출의 추적은 대부분의 언어에서 지원하지 않고 있지요(예를 들어 Windows API를 이용하여 debug 모드로 프로그램을 열어 프로그램 동작을 추적하여 발생시킬 수도 있지만, 매우 비상식적인 방법이므로 논외로 하겠습니다).
다른 방법으로는, controller 함수 호출을 zCallFunction(array($document_obj, 'insertDocument')); 등으로 인위적인 호출을 하여 이 함수 안에서 triggerCall을 하는 방법이 있을 수는 있습니다. 하지만 이는 언어적으로 매우 부자연스러운 방법인 데다, 원치 않는 버그가 발생할 수도 있습니다. 일반 addon 또는 module 개발자들에게 부조리가 될 수도 있습니다.
본론으로 돌아가서, 함수 추적을 하여서도 안 될 것입니다. 왜냐 하면, document.insertDocument의 기능은 document에서만 쓰이지만, 엄연히 “공용 trigger”라는 것이 존재할 가능성이 있기 때문입니다(addon에서도 무언가 출력하기 위해 module의 view 이후에 어떤 것을 출력하기 위해 common.moduleDisplay같은 것을 만들 가능성은 충분합니다). 이런 공용 trigger는 특별한 전담 함수가 없을 뿐만 아니라, 있어서도 안 될 것입니다.
제가 제안하는 방법은 trigger를 함수 종속적인 개념으로 보지 않는 것입니다. 그렇게 되면, triggerCall이라는 것이 자연스러운 방법이 될 수 있고, 공용 trigger라는 개념 없이도 마음대로 trigger의 조작을 할 수 있을 것이기 때문입니다.




예를 들어 글을 입력하기 전과 입력 성공후에 document.insertDocument 라는 trigger를 호출하도록 되어 있습니다.
그럼 스팸필터 모듈이나 포인터 모듈에서 이 trigger를 특정 method와 연결되도록 등록해 놓으면 그 method가 호출이 됩니다.
어떻게 보면 trigger라는 이름보다는 event라는 이름이 어울릴 수도 있지만 db trigger와 비슷한 개념으로 설계한 것이라서 trigger라는 이름으로 현재 사용되고 있습니다. ^^
이와 관련해서 더 필요한 정보가 있으시면 댓글 남겨주시면 추가하도록 하겠습니다.