지난 주말에 Zend Engine 2.0과 Changes in PHP 5/Zend Engine II를 읽었다. (Zend Engine은 PHP 프로그래밍 언어의 핵심 구현 부분이다. 나름대로 언어 중립적인 엔진이라고 할 수 있지만, 그다지-전혀?- 널리 쓰이지는 않았다.) PHP 5가 나온지가 오래인데, 왠 뒷북일까라고 생각하는 사람도 있을 듯하다.
PHP는 매우 널리 퍼져있는 프로그래밍 언어이지만, 또 반대로 여러 사람들이 미워하는(!) 프로그래밍 언어다. PHP를 많이 써보진 않았지만, 어느 정도는 써봤고, 내 나름대로의 PHP를 싫어하는 이유를 몇가지 가지고는 있지만, 사실 그것들이 결정적인 이유가 되기는 힘들다고 생각한다. 그리고, “PHP가 싫어, 미워”라고 외치는 주변의 사람들에게 물어보아도 설득력 있는 이유가 나오는 경우는 드물다. 아마도, Zend 사 자체의 PHP를 개선하려는 노력이 그러한 이유 중의 약간은 제시해줄 지도 모른다는 기대를 할 수 있지 않을까?
일단, 이 글에서는 Zend Engine 2.0에 나오는 항목들을 중심으로 얘기를 풀어나가보자.
Reference semantic on object variables
이 문서에서는 object model이 변화했다고 하지만, 좀 더 정확히 말하면, object를 가리키는 variable의 semantic이 변화했다고 볼 수 있다. PHP 4의 variable은 value semantic을 가지고 있다. 다시 말해 reference semantic이 아니다.
class SomeObject
{
var $memberVar = 0;
};
$a = new SomeObject;
$b = $a;
$b->memberVar++;
print $a->memberVar;
print $b->memberVar;
위와 같은 코드의 결과는 “01”이다. variable $a는 new를 통해 새로 만들어진 object를 가리키지만, variable $b는 $a의 복사본이다. 서로 다른 object를 가리키는 것이다.
PHP 5에서는 object를 가리키는 variable은 reference semantic으로 동작한다. (이러한 방식을 ‘handle’이라는 이름으로 자주 부른다.) 따라서, 위 코드의 결과는 “11”이 된다.
object가 복사되는 것은 (심지어 function call과 같이 서로 다른 module boundary를 넘어다니더라도) 매우 특별한 것을 의미한다. 매우 단순한 비유를 들어보자. 철수가 영희에게 지우개를 빌려주었을 때, 지우개는 두개가 되는 것이 아니라, 역시 하나만 존재한다. 이처럼 object에 관련된 대부분의 idiom이나 pattern에서도 reference semantic이 훨씬 자연스럽다. (e.g. Factory pattern)
물론 PHP 4에서 reference가 존재한다.
$c = &$a;
프로그래머들에게 reference를 사용하는 규칙(discipline)을 정해서, 위의 문제를 해결할 수 있다. 하지만, PHP 처럼 “쉬운” 것을 지향하는 언어에서 프로그래머가 매순간마다 reference semantic을 써야할 지, value semantic을 써야할 지 고민해야한다는 것은 괴로운 일이다. 프로그래밍 언어는 “무엇을 할 수 있는가/없는가”의 척도로 평가되는 것이 아니라, “무언가를 쉽게 할 수 있는가”의 척도로 평가되는 것이다. 따라서, reference semantic을 기본적인 semantic으로 선택하는 것은 매우 합리적이라고 생각된다.
C++에 익숙한 사람들은 C++의 object를 가리키는 variable은 value semantic이 아니냐고 반문할 수 있겠지만, C++에서 object는 주로 pointer나 reference로 나타내어지는 것이 보통이고 이러한 방식이 자연스럽다. C++이 value semantic을 사용하는 것은 순전히 performance에 대한 고려라고 생각된다. (역설적이게도, C에 익숙한 사람들은 pointer를 사용하는 것이 performance에 대한 고려가 아닌가 의아해할 수도 있을 듯하다.)
잘 알다시피 Java에서는 object를 가리키는 variable은 reference semantic을, primitive type를 가리키는 variable은 value semantic을 선택함으로써, 일종의 trade-off를 선택한다. 이는 Java가 Pure object-oriented programming language가 아니라는 근거로 채택되기도 한다.
Python이나 Ruby 같은 언어는 모두 reference semantic을 채용한다.
Improved Object Dereferencing
아래와 같은 문법을 허용하기 시작했다. 사실상, parser가 그다지 똑똑하지 못했다고 볼 수 밖에 없다.
$object->method()->method()
Object Cloning
reference semantic으로 바뀌면서, 당연히 따라오는 변화이다. object를 copy하는 방법을 __clone() method를 통해서 프로그래머가 지정할 수 있다. 사용시에는 다음과 같은 문법을 가진다.
$copy_of_object = $object->__clone();
Constructor/Destructor
이해할 수 없는 PHP 4의 특징 가운데 하나가, constructor는 있으나, destructor는 없는 것이었다. (destructor와 같은 기능을 하도록 꽁수를 쓰는 방법이 있긴 했지만) destructor를 추가하면서 destructor는 __destruct(), constructor는 __construct()라는 이름의 method로 바뀌었다.
특기할만한 점은 parent class의 destructor가 implicit하게 호출되지 않기 때문에, parent::__destruct()로 직접 호출해주어야 한다는 점이다.
delete statement
object를 참조하고 있는 곳이 있어서 garbage collection이 안되는 object라고 하더라도, 강제로 지우기 위한 방법이다.
어떤 사람들은 garbage collection과 object의 강제 삭제는, 서로의 이익을 침해하기 때문에 상극이라고 주장하기도 한다. 간단히 얘기해서, C++에서 pointer과 delete를 사용하는 것은 object의 life time을 프로그래머에게 위임해서 성능을 올리겠다는 목적을 가지고 있고, 여타의 다른 언어들에서 garbage collection을 사용하는 것은 프로그래머가 object의 life time을 전혀 신경 안쓰겠다는 것이기 때문이다. 이 주제에 대해서는 논란의 여지가 있기 때문에, 더이상 언급을 하지 않겠다.
Multiple Inheritance
Zend Engine 2.0의 feature인데, PHP 5 상에서는 interface의 Multiple inheritance만을 지원하는 듯 하다. parent class의 같은 이름의 method가 충돌하는 경우에 대해서는 항상 override해야하는 (C++과 동일한) 규칙을 사용한다.
Abstract Classes and Methods
구현을 가지지 않는 method를 abstract method라고 하고, 이를 포함하는 class는 abstract class로 선언되어야 한다. abstract class는 instantiate할 수 없다.
기본적인 동작을 구현하고, 특정 동작의 구현을 하위 클래스에 위임하는 Framework 내의 클래스에서 유용할 것이다. C++에서라면 constructor를 protected로 만드는 경우가 여기에 해당할 것이다.
Interface
모든 method가 public이고, 구현을 가지지 않는다. 일반적인 interface 개념과 크게 다르지 않다.
Visibility
object 내부의 정보를 감출 수 (Information hiding) 없는 것은 PHP 4의 커다란 결함 중의 하나이다. public, protected, private 키워드가 추가되었고, Java와 유사한 문법을 사용한다. Visibility에 관한 manual page를 보면, class member와 class method라는 용어를 사용하는데, 일반적인 usage(class method == static method)를 생각하면 좀 짜증스럽다.
Static member variables
static method는 지원했으나, static member는 지원하지 않았던 문제를 해결함으로써, Singleton pattern을 구현할 수 있게되었다.
Exception
대부분의 modern programming language에서는 exception을 지원한다. exception은 크게, error handling을 중심 logic으로부터 제거하는 효과와 error handling을 적절한 장소에서 수행할 수 있는 효과를 가진다.
다른 언어들과 비슷하게 try/throw/catch keyword를 사용하지만, Java/Python의 finally나 Ruby의 ensure와 같은 syntax가 없는 점은 좀 아쉽다고 하겠다.
String offset syntax
PHP 4에서는 string offset syntax와 array offset syntax가 같기 때문에, null variable에 대해서, offset syntax를 사용하면, 애매모호한 문제가 발생해서, array로 변환되는 문제가 있다. PHP 5에서는 string offset syntax를 변경해서 이를 해결한다. PHP에서 자주 발견되는 애매모호한 문법들 중의 하나가 해결된 듯 하다.
Class Type Hints
PHP는 원래 weak typed programming language지만, class에 한정해서 type을 명시할 수 있다. 이는 strong typing의 이익 중의 하나인 Safety 보다는, type을 이용해 contract를 구현하는 abstraction의 역할에 가깝다고 생각된다. interface와 함께 유용하게 쓰일 수 있을 듯 하다. COM이나 다른 OO 언어들과의 interoperation에서도 유용하리라고 생각된다.