php5가 새로이 릴리즈 되었다. 아니 정확히 말하면 5.0.0 beta부터 말하자면 이미 나온 지는 1년이 넘어간다. 그리고 이제 5.01이 릴리즈 되었다.
아직 대부분의 사이트에서는 5.0은 그리 많아 보이지 않는다. 솔직히 4.x 버전도 크게 문제되진 않는다. 하지만 5.0 버전이 새로이 나왔다는 것은 필자를 포함하여 여러 사람에게 꽤나 흥미 있는 이야기가 아닐 수 없다. 이 글은 php5의 새로운 기능과 그에 관련된 전반적인 내용에 대해 알고 싶어하는 사람을 위한 글이다.
옛것 그리고 새로운 것 php는 현재 가장 많이 사용되고 있는 웹 프로그래밍 언어중 하나이다. 엄청나게 강력한 기능을 제공함에도 불구하고 공짜로 구할 수 있으며 또한 여러 플랫폼의 지원까지 이루 말할 수 없는 수많은 장점을 지닌 언어라는 것은 따로 말할 필요가 없을 것이다.
그러나 이러한 강점들도 완벽한 것은 아니다. 필자 역시 개발 당시 “역시 이러한 것은 아직 안 되는군” 이라고 말하며 아쉬워했던 부분이 있었으니 말이다. 독자 역시 그러한 경험이 있었을 것이다.
그리고 php5가 나왔다. 다음에 무슨 말이 나올지 당연히 짐작되는 바와같이 php5는 php4의 수많은 문제점들이 수정되었다. 그리고 여러 가지 새로운 개념이 들어가게 되었다.
필자는 php5의 새로운 기능에 대해서 여러 가지로 보여 줄 것이다. 그러나 php5의 모든 기능을 다루진 못할 것이다. 좀 더 많은 아직 php5의 소식을 듣지 못한 개발자나 혹은 듣기만 했을 뿐 아직 다뤄보지 않은 개발자 그리고 관심을 갖는 모른 사람들은 지금 당장 php5를 설치해 보기 바란다. 그리고 좀더 새로운 php5의 세계에 발을 담가보기 바란다. 자 그럼 이제부터 php의 새로운 기능에 폭 빠져보도록 하자!
“php5는 객체 지향적인 웹 프로그래밍 언어이다.” 필자는 방금 제목에서부터 거짓말을 했다. php는 객체지향 프로그래밍 언어가 아니다. 하지만 php5는 그런 거짓말을 진실로 만들어 버릴지도 모른다는 생각이 든다. php는 객체 지향적인 언어가 아니다. 그러나 php5는 상당히 많은 부분의 객체 지향적인 부분을 지원하게 되었다. 그리고 이 점이 바로 php5의 가장 큰 수정 부분이고 또한 가장 매력적인 부분일 것이다.
php4도 객체 지향적인 부분을 이미 지원하고 있었다. 즉 class를 사용할 수 있는 기능이 있었고 또한 수많은 전 세계의 개발자들이 이를 사용하여 프로그래밍을 하였다. 물론 필자도 그 중 하나이다. 그러나 (아닌 사람도 있을 수 있겠지만) 꽤나 많은 사람들은 이 기능에 실망을 했으리라 믿는다. php4에서 지원하는 객체 지향적이란 의미는 상당히 제한적이며 수많은 기능이 빠진 절제된 객체 지향이라고 할 수 있었다.
php4의 객체 지향적인 의미를 이렇게까지 낮게 평가하는 이유가 무엇일까? 아주 단순하다. php5가 그만큼 많은 것이 바뀌었기 때문이다. php5는 객체에 대한 코드가 완전히 새로이 쓰여졌다. 그만큼 성능 향상이 올라간 것은 물론이고 개념 역시 완전히 바뀌어서 대부분의 객체지향적인 의미의 내용이 포함되게 되었다. 그럼 이제부터 php5의 객체지향적인 부분의 새로운 면을 살펴 보도록 하자.
생성자와 소멸자 (Constructors And Destructors) 생성자와 소멸자는 각각 클래스의 인스턴스가 처음 생성될 때 그리고 그 인스턴스가 소멸될 때 자동으로 불려지는 메소드이다. php5 이전의 class에서도 생성자가 존재 했었다. class의 이름과 같은 이름의 메소드가 바로 이전 버전의 생성자이다.
그러나 php5 에서는 유일한 이름의 생성자가 사용된다. 이전 버전에서는 class의 이름과 같은 이름의 함수가 class의 생성자로 사용되었다면 이제는 생성자의 이름이 __consturct()인 메소드가 생성자로 선언되게 되는 것이다. 또한 이전에는 존재하지 않았던 소멸자 역시 __destruct() 라는 이름으로 새로이 생겼다.
<리스트 1> 생성자와 소멸자
<? class BaseClass { function __construct() { print "BaseClass constructor\n"; } function __destruct() { print "BaseClass destructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "SubClass constructor\n"; } function __destruct() { parent::__destruct(); print "Subclass destructor\n"; } } $obj = new SubClass(); ?>
result (1-1) BaseClass constructor SubClass constructor BaseClass destructor Subclass destructor
위는 php5의 새로운 생성자와 소멸자를 보여주는 소스이다. 위에서 볼 수 있듯이 생성자와 소멸자는 __construct()와 __destruct()라는 이름의 함수로 정의된다. 그리고 상속된 클래스의 인스턴스가 생성되고 소멸될 때에는 암묵적으로 부모 클래스의 생성자와 소멸자는 호출되지 않는다. 즉 부모 클래스의 생성자와 소멸자를 호출하고 싶으면 위의 소스에서 볼 수 있듯이 직접 부모 클래스의 생성자와 소멸자를 다음과 같이 호출해 주어야 한다.
parent::__construct(); parent::__destruct();
접근 제한자 (public/protected/private) 이전 버전에서는 클래스의 멤버 변수를 지정할 때에 var이라는 키워드가 사용되었다. 하위 호환을 위해서 php5에서도 var를 쓴다고 해서 작동하지 않는 것은 아니지만 기본적으로 php5에서는 멤버 변수를 선언할 때에는 public, protected, private를 사용하도록 되어 있다. C++이나 JAVA를 이용해 본 사용자라면 너무나도 익숙한 키워드일 것이다.
이를 사용함으로써 php 도 진정한 의미의 캡슐화(encapsulation)를 지원하게 된 것이다. 미리 말하자면 C++이나 JAVA를 사용해 본 유저라면 위의 키워드들의 의미를 쉽게 상상할 수 있을 것이고 그 상상이 틀리진 않을 것이다. 그러면 이제부터 의미를 간단히 살펴보면 다음과 같다.
public : 어디서나 접근 가능한 제한자 protected : 클래스의 내부에서 사용 가능하다. 그러나 상속될 수 있다. private : 클래스 내부에서만 사용 가능하다.
이전의 var를 사용하여 클래스 변수를 선언했을 때에는 위에서 public을 사용하여 변수를 지정한 것과 같은 효과를 나타냈었다. 결국 어디서나 접근 가능한 변수는 진정한 의미의 캡슐화를 지원하지 못했던 것이다.
정적 멤버, 클래스 상수(static, const) php5는 정적 멤버 변수와 클래스 상수를 선언할 수 있다 정적 멤버 변수는 static 키워드를 사용하여 선언이 가능하고 클래스 상수의 경우는 const 키워드를 사용하여 선언 가능하다. 한가지 알아둬야 할 것은 const로 선언된 클래스 상수의 경우에는 변수가 아니기 때문에 $가 붙지 않는다.
<리스트 2> 정적 멤버 변수 선언
<? class TestClass { const constant_val = "My Constant\n"; public static $static_val = "My Static\n"; public function staticvalue() { return self::$static_val; } public function setstatic() { self::$static_val = "My Second Static\n"; } public function view_constant() { print self::constant_val; } } $obj = new TestClass(); print $obj->staticvalue(); print $obj->$static_val; // Undefined $obj->setstatic(); $obj2 = new TestClass(); print $obj2->staticvalue();
print TestClass::constant_val; $obj->view_constant(); print $obj->constant_val; // cannot access /* print $obj::constant_val; is not allowed */ ?>
result(1-2) My Static My Second Static My Constant My Constant
위에서 볼 수 있듯이 정적 멤버변수로 선언된 변수는 어느 인스턴스에서 수정되어도 모든 인스턴스에 적용이 된다. 위에서 21째 줄에서 보이듯이 $obj 인스턴스에 의해 수정된 정적 멤버 변수는 $obj2의 다른 인스턴스에도 모두 영향을 미친다. 그리고 주의해야 할 점이 한 가지 있는데 20 번째 줄에서 알 수 있듯이 정적 변수는 인스턴스에 의해서 직접 억세스될 수 없다는 점이다. 이는 클래스 상수에서도 동일하게 적용된다.
즉 클래스 상수도 생성된 인스턴스에서 직접 접근이 불가능하다. 이는 다른 말로 바꾸면 클래스 안에서 $this 키워드를 이용해서도 접근이 불가능하다는 말과 같다. 즉 위에서 보인 것과 같이 self 키워드를 이용해서 접근해야만 할 것이다.
추상 클래스, 추상 메소드(abstract) 추상 클래스란 abstract로 선언된 클래스를 말한다. 그리고 추상 클래스는 인스턴스화 될 수 없다는 특징을 가지고 있다. 즉 추상 클래스로 만들어진 클래스는 오직 상속을 하는 용도로만 사용될 수 있다는 뜻이다.
추상 메소드로 선언된 메소드는 그 정의를 상속하는 메소드로 넘기게 된다. 추상 메소드로 선언된 메소드를 포함하고 있는 클래스는 또한 추상 클래스로 선언된다. 이때 알아두어야 할 사항은 추상 메소드가 protected 로 선언되었다면 상속하는 클래스는 반드시 그 메소드를 public 이나 protected 접근자로 재정의해야만 한다. 추상 클래스와 추상 메소드의 사용 방법은 다음과 같다.
<리스트 3> 추상 클래스와 추상 메쏘드 사용 방법
<? abstract class AbstractBaseClass { abstract protected function getValue(); public function printOut() { print $this->getValue(); } } class SubClass extends AbstractBaseClass { protected function getValue() { return "This is SubClass\n"; } } class SubClass2 extends AbstractBaseClass { protected function getValue() { return "This is SubClass2\n"; } }
$obj = new SubClass; $obj->printOut(); $obj2 = new SubClass2; $obj2->printOut(); ?>
result (1-3) This is SubClass This is SubClass2
위의 소스에서 추상 클래스를 상속받는 SubClass나 SubClass2에서 추상 메소드로 선언된 getValue()를 재정의하지 않았다면 이 코드는 다음과 같은 에러를 출력했을 것이다.
Fatal error: Cannot instantiate abstract class SubClass2
인터페이스(interface) php5는 인터페이스를 지원한다. 인터페이스는 위의 추상 클래스와 같이 다른 클래스들이 구현할 행동을 제시할 수 있다. 인터페이스는 생성해야 할 클래스가 어떤 메소드를 포함해야 할지를 보여준다. 인터페이스는 interface라는 키워드를 사용하여 선언된다. 이때 선언되는 방법은 class를 선언하는 것과 같은 방법으로 선언하면 된다. 이 인터페이스를 사용하는 방법은 implements를 이용하면 된다.
다음은 interface를 사용하는 예제이다.
<리스트 4> interface를 사용한 예
<? interface Worker{ function run(); } class HardWorker implements Worker { function run() { print "very fast run\n"; } } class LazyWorker implements Worker { function run() { print "slow slow slow\n"; } }
$hw = new HardWorker; $hw->run(); $lw = new LazyWorker; $lw->run(); ?>
final 키워드 final로 선언된 클래스나 메소드는 더 이상 상속되거나 재정의 될 수 없다. 만약 final로 정의된 클래스나 메소드를 상속하거나 재정의 할 경우에는 Fatal Error를 낼 것이다. 이는 예제로 보여주진 않겠다.
명시적인 객체의 복사(clon) php5에서는 객체를 복사하기 위한 방법으로 clon이라는 키워드가 제공된다. clon을 이용하면 핸들의 복사가 아닌 직접적으로 객체 자체가 복사가 된다. 이때 그 복사되는 클래스에 __clon() 이라는 메소드를 지정할 수 있는데 이 메소드는 clon을 이용하여 객체가 복사될 때 불리어지는 메소드이다.
<리스트 5> clon의 예제
<? class ClonClass { static $instance_counter = 0; public $instance; function __construct() { $this->instance = ++self::$instance_counter; } function __clone() { print "clon run\n"; $this->instance = ++self::$instance_counter; } function ShowCounter() { print "Counter : ".$this->instance."\n"; } }
$obj = new ClonClass(); $obj->ShowCounter(); $clon_obj = clone $obj; $clon_obj->ShowCounter(); ?>
Counter : 1 clon run Counter : 2
위는 clone의 예제를 보인 것이다. 위에서 알 수 있듯이 clone을 사용하면 복사되는 클래스의 __clone()이 자동으로 실행되게 된다.
위에서 알 수 있듯이 php5는 php4와는 객체 지향적이라는 관점에서 봤을 때 상당히 다르다는 것을 알 수 있다. 그리고 그 다르다의 의미는 확실히 긍정적인 의미의 발전임을 알 수 있을 것이다.
php5는 예전의 상당히 부족해 보이고 또 맛보기 정도로만 제공되었던 수준을 넘어서 비로소 자바와 같은 순수 객체지향적인 언어에서 제공하는 듯한 느낌의 객체 지향적 프로그래밍이 가능해지게 되었다. (예전과 비교해 보라) 이는 php5가 가장 많이 변한 점이라 할 수 있겠다.
예외 처리 php5 에서는 try, throw, catch를 이용한 구조적 예외처리를 지원한다. 예외처리를 사용하고자 하는 개발자는 Exception class를 상속한 객체를 throw할 수 있다. Exception class는 built-in 된 클래스로 이미 제공되는 것이니 그냥 상속해서 사용하면 된다.(Exception class는 아래를 참고하길 바란다.)
예외 처리는 php에서 그 동안 사용하던 if 문 등의 예외 방식에서 벗어나 좀더 명시적인 예외 처리 구문이 가능하게 될 것이다. 그러나 하위 호환등의 이유로 대부분의 내부 함수들은 예외로 던질 수 없다는 것도 알아두기 바란다. 예외처리에 대한 예는 아래를 참고하기 바란다.
<리스트 6> Built-in Exception class class Exception {
protected $message = 'Unknown exception'; // exception message protected $code = 0; // user defined exception code protected $file; // source filename of exception protected $line; // source line of exception
function __construct(string $message=NULL, int code=0);
final function getMessage(); // message of exception final function getCode(); // code of exception final function getFile(); // source filename final function getTrace(); // an array of the backtrace() final function getTraceAsString(); // formated string of trace
/* Overrideable */ function _toString(); // formated string for display }
<리스트 7> 예외 처리의 예
<? class UserException extends Exception { public $var; function __construct($var) { $this->var = $var; } } try{ // throw exception throw new UserException("UserException"); print "Never Excuted"; }catch(UserException $e){ print "caught exception $e->var"; }catch(Exception $e){ print "caught exception unknown"; } ?>
caught exception UserException 참조에 관한 여러 가지... php는 변수나 함수에 대한 참조가 가능하다. 그리고 php5에서는 이전 버전과는 다른 사항이 몇 가지 추가되었다. 우선 array에 관한 내용을 들 수 있다.
php 이전 버전에서는 foreach를 이용하여 array에 대한 반복문을 수행할 때 그 변수를 수정할 수 없었다. 그러나 php5 에서는 참조를 이용하여 foreach 문을 수행할 수 있게 바뀜으로써 그 변수를 바로 수정이 가능하도록 바뀌었다.
또한 과거에는 함수의 디폴트 변수를 지정할 때 참조를 이용한 디폴트 변수의 지정은 불가능했었다. 그러나 이제는 참조를 이용한 디폴트 변수의 지정이 가능하게 되었다. 다음의 코드를 보면 더욱 확실히 이해가 될 것이다.
<리스트 8> 참조의 이용
<? function defRefArgTest(&$var = null) { if($var === NULL) print "var is empty\n"; else $var++; } $a=5; defRefArgTest($a); defRefArgTest(); print $a."\n";
$array = array("father","mother","NULL","brother"); foreach ($array as &$value) { if ($value === "NULL") { print "is NULL\n"; $value = NULL; } else print $value."\n"; } ?>
var is empty 6 father mother is NULL brother
MySQLi(ext/mysqli) mysql은 php와 더불어 가장 많이 사용되는 DB이다. mysql은 php와 궁합을 같이 한지 상당히 지났다. 그러나 php5와 mysql4.1의 소개 이후 이 둘 사이에는 문제가 생긴 것 같아 보였다. 그러나 mysql 4.1이후 버전을 지원하는 용도로 php5를 위한 mysqli 확장을 개발했다. 하지만 특별한 문제가 없다면 그냥 이전의 것을 사용하는 것도 별 문제가 없어 보인다.
그러나 php5에서 제공되는 mysqli 확장은 이전에 비해 엄청난 속도가 보장된다. 심지어 특정 조건에서 40배 가량의 속도 향상을 보일 수 있다고 하니 말이다. 그리고 이전의 확장보다 훨씬 안전한 보안을 책임진다.
그렇다면 mysqli 확장 기능은 어떻게 사용하는 것일까? 이전의 확장기능과 비슷한 방식의 사용법이 존재하지만 여기서는 객체 지향적인 접근을 시도해 보도록 하겠다.
<리스트 8> MySQLi 확장의 예
<?php $mysqli = new mysqli("localhost", "user", "password", "db");
if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); }
if ($stmt = $mysqli->prepare("SELECT Name, phone FROM User WHERE id LIKE ?")) {
$stmt->bind_param("s", $id); $id = "770311%";
$stmt->execute();
$stmt->bind_result($col1, $col2);
while ($stmt->fetch()) { printf("%s %s\n", $col1, $col2); }
$stmt->close(); }
$mysqli->close(); ?>
위는 mysqli 확장을 prepared statement 와 함께 사용한 예이다. 이전의 사용 방법과는 좀 다르다는 것을 알 수 있을 것이다. 상당히 객체 지향적인 방법을 사용하고 있으며(이렇게 사용하지 않아도 된다.) Query 문이 완성되어 있지 않다는 것도 알게 될 것이다. 쿼리문은 prepare를 이용하여 미리 준비해 놓고 bind_param를 이용하여 특정 변수를 쿼리에 string으로 추가한다는 내용이다.
php5의 도전 그리고 php5로의 도전 php5 는 기존의 유명세에 힘입어 새로운 패러다임을 향해 나아가고 있다. 위에서 설명한 것만이 php5에 추가된 모든 기능들은 아니다. 위에서 설명한 것 이외에도 SQLite의 지원이나 펄 확장(PECL:PHP Extension Community Library) 그리고 새로운 Zend 엔진 등등 php5는 많은 추가적인 기능들이 들어 있다. 게다가 php는 이전에 비해 훨씬 좋아지고 있으며 이 글을 쓰는 동안에도 php 개발팀은 또 다른 버그와 그리고 새로운 기능들과 싸우고 있을 것이다.
php5는 아직 php4를 완벽히 대체했다고는 할 수 없다. 왜냐하면 아직도 많은 곳에서 php4를 이용하여 웹 서비스를 하고 있기 때문이다. 하지만 대세는 곧 바뀌게 될 것이다. 이전의 것을 아는 것은 상당히 중요하다. php 역시 이전 버전의 내용을 아는 것은 앞으로의 진전에도 큰 도움이 된다. 그러나 새로운 것을 알아가는 것 역시 상당히 중요하다. 독자는 php5의 위에서 설명해 놓은 기능들 뿐만 아니라 그 외에 다른 기능들을 찾아보길 바란다. @
* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다.
|