Манипулировать PHP-instanceof-оператором для класса-оболочки - PullRequest
6 голосов
/ 06 февраля 2011

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

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

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

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

Ответы [ 5 ]

2 голосов
/ 11 июля 2016

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

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

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

2 голосов
/ 07 июля 2016

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

Я думаю, что лучший способ подготовить решение в этом случае - использовать черты (описано * 1004).* здесь ).С помощью признаков вы можете добавлять методы в любой класс без прямого наследования, и он может перезаписывать методы из базового класса.Для перезаписи метода с чертами вам, конечно, нужны подклассы, но они могут быть созданы динамически.Я ничего не знаю о вашем процессе упаковки, но в своем решении я использовал специальный класс для него.Давайте посмотрим на мое решение:

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 голосов
/ 11 июля 2016

Не возможно вообще. На самом деле, может быть, в будущем: https://bugs.php.net/bug.php?id=71352

0 голосов
/ 09 июля 2016

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

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

Вы не можете манипулировать оператором 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;
}

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

0 голосов
/ 06 февраля 2011

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

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

...