Manipulate PHP-instanceof-operator for a wrapper class

I would like to have a common wrapper class for some classes to intercept and manipulate some method calls. Call forwarding method, interception, no problem so far. But, after thinking, I found a problem for which I have no solution: I use the built-in instance-operator everywhere in my application. Of course, this will no longer work, because the shell is not an instance of the class inside it. I would like to continue to use the operator, and not replace it with another function.

Is there a way to implement a workaround for this problem? How does this operator work? Does it call the main function of classes that I can probably rewrite in my wrapper?

I know that this would not be a really “clean” solution for manipulating this operator, but I think that would be the easiest solution for me. And, as we know, in PHP there are many things that are not so clean ... :-)

Thanks for your answers, Ben

+6
php decorator aop instanceof interceptor
source share
5 answers

I don’t know if the instanceof operator can be fooled the way you want (recognize the class as a subclass if it’s not), but I think I have found a solution that can satisfy your needs. If I understand your problem correctly, you just want to introduce some methods into any class with minimal changes in the whole code.

I think the best way to prepare a solution in this case is to use traits (described here ). Using attributes, you can add methods to any class without direct inheritance and rewrite methods from the base class. For the rewriting method with traits, you certainly need subclasses, but they can be created dynamically. I don’t know anything about your packaging process, but in my decision I used a special class for it. Let's 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 happens during the packaging process.

If you have more shells, you can prepare the new wrapped class name in another way (for example, to associate it with the shell name).

+2
source share

Perhaps I can describe a solution for your needs. (disclaimer: I am the 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 can take a look at the Aspect-Oriented Paradigm that introduces the concept of interceptors for your source code, more importantly, your original classes will not be affected.

To have an idea of ​​how this can be applied to your code, you can also take a look at my article http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/ , which highlights all the advantages and disadvantages of classic object-oriented models, such as decorator, proxy. I can conclude that all interceptors cannot be separated into separate modules in an object-oriented way due to the significant complexity and limitations of PHP for solving end-to-end tasks. AOP extends the traditional OOP model, so it will be possible to get interceptors (called tips) into separate classes (called aspects).

The brilliant feature of AOP is that it stores your original class names, which means you don't have to change the type of code you enter or even capture the instanceof operator. You will get your class with additional logic.

+2
source share

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

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

0
source share

See the decorator pattern . If your wrappers / wrapped classes implement the same interface, you can do everything elegantly (and use the instanceof interface in the whole code).

Is there a way to implement a workaround for this problem? How does this operator work? Does it call the main function of classes that I can probably rewrite in my wrapper?

You cannot manipulate the instanceof operator. Since you were wondering how the instanceof operator is executed, here's a presentation of the PHP C source 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 that the record is placed on $ interfaces or $ parent fields, which remains unchanged until the script completes.

0
source share

Impossible at all. Actually, possibly in the future: https://bugs.php.net/bug.php?id=71352

0
source share

All Articles