为包装类操作 PHP-instanceof-operator

Posted

技术标签:

【中文标题】为包装类操作 PHP-instanceof-operator【英文标题】:Manipulate PHP-instanceof-operator for wrapper-class 【发布时间】:2011-06-22 08:59:17 【问题描述】:

我想为某些类提供一个通用包装类来拦截和操作某些方法调用。方法-调用-转发,拦截,目前没问题。但想了一会儿,我发现了一个我没有解决方案的问题:我在我的应用程序中到处使用内置的 instanceof-operator。当然,这将不再起作用,因为包装器不是其中的类的实例。我想继续使用该运算符,而不是用其他函数替换它。

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

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

谢谢你的回答,本

【问题讨论】:

这与我试图解决的问题完全相同。你设法让它以某种方式工作吗?如果是这样,我会很感兴趣你是怎么做到的.. 【参考方案1】:

我不知道是否有可能以您想要的方式欺骗​​ 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://***.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 = "$baseClassWrapped";

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

如您所见,一切都发生在包装过程中。

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

【讨论】:

这是一个聪明的解决方案,但我不想使用反射和评估,因为它们太慢了,而且它们会在我的用例中大量使用。也许我会解决代码生成问题并为我需要包装的每个类制作一个包装器,如果包装器从包装的类扩展,那么我的问题就解决了,但我真的不喜欢它。我希望有一个神奇的方法 __instanceOf 来解决这个问题。还是谢谢。【参考方案2】:

也许我可以描述一个满足您需求的解决方案。 (免责声明:我是Go! AOP Framework 的作者)从您的描述看来,您希望在不触及类的情况下动态地为您的方法添加额外的逻辑。如果我是对的,那么您可以查看Aspect-Oriented Paradigm,它为您的源代码引入了拦截器的概念,更重要的是 - 您的原始类将保持不变。

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

AOP 的绝妙特性是它保留了您原来的类名,这意味着您不应该更改代码中的类型提示,甚至不应该劫持instanceof 运算符。您将获得具有附加逻辑的课程。

【讨论】:

【参考方案3】:

根本不可能。其实,也许在未来:https://bugs.php.net/bug.php?id=71352

【讨论】:

【参考方案4】:

使用接口而不是具体类。将接口应用到 Wrapper 和 Concrete Class。

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

【讨论】:

我想为多个不实现相同接口且没有相同类的类提供一个通用包装器。因此,对于您的解决方案,我需要为每个不同的类提供一个包装类。还是我错了?【参考方案5】:

看看decorator pattern。如果你的包装器/被包装的类实现了相同的接口,你可以优雅地做所有事情(并在整个代码中使用 instanceof interface)。

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

您不能操纵 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 字段中,这些字段在脚本终止之前保持不可变

【讨论】:

以上是关于为包装类操作 PHP-instanceof-operator的主要内容,如果未能解决你的问题,请参考以下文章

包装类

Java包装类详解

java-----基本数据类型包装类

JAVA SE基础篇34.包装类

浅析java中的包装类

如何将 ASP.NET Core 5 Web API 控制器操作的失败模型验证结果包装到另一个类中并将响应返回为 OK