Re: [PHP-DEV] Decorators Revisited
On 14 August 2012 at 20:58 Stas Malyshev smalys...@sugarcrm.com wrote: Hi! Simply because your object responds to all the same methods of, for example, the FooInterface, does not make it a FooInterface subtype. It just means that in the duck typing sense of the phrase, it can act like a FooInterface for people that are not necessarily concerned that it's actually not is_a() FooInterface. Excellent point here. I have a feeling that with these proposals people want to eat a cake and have it too. To have strictly defined typing structure enforced by strict parameter checks, instanceof checks, etc. and at the same time have the freedom of duck typing. I don't think it's going to work well - if you want duck typing, that's one thing, if you want class hierarchy, that's another thing. Both are viable models for different cases, but I don't see how they can work using the same operators and language constructs. They should be distinct. This is something which bothers me about PHP as it is at the moment. PHP seems to me like it should be a dynamic language, and it is quite dynamic: no static typing, has weak typing, you can add instance members at runtime, etc. However, there has been a trend for adding more... non-dynamic (?) features to the language. Interfaces for example. Yet you can also duck-type things. I think PHP risks becoming very unclear as to what the right way to do things is. It must be confusing to write code that has to deal with both duck-typing and interfaces. I just think perhaps PHP should decide if it's supposed to be dynamic or non-dynamic, because just now it looks like a confusing mix of both. (although I guess duck-typing can be used for interfaces, just not really the other way round, i.e. if you expect an object of an interface you can't just pass one implementing the required methods) Or, if it is going to support both models, it should at least have first-class support for both. Being able to dynamically modify classes, for instance. Now, we could probably make duck typing a bit easier by allowing to check if specific object can respond to specific interface. But I'm not sure if it's worth the effort - why not just have it implement the interface then? In any case, I think duck typing improvement may be a good place for proposals, but let's not confuse it with inheritance hierarchy. What if asking for an interface just checked to see if a class implemented the required types, and implements didn't make the class have that interface, but just made the compiler error if it didn't implement everything required. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php -- Andrew Faulds http://ajf.me/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Hi! 2. Move instanceof to a handler from the current standalone function. Then each (pecl level) class could choose its own implementation if it is needed. The function could still exist for BC reasons, but would proxy to the handler method if it existed... Then, the wrapped class (the additional pointer) would only need to live on the objects internal store... I don't think overriding instanceof is a very good idea. instanceof has very defined meaning, which implies certain contract between classes and their clients, and allowing everybody to override it to mean whatever one likes looks very dangerous to me. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
On Tue, Aug 14, 2012 at 8:36 AM, Stas Malyshev smalys...@sugarcrm.comwrote: Hi! 2. Move instanceof to a handler from the current standalone function. Then each (pecl level) class could choose its own implementation if it is needed. The function could still exist for BC reasons, but would proxy to the handler method if it existed... Then, the wrapped class (the additional pointer) would only need to live on the objects internal store... I don't think overriding instanceof is a very good idea. instanceof has very defined meaning, which implies certain contract between classes and their clients, and allowing everybody to override it to mean whatever one likes looks very dangerous to me. I think you are excagerating, the proposal is not allowing everybody to overwrite instanceof it to do whatever. That would be if we could overwrite the implementation of instanceof in userland. Instead it simplifies applying the decorator pattern on the language level, and the instanceof only in this use-case. @Anthony: Does this handle get_class() as well and all the Reflection APIs?
Re: [PHP-DEV] Decorators Revisited
Would this work with methods which take arguments by reference or return by reference? Stan -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
hi Anthony, On Sun, Aug 12, 2012 at 8:17 PM, Anthony Ferrara ircmax...@gmail.com wrote: What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. Thoughts? I very much like this idea! And I'm convinced that more tests/efforts/thoughts should be put into this. One point I worry about is performance. As of 5.3 5.4, the object usage performance has been significantly improved. I wonder what would be the impact of this change on this gain. Did you look at that already? Surely too early but still good to get an idea or to keep this parameter in mind:). Keep the great work! Cheers, -- Pierre @pierrejoye | http://blog.thepimp.net | http://www.libgd.org -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
In general, I think it would be nice to have something that does this for you, but I am not necessarily a fan of changing the meaning of instanceof. That's a lot of boilerplate for each possible iteration. This is one reason people like traits so much, as it's easier to just do automated copy/paste than use the proper patterns. Couldn't a dynamic trait be a better? It would work like this: trait SplDynamicProxyTrait { protected $proxyObject; // enumerate all public method with the following: public function $name($signature) { return $this-proxyObject::$name($signature); } } usage would then be the following: class MyDecorator implements FooInterface { use SplDynamicProxyTrait; // compile time code generation public function __construct(Foo $foo) { $this-proxyObject = $foo; } // override trait public function method2($a) { if (!$this-hasCache('method2', $a)) { $ret = $this-proxyObject-method2($a); $this-setCache('method2', $a, $ret); } return $this-getCache('method2', $a); } } So, example code like: class Foo {} class Bar extends SplDecorator {} $b = new Bar(new Foo); var_dump($b instanceof Foo); // true It also works with type hints: function test(Foo $f) {} test($b); These would all still work, instanceof would actually be an instanceof though. Now, there's a lot more to do (property cascading, interface validation, etc), but the initial proof-of-concept is there. What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. Thoughts? In the above scenario, we've reused traits and we haven't broken instanceof or the liskov principle (which basically your implementation does since SplDectorator bypasses all method signature checking until runtime). Thoughts? :) -ralph -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Ralph, On Tue, Aug 14, 2012 at 12:49 PM, Ralph Schindler ra...@ralphschindler.comwrote: In general, I think it would be nice to have something that does this for you, but I am not necessarily a fan of changing the meaning of instanceof. That's a lot of boilerplate for each possible iteration. This is one reason people like traits so much, as it's easier to just do automated copy/paste than use the proper patterns. Couldn't a dynamic trait be a better? It would work like this: trait SplDynamicProxyTrait { protected $proxyObject; // enumerate all public method with the following: public function $name($signature) { return $this-proxyObject::$name($**signature); } } usage would then be the following: class MyDecorator implements FooInterface { use SplDynamicProxyTrait; // compile time code generation public function __construct(Foo $foo) { $this-proxyObject = $foo; } // override trait public function method2($a) { if (!$this-hasCache('method2', $a)) { $ret = $this-proxyObject-method2($**a); $this-setCache('method2', $a, $ret); } return $this-getCache('method2', $a); } } So, example code like: class Foo {} class Bar extends SplDecorator {} $b = new Bar(new Foo); var_dump($b instanceof Foo); // true It also works with type hints: function test(Foo $f) {} test($b); These would all still work, instanceof would actually be an instanceof though. Now, there's a lot more to do (property cascading, interface validation, etc), but the initial proof-of-concept is there. What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. Thoughts? In the above scenario, we've reused traits and we haven't broken instanceof or the liskov principle (which basically your implementation does since SplDectorator bypasses all method signature checking until runtime). Thoughts? :) -ralph Well, I like it at first glance. There are two main problems that I see with it: 1. It still requires a separate decorator class for every combination of interfaces you want to decorate. So, for example if you wanted to decorate a Foo interface, but sometimes it's used with Iterator as well. Then you'd need two classes. If there was a third interface, you'd need four separate classes. Same problem with class bloat minus a lot of the boilerplate. 2. It's still not possible to decorate a typed class. So I can't decorate PDO without first extending PDO with a user-supplied interface. And I don't understand how this breaks LSP... We're not extending the decorated class. In fact, we can add construct time checking to ensure the original interfaces are all enforced (and even go a step further and ensure that all method signatures are the same that are overridden). LSP is not a compile time concern, it's a purely runtime concern (in PHP at least). The fact that interface contracts are enforced at compile time is nice, but it's not a strict necessity to enforcing LSP. Anthony
Re: [PHP-DEV] Decorators Revisited
Well, I like it at first glance. There are two main problems that I see with it: 1. It still requires a separate decorator class for every combination of interfaces you want to decorate. So, for example if you wanted to decorate a Foo interface, but sometimes it's used with Iterator as well. Then you'd need two classes. If there was a third interface, you'd need four separate classes. Same problem with class bloat minus a lot of the boilerplate. You'd need a class anyway for your custom logic to live, that custom logic is the reason you want a Proxy in the first place. In your example, you want to proxy and object, but for one of the method, presumable an expensive method, you want to be able to cache the output before needing to go to the actual object. If Iterator was required for the Foo contract/interface, that would have bee in the Foo interface definition as well. Even in your example, you still have this: class Bar extends SplDecorator {} But what you are really intending is: class Bar extends MagicToMakeThisActLikeAnyType {} Ultimately, the instance you are decorating and decorator itself need to be type equivalent. Meaning the generally accepted understanding of is_a() and instanceof constructs need to also apply to the decorator object just as it would any other object. 2. It's still not possible to decorate a typed class. So I can't decorate PDO without first extending PDO with a user-supplied interface. I don't follow. What do you mean by a typed class? And I don't understand how this breaks LSP... We're not extending the decorated class. In fact, we can add construct time checking to ensure the original interfaces are all enforced (and even go a step further and That is where we are going to differ. Simply because your object responds to all the same methods of, for example, the FooInterface, does not make it a FooInterface subtype. It just means that in the duck typing sense of the phrase, it can act like a FooInterface for people that are not necessarily concerned that it's actually not is_a() FooInterface. In other words, yes, it will pass all sorts of method_exists() checks, but it'd never be able to pass an instanceof, is_a, or is_subclass_of() check on FooInterface. ensure that all method signatures are the same that are overridden). LSP is not a compile time concern, it's a purely runtime concern (in PHP at When I say compile time I mean PHP's compile time, in so far as PHP is a compile and execute style interpreted language. The following script demonstrates the enforcement of subtype expectations: ?php interface Foo { public function bar(); } class Bar implements Foo { public function bar(array $a) {} } ? That will produce: PHP Fatal error: Declaration of Bar::bar() must be compatible with that of Foo::bar() in ... That is an LSP check, and it is done at PHP's compile time. The rule Preconditions cannot be strengthened in a subtype is violated since you attempted to add an argument to the bar() method of the Foo contract. least). The fact that interface contracts are enforced at compile time is nice, but it's not a strict necessity to enforcing LSP. Why not do it at compile time? When php is building up its class entry table, it should be in its best interest to not have incomplete or invalid types in there, right? But, I do support getting more duck typing facilities in PHP, and reusing traits in places where we need more compiler assisted copy/paste when boilerplate is cumbersome. -ralph -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Hi! Simply because your object responds to all the same methods of, for example, the FooInterface, does not make it a FooInterface subtype. It just means that in the duck typing sense of the phrase, it can act like a FooInterface for people that are not necessarily concerned that it's actually not is_a() FooInterface. Excellent point here. I have a feeling that with these proposals people want to eat a cake and have it too. To have strictly defined typing structure enforced by strict parameter checks, instanceof checks, etc. and at the same time have the freedom of duck typing. I don't think it's going to work well - if you want duck typing, that's one thing, if you want class hierarchy, that's another thing. Both are viable models for different cases, but I don't see how they can work using the same operators and language constructs. They should be distinct. Now, we could probably make duck typing a bit easier by allowing to check if specific object can respond to specific interface. But I'm not sure if it's worth the effort - why not just have it implement the interface then? In any case, I think duck typing improvement may be a good place for proposals, but let's not confuse it with inheritance hierarchy. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Ralph, On Tue, Aug 14, 2012 at 3:30 PM, Ralph Schindler ra...@ralphschindler.comwrote: Well, I like it at first glance. There are two main problems that I see with it: 1. It still requires a separate decorator class for every combination of interfaces you want to decorate. So, for example if you wanted to decorate a Foo interface, but sometimes it's used with Iterator as well. Then you'd need two classes. If there was a third interface, you'd need four separate classes. Same problem with class bloat minus a lot of the boilerplate. You'd need a class anyway for your custom logic to live, that custom logic is the reason you want a Proxy in the first place. In your example, you want to proxy and object, but for one of the method, presumable an expensive method, you want to be able to cache the output before needing to go to the actual object. If Iterator was required for the Foo contract/interface, that would have bee in the Foo interface definition as well. No, it would be required by the class itself. That's the point. I don't want to decorate an interface, but an instance. Otherwise it makes decorating real world classes much more difficult (which was the pain I'm looking to clean up). Even in your example, you still have this: class Bar extends SplDecorator {} But what you are really intending is: class Bar extends MagicToMakeThisActLikeAnyType {} Ultimately, the instance you are decorating and decorator itself need to be type equivalent. Meaning the generally accepted understanding of is_a() and instanceof constructs need to also apply to the decorator object just as it would any other object. It sounds to me like you haven't tried to use decorators for any complex logic. Making it type equivalent leads to very vebose code. And a PITA to write. This is one of the reasons that traits are so heralded. Because problems that are easy to solve with decorators (in general) are difficult to solve with PHP, so people wind up writing copy/paste systems (which is what traits are) to solve them in a more brittle and less flexible manner. I'm after flexibility in addition to enforcement. They are not opposites. The whole point of interfaces is not typing, but a contract. There's nothing stopping you from making an Iterator which uses the valid() class to do logic. The accepted contract though says what it should do. But interfaces are only half of that contract. We assume that if you implement an interface that you're going to abide by the whole contract. But without interface level preconditions, postconditions and invarients, it's not an All or nothing approach. By using interfaces I can gain some compile-time assurance that the contract is met, but only manual review will confirm this. What I'm proposing here can be abused. Sure, it can be used to do all sorts of nasty things. But so can traits. So can eval. So can inheritance. So can regular expressions. That doesn't mean that they aren't incredibly useful... 2. It's still not possible to decorate a typed class. So I can't decorate PDO without first extending PDO with a user-supplied interface. I don't follow. What do you mean by a typed class? A class which is checked for directly. There is plenty of user-land code that uses PDO as a type hint. That means I'd need to extend PDO to add functionality, thereby rendering decorators useless (and instead causing all sorts of issue). And even if I wanted to make a new class for my using to decorate, I'd need to create an interface and class: interface PDOLike { } class MyPDO extends PDO implements PDOLike { } That winds up creating really weird coupling and hard to reuse code. So that means that I either need to depend on my PDO instance, or not be able to use decorators at all. Not a good tradeoff if you ask me. And I don't understand how this breaks LSP... We're not extending the decorated class. In fact, we can add construct time checking to ensure the original interfaces are all enforced (and even go a step further and That is where we are going to differ. Simply because your object responds to all the same methods of, for example, the FooInterface, does not make it a FooInterface subtype. It just means that in the duck typing sense of the phrase, it can act like a FooInterface for people that are not necessarily concerned that it's actually not is_a() FooInterface. But the flip side is true as well. Just because a class implements a FooInterface doesn't mean it can act like a FooInterface is supposed to. That's one reason that relying on interfaces alone for that much is a mistake. It takes review. Or (in other languages) formal contracts to ensure that the contract is enforced. And since interfaces in PHP are weak signature based contracts, we can implement them dynamically without absolutely any break in the contract. That's why this style system (letting a decorator assume the interfaces of the parents, as long as the decorator
Re: [PHP-DEV] Decorators Revisited
Hey Anthony, There's a lot here, so I'm only going to address a few things. It sounds to me like you haven't tried to use decorators for any complex logic. Making it type equivalent leads to very vebose code. And a PITA I actually have used both Decorators and Proxies (it's cousin) a number of times and some in complex situations (these are all ZF2) http://git.io/HdRe6g (Zend\Di), http://git.io/6EGv4Q (Zend\ServiceManager), and http://git.io/DhXDFA (Zend\Db Sql Abstraction), all that aside ... to write. This is one of the reasons that traits are so heralded. Because problems that are easy to solve with decorators (in general) are difficult to solve with PHP, so people wind up writing copy/paste If what you're trying to do is decorate an instance of a particular concrete type, the basic structure/foundation for creating a Decorator (as defined by wikipedia), can be achieved in PHP with about 3 lines of code: class FooDecorator extends Foo { protected $subject; public function __construct(Foo $foo) { $this-subject = $foo; foreach (get_object_vars($foo) as $n = $v) { $this-{$n} = $foo-{$n}; } } } At this point, any instance of new Foo is statefully and type equivalent to new FooDecorator($foo). Now, you're free to add in your custom decoration logic, overridden methods, additional methods, new interfaces, etc. An instance of FooDecorator can be used an any place an instance of Foo would have been used. i.e., it's bound to the Foo context. Let's get one thing clear here. It's not a LSP check. In that exact example it is. But in general, it also prevents the LSP valid: interface foo { public function bar(array $a); } class Bar implements Foo { public function bar($a); } That's 100% in compliance with LSP. Yet it's disallowed. The reason is that interfaces are not primarily a LSP enforcer. They are primarily a contract enforcer. LSP is a usual side-effect, but it's not a guarantee. I'll give you this one. That is an example of contravariance of method arguments, which I'd argue should probably be allowed (if it's not that expensive to do so). PHP does not specifically say it or define type sets, but one would assume that no type required should mean all types which technically might include array. And I don't need to resort to duck typing for it to work. All I need is the ability to decorate at will. I'm not talking about nuking the meaning of the interfaces (as they'd be proxied to). But more just We're going to have to disagree on the meaning of interfaces. Interfaces are a foundational element of a class based type hierarchy. If you want Interfaces without them being in an instance type hierarchy, I'm not sure what you call that; but it's not an Interface. The decisions made in the PHP group around the class type system more closely resemble those made by Java and C#. Concerning your code snippets above. It sounds like you want to dynamically build classes and methods, on the fly, or you want a magical subclass that is free from the current rules of is_a, instanceof, etc. Put another way, some developer somewhere built a system and has a method signature like class Controller { public function dispatch(Dispatchable $dispatchable) { ... } } and they have provided the Dispatchable interface. But you want to take any old object that does not implement Dispatchable and be able to modify it on the fly to bypass his type check in his type check in the method signature of his dispatch(). to solve this problem. I'm talking about runtime composability of functionality. Not compile time. I want to be able to construct my objects from other interactions (like database settings), not just be limited to having to copy/paste for everything. That's just crazy talk right there! ;) composing functionality != composing types In PHP (currently, as anything could change), is_a, instanceof and method type signatures are used by people who believe in a class based type inheritance system and the system of checks built around that. Your proposal attempts to change that such that special types can be exempt from the type checking rules for the purpose of a particular pattern (Decorator). I've personally never had a problems with decorating, so it is hard for me to envision the system you are building where Decorators are a problem. -ralph -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Ralph, to write. This is one of the reasons that traits are so heralded. Because problems that are easy to solve with decorators (in general) are difficult to solve with PHP, so people wind up writing copy/paste If what you're trying to do is decorate an instance of a particular concrete type, the basic structure/foundation for creating a Decorator (as defined by wikipedia), can be achieved in PHP with about 3 lines of code: class FooDecorator extends Foo { protected $subject; public function __construct(Foo $foo) { $this-subject = $foo; foreach (get_object_vars($foo) as $n = $v) { $this-{$n} = $foo-{$n}; } } } At this point, any instance of new Foo is statefully and type equivalent to new FooDecorator($foo). Now, you're free to add in your custom decoration logic, overridden methods, additional methods, new interfaces, etc. An instance of FooDecorator can be used an any place an instance of Foo would have been used. i.e., it's bound to the Foo context. That's an alternative? Really? Some hackery involving inheritance abuse...? Let me pose a real world example. Let's say you have a house. Let's call it `class House`. Then let's say you want to add christmas lights to it. You don't go bulldoze the existing house and construct a new one (class HouseWithLights). And you don't create a new one, move all the furnature into the new one and then move the original one to another temporary spot (your extends example). You go get lights (class Lights) and decorate the existing house with them. All the properties and features of the house still exist, but with the added functionality (and responsibility) of the new lights. Then, let's say you want to add a deck. You don't create a new class HouseWithLightsAndDeck. You add the deck to the existing instance. Now, you could also do this with a bridge pattern. But with a bridge, you need to specially design the construction for the particular type you're doing. So you would build a bridge for a house, and a separate one for an office building. But with a decorator, you could decorate both the house and the office building with the same lights, and the same deck. And I don't need to resort to duck typing for it to work. All I need is the ability to decorate at will. I'm not talking about nuking the meaning of the interfaces (as they'd be proxied to). But more just We're going to have to disagree on the meaning of interfaces. Interfaces are a foundational element of a class based type hierarchy. If you want Interfaces without them being in an instance type hierarchy, I'm not sure what you call that; but it's not an Interface. The decisions made in the PHP group around the class type system more closely resemble those made by Java and C#. I'm absolutely talking about the interfaces being in the *instance* type hiearchy. That's the whole point of the proposal. What I'm talking about here is adding the ability for it to be in the instance type hiearchy without it being in the class hiearchy... Concerning your code snippets above. It sounds like you want to dynamically build classes and methods, on the fly, or you want a magical subclass that is free from the current rules of is_a, instanceof, etc. Absolutely not. What I'm talking about is a magic class which *inherits* type information from its parent. Not which will magically pass all instanceof queries. It would behave as if you extended whatever class was passed in, without doing the actual extension (and all the coupling baggage that comes with)... Put another way, some developer somewhere built a system and has a method signature like class Controller { public function dispatch(Dispatchable $dispatchable) { ... } } and they have provided the Dispatchable interface. But you want to take any old object that does not implement Dispatchable and be able to modify it on the fly to bypass his type check in his type check in the method signature of his dispatch(). Not in the least. I want to be able to take an existing object that implements Dispatchable, and wrap that with my decorator to either add new functionality or alter existing (while keeping the typing contracts enforced, so the interface of the parent cannot be violated in the decorator without a construction time error). to solve this problem. I'm talking about runtime composability of functionality. Not compile time. I want to be able to construct my objects from other interactions (like database settings), not just be limited to having to copy/paste for everything. That's just crazy talk right there! ;) composing functionality != composing types In PHP (currently, as anything could change), is_a, instanceof and method type signatures are used by people who believe in a class based type inheritance system and the system of checks built around that. And I'm not arguing that in the least. In fact, I'm embracing that by allowing a
Re: [PHP-DEV] Decorators Revisited
Heya, What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. I can't speak for the feasibility, but from a usage point of view I do like that it opens up some sort of duck-typing by allowing decorators to be drop-in replacements even if there is no interface to implement. Cheers -- Jordi Boggiano @seldaek - http://nelm.io/jordi -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
Just seeing my previous reply only went to Anthony in person. so i have to repeat here: Its an awesome idea, i really like it :) On Mon, Aug 13, 2012 at 9:10 PM, Jordi Boggiano j.boggi...@seld.be wrote: Heya, What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. I can't speak for the feasibility, but from a usage point of view I do like that it opens up some sort of duck-typing by allowing decorators to be drop-in replacements even if there is no interface to implement. Cheers -- Jordi Boggiano @seldaek - http://nelm.io/jordi -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
On 08/12/2012 08:17 PM, Anthony Ferrara wrote: Thoughts? Yes, please. I think this would be a great simplification for all kinds of extensibility / plugin mechanisms implemented in various PHP projects. Typically these either end up with the boilerplate you described or try to come up with some kind of hack (e.g. dynamic code generation) to circumvent the problem of having to explicitly define parent classes if one wants to dynamically decorate objects multiple times while maintaing interfaces. Cheers, Nils -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
I agree great idea. Would really help to simplify benchmarking code also. On Mon, Aug 13, 2012 at 4:16 PM, Nils Adermann nader...@naderman.de wrote: On 08/12/2012 08:17 PM, Anthony Ferrara wrote: Thoughts? Yes, please. I think this would be a great simplification for all kinds of extensibility / plugin mechanisms implemented in various PHP projects. Typically these either end up with the boilerplate you described or try to come up with some kind of hack (e.g. dynamic code generation) to circumvent the problem of having to explicitly define parent classes if one wants to dynamically decorate objects multiple times while maintaing interfaces. Cheers, Nils -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Decorators Revisited
All, As promised, here's a link to the proof-of-concept implementation: https://github.com/ircmaxell/php-src/blob/spl_decorator/ext/spl/spl_decorator.c#L77 I'm not really looking for feedback about whether or not we should implement a decorator helper, but more for this exact method of doing it (having a magic base class which would be extended)... The interface addition is mainly a quick tool to test it with. I would think that's too dirty to go with in an official proposal. In the official proposal I could foresee two main ways of solving it: 1. Add an additional pointer to the zend_class_entry for the wrapped class. Then change instanceof and the like to check for that pointer as well. 2. Move instanceof to a handler from the current standalone function. Then each (pecl level) class could choose its own implementation if it is needed. The function could still exist for BC reasons, but would proxy to the handler method if it existed... Then, the wrapped class (the additional pointer) would only need to live on the objects internal store... Thanks, Anthony On Mon, Aug 13, 2012 at 7:50 PM, Nicholas Curtis nich.cur...@gmail.comwrote: I agree great idea. Would really help to simplify benchmarking code also. On Mon, Aug 13, 2012 at 4:16 PM, Nils Adermann nader...@naderman.de wrote: On 08/12/2012 08:17 PM, Anthony Ferrara wrote: Thoughts? Yes, please. I think this would be a great simplification for all kinds of extensibility / plugin mechanisms implemented in various PHP projects. Typically these either end up with the boilerplate you described or try to come up with some kind of hack (e.g. dynamic code generation) to circumvent the problem of having to explicitly define parent classes if one wants to dynamically decorate objects multiple times while maintaing interfaces. Cheers, Nils -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
[PHP-DEV] Decorators Revisited
Hey all I've posted before about adding the ability to do dynamic decorators before. I think I have come up with a method to do so in core. Basically, the problem is that I can't create a decorator for a class at all. I would have to extend that class (with all of the coupling issues that brings). I can create a decorator for interfaces, but I'd need to list proxy methods for each and every interface combination in each decorator. This leads to tons of boilerplate issues. For example: class CachableFooDecorator implements foo { protected $parent; public function __construct(Foo $obj) { $this-parent = $obj; } public function __call($method, $args) { return call_user_func_array(array($this-parent, $method), $args); } public function method1($a, $b) { return $this-parent-method1($a, $b); } public function method2($a) { if (!$this-hasCache('method2', $a)) { $ret = $this-parent-method2($a); $this-setCache('method2', $a, $ret); } return $this-getCache('method2', $a); } } That's a lot of boilerplate for each possible iteration. This is one reason people like traits so much, as it's easier to just do automated copy/paste than use the proper patterns. So, I've wanted to add a dynamic way of being able to decorate classes at the core level. So you'd declare the class in a special way and it would handle that boilerplate for you. I've come up with a method to do so. Basically, I created a class SplDecorator which looks like this: class SplDecorator { private $parent; public function __construct($obj) { $this-parent = $obj; } public function __call($method, $args) { return call_user_func_array(array($this-parent, $method), $args); } public function getDecoratedObject() { return $this-parent; } } The other difference, is there's an object creation handler which adds the parent's class entry to the current instances interface list. Basically: Z_OBJCE_P(this)-interfaces = safe_realloc(Z_OBJCE_P(object)-interfaces, Z_OBJCE_P(object)-num_interfaces + 1, sizeof(zend_class_entry), 0); Z_OPJCE_P(object)-interfaces[Z_OBJCE_P(object)-num_interfaces] = Z_OBJCE_P(parent); Z_OBJCE_P(object)-num_interfaces++; Now, the internal instanceof function is recursive, so it will return true when tested against this class list. So, example code like: class Foo {} class Bar extends SplDecorator {} $b = new Bar(new Foo); var_dump($b instanceof Foo); // true It also works with type hints: function test(Foo $f) {} test($b); Now, there's a lot more to do (property cascading, interface validation, etc), but the initial proof-of-concept is there. I have it working locally, I'll push it to github on my branch in a day or two so that you can play around with it... What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. Thoughts? Anthony