2011-02-06 6 views
6

몇개의 클래스는, 메서드 호출의 일부를 가로 채, 조작하기 위해서, 일반적인 래퍼 클래스를 갖고 싶습니다. 메소드 호출 포워딩, 인터셉트, 지금까지 문제 없음. 그러나 잠시 생각한 후에, 나는 해결책이없는 문제를 발견했다 : 나는 내장 된 instanceof-operator를 응용 프로그램의 모든 곳에서 사용하고있다. 래퍼가 그 안에있는 클래스의 인스턴스가 아니기 때문에 이것은 더 이상 작동하지 않습니다. 연산자를 계속 사용하고 다른 함수로 바꾸지 않으려합니다.PHP-instanceof-operator를 래퍼 클래스 용으로 조작하십시오.

이 문제의 해결 방법을 구현할 방법이 있습니까? 이 연산자는 어떻게 작동합니까? 그것은 아마도 래퍼에서 덮어 쓸 수있는 클래스의 핵심 기능을 호출합니까?

이 연산자는이 연산자를 조작하는 데 "깨끗한"솔루션이 아니라는 것을 알고 있습니다. 그러나 이것이 나를위한 가장 간단한 해결책이라고 생각합니다. 우리가 알고 있듯이, 그 깨끗하지 않은 PHP에서 많은 일들이 ... :-) 귀하의 답변

감사합니다, 벤

+0

이것은 정확히 제가 말하려고하는 것과 같은 문제입니다. 어떻게 든이 일을 할 수 있었나요? 만약 그렇다면, 당신이 어떻게했는지 매우 흥미가있을 것입니다. – mrjames

답변

0

사용하는 인터페이스 대신 구상 클래스가 있습니다. Wrapper 및 Concrete Class에 인터페이스를 적용하십시오.

당신이 원하는 방식으로 instanceof 연산자를 트릭 (그렇지 않은 경우는 서브 클래스로 클래스를 인식)하는 나도 몰라 http://de3.php.net/manual/en/language.oop5.interfaces.php

+0

동일한 인터페이스를 구현하지 않고 동일한 클래스를 가지고 있지 않은 여러 클래스에 대한 일반 래퍼를 갖고 싶습니다. 그래서 당신의 솔루션으로 모든 다른 클래스에 대한 래퍼 클래스가 필요합니다. 또는 나는 틀린가? – Ben

+0

@Ben 당신이 맞습니다. 그것은 단점입니다. – Gordon

2

이 가능합니다 참조하지만 난 당신의 요구에 맞게 수있는 해결책을 찾은 것 같아요 . 문제를 올바르게 이해했다면, 전체 코드에서 최소한의 변경만으로 어떤 클래스에 몇 가지 메소드를 주입하기 만하면됩니다.

이 경우 솔루션을 준비하는 가장 좋은 방법은 특성을 사용하는 것입니다 (here). 특성을 사용하면 직접 상속없이 모든 클래스에 메서드를 추가 할 수 있으며 기본 클래스의 메서드를 덮어 쓸 수 있습니다. 특성을 가진 메소드를 덮어 쓰려면 물론 하위 클래스가 필요하지만 동적으로 만들 수 있습니다. 랩핑 프로세스에 대해서는 아무 것도 모르지만 제 솔루션에서는 특수 클래스를 사용했습니다. 내 솔루션을 보자 :

namespace someNameSpace; 

//this is one of your class that you want to wrap - it can be declare under some other namespace if you need 
class yourBaseClass { } 

//your wrapper class as a trait 
trait yourWrapper { } 

//class for wrapping any object 
class ObjectWrapperClass 
{ 
    //method for change object class (described on http://stackoverflow.com/a/3243949/4662836) 
    protected static function objectToObject($instance, $className) 
    { 
     return unserialize(sprintf('O:%d:"%s"%s', strlen($className), $className, strstr(strstr(serialize($instance), '"'), ':'))); 
    } 

    //wrapping method 
    //$object is a object to be wrapped 
    //$wrapper is a full name of the wrapper trait 
    public static function wrap($object, $wrapper) 
    { 
     //take some information about the object to be wrapped 
     $reflection = new \ReflectionClass($object); 
     $baseClass = $reflection->getShortName(); 
     $namespace = $reflection->getNamespaceName(); 

     //perpare the name of the new wrapped class 
     $newClassName = "{$baseClass}Wrapped"; 

     //if new wrapped class has not been declared before we need to do it now 
     if (!class_exists($newClassName)) { 
      //prepare a code of the wrapping class that inject trait 
      $newClassCode = "namespace {$namespace} { class {$newClassName} extends {$baseClass} { use {$wrapper}; } }"; 

      //run the prepared code 
      eval($newClassCode); 
     } 

     //change the object class and return it 
     return self::objectToObject($object, $namespace . '\\' . $newClassName); 
    } 

} 

//lets test this solution 

$originalObject = new yourBaseClass(); 

$wrappedObject = ObjectWrapperClass::wrap($originalObject, 'yourWrapper'); 

if ($wrappedObject instanceof yourBaseClass) { 
    echo 'It is working'; 
} 

위와 같이 포장 과정에서 모든 것이 발생하는 것을 볼 수 있습니다.

래퍼가 더 많은 경우 새로운 래핑 된 클래스 이름을 다른 방법으로 준비 할 수 있습니다 (예 : 래퍼 이름을 사용하여 코어 될 수 있음).

+0

그건 영리한 해결책이지만, 너무 느리며 반영 사례 나 평가를 사용하지 않고 사용 사례에 많이 사용됩니다. 어쩌면 코드 생성 문제를 해결하고 포장해야하는 모든 클래스에 대한 래퍼를 작성합니다. 래퍼가 래핑 된 클래스에서 확장되면 내 문제는 해결되지만 실제로는 그다지 좋아하지 않습니다. 이 문제를 해결하기 위해 __instanceOf라는 마법 메서드가 있었으면 좋겠습니다. 어쨌든 고마워, . – hchinchilla

0

decorator pattern을 살펴보십시오. 래퍼/래핑 된 클래스가 동일한 인터페이스를 구현하는 경우 모든 것을 우아하게 처리 할 수 ​​있습니다 (코드 전체에서 instanceof ).

이 문제의 해결 방법을 구현할 방법이 있습니까? 이 연산자는 어떻게 작동합니까? 그것은 아마도 래퍼에서 덮어 쓸 수있는 클래스의 핵심 기능을 호출합니까?

instanceof 연산자를 조작 할 수 없습니다. 당신이 구현하는 클래스를 선언 할 때마다

class php_class { 
    public $interfaces = array(); // array of php_class objects (php classes can implement more than one interface) 
    public $parent = null; // php_class object (php classes can only extend one class) 
} 

function instanceof_operator($implementation, $abstraction) { 
    // forward recursion (iterates recursively through interfaces until a match is found) 
    for($i=0; $i<count($implementation->interfaces); $i++) { 
     if(instanceof_operator($implementation->interfaces[$i], $abstraction)) { 
      return true; 
     } 
    } 
    // backward recursion (iterates recursively through parents until a match is found) 
    while($implementation!=null) { 
     if($implementation == $abstraction) { 
      return true; 
     } 
     $implementation = $implementation->parent; 
    } 
    // no match was found 
    return false; 
} 

것은/인터페이스/클래스를 확장 항목이 $ 인터페이스 나 상에 증착된다 상상 : 당신이 구현되는 방법 instanceof를 연산자 관심이 있기 때문에, 여기에 원래의 C 코드의 PHP 표현이다 스크립트가 종료 될 때까지 인 $ 부모 필드는 변경 불가능한입니다.

2

아마 당신의 필요에 맞는 해결책을 설명 할 수 있습니다. (면책 조항 : 저자는 Go! AOP Framework입니다.) 귀하의 설명에서 클래스를 건드리지 않고 동적으로 추가 논리를 메서드에 추가하려는 것 같습니다. 그렇다면 Aspect-Oriented Paradigm에서 소스 코드에 대한 인터셉터 개념을 소개합니다. 더 중요한 것은 원본 클래스가 변경되지 않습니다.

이것이 코드에 어떻게 적용될 수 있는지 알고 싶다면 decorator, proxy와 같은 고전적인 객체 지향 패턴의 모든 장점과 단점을 강조한 내 기사 http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/을 살펴볼 수도 있습니다. 모든 인터셉터는 객체 지향적 인 방식으로 분리 된 모듈로 추출 될 수 없다는 결론을 내릴 수 있습니다. 이는 교차 절단 문제를 해결하기위한 PHP의 필수적인 복잡성과 한계로 인해 가능합니다. AOP는 전통적인 OOP 모델을 확장하므로 인터셉터 (advices)를 별도의 클래스 (애스펙트)로 추출 할 수 있습니다.

AOP의 뛰어난 기능은 원래 클래스 이름을 유지하므로 코드에서 유형 힌트를 변경하거나 instanceof 연산자를 도용해서는 안된다는 뜻입니다. 추가 논리로 수업을 듣게됩니다.

관련 문제