2008년 4월 18일 PHP에서 "가짜" 클로저 쓰기
PHP는 언어적인 측면으로 봤을 때 쓸데 없는 기능(예: register_globals
)이 많고 정작 있으면 좋을 기능들이 쓸데 없이 부족(예: 유니코드 지원)한 문제로 여러 개발자들을 괴롭히곤 한다. 후자에 속하는 한 가지 기능으로는 클로저가 있는데, 말하자면:
stack := fun
var data = []
return {
empty: fun -> data.empty?,
top: fun -> data[-1],
push: fun v -> data.append(v),
pop: fun v -> data.remove(-1),
}
end
st = stack()
println st[:empty]() --> true
st[:push](42)
println st[:empty]() --> false
st[:push](54)
println st[:pop]() --> 54
println st[:top]() --> 42
이런 식으로 scope를 빠져 나간 뒤에도 그 scope 안에 잡혀 있는 변수들은 해당 scope를 더 이상 사용하지 않을 때까지 유지되도록 하는 컨셉이다. (오랜만에 나루 코드 좀 써 봤다. 저 부분 문법은 이제 거의 확정이다.)
간만에 PHP로 비슷한 걸 짜 봤으면 좋겠다 하는 생각이 들어서 잠시 고민하다가, create_function
과 정적(static) 변수를 응용하면 될 것 같다! 라는 생각이 들어서 급히 짜 본 게 다음과 같은 코드.
function outer($arg1, $arg2, &$local1, &$local2) {
$ret = array($local1, $local2);
$local1 = $arg1; $local2 = $arg2;
return $ret;
}
function create_closure_outer() {
return create_function('$arg1, $arg2',
'static $local1 = 1, $local2 = 2;
return outer($arg1, $arg2, $local1, $local2);');
}
$outer = create_closure_outer();
var_dump($outer(3, 9)); // array(1, 2)
$outer2 = create_closure_outer();
var_dump($outer2(7, 6)); // array(1, 2)
var_dump($outer(15, 10)); // array(3, 9)
var_dump($outer2(0, 0)); // array(7, 6)
…좀 변태적인 코드기도 하고 너무 많은 클로저가 존재하게 되면 메모리를 열심히 잡아 먹는 괴물이 될 수도 있지만1 적절히 쓰면 유용할 것이다. 일반화시키기도 어렵지 않을 듯. (PHP5의 Reflection API를 쓰면 될 거다. 아마도)
이런 게 어디 쓸모가 있을까 싶어서 설명하면: 이 코드는 원래 preg_replace_callback
의 인자로 쓸려고 만든 건데, 단 하나의 함수를 위해 state를 담는 객체를 만들고 하는 게 너무 번잡해서 그랬다. 이 코드는 현재 메아리 내부 스크립트에 포함되어서 잘 돌아 가고 있다.
…근데 이런 코드 짜느니 그냥 PHP를 버리는 게 낫겠다.
PHP의 함수는 한 번 생성되면 삭제되지 않는다.
create_function
으로 생성된 것도 마찬가지. ↩