phpclass/객체모델2002. 12. 21. 15:19
클래스 안의 클래스(Nested class)
클래스 안의 클래스는 클래스를 포함한 클래스를 의미하며 포함하고 있는 외부클래스와 포함되어진 내부클래스로 나누어 집니다.
내부클래스는 다른 클래스에 들어 있는 클래스입니다. 내부클래스를 정의하는 것은 클래스 이름을 숨기고 클래스에 필요한 자원의 지역적 할당을 가능하게 해 줍니다. 클래스를 별도로 작성하기에는 그 기능이 제한적이거나 외부 클래스에 종속적일 때 사용할 수 있으며 그래서 같은 일을 하는 클래스들끼리 기능적으로 묶는 역할을 하는데 이용할 수 있습니다.
 아래의 예를 보면 클래스 Database 내부에 데이터베이스 접속을 담당하는 내부클래스가 각 데이터베이스별로 작성되어 있습니다.
[code php;gutter:false] <?php
class Database {
class MySQL {
var $host = "";

function db_connect($user) {
print "Connecting to MySQL database '$this->host' as $user\n";
}
}

class Oracle {
var $host = "localhost";

function db_connect($user) {
print "Connecting to Oracle database '$this->host' as $user\n";
}
}
}

$MySQL_obj = new Database::MySQL();
$MySQL_obj->db_connect("John");

$Oracle_obj = new Database::Oracle();
$Oracle_obj->db_connect("Mark");
?> [/code]
때에 따라서는 내부클래스를 외부클래스 안쪽에 정의하지 않고 별도로 빼내어 아래와 같이 범위연산자 '::'를 이용하여 작성할 수 있습니다. 이러한 방법으로 작성하게 되면 소스 코드가 좀더 깔끔하게 되어 가독성이 좋아질 것입니다.
[code php;gutter:false] <?php
class Database::MySQL {
var $host = "";

function db_connect($user) {
print "Connecting to MySQL database '$this->host' as $user\n";
}
}

class Database::Oracle {
var $host = "localhost";

function db_connect($user) {
print "Connecting to Oracle database '$this->host' as $user\n";
}
}

$MySQL_obj = new Database::MySQL();
$MySQL_obj->db_connect("John");

$Oracle_obj = new Database::Oracle();
$Oracle_obj->db_connect("Mark");
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:18
객체 삭제(Forced deletion of objects)
Zend 엔진 1.0에서는 스크립트의 실행이 종료되면 객체들도 자동적으로 제거되기 때문에 스크립트 종료 직전에는 객체를 삭제할 필요가 없습니다만 만약 스크립트를 실행하는 중간에 객체를 삭제할 필요가 있는 경우라면 보통 unset() 함수를 이용하여 객체를 메모리에서 제거하게 됩니다.
[code php;gutter:false] unset($object); [/code]
특정 객체에 대한 별도의 객체 참조가 존재하지 않는다면 unset() 함수를 이용하면 별문제없이 객체를 삭제할 수 있으나 만약 특정 객체에 대한 객체 참조가 존재한다면 이 문장은 제대로 동작하지 않습니다. 객체 참조가 존재하는 한  해당 객체를 강제로 삭제할 수가 없다는 것입니다.
Zend 엔진 2.0에서는 좀더 진보적(?)으로 객체를 삭제할 수 있는 delete 문을 제공합니다.
[code php;gutter:false] delete $object; [/code]
delete는 새롭게 추가된 예약어로써 만약 이전의 작성된 문서에서 사용자 정의 delete()문을 가지고 있다면 이 문서를 Zend 엔진 2.0로 읽혀진다면 parser 에러가 발생할 것입니다.
새롭게 추가된 delete 문은 우선 해당 객체의 소멸자를 호출한 후에 객체가 다른 곳에서 참조되고 있는 경우에도 해당 객체를 강제로 삭제시킵니다. 따라서 해당 객체에 대한 모든 참조는 delete 문에 의해 객체가 삭제되는 순간에 그 의미가 없어지게 됩니다. 만약 delete 문에 의해 삭제된 객체를 참조를 통해 접근한다면 Zend 엔진 2.0은 해당 문서에 대하여 fatal 에러를 발생시킵니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:16
객체 역참조(Object Dereferencing)
참조 변수를 통한 역참조
Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서의 객체 역참조는 참조 연산자 &로 정의된 참조 변수를 통해 구현할 수 있었습니다.
[code php;gutter:false] <?php
class test {
var $mb;

function test($mb="default") {
$this->mb = $mb;
}
}

$a = &new test; // 객체 참조
?> [/code]
위의 예제에서 '$a = &new test'를 수행하게 되면 클래스 test로부터 생성된 객체 원본에 대한 참조변수 $a를 정의하게 됩니다.
[code php;gutter:false] <?php
.
. 생략
.

$a = &new test; // 객체 참조
echo $a->mb; // 객체 역참조
?> [/code]
'$a->mb'와 같이 참조변수 $a를 통하여 test로부터 생성된 객체 원본의 모든 멤버에 접근할 수 있으며 이와 같이 객체 참조를 통해서 역으로 객체 멤버에 접근하는 과정을 역참조(dereference)라 합니다.
Zend 엔진 2.0부터는 자바와 같이 객체가 참조로 전달되기 때문에 참조 연산자 &를 명기하지 않습니다. 위의 역참조 소스를 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)용으로 재작성한다면 아래와 같게 되겠지요.
[code php;gutter:false] <?php
class test {
var $mb;

function test($mb="default") {
$this->mb = $mb;
}
}

$a = new test; // 객체 참조
echo $a->mb; // 객체 역참조
?> [/code]
Zend 엔진 2.0에서의 객체 역참조
Zend 엔진 2.0에서는 참조변수가 아닌 함수 또는 메소드로부터 반환된 객체를 통해 역참조할 수 있습니다. 이때 함수 또는 메소드로부터 반환된 객체는 당연히 참조가 되겠지요.
함수로부터 반환된 객체의 역참조(dereferencing objects returned from functions)
우선 Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서 함수로부터 반환된 객체를 역참조하는 예를 들어보지요.
[code php;gutter:false] <?php
class Circle {
function draw() {
print "Circle\n";
}
}

class Square {
function draw() {
print "Square\n";
}
}

function &ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}

$obj = &ShapeFactoryMethod("Circle"); // 함수로부터 반환된 객체 참조
$obj->draw(); // 객체 역참조
$obj = &ShapeFactoryMethod("Square") // 함수로부터 반환된 객체 참조
$obj->draw(); // 객체 역참조
?> [/code]
Zend 엔진 1.0이 탑재된 PHP 4.0.4 이상 버전에서는 위의 예에서 보는 것과 같이 반환되는 변수의 타입이 객체일 때 객체 그 자체가 아닌 참조로 반환하기 위해서는 참조 연산자 &를 붙여야 합니다.
그러나 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)에서는 반환되는 변수의 타입이 객체인 경우에 참조 연산자 &를 붙이지 않더라도 기본적으로 객체 참조가 반환됩니다.
또한 Zend 엔진 1.0과는 달리 반환된 객체 참조를 또 다른 참조 $obj를 거치지 않더러도 함수로부터 반환된 객체 참조를 가지고 바로 역참조할 수 있습니다.
아래는 Zend 엔진 2.0이 탑재된 PHP 4 CVS (4.3.0-dev)에서 함수로부터 반환된 객체 참조를 가지고 역참조하는 예를 보여줍니다.
[code php;gutter:false] <?php
class Circle {
function draw() {
print "Circle\n";
}
}

class Square {
function draw() {
print "Square\n";
}
}

function ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}

ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?> [/code]
정적 함수를 통한 또 다른 예를 보면 아래와 같습니다.
[code php;gutter:false] <?php
class Counter {
var $counter = 0;

function increment_and_print() {
print ++$this->counter;
print "\n";
}
}

class SingletonCounter {
static $m_instance = NULL;

function Instance() {
if (self::$m_instance == NULL) {
self::$m_instance = new Counter();
}
return self::$m_instance;
}
}

SingletonCounter::Instance()->increment_and_print();
SingletonCounter::Instance()->increment_and_print();
SingletonCounter::Instance()->increment_and_print();
?> [/code]
메소드로부터 반환된 객체의 다중 역참조(Multiple dereferencing of objects returned from methods)
아래의 예와 같이 역참조로 반환된 객체 참조를 가지고도 반복해서 역참조할 수 있습니다.
[code php;gutter:false] <?php
class Name {
function Name($_name) {
$this->name = $_name;
}

function display() {
print $this->name;
print "\n";
}
}

class Person {
function Person($_name, $_address) {
$this->name = new Name($_name);
}

function getName() {
return $this->name;
}
}

$person = new Person("John", "New York");
print $person->getName()->display();
?> [/code]
역참조된 멤버변수에 값 할당하기
역참조에 의해 메소드를 호출하거나 메소드의 값을 되돌려 받을 수 있음을 위에서 알아보았습니다. 역참조에 의해 접근된 멤버변수에 대하여도 값을 읽을 수도 있고 더 나아가 아래와 같이 값을 할당할 수도 있습니다.
[code php;gutter:false] <?php
class Name {
var $id;

function Name($_name) {
$this->name = $_name;
}

function display() {
print $this->name;
print "\n";
}
}

class Person {
function Person($_name, $_address) {
$this->name = new Name($_name);
}

function getName() {
return $this->name;
}
}

$person = new Person("John", "New York");
$person->getName()->id = "hwooky";
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:15
객체 복제(Object Cloning)
Zend 1.0 객체 모델에서는 객체를 다룰 때 기본적으로 값으로 다루도록 설계되었기 때문에 객체를 함수의 인자로 전달하거나 함수로부터 객체가 반환될 때 또 할당문을 이용하여 객체가 할당될 때 기본적으로 객체 복사가 됩니다. 복사하는 동안에 Zend 엔진 1.0은 객체 프로퍼티의 전부에 대한 동일한 복사본을 만들기 위하여 비트 단위 복사(bitwise copy)를 하였습니다.
그러나 이러한 복사를 기반으로 한 객체모델에서는 자원 이용의 비효율성과 실행 시간의 지연과 같은 문제를 근원적으로 떠안고 있을 수밖에 없었으며 객체지향 프로그래밍의 여러 특징을 구현하는데 한계를 가지게 되었습니다. 사실 대부분의 경우에서 개발자들은 완전히 복제된 프로퍼티를 가진 객체의 복사를 생성할 필요는 없었습니다.
Zend 2.0에서의 객체 복제
객체 참조
Zend 2.0 객체 모델에서는 객체를 다룰 때 값이 아닌 참조로 다루도록 개선되었습니다. 이러한 이유로 원본 객체로부터 할당된 객체 또는 함수에 의해 반환된 객체를 다루게 되면 원본 객체 그 자체를 다루게 되지요.
객체 복사
그런데 때로는 원본 객체를 원래 상태로 유지하여야 할 경우도 있을 것입니다. 이러한 경우에는 예전 Zend 엔진 1.0에서처럼 참조가 아닌 복사를 하여 복사된 객체를 가지고 작업을 하여야 합니다. Zend 엔진 1.0에서야 기본적으로 객체 참조가 아닌 객체 복사였기 때문에 전혀 문제가 되지 않았었지만 Zend 엔진 2.0에서는 기본적인 처리가 객체 참조가 되기 때문에 객체 복사를 위한 새로운 수단이 필요하게 되었지요.
Zend 엔진 2.0에서 객체 복사를 위한 수단이 바로 __clone() 메소드입니다. __clone 메소드를 호출함으로써 원본 객체를 복제한 새로운 객체가 생성되는 것이지요.
[code php;gutter:false] <?php
$copy_of_object = $object->__clone();
?> [/code]
객체 복사를 하기 위해서는 반드시 __clone() 메소드를 이용해야 합니다. __clone() 메소드를 이용하면 원본 객체를 복사한 동일한 객체를 만들 수 있으며 이 객체의 멤버를 수정하더라도 원본 객체는 원래 상태를 그대로 유지할 수가 있습니다.
__clone() 메소드의 사용자 정의
새로운 객체의 복사본을 생성하기 위하여 __clone() 메소드를 호출하게 되면 Zend 엔진 2.0은 클래스 내에 정의된 __clone() 메소드가 존재하는지 확인할 것입니다.
만약 정의된 __clone() 메소드가 없다면 Zend 엔진 2.0에 내장된 __clone() 메소드를 호출하게 되고 이 메소드에 의해 객체 프로퍼티 전체를 복사할 것입니다.
만약 정의된 __clone() 메소드가 있다면 생성된 객체에서 필요로하는 프로퍼티에 대하여는 정의된 __clone() 메소드 내에서 직접 설정해 주어야 합니다.
[code php;gutter:false] <?php
class MyCloneable {
static $id = 0;

function MyCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this->name = $clone->name;
$this->address = 'New York';
$this->id = self::$id++;
}
}

$obj = new MyCloneable();

$obj->name = 'Hello';
$obj->address = 'Tel-Aviv';

print $obj->id . "\n";

$obj = $obj->__clone();

print $obj->id . "\n";
print $obj->name . "\n";
print $obj->address . "\n";
?> [/code]
자식클래스는 부모클래스의 __clone() 메소드를 오버라이드(override) 할 수 있습니다만, 그 경우 자식클래스에서 부모클래스의 __clone() 메소드를 호출하려면 아래와 같이 'parent' 키워드를 이용하여 참조합니다.
[code php;gutter:false] <?php
class SubCloneable extends SuperCloneable {
static $id = 0;

function SubCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this = parent::__clone();
$this->name = $clone->name;
$this->address = 'New York';
$this->id = self::$id++;
}
}
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:14
소멸자(Destructors)
소멸자의 역할
생성된 객체가 소멸되기 직전에 자동으로 불려지는 함수이며 주로 객체에 관련된 메모리를 정리하기 위하여 수행됩니다.
소멸자의 동작
클래스가 상속관계에 있을 때에 각 클래스에 있는 소멸자의 실행순서는 생성자와 반대로 동작하게 됩니다. 즉, 파생클래스의 소멸자가 먼저 실행된 다음에 부모클래스의 소멸자가 순서대로 실행됩니다.
소멸자의 특징
인자와 리턴값이 없으며 상속되지 않습니다.
Zend 엔진 1.0에서의 소멸자
PHP3 또는 Zend 엔진 1.0이 탑재된 PHP4에서는 소멸자를 지원하지 않습니다. 따라서 객체가 소멸할 때 처리해야 할 것이 있다면 임시방편적으로 register_shutdown_function() 함수를 이용하였습니다. 그러나 register_shutdown_function() 함수는 현재 문서의 요청(request)가 종료되는 시점에서 불려지는 PHP 내장함수로 특정 객체의 소멸과는 관계없다는 것에 유의해야 할 것입니다.
Zend 엔진 2.0에서의 소멸자 지원
비록 Zend 엔진 1.0에서도 register_shutdown_function() 함수를 이용하여 소멸자와 유사한 일을 할 수 있었지만 클래스 내의 일은 클래스 내에서 처리한다는 캡슐화 개념하고는 거리가 먼 전역적인 작업이 될 수밖에 없었습니다.
Zend 엔진 2.0에서는 소멸자를 공식 지원하기 때문에 객체를 처리하는 능력이 한층 높아지게 되었습니다. 소멸자를 통하여 디버깅에 대한 메시지들을 기록할 수 있게 되었고, 데이터 베이스 연결들을 닫는 등의 관련작업을 깨끗이 마무리할 수 있게 되었습니다.
복사가 아닌 참조를 바탕으로 설계된 Zend 엔진 2.0에서는 객체의 마지막 참조가 소멸될 때 해당 객체가 메모리로부터 해제됩니다. 소멸자는 객체가 메모리로부터 해제되기 직전에 호출되어 수행되며 소멸자명은 __destruct()으로 단일화되어 있으며 인자없이 호출됩니다.
[code php;gutter:false] <?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = 'MyDestructableClass';
}

function __destruct() {
print'Destroying'. $this->name. "\n";
}
}

$obj = new MyDestructableClass();
?> [/code]
생성자와는 다르게 부모클래스의 소멸자가 자동적으로 호출되지는 않습니다. 부모클래스의 소멸자를 실행하기 위해서는 아래의 예와 같이 명시적으로 부모클래스의 소멸자를 호출하여야 합니다.
[code php;gutter:false] <?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

class ChildDestructableClass {
function __destruct() {
parent::__destruct();
}
}

$obj = new ChildDestructableClass();
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:12
단일화된 생성자(Unified Constructors)
생성자명
생성자(constructor)는 'new' 키워드에 의하여 클래스의 새로운 인스턴스가 생성될 때 자동적으로 호출되는 클래스 내의 특별한 메소드이며, 객체의 다른 멤버가 사용되기 전에 필요로 하는 초기화같은 작업을 생성자를 통하여 수행하게 됩니다.
PHP3 및 Zend 엔진 1.0에서의 생성자는 클래스명과 동일한 이름을 가지게 됩니다. 아래와 같이 클래스명과 동일한 이름의 메소드가 존재한다면 이것을 자동적으로 생성자로 취급합니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};
?> [/code]
Zend 엔진 2.0에서의 생성자는 클래스명과 관계없이 단순히 __construct()라는 이름으로 단일화된 생성자를 호출할 수 있습니다.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};
?> [/code]
생성자명을 __construct()로 단일화한 이유
PHP3 및 Zend 엔진 1.0에서 생성자명으로 생기는 문제
클래스의 상속관계에 변화가 생겨서 수정하는 경우를 생각해 보세요. 파생한 클래스로부터 부모클래스의 생성자를 호출하는 것은 흔히 있는 일이기 때문에 Zend 엔진 1.0에서 수행하던 방법에 의하여 작성된 상속관계에 변화가 생겨 클래스를 이동하려면 약간의 수고가 필요할 것입니다.
수정하기 전에 작성된 초기의 클래스 상속관계가 아래와 같다고 예를 들어보지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};

class Square extends Shape {
function Square() {
parent::Shape();
// square-specific initialization code

}

};
?> [/code]
어떠한 이유에 의하여 클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되어 상속관계에 변화가 생겼다고 가정해 보지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code

}

};

class Rectangle extends Shape {
function Rectangle() {
parent::Shape();
// rectangle initialization code
}

};

class Square extends Rectangle {
function Square() {
parent::Rectangle();
// square-specific initialization code

}

};
?> [/code]
수정전과 후의 코드를 비교해 보았을 때 클래스 Square의 생성자 내에서 부모클래스를 호출하는 부분에 변화가 생긴 것을 볼 수 있습니다. 원래는 클래스 Square의 부모클래스가 Shape였으나 클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되었기 때문에 클래스 Square의 부모클래스가 Rectangle로 바뀌었습니다. 이에 따라 부모클래스를 호출하는 부분이 parent::Shape()에서 parent::Rectangle()로 수정되었습니다.
상속관계에 변화가 생길 때마다 생성자 내용을 일일이 수정해야 한다는 것은 여간 신경쓰이는 일이 아닙니다. 문서의 규모가 커지고 상속관계가 복잡해 진다면 다소 부담스러울 수도 있겠지요.
이러한 이유로 Zend 엔진 2.0에서의 생성자는 클래스명과 관계없이 단순히 __construct()라는 이름으로 생성자를 호출할 수 있도록 개선되었습니다.
Zend 엔진 2.0에서 단일화된 생성자
위의 예를 Zend 엔진 2.0에서는 어떻게 처리되는지 살펴보겠습니다. Zend 엔진 2.0으로 작성된 수정하기 전의 초기의 클래스 상속관계는 아래와 같을 것입니다.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};

class Square extends Shape {
function __construct() {
parent::__construct();
// square-specific initialization code

}

};
?> [/code]
클래스 Square와 Shape 사이에 새로운 클래스 Rectangle가 삽입되어 상속관계에 변화가 생겼을 때의 수정된 소스는 아래와 같겠지요.
[code php;gutter:false] <?php
class Shape {
function __construct() {
// shape initialization code

}

};

class Rectangle extends Shape {
function __construct() {
parent::__construct();
// rectangle initialization code
}

};

class Square extends Rectangle {
function __construct() {
parent::__construct();
// square-specific initialization code

}

};
?> [/code]
Zend 엔진 1.0으로 작성된 소스와는 달리 수정전과 후의 코드를 비교해 보았을 때 클래스 Square의 생성자 내에서 부모클래스를 호출하는 부분에 변화가 전혀 없음을 볼 수 있습니다.
Zend 엔진 2.0에서는 생성자명을 클래스명 대신에 '__construct()'라는 이름으로 호출할 수 있도록 생성자 선언 방법을 표준화함으로써 클래스의 상속관계에 변화가 생겼을 때에 생성자에서 부모클래스를 호출하는 부분을 일일이 수정해야 할 필요가 없어졌습니다. 이것이 생성자를 __construct()로 단일화했을 때에 얻을 수 있는 큰 이점이라 할 수 있지요.
Zend 엔진 2.0에서의 생성자 호출에 관한 하위호환성
생성자 호출 메카니즘
만약 Zend 엔진 2.0이 정의된 클래스에 __construct()를 발견할 수 없으면 하위 호환성을 가지기 위하여, Zend 엔진 1.0에서의 생성자, 즉 클래스명과 동일한 이름의 메소드를 찾을 것입니다.
따라서 아래와 같이 Zend 엔진 1.0용으로 작성된 소스를 Zend 엔진 2.0에서 수행하더라도 정상적으로 동작할 것입니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0

}

};
?> [/code]
같은 이유로 아래와 같이 Zend 엔진 1.0용 생성자와 Zend 엔진 2.0용 생성자가 모두 정의되어 있다면 Zend 엔진 1.0용 생성자는 무시되고 Zend 엔진 2.0용 생성자 __construct() 만 호출되겠지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0

}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};
?> [/code]
Zend 엔진 1.0 및 Zend 엔진 2.0 에서 모두 동작하는 생성자
Zend 엔진 1.0이 탑재된 PHP4에서 Zend 엔진 2.0과의 호환성을 가지도록 생성자를 정의한다면 아래와 같이 작성하면 가능하니라 생각합니다.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0
$this->__construct();
}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};

$obj = new Shape();
?> [/code]
위의 소스는 Zend 엔진 1.0과 2.0에서 모두 동일한 결과를 얻게 될 것입니다. Zend 엔진 1.0을 더 이상 필요없다면 Zend 엔진 1.0용 생성자인 Shape()을 완전히 삭제하면 되겠지요.
Zend 엔진 1.0용 생성자 Shape()에서 $this 객체를 이용하여 Zend 엔진 2.0용 생성자를 호출하기 때문에 static 멤버로는 접근할 수 없으며 반드시 인스턴스화한 객체를 가지고 접근할 때만 정상적으로 동작됩니다. 물론 생성자라는 것이 인스턴스화할 때만 동작하는 특별한 메소드이기 때문에 별 문제는 되지 않지만 좀더 소스 코드를 명료하게 작성한다면 이를 아래와 같이 $this 객체 대신에 클래스명과 범위연산자를 이용하면 될 것입니다. 수정된 코드에서의 단점이라면 현재 클래스명 Shape가 변경된다면 Shape::__construct() 부분도 함께 수정되어야 한다는 것이지요.
[code php;gutter:false] <?php
class Shape {
function Shape() {
// shape initialization code for Zend Engine 1.0
Shape::__construct();
}

function __construct() {
// shape initialization code for Zend Engine 2.0

}

};

$obj = new Shape();
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 15:10
비공개 멤버 변수(Private Members Variable)
접근 권한(Access Right)
C++ 또는 자바와 같은 객체지향 프로그래밍 언어에는 멤버에 대한 접근 권한을 지정하는 public, protected, private라는 접근지정자(또는 접근제한자,접근변경자)가 있습니다. 여러분이 작성하고자 하는 코드를 더 잘 캡슐화하기 위하여 접근지정자를 통해 멤버(메소드 및 변수)에 대한 접근을 제어하는 것입니다.
private : 자신 클래스 안에서만 접근할 수 있으며 하위 클래스에서는 접근할 수 없다.
protected : 자신 클래스 및 하위 클래스에서만 접근할 수 있다.
public : 모든 클래스에서 접근할 수 있다.
C++ 이냐 자바냐에 따라 그 의미가 약간씩 다르기도 하고 추가되는 개념도 있습니다. 가장 큰 차이점은 protected 접근지정자로 C++에서는 바로 아래에 있는 하위 클래스에서만 접근할 수 있는 반면에 자바에서는 패키지라는 개념이 도입되어 패키지 안에 있는 어떤 클래스도 접근할 수 있게 해 줍니다.
PHP에서의 접근 권한 지정
Zend 엔진 1.0에서는 멤버변수를 정의할 때 아래와 같이 var 키워드를 이용하게 됩니다. 이와 같이 접근지정자를 별도로 지정할 수 없으며 무조건 public 특성을 가지게 됩니다.
[code php;gutter:false] var $member; [/code]
Zend 엔진 2.0에서 추가된 키워드인 static으로 지정된 정적멤버변수의 경우에도 public 특성을 가지기 때문에 외부클래스에서 자유롭게 접근할 수 있습니다.
[code php;gutter:false] static $static_member; [/code]
앞서 작성된 "클래스를 사용해야하나?(부제:클래스를 이용한 모듈화 프로그래밍)"라는 글에서함수 내부의 정보를 숨기기위한 모듈화를 소개하면서 같은 개념으로 객체 수행의 내부 동작을 숨기기 위한 캡슐화를 살짝 언급하였었지요. 이러한 캡슐화를 통해 정의된 인터페이스를 통해서만 객체 내부에 접근하도록 허용하는 것입니다.
함수의 모듈화를 위해 전역변수가 아닌 지역변수가 요구되듯이 클래스의 캡슐화를 위해서는 public이 아닌 자신의 클래스 내에서만 접근할 수 있는 private라는 접근지정자가 절실히 요구되지요.
Zend 엔진 2.0에서는 접근지정자 private를 추가하여 캡슐화 도구로써 진일보하게 되었습니다. 멤버변수를 private로 지정하게 되면 멤버변수가 선언된 클래스 외부에서는 이 멤버 변수에 절대로 접근할 수가 없게 됩니다.
[code php;gutter:false] <?php
class MyClass {
private $Hello ="Hello, World! \n";

function printHello() {
print $this->Hello;
}
}

class MyClass2 extends MyClass {
function printHello() {
MyClass::printHello(); /* Should print */
print $this->Hello; /* Shouldn't print out anything */
}
}

$obj = new MyClass();
print $obj->Hello; /* Shouldn't print out anything */
$obj->printHello(); /* Should print */

$obj = new MyClass2();
print $obj->Hello; /* Shouldn't print out anything */
$obj->printHello();
?> [/code]

Posted by 방글24
phpclass/객체모델2002. 12. 21. 14:52
정적 멤버변수(Static Class Member Variables)
정적 멤버변수의 필요성
생성된 모든 인스턴스들이 공유하는 공통적인 정보가 필요할 때에 사용되는 것이 정적 멤버변수입니다. 정적 멤버변수는 다른 일반 멤버변수와 달리 각각의 인스턴스에 기억장소가 할당되는 것이 아니라, 단 한 개의 기억장소만이 할당되고 모든 인스턴스에 의하여 공유되거나 인스턴스없이 바로 참조할 수 있습니다.
클래스 스코프
정적 멤버변수는 객체 내의 멤버 변수로서 확보 되는것이 아니라, 별도의 영역(클래스 영역)에 확보 되어지는 멤버변수입니다. '클래스와 인스턴스' 글에서 '클래스와 인스턴스 개념을 이용한 객체의 모델링'을 설명하면서 아래와 같은 그림을 보신 적이 있을 것입니다.
< 클래스와 인스턴스 개념을 이용한 객체의 모델링 >
이 그림을 가지고 정적 멤버변수를 모델링하게 되면 아래와 같을 것입니다.
< 클래스와 인스턴스 개념을 이용한 객체의 모델링 >
객체를 생성할 때마다 정적 멤버변수인 '$차량대수'를 한 대씩 증가시키는 코드에서 '$둘리', '$밍키', '$후키' 객체를 생성하게 되면 정적 멤버변수 '$차량대수'는 3이 저장되어 있을 것입니다. 이와 같이 모든 객체가 공유할 필요가 있는 멤버변수를 정적 멤버변수로 지정하게 되면 어떠한 객체에서 접근하더라도 동일한 멤버변수에 접근할 수 있습니다. 즉, '$둘리->차량대수', '$밍키->차량대수', '$후키->차량대수'는 모두 동일한 멤버변수를 가르키고 있습니다.
Zend 엔진 1.0에서의 정적 멤버변수
Zend 엔진 1.0에서 객체 인스턴스 대신에 클래스명을 통해 클래스 메소드(class method), 즉 정적 멤버함수를 호출할 수 있었으나 객체 인스턴스가 아닌 클래스명을 통하여 클래스 스코프를 갖는 변수에 접근할 수 있는 방법을 지원하지 않았습니다.
정적 멤버함수가 다루어야 할 가장 기본적인 요소(멤버)는 정적 멤버변수입니다. 정적 멤버변수 역시 정적 멤버함수와 마찬가지로 객체와 관계없이 클래스 스코프(사용범위)를 가지고 있기 때문에 정적 멤버함수에서 다룰 수 있는 유일한 멤버변수입니다.
그러나 Zend 엔진 1.0에서 정적 멤버함수만 제공하고 정적 멤버변수를 지원하지 않았기 때문에 정적 멤버함수의 기능을 온전히 이용하기에는 한계가 있었습니다. 예를 들면 각종 문자열 처리 메소드를 모아놓은 라이브러리와 같이 멤버변수가 필요없는 경우에 한하여 정적 멤버함수 기능을 이용할 수 있었지요.
정적 멤버변수의 정의
클래스를 정의할 때 'var' 키워드로 정의되는 멤버변수는 클래스가 인스턴스화할 때마다 새로 만들어지는 인스턴스 변수입니다. 따라서 한 인스턴스 변수에 접근하려면 먼저 그 클래스의 인스턴스를 'new'라는 키워드로 생성해서 접근해야 합니다.
만약 모든 클래스간에 공유하여야 할 변수가 필요하다면 어떻게 할까요. Zend1 엔진에서는 이를 지원하지 않았었으나 2002년 6월경 릴리즈된 Zend 엔진 2.0 알파2 버전에 의하면 Zend2 엔진에서는 클래스간에 공유할 변수를 정의할 수 있게 되었습니다. 이를 위한 키워드가 'static'이며 다음과 같은 형식으로 정의됩니다.
<형식> static 변수명 = 변수값;
정적 멤버변수를 이용하기
일반 멤버변수는 클래스만 선언한다고 해서 호출할 수 있는 것이 아니라 반드시 객체를 생성한 후에 인스턴스를 통하여 호출되어야 합니다. 반면 정적 멤버변수는 인스턴스가 없어도 호출할 수 있습니다. 정적 멤버변수는 특정 인스턴스를 대상으로 수행되는 것이 아니고 위에서 설명한 클래스 스코프를 갖는 멤버이기 때문에 인스턴스없이도 호출이 가능합니다.
정적 멤버변수는 그 클래스 자체에 전역적(클래스 스코프)이며 다른 클래스나 객체에 서로 사용될 수 있습니다. 그래서 정적 멤버변수는 그 클래스의 인스턴스가 있든 없든 상관없이 어느 곳에서나 사용될 수 있는 것입니다. 이러한 경우에는 정적 멤버변수를 정의하고 있는 클래스의 이름을 범위연산자(scope resolver) "::"와 연결되어 사용되어야 합니다.
<형식> 클래스명::정적멤버변수
[code php;gutter:false] class Logger {
static $m_Instance = NULL;

function Instance() {
if (Logger::$m_Instance == NULL) {
Logger::$m_Instance = new Logger();
}

return Logger::$m_Instance;
}

function Log() {

}
};

$Logger = Logger::Instance();
$Logger->Log(); [/code]
따라서 '클래스 스코프'의 예제에서 정적 멤버변수 $차량대수는 앞 항목 '클래스 스코프'의 예와 같이 $둘리->차량대수, $밍키->차량대수, $후키->차량대수와 같이 참조할 수도 있으나 '클래스명::차량대수'와 같이 인스턴스없이 참조할 수도 있습니다. 어떻게 참조하든지 동일한 멤버를 다루게 되는 것이지요. 이와 같이 정적 멤버변수가 클래스 스코프를 갖는다는 의미에서 자바에서는 이 변수를 클래스 변수라고 호칭합니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 14:50
개선된 객체모델(Revamped object model)
객체 핸들(object handle)
Zend 엔진 1.0 객체 모델에서는 생성된 객체를 값으로 취급하도록 설계되어 있습니다. 이것은 프로그래머가 변수에 값을 할당하거나 함수에 인수를 전달하는 것과 같은 조작을 할 때 정수와 문자열과 같은 다른 기본형(primitive types)을 다루는 것과 같은 방법과 매우 유사하게 객체를 다룬다는 것을 의미합니다. 이것은 모든 객체가 복사되고 있다는 것을 의미합니다.
반면에 자바에서는 값이 아닌 핸들을 가지고 객체를 참조합니다. 핸들이란 객체 ID라고 생각할 수 있지요. 제 홈페이지에서 설명하고 있는 '참조(레퍼런스)' 정보에서 일부 언급하였듯이 자바에서는 객체를 변수에 할당하거나, 객체를 매개변수로 메소드에 전달하거나 메소드로부터 반환할 때, 전달되는 것은 이러한 객체의 참조(레퍼런스)이지 객체의 복사본이 아닙니다.
그러나 PHP3 또는 PHP4에서는 객체 핸들이 아닌 객체 자체를 다루도록 모델링되어 있기 때문에 객체를 다루는데 있어서 매우 비효율이며 소멸자와 같은 객체 특성을 구현하는데 많은 문제가 발생합니다. 결국 객체를 다룰 때는 자바에서와 같이 참조로 전달하는 것이 효율적이며 이와같이 객체 모델을 핸들 지향 모델(handle oriented model)로 변경하게 되면 어드레싱을 하는데 있어 소멸자, 메소드 반환값으로 역참조하기(de-referencing method return values), 객체 복사(object duplication)에 관하여 매우 엄격히 제어할 수 있는 등 객체와 관련된 많은 요구를 수용할 수 있게 됩니다.
새로 제안된 객체 모델은 자바 모델에 의해서 매우 많이 영향을 받습니다. 일반적으로, 새로운 객체를 생성할 때 객체 그 자체대신에 해당 객체의 핸들을 얻게 될 것입니다. 이 핸들을 함수에 전달하고, 할당하고 복사하였을 때 복사되고 보내지고 할당되는 것은 핸들만 입니다. 객체 자체는 결코 복사 또는 중복되지 않습니다.
이것은 한 객체의 모든 핸들이 항상 동일한 객체를 지정하도록 하기 때문에 객체를 다루는데 의미가 분명해지며 불필요한 중복과 혼란스러운 동작을 줄여줄 수 있습니다.
Zend 엔진 1.0 객체 모델
앞에서도 설명하였지만 PHP4에 탑재된 Zend 엔진 1.0 객체 모델에서는 생성된 객체를 값으로 취급하도록 설계되어 있습니다.
다음 코드가 Zend 엔진 1.0에서 어떻게 동작하는지 살펴보겠습니다.
[code php;gutter:true] class MyClass {
function setMember($value) {
$this->member = $value;
}

function getMember() {
return $this->member;
}
}

function foo($obj) {
$obj->setMember("foo");
}

$object = new MyClass();
$object->setMember("bar");
foo($object);
print $object->getMember(); [/code]
16행에서 객체의 멤버변수 member은 문자열 "bar"을 값으로 설정됩니다. 17행에서 객체 $object가 함수 foo에 값에 의해 보내질 때(call by value) 객체는 복제(duplicate)됩니다. 그러므로 foo()를 호출하면 $object의 복제물을 가지고 12행에서 $obj->setMember("foo")를 호출하게 될 것입니다. 그러면 18행에 의해 출력된 결과는 "bar"가 될 것입니다.
이것이 오늘날까지 PHP3 또는 PHP4 스크립팅 엔진이 동작하던 방식입니다. 어쩌면 많은 개발자들이 아직까지도 PHP3 또는 PHP4에서 항상 동일한 객체를 다루지 않고 복제된 객체를 다룬다는 사실 조차도 눈치채지 못하였을 수도 있을 것입니다. 제 홈페이지 '참조(레퍼런스)' 기사를 주의깊게 읽어본 독자라면 이 문제에 대하여 아래와 같이 복제 대신에 참조에 의한 객체 전달을 통하여 해결할 수 있다는 것을 알았겠지요.
[code php;gutter:true] class MyClass {
function setMember($value) {
$this->member = $value;
}

function getMember() {
return $this->member;
}
}

function foo(&$obj) {
$obj->setMember("foo");
}

$object = new MyClass();
$object->setMember("bar");
foo($object);
print $object->getMember(); [/code]
위의 참조(레퍼런스)를 이용한 개선된 예제에서 객체 생성할 때의 발생하는 객체 복제의 비효율성까지 추가로 개선코자 한다면 15행의 '$object = new MyClass();' 대신에 '$object = & new MyClass();'라고 코딩하였을 수도 있었겠지요.
Zend 엔진 2.0에서의 개선된 객체 모델
개선된 Zend 엔진 2.0 객체 모델에서는 이전과 같이 객체를 다루지 않아도 됩니다. 객체 모델의 커다란 변화에도 불구하고 객체의 기본적인 사용법은 참조기호 & 없이 이전 버전의 스크립트 엔진에서와 거의 동일할 것입니다. 새로운 객체 인스턴스를 생성하고 사용하기 위해서는 다음과 같이 합니다.
[code php;gutter:false] $object = new MyClass();
$object->method(); [/code]
위의 코드에서 클래스 MyClass의 새로운 인스턴스에 대한 핸들을 $object에 할당할 것이고 새로운 인스턴스에 대한 메소드를 호출할 것입니다.
[code php;gutter:true] class MyClass {
function setMember($value) {
$this->member = $value;
}

function getMember() {
return $this->member;
}
}

function foo($obj) {
$obj->setMember("foo");
}

$object = new MyClass();
$object->setMember("bar");
foo($object);
print $object->getMember(); [/code]
새로운 객체 모델은 코드의 직관적인 구현을 훨씬 더 많이 허용할 것입니다. 17행에서 객체 핸들(ID)이 값으로 foo()에 전달됩니다. foo() 내부에서 객체는 이 핸들에 의하여 가져오므로 setMember() 메소드는 원래의 인스턴스화된 객체를 호출하는 것이며 복사본으로 호출하는 것이 아닙니다. 그러므로 18행에 의해 "foo"가 출력될 것입니다.
이러한 접근은 객체가 생성되고 복제될 때 개발자에게 좀더 엄격하게 관리하도록 해줍니다. 이와 같이 객체 핸들을 foo()에 전달하여 객체를 다루도록 하면 불필요한 객체 복제를 줄여줄 것이고 이에따라 실행시간 성능을 부가적으로 개선시킬 것입니다.

Posted by 방글24
phpclass/객체모델2002. 12. 21. 14:42
Zend 엔진 2.0의 새로운 객체 모델
앞서 작성된 "클래스를 사용해야하나?(부제:클래스를 이용한 모듈화 프로그래밍)"라는 글에서 언급했던 것과 같이 현재 Zend 엔진 1.0을 기반으로하는 PHP4에서 제공되는 객체 관련 기능들이 여러가지로 부족한 면이 많이 있었습니다.
이에 따라 www.zend.com에서는 개선된 객체 모델을 도입한 Zend 엔진 2.0이 개발 중에 있습니다. 2001년 11월에 발표된 Zend 엔진 2.0 설계 초안(Zend Engine 2.0 Design Draft)과 2002년 6월에 릴리즈된 Zend 엔진 2.0 알파2 버전(Alpha 2 of PHP w/ the Zend Engine 2 Now Available)에서 개선된 내용을 정리해 보면 대략 아래와 같이 세가지로 분류할 수 있습니다.
  • 개선된 객체 모델(Revamped Object Model)
  • 예외상황 처리(Exception Handling)
  • 향상된 외부 지원 객체지향 APIs(Improved Interface with 3rd party OO APIs)
분류에 포함되지 않은 String offset syntax와 같은 문법 변경 사항들이 일부 있으나 변경된 내용을 살펴보면 새로운 객체 모델을 구현하기 위한 기술에 거의 집중되고 있음을 볼 수 있습니다. 이 문서에서는 개선된 객체 모델에 대하여 집중적으로 살펴보고자 합니다.
Zend 엔진 2.0에 추가된 주된 기능 중에서 예외상황 처리(exception handling) 및 자바 또는 닷넷과 같은 외부 기술에 관한 사항은 관련문서를 참조하기 바라며
외부 객체지향 APIs(COM 컴포넌트 또는 자바 객체)에 대하여는 OO Syntax Overloading 기술로  접근할 수 있도록 Zend 엔진 2.0를 개발하고 있으며 이 기술은 객체역참조(Object Dereferencing)라는 객체지향 개념을 통해 구현하고 있습니다.
Zend 엔진 2.0에서 지원하는 객체 특성을 살펴보면 아래 표와 같이 설계 초안에서 알파2 버전으로 넘어오면서 다중상속 특성이 빠진 반면 nested namespace 개념(nested 클래스, import 처리 등)이 추가되었습니다.
< PHP의 객체지향 프로그래밍 특성 >
객체 특성 PHP3 or
Zend 엔진1.0
Zend 엔진 2.0
설계초안
Zend 엔진2.0
알파2

객체 핸들
revamped object model using object handles

객체복사

객체참조

객체참조

static 멤버 변수
static class member variables

x

o

o

private 멤버 변수
private member variables

x

o

o

단일화된 생성자
unified constructors

x

o

o

소멸자
destructors

x

o

o

객체 복제
object cloning

N/A

o

o

함수 반환 객체 역참조
dereferencing objects returned from functions

x

o

o

객체 삭제
forced deletion of objects

x

o

o

Nested 클래스
nested classes

x

x

o

클래스 상수
class constants

x

x

o

다중 상속
multiple Inheritance

x

o

x

(o:포함됨, x:포함되지 않음, N/A:Non Applicable)
현재도 Zend 엔진 2.0의 모든 특성들에 대하여 www.zend.com 웹사이트와 메일링 리스트(engine2-subscribe@lists.zend.com)를 통해 계속 토론되어지는 과정에 있으므로 앞으로 발표될 Zend 엔진 2.0의 최종안에서는 새로운 특성들이 포함될 수도 있으며 반대로 현재 포함되어 있는 특성들이 제외될 수도 있습니다.
참고 자료

Posted by 방글24