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