생성자 및 소멸자 체인
일반적인 객체지향언어에서 보면 클래스 객체를 생성할 때마다 생성자가 실행되는데 객체를 생성할 때에 먼저 부모클래스의 생성자를 호출한 후 자식클래스의 생성자를 호출합니다.
그러나 PHP3 또는 PHP4 스크립트에서는 마지막으로 파생된 클래스의 생성자만을 호출하고 그 이상의 부모클래스의 생성자는 실행하지 않고 종료합니다. PHP에서의 생성자 동작 원리는 좀 더 복잡합니다. 자세한 것은 메뉴 "핍클래스홈>>클래스 사용법>>생성자"를 참조하기 바랍니다.
따라서 클래스를 정의할 때는 연속된 생성자 실행(생성자 체인)을 위해 반드시 생성자를 정의하고 그 생성자 내에서 부모클래스에 대한 생성자를 호출할 필요가 있습니다.
반면 클래스 객체가 파괴될 때마다 클래스 소멸자가 실행되는데 프로그램은 먼저 자식클래스의 소멸자를 호출한 후 부모클래스의 소멸자를 호출합니다.
"후키라이브러리"를 이용하여 클래스를 정의하면 생성자 및 소멸자 체인을 자동으로 구현할 수 있습니다.
[code php;gutter:false] /*
파일명 class.c.php
조상클래스 c 정의
*/

classdefine("
class cInterclass {}
");

class c extends cInterclass {
function c() {
.... 생성자에서 처리할 내용을 이곳에 작성합니다.
}

function _c() {
.... 소멸자에서 처리할 내용을 이곳에 작성합니다.
}

.... 이곳에 조상클래스 c의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.b.php
부모클래스 b 정의
*/

blassdefine("
class bInterclass extends c {}
");

class b extends bInterclass {
function b() {
.... 생성자에서 처리할 내용을 이곳에 작성합니다.
}

function _b() {
.... 소멸자에서 처리할 내용을 이곳에 작성합니다.
}

.... 이곳에 부모클래스 b의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.a.php
자식클래스 a 정의
*/

classdefine("
class aInterclass extends b {}
");

class a extends aInterclass {
function a() {
.... 생성자에서 처리할 내용을 이곳에 작성합니다.
}

function _a() {
.... 소멸자에서 처리할 내용을 이곳에 작성합니다.
}

.... 이곳에 자식클래스 a의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
클래스 a를 이용하여 객체를 생성한다면 객체를 생성할 때 생성자 체인에 의해 c, b, a 생성자가 순서대로 실행되며, 현 문서가 종료되는 시점에서 소멸자 체인에 의해 _a, _b, _c 소멸자가 순서대로 실행됩니다.
소멸자는 문서가 종료되는 시점에서 수행되기 때문에 현 문서가 출력되는 페이지에서는 그 실행여부를 확인할 수 없습니다. 만약 소멸자의 실행여부를 확인하려면 소멸자 실행 내용을 파일 등에 기록한 후 다음 페이지에서 이 파일 내용을 확인하면 될 것입니다.
[code php;gutter:false] class a extends aInterclass {
function a() {
.... 생성자에서 처리할 내용을 이곳에 작성합니다.
}

function _a() {
if ($fp=@fopen("/tmp/destr.txt", "a+")) {
flock($fp, LOCK_EX);
fwrite($fp, "_a() 실행 \n");
flock($fp, LOCK_UN);
fclose($fp);
}
}
} [/code]

Posted by 방글24
다중 상속이란?
다중 상속(multiple inheritance)은 자식클래스가 하나 이상의 부모클래스로부터 그 특성을 상속받는 것을 말합니다. 다중 상속된 자식클래스는 모든 부모클래스의 데이터멤버 및 멤버함수의 특성을 다 가지게 되므로 부모클래스의 데이터멤버 및 멤버함수를 사용할 수 있습니다.
다중상속의 필요성
객체지향 프로그래밍을 하다보면 많은 경우에 접하게 되는 것이 다중 상속 개념입니다. 다중 상속이야말로 특성 상속의 가장 기본적인 것이라고 할 수 있습니다.
< 다중 상속의 예 >
그림의 예에서 보듯이 서로 독립적인 4개의 클래스(더하기, 빼기, 곱하기, 나누기 클래스)로부터 데이터멤버 및 멤버함수를 상속받은 계산기 클래스가 만들어지게 됩니다.
PHP에서의 다중 상속
PHP에서는 단일상속에 관하여는 별문제없이 그 부모클래스의 특성을 상속받을 수 있습니다. 그러나 복수의 부모클래스의 특성을 상속받을 수는 없습니다. 이러한 다중상속의 문제는 웹사이트 구조가 복잡해 질 수록 절실해지게 되며, 자원(클래스)의 재사용이란 측면에서 볼 때도 꼭 필요한 기능이라고 할 수 있습니다. 이러한 다중상속을 후키라이브러리를 이용하면 손쉽게 구현할 수 있습니다.
다중 상속 정의하기
[code php;gutter:false] /*
파일명 class.더하기.php
부모클래스 더하기 정의
*/

classdefine("
class 더하기Interclass {}
");

class 더하기 extends 더하기Interclass {
.... 이곳에 부모클래스 더하기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.빼기.php
부모클래스 빼기 정의
*/

classdefine("
class 빼기Interclass {}
");

class 빼기 extends 빼기Interclass {
.... 이곳에 부모클래스 빼기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.곱하기.php
부모클래스 곱하기 정의
*/

classdefine("
class 곱하기Interclass {}
");

class 곱하기 extends 곱하기Interclass {
.... 이곳에 부모클래스 곱하기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.나누기.php
부모클래스 나누기 정의
*/

classdefine("
class 나누기Interclass {}
");

class 나누기 extends 나누기Interclass {
.... 이곳에 부모클래스 나누기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.계산기.php
자식클래스 계산기 정의
*/

classdefine("
class 계산기Interclass extends 더하기, 빼기, 곱하기, 나누기 { }
");

class 계산기 extends 계산기Interclass {
.... 이곳에 자식클래스 계산기의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
위와 같은 예를 보면 현재클래스 계산기는 부모클래스 더하기, 빼기, 곱하기 및 나누기로부터 다중 상속받도록 되어 있습니다.
classdefine 함수는 클래스 정의 문자열을 해석하면서 클래스 다중 상속을 지원할 수 있도록 상속에 필요한 부모클래스 정의 파일을 인클루드 시킵니다. PHP 스크립트는 클래스 다중 상속을 지원하지 않기 때문에 클래스 정의 문자열을 해석하면서 단일 상속으로 변환시켜 줍니다. 즉, 다중 상속을 단일 상속형태로 변환시켜 주는 역할을 자동적으로 수행합니다.
동일한 조상클래스(ancestor class)를 가진 다중 상속
동일한 부모클래스로부터 서로 다른 길을 통해 상속된 두 개 이상의 파생클래스를 다중 상속할 수 있습니다. 동일한 클래스를 두번 이상 정의하게 되면 PHP는 에러를 발생시킵니다. 이러한 문제를 해결하기 위해 "후키라이브러리"에서는 동일한 부모클래스를 단 한번만 포함하도록 동작합니다. 이는 C++에서 가상클래스(virtual class)를 이용하는 것과 비슷한 기능을 한다고 볼 수 있습니다. 즉, 가상클래스를 이용하는 것은 파생클래스가 동일한 부모클래스를 여러 번 상속했다고 하더라도 하나의 클래스만을 파생클래스에 포함시키도록 하는 것입니다.
< 동일한 조상클래스를 가진 다중 상속의 예 >
[code php;gutter:false] /*
파일명 class.숫자.php
조상클래스 숫자 정의
*/

classdefine("
class 숫자Interclass {}
");

class 숫자 extends 숫자Interclass {
.... 이곳에 조상클래스 숫자의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.더하기.php
부모클래스 더하기 정의
*/

classdefine("
class 더하기Interclass extends 숫자 {}
");

class 더하기 extends 더하기Interclass {
.... 이곳에 부모클래스 더하기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.빼기.php
부모클래스 빼기 정의
*/

classdefine("
class 빼기Interclass extends 숫자 {}
");

class 빼기 extends 빼기Interclass {
.... 이곳에 부모클래스 빼기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.곱하기.php
부모클래스 곱하기 정의
*/

classdefine("
class 곱하기Interclass extends 숫자 {}
");

class 곱하기 extends 곱하기Interclass {
.... 이곳에 부모클래스 곱하기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.나누기.php
부모클래스 나누기 정의
*/

classdefine("
class 나누기Interclass extends 숫자 {}
");

class 나누기 extends 나누기Interclass {
.... 이곳에 부모클래스 나누기의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.계산기.php
자식클래스 계산기 정의
*/

classdefine("
class 계산기Interclass extends 더하기, 빼기, 곱하기, 나누기 { }
");

class 계산기 extends 계산기Interclass {
.... 이곳에 자식클래스 계산기의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
다중 상속으로 생기는 문제점
다중 상속이 가진 문제점은 클래스 상속 구조가 매우 복잡해지고, 멤버 특성의 상속 관계가 모호해 진다는 것입니다.
예를 들면, 만일 동일한 이름의 데이터멤버 x가 더하기, 빼기 클래스에서 각각 정의되어 있을 때 더하기, 빼기 클래스를 다중으로 상속받게 되는 계산기 클래스에서 데이터멤버 x에 접근할 때 더하기 클래스에 있는 데이터멤버 x에 접근해야 할지 아니면 빼기 클래스에 있는 데이터멤버 x에 접근해야 할지 혼란이 발생하게 됩니다. 따라서 각 클래스에서 데이터멤버를 정의할 때는 하위클래스에서 동일한 이름의 데이터멤버를 참조할 경우를 대비하지 않으면 안됩니다. 이러한 문제는 멤버함수를 정의할 때도 동일하게 나타나는 문제입니다.
아래 예와 같이 다중 상속되었을 경우를 보면 후키라이브러리에서는 왼쪽에 있는 자식클래스에서 우선적으로 멤버를 찾게 됩니다. 이 예에서는 더하기에 있는 데이터멤버 또는 멤버함수를 먼저 참조하도록 되어 있습니다. 더하기에서 찾지못하였으면 빼기에서, 빼기에서 찾지 못하였으면 곱하기에서, 곱하기에서 찾지 못하였으면 나누기에서 찾게됩니다. 이러한 규칙은 파이썬과 유사하다고 할 수 있습니다.
[code php;gutter:false] classdefine("
class 계산기Interclass extends 더하기, 빼기, 곱하기, 나누기 { }
");

class 계산기 extends 계산기Interclass {
.... 이곳에 자식클래스 계산기의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
그러나 이러한 규칙에 의존하여 클래스를 정의하지 말기 바랍니다. 이는 매우 관리하기 힘들고 약간의 실수에도 큰 문제를 발생시킬 수 있기 때문에 가능하면 각 클래스 별로 다른 클래스와 구분할 수 있는 멤버 이름을 사용할 것을 권합니다. 꼭 다중 상속을 이용해서만이 아니더라도 PHP에서는 멤버의 접근 권한(access right)을 임의로 지정할 수 없으며 무조건 public으로 설정되도록 되어 있기때문에 이 문제는 항상 상존한다고 보아야 합니다. C++의 경우를 보면 멤버의 접근 권한이 public 외에도 private, protected를 선택할 수 있으며 private를 지정할 경우에 하위클래스에서 동일한 이름의 멤버를 접근하는데 혼란이 발생하지는 않을 것입니다.

Posted by 방글24
1. classdefine : 클래스 정의 함수
2. newobject : 객체 생성 함수
3. classchain : 클래스 상속관계 반환 함수
클래스 정의 함수 classdefine
[code c;gutter:false] string classdefine(string def_class); [/code]
기능
클래스 정의 문자열을 입력받아 해석하여 후키라이브러리에서 지원하는 기능을 사용할 수 있도록 합니다.
클래스 정의
보통 클래스를 정의하려면 아래와 같이 클래스 내에 멤버함수 및 데이터멤버를 정의하게 될 것입니다.
[code php;gutter:false] /*
클래스 a 정의
*/

class a {
.... 이곳에 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
후키라이브러리를 이용하여 클래스 a를 정의하려면 아래와 같이 classdefine 함수로 현재클래스에 대한 중간클래스를 먼저 정의하여야 하며 현재클래스는 extends 키워드를 이용하여 중간클래스와의 관계를 정의해 주어야 합니다. 중간클래스명은 현재클래스명에 "Interclass"를 붙여줍니다. 그리고 나서 현재클래스 내에 멤버함수 및 데이터멤버를 정의하여 주면 됩니다.
[code php;gutter:false] /*
클래스 a 정의
*/

classdefine("
class aInterclass {}
");

class a extends aInterclass {
.... 이곳에 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
단일 상속의 예
보통 PHP에서는 클래스 a가 부모클래스 z로부터 그 특성을 상속받으려는 자식클래스라면 아래와 같이 extends 키워드를 이용하여 그 관계를 정의하여 주게 되지요.
[code php;gutter:false] /*
부모클래스 z 정의
*/

class z {
.... 이곳에 부모클래스의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
자식클래스 a 정의
*/

class a extends z {
.... 이곳에 자식클래스의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
후키라이브러리를 이용하여 단일 상속받으려면 아래와 같이 extends 키워드를 이용하여 그 관계를 정의하여 주게 되지요. 이 때 classdefine() 함수로 중간클래스를 먼저 정의하여야 하며 여기에서 현재클래스와 부모클래스 사이를 extends 키워드를 이용하여 연결해 줍니다.
[code php;gutter:false] /*
파일명 class.z.php
부모클래스 z 정의
*/

classdefine("
class zInterclass {}
");

class z extends zInterclass {
.... 이곳에 부모클래스의 멤버함수 및 데이터멤버를 정의합니다.
}

/*
파일명 class.a.php
자식클래스 a 정의
*/

classdefine("
class aInterclass extends z {}
");

class a extends aInterclass {
.... 이곳에 자식클래스의 멤버함수 및 데이터멤버를 정의합니다.
} [/code]
위에서 한가지 주의할 점은 부모클래스 z와 자식클래스 a는 반드시 별도의 파일로 관리하여야 합니다.
다중 상속의 예
다중 상속에 관한 자세한 내용은 "다중 상속" 장을 참조바랍니다.
중간클래스의 접미어를 변경하는 방법
접미어 "Interclass"를 디폴트 값이며 다른 접미어를 사용하려면 classdefine 함수의 두번째 인자로 지정하여 주면 됩니다. 아래는 "Interclass" 대신에 "Linkclass"를 접미어로 지정하는 예를 보여줍니다.
[code php;gutter:false] classdefine("
class aLinkclass extends b { }
", "Linkclass");

class a extends aLinkclass {

} [/code]
객체 생성 함수 newobject
[code php;gutter:false] include("./lib.hwooky.php"); // 후키라이브러리(0.1.0)

$obj = & newobject(CLASS_NAMES, ARGV); [/code]
기능
멤버 특성을 상속하려는 1개 이상의 부모클래스명 및 필요한 입력인자를 지정하게 되면 새로운 클래스 객체를 생성하여 되돌려 줍니다. 클래스 객체를 생성하기 전에 지정된 모든 부모클래스를 정의한 파일들을 순서대로 인클루드 시켜 줍니다.
부모클래스명 CLASS_NAMES
[code php;gutter:false] $obj = & newobject("clsDate", ARGV); [/code]
CLASS_NAMES은 상속받으려는 부모클래스명을 지정합니다. 위의 예에서는 클래스 "claDate"로 부터 객체를 생성합니다. newobject 함수에서 직접 다중상속하려는 경우에는 아래와 같이 복수 개의 부모클래스를 콤마(,)로 분리하여 지정합니다. 이 때 지정되는 순서는 자식클래스로부터 부모클래스 순으로 합니다. 따라서 아래의 예에서는 클래스 Bank가 마지막 파생클래스가 됩니다.
[code php;gutter:false] $obj = & newobject("Bank, clsDate", ARGV); [/code]
클래스 파일 관리 classfile.info
클래스 파일 관리는 classfile.info 파일 내에서 일괄적으로 다루어주기 때문에 사용자가 작성하는 스크립트 내에서는 파일 위치 또는 파일명을 지정하지 않아도 됩니다. 대신에 classfile.info 파일의 위치는 newobject()의 두번째 인자인 배열의 한 요소(첨자명 "path-to-classfile")로 지정됩니다.
[code php;gutter:false] $argv = array(
"path-to-classfile" => "./class"
);

$father = & newobject("Bank", $argv); [/code]
위와 같이 classfile.info의 위치를 "./class"를 지정하게 되면 현 문서의 하위디렉토리인 "./class"에서 classfile.info 파일을 찾게 됩니다. 이곳에 이 파일이 존재하지 않게되면 에러가 발생하게 되지요. classfile.info 파일에는 연관배열로 각 클래스명에 해당되는 클래스 정의 파일의 위치와 파일명이 할당되어 있습니다. 이 때 배열명은 반드시 $hkClassSpec로 지정하여야 합니다.
[code php;gutter:false] <?php

/*
classfile.info
*/

$hkClassSpec["clsDbsql"] = "base/class.mysql.php";
$hkClassSpec["clsSession"] = "session/class.session.php";
.
.
.

?> [/code]
classfile.info에 지정된 디렉토리 "base" 및 "session"은 classfile.info 파일이 있는 곳을 기준으로 지정합니다. 이와 같은 규칙만 지켜준다면 상호 독립적인 클래스를 별도의 디렉토리 별로 저장하여 관리할 수 있으며, 필요에 따라 필요한 클래스를 모두 쉽게 상속받아 사용할 수 있을 것입니다. 지정되는 디렉토리는 classfile.info 파일의 위치를 기준으로 지정하게 되며 현재 디렉토리를 의미하는 "."은 붙이지 않습니다.
입력값 ARGV
newobject 함수의 두번째 입력인자인 ARGV는 위에서 설명한 "path-to-classfile" 외에도 클래스 내에서 필요한 모든 입력인자를 지정할 수 있습니다. 이와같이 지정된 ARGV는 최상위 클래스로부터 최하위 클래스까지 모든 생성자에 동일하게 전달됩니다. ARGV의 데이터형은 연관배열(associative array)이며 객체 생성할 때에 필요한 인자를 배열에 담아 부모클래스 생성자로 전달합니다. 따라서 필요한 만큼 제한 없이 인자를 지정할 수 있습니다.
[code php;gutter:false] $argv = array(
arg1 => "입력값1",
arg2 => "입력값2",
.
.
.
);

$obj = & newobject("Bank", $argv); [/code]
리턴값
생성된 클래스 객체를 되돌려 줍니다. 반환된 객체는 아래 예에서와 같이 반드시 참조로 받아야 합니다. 참조로 받지 않으면 대부분의 경우에는 정상적으로 동작하지만 소멸자에서 데이터멤버를 다룰 때와 같은 경우에는 정상적으로 동작하지 않습니다.
[code php;gutter:false] $argv = array(
"path-to-classfile" => "./class"
);

$obj = & newobject("Bank, clsDate", $argv);

$obj->abc(); // abc()가 멤버함수로 정의되어 있다면... [/code]
복수의 객체를 생성할 때
아래는 동일한 상속 관계를 유지하는 클래스를 이용하여 복수 개의 객체를 생성하는 방법입니다.
[code php;gutter:false] $argv = array(
"path-to-classfile" => "./class"
);

$father = & newobject("Bank", $argv);
$mother = & newobject("Bank", $argv);
$son = & newobject("Bank", $argv); [/code]
위의 예와 다른 상속 관계를 가지고 있는 클래스를 이용하여 위와 동시에 객체를 생성할 수도 있습니다.
[code php;gutter:false] $argv = array(
"path-to-classfile" => "./class"
);

$father = & newobject("Bank", $argv);
$mother = & newobject("Bank", $argv);
$son = & newobject("Bank", $argv);

$argv["path-to-classfile"] = "./class/logic";

$o1 = & newobject("Group", $argv);
$o2 = & newobject("Group", $argv);
$o3 = & newobject("Group", $argv); [/code]
클래스 Bank와 클래스 Group이 서로 다른 classfile.info에 기록/유지되고 있다면 위에서와 같이 "path-to-classfile"를 객체 생성 직전에 올바르게 지정하여 주어야 합니다. 위의 예에서 보면 클래스 Bank는 현 문서를 기준으로 "./class/classfile.info"에 기록되어 있으며 클래스 Group는 "./class/logic/classfile.info"에 기록되고 있음을 알 수 있습니다.
클래스 상속관계 반환 함수 classchain
[code php;gutter:false] classchine(CLASS_NAMES); [/code]
기능
이전 버전의 classinfo 함수와는 달리 클래스의 상속관계를 브라우저에 표시하지 않고 단지 상속관계를 배열에 담아 되돌려 줍니다.
설명
배열에는 최상위 클래스명부터 최하위 파생클래스명까지 순서대로 기록되어 있습니다. classchine 함수를 사용하기 위해서는 먼저 newobject 함수를 실행하여야 하며, newobject 함수가 실행된 후에는 어느 곳에서나 실행할 수 있습니다. classchain 함수에 지정하게 되는 입력인자는 newobject 함수에서 지정하였던 첫번째 입력인자를 의미합니다.
사용예
[code php;gutter:false] function print_chain($names) {
$chain = classchain($names);
$str = $indent = "";

foreach ($chain as $classname) {
$str .= "$indent$classname\n";
$indent .= ".";
}

print $str;
}

include("./lib.hwooky.php"); // 후키라이브러리(0.1.0)

$argv = array(
"path-to-classfile" => "./class"
);

$father = & newobject("Bank", $argv);
$mother = & newobject("Bank", $argv);
$son = & newobject("Bank", $argv);

$argv["path-to-classfile"] = "./class/logic";

$o1 = & newobject("Group", $argv);
$o2 = & newobject("Group", $argv);
$o3 = & newobject("Group", $argv);

print("<P>----클래스 Bank의 상속관계----</P>");
print_chain("Bank");
print("<P>----클래스 Group의 상속관계----</P>");
print_chain("Group"); [/code]
위와 같은 경우의 한 예를 보면 아래와 같이 출력될 것입니다. 여기서 클래스 Bank는 부모클래스 clsArray, clsString, clsDate로부터 상속되었다고 가정하였으며, 클래스 Group는 부모클래스 Check, Serialize로부터 상속되었다고 가정하였습니다.
----클래스 Bank의 상속관계----

clsArray
.clsString
..clsDate
...Bank

----클래스 Group의 상속관계----

Check
.Serialize
..Group

Posted by 방글24