2011-02-06 3 views
6

Я хотел бы иметь общий класс-оболочку для некоторых классов для перехвата и обработки некоторых вызовов метода. Метод-переадресация вызова, перехват, никаких проблем до сих пор. Но, подумав, я нашел проблему, для которой у меня нет решения: я использую встроенный экземпляр-operator везде в своем приложении. Конечно, это больше не будет работать, потому что оболочка не является экземпляром класса внутри него. Я хотел бы продолжать использовать оператор, а не заменять его другой функцией.Манипулировать PHP-instanceof-operator для класса-оболочки

Есть ли способ реализовать обходной путь для этой проблемы? Как работает этот оператор? Вызывает ли он основную функцию классов, которые, вероятно, я могу перезаписать в своей обертке?

Я знаю, что это не было бы действительно «чистым» решением для манипулирования этим оператором, но я думаю, что это было бы самым простым решением для меня. И, как мы знаем, есть в PHP много вещей, которые не являются чистыми, что ... :-)

Спасибо за ваши ответы, Бен

+0

Это точно та же проблема, которую я пытаюсь решить. Вам удалось заставить это работать как-то? Если это так, мне было бы очень интересно, как вы это сделали. – mrjames

ответ

0

использовать интерфейс вместо конкретного класса. Примените интерфейс к Wrapper и Concrete Class.

См http://de3.php.net/manual/en/language.oop5.interfaces.php

+0

Я хотел бы иметь общую оболочку для нескольких классов, которые не реализуют один и тот же интерфейс и не имеют одинаковых классов. Поэтому с вашим решением мне нужен класс-оболочка для каждого класса. Или я ошибаюсь? – Ben

+0

@Ben, вы правы. Это недостаток. – Gordon

2

Я не знаю, можно ли обмануть instanceof оператор в пути вы хотите (распознать класс как подкласс, если это не так), но я думаю, я нашел решение, которое может удовлетворить ваши потребности , Если я правильно понимаю вашу проблему, вы просто хотите ввести некоторые методы в любой класс с минимальными изменениями во всем коде.

Я думаю, что лучший способ подготовить решение в этом случае - использовать черты (описано 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

Это умное решение, но я не хочу использовать рефлексию или eval, потому что они слишком медленны, и они будут использованы для моего использования. Возможно, я разрешу проблему с генерацией кода и созданием обертки для каждого класса, который мне нужно обернуть, если обертки распространяются из обернутых классов, тогда моя проблема решена, но мне это не очень нравится. Хотелось бы, чтобы был волшебный метод __instanceOf для решения этой проблемы. Спасибо в любом случае. – hchinchilla

0

Посмотрите на decorator pattern. Если ваши обертки/обернутые классы реализуют один и тот же интерфейс, вы можете сделать все элегантно (и использовать интерфейс по всему коду).

Есть ли способ реализовать обходной путь для решения этой проблемы? Как работает этот оператор? Вызывает ли он основную функцию классов, которые, вероятно, я могу перезаписать в своей обертке?

Вы не можете манипулировать оператором instanceof. Так как вы были заинтересованы как InstanceOf оператор реализовано, здесь PHP представление оригинального кода C:

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; 
} 

Всякий раз, когда вы объявляете класс для реализации/расширение интерфейсного/класса, представьте запись осаждаются на $ интерфейсов или $ родительские поля, которые остаются неизменяемым до завершения сценария.

2

Возможно, я смогу описать решение для ваших нужд. (отказ от ответственности: я автор Go! AOP Framework). Из вашего описания, похоже, вы хотите динамически добавлять дополнительную логику к своим методам, не касаясь класса. Если я прав, то вы можете взглянуть на Aspect-Oriented Paradigm, в котором представлена ​​концепция перехватчиков для вашего исходного кода, что более важно - ваши первоначальные классы будут не затронуты.

Чтобы понять, как это можно применить к вашему коду, вы также можете взглянуть на мою статью http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/, в которой указаны все преимущества и недостатки классических объектно-ориентированных шаблонов, таких как декоратор, прокси. Я могу сделать вывод, что все перехватчики не могут быть выделены в отдельные модули объектно-ориентированным способом из-за существенной сложности и ограничений PHP для решения сквозных задач. AOP расширяет традиционную модель ООП, поэтому можно будет получить перехватчики (советы) в отдельные классы (аспекты).

Блестящая особенность АОП заключается в том, что он хранит ваши первоначальные имена классов, а это означает, что вы не должны менять тип ввода кода или даже угонщик instanceof. Вы получите свой класс с дополнительной логикой.