[펌] [cyberuls.springnote.com]

 

php4에서는 단일 상속과 몇가지에 대한 지원밖에는 해주지 못했지만.. php5에서는 자바의 객체지향 모델을 가지고 옴으로써 여러가지를 지원해주게 됩니다.

 

  1. 상속

여전히 상속은 단일 상속만을 지원합니다.

하지만 interface 개념을 도입함으로써 여러가지로 작동하는 원리로 만들어줍니다. interface는 다중상속을 위해서 나온 것은 아닙니다. 그러니 다중상속 개념은 없다고 보는것이 맞겠지요...

 

2. 인터페이스  

     php5에서는 interface 개념을 지원합니다. interface는 다른 객체와 잘 부합할 수 있도록 객체의 표준을 마련하는 것이라 보면 쉽겠습니다.

 

3.  추상 클래스

    인터페이스와 비슷한 개념으로 추상클래스를 지원합니다. 하지만 추상클래스와 인터페이스의 용도는 좀 다르죠.. 일단 클래스이기 때문에 상속은 단일 상속밖에 할 수 없습니다. 그리고 추상메소드를 정의를 해야하고 abstract 라는 키워드를 써서 추상클래스임을 명시해야합니다. 일단 설계하는 클래스가 여러가지로 몇가지 기능을 빼고는 다 같은 방식으로 작동이 된다면 추상클래스는 아주 좋은 선택이 될 수도 있습니다.

 

  •  php5에서는 추상 클래스에서 인터페이스를 구현(implements) 하면 인터페이스에 속해 있는 메소드들은 무조건 실제 작동하는 메소드로 생성이 되어야 합니다. 하지만 자바에서는 인터페이스 메소드들도 그대로 추상메소드로 되어질 수 있습니다. 이것이 조금 번거로울 수도 있겠네요.

예를 들어서   java 는

interface A { public void a();}

abstract class B implements A { abstract public void a(); }

 

php는

 

interface A { public function a(); }

abstract class B implements A { public function a() { echo "aaa"; } }

 

위의 예제와 같이 java는 interface의 메소드(추상메소드이지요)는 추상클래스내에서 다시 추상메소드가 될 수 있지만 php는 반드시 구현되어야 하는 메소드가 되버립니다.

 

4.  오버로딩(Overloading), 오버라이딩 (Overriding)

오버로딩은 같은 클래스 내에서 이미 정의해 놓은 메소드를 같은 의미이지만 다른 매개변수를 사용 할 때 같은 이름의 메소드를 정의 할 수있게 해주는 것을 말합니다.

오버라이딩은 상속관계에서 같은 메소드이지만 다른 내용으로 만들고 싶을 때 메소드를 재정의 하는 것을 말합니다.

  php5 에서 사용하는 방법은 조금 신기합니다. ㅋㅋ

     일단 오버라이딩은 자동으로 됩니다... php에서는 같은 이름의 함수가 있으면 가장 마지막에 선언이 되어진 함수로 실행을 하기 때문에 오버라이딩은 자동으로 되게 되어있습니다. 그렇다면 오버로딩은 어떨까요?

 

     php5 에서는 몇가지 매직함수를 지원하는데요.. 그중에서 __call 이라는 놈이 재미난 놈입니다...

     이놈은 선언 되어지지 않은 메소드가 실행이 될때 실행되어지는 메소드 입니다.

 

     예를 들어서

 

     class AAA   {

          function run() {

 echo "뛰어";

          }

}

 

$a = new AAA();

$a->run();

 

   일반적으로는 이렇게 실행을 하는데요....

 

   $a->run_to("진호");

 

      이런식으로 선언되어지지 않은 메소드를 사용하면 __call 이 자동으로 불리어집니다. ㅋㅋ 오~~ 여기서 부터 재밌습니다.

      그럼 __call은 어떻게 쓰느냐... ㅋ

 

     class AAA   {

          function run() {

 echo "뛰어";

          }

 

function __call($func, $args) {

     echo "함수 : ", $func, ", 매개변수 리스트 ", implode(",", $args) ;

   }

}

    

      이런식으로 __call 은 메소드 이름과 매개변수 리스트를 배열로 받아들인다. ㅋㅋ  자 이간단한 개념을 가지고 살짝 재밌는 기능을 구상해 보자.

 

     요즘 웹프레임워크 중에 제일 잘 나가고 있는 루비의 일부 기능을 흉내내보겠당... 루비는 메타프로그래밍이 가능한 아주 유연한 언어이다.

    실행중에 클래스 메소드 자체를 마음대로 바꾸어 줄 수 있다. 그런 기능을 이용해서 레일스에서는 액티브 레코드에 여러가지 기능을 넣어두었다.

 

    예를 들어

 

  obj.find_by_name("진호")     --- name 필드가 진호인 것을 찾는다.

 

     여기서 주의깊게 봐야할 부분이 name 부분인데 이것은 필드 이름인데 언제 어디서든 바뀔 수가 있답니다. 그렇다면 메소드가 동적으로바뀐다는 말이되겠군요..

     어! 메소드가 동적으로 바뀌네 하면 php에서는 __call 을 떠올리세요. ㅋㅋ

 

    function __call($func, $args) {

       if (strpos($func, "find_by_") == 0) {

             // 자 이제 메소드에서 나머지 부분만 때어 볼까요?

            $arr = explode("find_by_", $func);

         $field = $arr[1];      // 이렇게 하면 필드 이름이 나오겠네요.. 오호~~

            $value = $args[0];   // 이렇게 하면 검색 하는 값이 나오겠네요. .오.. 정말..

 

           //  오 쉽게 select 구문이 되네요.. ㅋ

           $sql = "select * from 테이블 where {$field} = '{$value}' ";

 

           // 나머지는 상상에 맡길게요.. ㅋ

       }

    }

 

    제가 여기서 말하고자 하는 것은 php5 오면서 상당히 유연해졌다는 것입니다. 그리고 pecl에 있는 라이브러리 중에서는 실행시간 중에도 php의 메소드를 바꿔주는 메소드도 있습니다.. 즉 메타프로그래밍이 된다는 거죠.... ^^

 

    앞으로도 php는 발전 가능성이 너무 많네요.... ㅋ

 

5. 다형성

    다형성이라는 말은 여러가지로 해석이 될 수 있는데요,,,  하나의 자료형에 여러가지 다른 자료형을 넣을 수 있느냐는 것과 서로 유사한 클래스들을 하나의 인터페이스로서 제어 할 수 있느냐는 것도 다형성 중에 하나의 요소가 됩니다.   이것에 대해서는 php는 왕이죠.. 거의..  (필자가 생각하기에는.. ㅋㅋ )

 

    array  하나로 모든 것을 저장하고 내 뱉고 검색 할 수 있답니다.. 심지어는 array_multi_sort 라는 함수를 이용하면 db 에 있는 데이타를 가지고 와서 배열에 담아두고 그 자체로 다시 정렬 할 수 있습니다. 그래서 php는 기본적으로 다형성을 가지고 시작을 합니다. 모든 자료형은 동적으로 생성이 됩니다. 스크립트 언어들의 거의 기본적인 특징이죠... 그래서 좀 더 유연하고 쉽고 빠르게 프로그래밍을 배울 수 있답니다. 하지만 쉽다는 것이 곧 아무나 한다는 의미는 아니니 명심하세요...

 

6. 동적 바인딩

 동적 바인딩은  실행시간에 실제 그 객체의 메소드를 정확하게 찾아서 메소드를 실행 시키는 방법입니다. 이 말은 C++이나 자바에서 많이 접할 수 있는데요...

 C++이나 자바는 미리 클래스를 선언해주고 변수에 객체를 할당합니다. 그래서 그 클래스 변수에는 딱 정해진 자료형으로만 사용되어지는데요...

 그래서 인터페이스나 추상클래스를 두고 실제 움직이는 메소드에 대해서는 다른 클래스에 맡겨 두는 방식이 됩니다. 그렇게 되면 여러 클래스들이 인터페이스나 추상클래스 앞으로 모일 수가 있답니다. 즉 인터페이스와 추상클래스를 가지고 다른 클래스들을 공통적인 방법으로 실행하지만 실제 실행되는건 인터페이스나 추상클래스가 아닌 실제 그것을 구현한 클래스에 들어있는 메소드라는 것입니다.. 자기 자신을 아는 것이죠...

 

   하지만 php는 애초에 자료형을 지정해줄 필요가 없답니다. 그래서 지금 내가 할당되어진 객체 그 자체로 실행을 하기 때문에 언제든 동적 바인딩이 된다고 볼수가 있답니다. 그러니 좀 더 편하게 프로그래밍을 할 수 있답니다.


마법 메서드 (Magic Methods): __call(), __get(), __set(), __isset(), __unset()

이건 다들 아는 기능이다. 메서드 호출, 멤버 접근 등을 저 메서드를 통해서 속일 수 있다. 예를 들어 PHP에서는 원래 메서드 오버로딩이 안되는데, 이걸 이용하면 오버로딩을 흉내낼 수도 있다.

SPL

원래 확장으로 만들어져 PHP 5.2부터는 기본으로 포함하게 된 SPL은 엄청난 축복이다. 이름을 보면 짐작할 수 있듯, C++ STL의 반복자 개념에 영향을 받아 만든 라이브러리다(나도 STL을 좋아한다). 이것을 잘 이용하면 유사 배열 객체를 만들 수 있다.

Traversable

이 인터페이스를 구현하면 foreach문에 객체를 집어넣어서 배열처럼 돌릴 수 있다. Traversable은 유사 인터페이스고, 실제로는 Iterator 혹은 IteratorAggregate를 구현하면 된다. 전자는 반복자를 직접 구현하는 방식이고, 후자는 IteratorAggregate->getIterator() 메서드를 구현하여 반복자 객체를 위임하는 방식이다. 간단하게 배열처럼만 작동하는 객체를 의도한다면 후자가 좋고, 좀더 정교한 것을 원하면 직접 Iterator를 구현하는 것이 좋다.

Countable

이 인터페이스를 구현하면 count() 함수에 넣었을 때 배열처럼 원소 개수를 반환하게 할 수 있다.

ArrayAccess

정말 유용하다. 이거 모르는 사람이 매우 많은데, 첨자 연산자([])를 오버로드할 수 있다. 단순히 값을 반환하는 것 말고도 해당 키가 존재하는지 확인하는 것(isset($obj[$key])), 해당 키의 원소를 삭제하는 것(unset($obj[$key])), 원소를 덧붙이는 것($obj[] = $value), 해당 키의 원소 값을 수정하거나 추가하는 것($obj[$key] = $value)이 가능하다.

아, 중요한 것 하나. 배열과 달리 ArrayAccess를 구현한 객체는 키 값으로 스칼라 외에 배열이나 객체도 받을 수 있다.

가변 인자: func_get_args()

가변적인 갯수의 인자를 전달받을 때 사용한다. PHP는 함수 선언시에 명시한 시그너쳐에 맞지 않는 호출을 하면 오류를 내는데, 시그너쳐의 갯수보다 많은 인자를 받을 땐 오류를 내지 않는다. 이걸 이용하면 가변 인자를 받으면서 쉽게 오류 처리를 할 수 있다. 예를 들어 인자 갯수는 가변적이지만, 꼭 맨 처음의 인자 하나는 배열로 받아야 한다는 제약 사항이 있다면?

function get_array_and() {
    $args = func_get_args();
    if(!count($args) or !is_array($args[0]))
        throw new InvalidArgumentException('First parameter must be array');
    $array = $args[0];

위와 같이 할 수도 있지만, 아래처럼 하면 굳이 제약 사항을 검사하고 오류를 내는 코드를 직접 작성할 필요가 없다.

function get_array_and(array $array) {
    $args = func_get_args();

주의할 것이 있다. 이 함수의 호출은 항상 맨 첫 문장, 대입식을 제외한 가장 바깥 표현식이어야 한다. 그렇지 않으면 의도한대로 작동하지 않는다. 이유는 나도 모른다. 하지만 이 버그는 PHP쪽에서는 유명하다.

eval()

전달된 PHP 코드 문자열을 해당 문맥에서 실행한다. eval('echo 1234;')1234를 출력한다. 그렇지만 표현식을 받지는 않는다.

$expr = '123 + 456';
$value = eval($expr);

위와 같이 하면 문장이 세미콜론(;)으로 종결되지 않았다며 구문 오류가 난다. 표현식을 평가하고 싶다면 아래와 같이 써야 한다.

eval("\$value = ($expr);");

Reflection

PHP 5에서 추가된 리플렉션 기능을 사용하면 클래스의 멤버와 메서드 목록을 얻는다던가, 함수의 시그너쳐를 추적하는 등의 일을 할 수 있다. 인자에 타입 힌트가 있는지, 있다면 어떤 타입인지, 기본값이 있는지, 변수 이름이 무엇인지 등도 알 수 있다. 이런걸 이용하면 Python의 키워드 인자도 흉내낼 수 있을 것이다.

__toString()

이것도 마법 메서드의 하나인데, PHP 5에서 추가된 기능이다. 이 메서드를 정의한 객체는 echo문으로 출력할 때 __toString()이 반환한 문자열을 출력한다. Python의 __str__ 속성과 비슷한 용도이다. 그런데 이게 PHP 5.2 이후로는 substr() 같이 문자열을 받는 함수에 전달될 때 자동으로 해당 문자열로 캐스팅도 된다. 물론 명시적으로 (string) 캐스팅 연산자를 쓰는 것도 잘 된다.

trigger_error()

웬만하면 예외를 던지는 것이 좋지만, PHP 에러를 내야할 필요가 있을 때 이걸 사용한다.

debug_backtrace()

이 함수를 사용하면 호출 스택을 추적하는 것이 가능하다. eval()을 통하는 것과, include, require를 통한 것도 모두 추적할 수 있다. 대체 이 함수를 어디에 쓰겠느냐고 무심코 지나치면 안된다. 이 함수를 기억하고 있는 것만으로도, 종종 우연히 이 함수를 이용하여 좀더 우아한 DSEL을 달성할 때가 많다.

이 함수를 이용하는 대신 예외 객체를 생성하여 Exception->getTrace() 메서드로 받아내는 방법도 있다.

__FILE__ 상수

__FILE__ 상수의 값은 include, require 등과 무관하게 해당 상수는 현재 코드가 위치한 실제 파일 경로이다. 예를 들어 require_once dirname(__FILE__).'/file.php'과 같이 해당 소스 파일과 같은 위치에 있는 file.php를 불러올 수 있다. (저렇게 하지 않으면 include 호출의 맨 위쪽에 있는 파일에 상대한 경로로 인클루드를 시도한다.)

operator 확장

PHP에 기본적으로 포함되지는 않았지만, 매우 유용한 확장이다. 연산자 재정의를 가능하게 해준다. C++의 Boost.Spirit 라이브러리 같은 것을 보면 연산자 재정의가 얼마나 언어의 표현력을 높여주는지 알 수 있다. 자세한 것은 내가 예전에 정리해둔 문서를 참고하시라.

 

출처: http://blog.dahlia.pe.kr/?s=lexical


+ Recent posts