操作包装类的 PHP-instanceof-operator

发布于 2024-10-16 03:44:06 字数 333 浏览 10 评论 0原文

我希望为某些类提供一个通用包装类来拦截和操作某些方法调用。方法-调用-转发,拦截,到目前为止没问题。但经过思考一段时间,我发现了一个我无法解决的问题:我在应用程序中的任何地方都使用内置的instanceof-operator。当然,这不再起作用,因为包装器不是其中类的实例。我想继续使用该运算符,而不是用其他函数替换它。

有没有办法解决这个问题?这个运算符是如何工作的?它是否调用了我可能能够在包装器中覆盖的类的核心函数?

我知道这不是一个真正“干净”的解决方案来操纵这个运算符,但我认为这对我来说是最简单的解决方案。正如我们所知,PHP 中有很多东西不是那么干净......:-)

感谢您的回答,Ben

I'd like to have a generic wrapper-class for some classes to intercept and manipulate some of the method-calls. Method-call-forwarding, intercepting, no problem so far. But after thinking a while, i found a problem for which i have no solution: I'm using the built-in instanceof-operator everywhere in my application. Of course this won't work anymore, because the wrapper isn't an instance of the class inside it. I would like to continue using the operator and not to replace it with an other function.

Is there a way to implement a workaround for this problem? How does this operator work? Does it call a core-function of the classes which i am probably able to overwrite in my wrapper?

I know that this would not be a really "clean" solution to manipulate this operator, but i think this would be the simplest solution for me. And as we know, there are many things in PHP which are not that clean... :-)

Thanks for your answers, Ben

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

当梦初醒 2024-10-23 03:44:06

我不知道是否可以按照您想要的方式欺骗​​ instanceof 运算符(如果不是,则将类识别为子类),但我认为我找到了一个可能适合您需求的解决方案。如果我正确理解您的问题,那么您只想在任何类中注入一些方法,而对整个代码进行最小的更改。

我认为在这种情况下准备解决方案的最佳方法是使用特征(此处描述< /a>)。使用特征,您可以向任何类添加方法,而无需直接继承,并且它可以覆盖基类中的方法。为了用特征覆盖方法,您当然需要一个子类,但它们可以动态创建。我对你的包装过程一无所知,但在我的解决方案中,我使用了一个特殊的类。让我们看看我的解决方案:

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

正如您所看到的,一切都发生在包装过程中。

如果您有更多包装器,那么您可以以其他方式准备新的包装类名称(例如与包装器名称相关)。

I don't know is it possible to trick a instanceof operator in way you want (recognize a class as subclass if it is not) but I think I found a solution that may suit your needs. If I understand correctly your problem then you simply want to inject some methods in any class with minimal changes in your whole code.

I think the best way to prepare a solution in this case is using traits (described here). With traits you can add methods to any class without direct inheritance and it can overwrite methods from base class. For overwriting method with traits you of course need a subclasses but they can be created dynamically. I don't know anything about your wrapping process but in my solution I used a special class for it. Lets look at my solution:

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

As you can see everything is happens during wrapping process.

If you have more wrappers then you can prepare the new wrapped class name in other way (for example to be corelated with wrapper name).

阳光下的泡沫是彩色的 2024-10-23 03:44:06

也许我可以描述一个满足您需求的解决方案。 (免责声明:我是 Go! AOP Framework 的作者)从您的描述来看,您似乎想要动态地在不接触类的情况下向您的方法添加额外的逻辑。如果我是对的,那么您可以看看面向方面的范式,其中介绍了源代码拦截器的概念,更重要的是 - 您的原始类将保持不变。

要了解如何将其应用于您的代码,您还可以查看我的文章 http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/ 突出了经典对象的所有优点和缺点面向模式,如装饰器、代理。我可以得出一个结论,由于 PHP 在解决横切问题方面的本质复杂性和局限性,所有拦截器都不能以面向对象的方式提取到单独的模块中。 AOP 扩展了传统的 OOP 模型,因此可以将拦截器(称为建议)提取到单独的类(称为方面)中。

AOP 的出色功能是它保留原始类名,这意味着您不应该更改代码中的类型提示,甚至不应该劫持 instanceof 运算符。您将为您的课程添加额外的逻辑。

Probably I can describe a solution for your needs. (disclaimer: I'm author of Go! AOP Framework) From your description it looks like you want to dynamically add additional logic to your methods without touching the class. If I'm right, then you could have a look at Aspect-Oriented Paradigm that introduces a concept of interceptors for your source code, what is more important - your original classes will be untouched.

To have an idea, how this can be applied to your code, you could also have a look at my article http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/ that highlights all advantages and disadvantages of classical object-oriented patterns like decorator, proxy. I can make a conclusion, that all interceptors can not be extracted into separate modules in object-oriented way because of essential complexity and limitations of PHP for solving cross-cutting concerns. AOP extends traditional OOP model, so it will be possible to extract interceptors (called advices) into separate classes (called aspects).

Brilliant feature of AOP is that it keeps your original class names and this means that you shouldn't change typehints in your code or even hijack a instanceof operator. You will get your class with additional logic.

云裳 2024-10-23 03:44:06

根本不可能。实际上,也许将来: https://bugs.php.net/bug.php? id=71352

Not possible at all. Actually, maybe in the future: https://bugs.php.net/bug.php?id=71352

一笔一画续写前缘 2024-10-23 03:44:06

使用接口而不是具体的类。将接口应用于包装器和具体类。

请参阅http://de3.php.net/manual/en/language。 oop5.interfaces.php

Use an interface instead of the concrete class. Apply the interface to Wrapper and Concrete Class.

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

┈┾☆殇 2024-10-23 03:44:06

查看装饰器模式。如果您的包装器/包装类实现相同的接口,您可以优雅地完成所有操作(并在整个代码中使用instanceofinterface)。

有没有办法解决这个问题?这个运算符是如何工作的?它是否调用了我可能能够在包装器中覆盖的类的核心函数?

您无法操作instanceof 运算符。由于您对如何实现 instanceof 运算符感兴趣,因此这里是原始 C 代码的 PHP 表示形式:

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 字段上,该条目仍然 不可变直到脚本终止。

Have a look at decorator pattern. If your wrapper/wrapped classes implement the same interface, you can do everything elegantly (and use instanceof interface throughout the code).

Is there a way to implement a workaround for this problem? How does this operator work? Does it call a core-function of the classes which i am probably able to overwrite in my wrapper?

You cannot manipulate instanceof operator. Since you were interested how instanceof operator is implemented, here is a PHP representation of original C code:

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

Whenever you declare a class to implement/extend an interface/class, imagine an entry is deposited on $interfaces or $parent fields that remains immutable until script terminates.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文