Hello all, I purposely tried to stay out of this conversation, but seeing as there's a lot of information flying around I think I'll give my $0.02...
As far as the Square-Rectangle example, that is a classic violation of the LSP. In fact, it's unsolvable using inheritance. For information as to why, check out this article: http://en.wikipedia.org/wiki/Circle-ellipse_problem As far as those who say that this is not an LSP issue, it actually is. Look at the LSP definition again. I'll post it here: > Let q(x) be a property provable about objects x of type T. Then q(y) should > be provable for objects y of type S where S is a subtype of T. That means that extending methods actually cannot be more general than the parent. More generic breaks since another subtype of the parent class is free to not make the generalization and hence break the substitution ability. And more specific breaks the substitution as well since it if you swap out a more generic method with a more specific one, it can break everything. Let's be clear here. This is not about *what works*, but about what is known to work. To see the problem with breaking signatures, let's look at the three cases (interface, abstract class and inheritance. Interfaces are a contract between your implementation and the outside world. The method signature is the contract. It's telling everyone that uses it (not implements, but type hints against) that all implementations that it receives will behave to that contract. Sure, we could go with duck typing and just provide the method name (no arguments) in the interface, but that's a pretty lousy contract. At that point, why even bother with interfaces? The interface defines what should be accepted, and any method that implements it should accept exactly that (no more, no less, no different). Otherwise you'll violate the contract and couple to the implementation instead of the interface. This causes the checked polymorphic ability of the interface to go out the window. The result is that the interface becomes completely useless. So, in order for interfaces to be useful, they should include the exact arguments in order and in type, and the runtime (PHP) should enforce that (which it does). Abstract classes are a contract between your implementation and the parent class only. They are used when you're implementing functionality, and you leave some methods for the child to define. But these definitions are important since the parent is likely calling them directly (or implementing part of an interface, in which the abstract method becomes an interface contract). So the parent is expecting a specific signature, and the child is expected to implement it. Now, the interesting thing here, is that in non-interface abstract class methods, generalization is ok in the child. You can make the child method more general (take more arguments, take broader arguments, etc) as long as it still behaves according to the interface provided. This is because the contract is to a known implementation (the parent), it's not a general contract. But in PHP, you can't do that since it uses the same contract enforcer for interfaces as it does for abstract classes. Overriding Methods are seemingly contractless, so you should be able to change the method signature from subclass to subclass. Indeed, on a simple first look, this makes sense. However, when we start to look at it, it becomes apparent that there is actually a contract in place due to the LSP. We can't change the function signature, since we expect all subtypes of T will be able to be swapped for each other. If we could change signatures, we'd loose this ability and hence violate LSP. Plus, this would also make the code completely resistant to the fundamental principle of OOP, polymorphism. Sure, you could swap out some of the subclasses, but not all. So all of a sudden, by loosening the restrictions on extended method parameters, we're actually causing tighter coupling and all but eliminating the benefits of OOP in general. With respect to the func_get_args argument, I see that as a non-issue. Sure, you can do it. But if you do, you're lying about the interface. You're telling the callers that you expect no arguments, but then all of a sudden you error out. You're pushing all of the interface declaration logic out of the interface and into code. That's only going to create maintainability, readability and quality issues as time goes on. Realistically, the only good use of func_get_args is when you need to take in an unlimited number of arguments. But if you do that, you should include the minimum number in the API itself. So if you have a function add() that can take any number of integers, the minimum that makes sense is 2. So you should declare add($left, $right), and then use func_get_args to get all of them to add together. However, with that said, I'd argue that it's bad design to do that at all. I'd recommend instead taking an array parameter and adding the elements of the array. Not only is it cleaner and easier to understand, it also solves the problem of extending that functionality (so you're not duplicating the func_get_args in each child)... Personally, I see this as practically a non-issue. You shouldn't be using inheritance that much anyway. Always favor composition over inheritance. There are cases where inheritance is good, but the vast majority of the times it's actually an anti-pattern. It creates tight-coupling and really ugly and hard to maintain code. Look at the design patterns demonstrated in the popular books and websites. Do any of them use inheritance? Nope... Now I know this is a little bit beyond the scope of this discussion, but I felt it was important to state since the majority of the justifications for allowing loose method inheritance and stop the runtime from enforcing inherited method signatures would be better rectified by switching from inheritance to composition. This is especially true in the Square-Rectangle problem cited earlier. Also, remember that classes should be open for extension, but closed for modification (the Open-Closed principle in SOLID design). And since when extending an interface you can't change the signature of a method, it stands to reason that in order to uphold the Open-Closed principle, method signature can't change in an interface. It's a slight leap to extend that to method signatures should change for the same exact reason. And we can remove that leap once we see that a regular class's signature is an interface and as such is a contract as well. So we can't change the signature without violating the contract and changing the API. This works for all non-private methods, since the method is open for extension, and as such is really declaring its own personal interface. For private methods, you can't override it anyway, so that's a non-issue from the start... That's my $0.02. I say leave it as is. The way it is working right now promotes good API design and makes difficult doing things that have significant implications (especially in maintainability). And seeing as there's no good way to do it without language support, removing it will just weaken the language as it removes the ability from doing design-by-contract at all... Anthony On Mon, Sep 19, 2011 at 7:25 AM, Laruence <larue...@php.net> wrote: > 2011/9/19 Etienne Kneuss <col...@php.net>: >> Hi, >> >> On Mon, Sep 19, 2011 at 12:40, Etienne Kneuss <col...@php.net> wrote: >> >>> Hi, >>> >>> On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes <glo...@nebm.ist.utl.pt>wrote: >>> >>>> Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss <col...@php.net> >>>> escreveu: >>>> >>>> >>>> >>>>> Apparently you guys are speaking about the initial implementation of an >>>>> abstract method, while I was talking about overriding a method, which is >>>>> not the relly same. So the above doesn't really apply. >>>>> >>>>> The initial implementation of an abstract method should match the >>>>> signature, >>>>> while overriding a method should be able to loosen the precondition in >>>>> many ways (type hints change, less arguments, etc..), IMO. >>>>> >>>>> >>>> I should like to hear why. As far as I can see, there's absolutely no >>>> difference. All I've seen in this thread to this respect are semantic >>>> pseudo-arguments. >>>> >>> >>> Well it is about semantics, and IMO defining a method as abstract is some >>> sort of declaration, and this declaration should be respected when the >>> method is actually implemented. >>> >>> On the other hand, interfaces define usage capabilities, and those usages >>> should work. >>> >>> There might be close to no difference in the way the are internall handled >>> currently, but IMO there is a semantic difference between the two. >>> >> >> Given that the discussion has now gone into many directions: >> - constructors/normal methods >> - abstract/interfaces/overriding >> >> Let me write some small RFC describing what we currently do w.r.t. prototype >> checks, and also summarize what people propose as changes to them. We can >> then discuss those and eventually vote for the way to go forward. > That will be nice, thanks. >> >> >>> >>> >>>> I'd say interfaces are much more likely to include more useless parameters >>>> than a concrete method definition, which most likely will only include the >>>> arguments it actually needs. >>>> >>>> An example: >>>> >>>> http://www.google.com/**codesearch#HmA4mAI_aLc/src/** >>>> main/java/terrastore/server/**impl/support/**JsonBucketsProvider.java&q=* >>>> *implements%5C%**20MessageBodyWriter&type=cs&l=**36<http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36> >>>> >>>> This is the most common scenario for implementations of this interface >>>> (see the other search results). >>> >>> >>>> -- >>>> Gustavo Lopes >>>> >>>> -- >>>> PHP Internals - PHP Runtime Development Mailing List >>>> To unsubscribe, visit: http://www.php.net/unsub.php >>>> >>>> >>> >>> >>> -- >>> Etienne Kneuss >>> http://www.colder.ch >>> >> >> >> >> -- >> Etienne Kneuss >> http://www.colder.ch >> > > > > -- > Laruence Xinchen Hui > http://www.laruence.com/ > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php