2011-02-06 42 views
6

我想要一些类的通用包装类来拦截和操作一些方法调用。方法呼叫转移,拦截,迄今为止没有问题。但经过一段时间后,我发现了一个问题,我没有解决方案:我在我的应用程序的每个地方都使用内置的instanceof-operator。当然,这将不再起作用,因为包装器不是其中的类的实例。我想继续使用操作员,而不是用其他功能替换它。操作包装类的PHP-instanceof-operator

有没有一种方法可以解决这个问题?这个操作员如何工作?它是否调用了我可能在我的包装中覆盖的类的核心函数?

我知道这不是一个真正的“干净”的解决方案来操纵这个操作符,但我认为这对我来说是最简单的解决方案。正如我们所知,在PHP很多事情都不是干净的...... :-)

谢谢您的回答,奔

+0

这是我试图解决的完全相同的问题。你有没有设法让它工作?如果是这样,我会非常感兴趣,你是如何做到这一点的。 – mrjames 2011-03-25 13:01:10

回答

0

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

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

+0

我想要为不具有相同接口且不具有相同类的多个类设计通用包装。因此,对于您的解决方案,我需要为每个不同类别的包装类。还是我错了? – Ben 2011-02-06 19:36:49

+0

@你是对的。这是缺点。 – Gordon 2011-02-06 19:49:09

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

看一看decorator pattern。如果你的包装/包装类实现相同的接口,你可以做一切优雅(并在整个代码中使用instanceof 接口)。

有没有办法为这个问题实施解决方法?这个操作员如何工作?它是否调用了我可能在我的包装中覆盖的类的核心函数?

您无法操作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; 
} 

当你声明一个类来实现/扩展接口/类,想象中的条目沉积在$接口或$ parent字段保持不可改变直到脚本终止。

2

也许我可以为你的需求描述一个解决方案。 (免责声明:我是Go! AOP Framework的作者)从你的描述看来,你想动态地添加额外的逻辑到你的方法,而不需要触及课程。如果我是对的,那么你可以看看Aspect-Oriented Paradigm,它为你的源代码引入了拦截器的概念,更重要的是 - 你的原始类将不会被触及。想知道如何将它应用到你的代码中,你也可以看看我的文章http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/,它强调了经典的面向对象模式(如装饰器,代理)的所有优点和缺点。我可以得出结论,由于PHP解决跨领域问题的复杂性和局限性,所有的拦截器都不能以面向对象的方式提取到单独的模块中。 AOP扩展了传统的OOP模型,因此可以将拦截器(称为建议)提取到不同的类中(称为方面)。

AOP的亮点在于它保留了原来的类名,这意味着您不应该更改代码中的类型提示,甚至可以劫持instanceof运算符。你会得到你的课程额外的逻辑。