Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?
You can always supply them and use func_get_args()
/ func_num_args()
/
etc. to read them.
It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.
By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).
Richard.
--
Richard Quadling
Twitter : EE : Zend : PHPDoc
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY : bit.ly/lFnVea
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.
Nikita
Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).
Hi,
I think Richards intended other methods often used in combination with
__construct:
class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }
=> PHP Strict Standards: Declaration of B::init() should be compatible with
that of A::init()
The example with __construct() is valid (at least in 5.3).
Devis
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).
2011/9/17 devis@lucato.it:
Hi,
I think Richards intended other methods often used in combination with
__construct:class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }=> PHP Strict Standards: Declaration of B::init() should be compatible with
that of A::init()
do you know any reason for this?The example with __construct() is valid (at least in 5.3).
Devis
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Laruence Xinchen Hui
http://www.laruence.com/
2011/9/17 devis@lucato.it:
Hi,
I think Richards intended other methods often used in combination with
__construct:class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }=> PHP Strict Standards: Declaration of B::init() should be compatible
with
that of A::init()
do you know any reason for this?
The reason for this is simply that B must act like A since every B object is
also an object of A.
The example with __construct() is valid (at least in 5.3).
Devis
On 17 September 2011 14:43, Nikita Popov nikita.ppv@googlemail.com
wrote:Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Laruence Xinchen Hui
http://www.laruence.com/--
--
Etienne Kneuss
http://www.colder.ch
2011/9/17 Etienne Kneuss colder@php.net:
2011/9/17 devis@lucato.it:
Hi,
I think Richards intended other methods often used in combination with
__construct:class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }=> PHP Strict Standards: Declaration of B::init() should be compatible
with
that of A::init()
do you know any reason for this?The reason for this is simply that B must act like A since every B object is
also an object of A.
sure, but why we need interface? in addition, I don't think this
can be done by restricting the argument
thanks
The example with __construct() is valid (at least in 5.3).
Devis
On 17 September 2011 14:43, Nikita Popov nikita.ppv@googlemail.com
wrote:Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Laruence Xinchen Hui
http://www.laruence.com/--
--
Etienne Kneuss
http://www.colder.ch
--
Laruence Xinchen Hui
http://www.laruence.com/
do you know any reason for this?
The reason for this is simply that B must act like A since every B object is
also an object of A.
The reason for restricting the method signature in the Subclass is to
guarantee that an instance of a Subclass should work where an instance
of the Superclass is expected.
I think that this can be achieved with much weaker rules than
expecting that the method in the Subclass has the exact signature as
the Superclass.
For example we do allow overriding visibility, but only weakening
it(eg. private->protected or protected->public is allowed but
public->protected isn't), else there could be cases where using the
instance of the Superclass works, but the Subclass won't.
Another thing that we should take into account: php is no java, so we
don't have to require exactly matching method signature, as in php the
method name cannot be ambiguous(we don't support declaring methods
with the same name but different argument signature), and because of
this passing more arguments than it was declared also supported in the
userland functions/methods.
So based on this, the following cases are which would make a Subclass
incompatible with it's Superclass:
- tightening visibility (we already restrict this)
- declaring more arguments in the Subclass than in the Superclass, and
having no default value for those. - declaring different typehints in the Subclass than it was declared
in the Superclass. theoretically we could also allow changing the
typehinting where it makes sense (for example it would be safe to
allow changing the typehint from Foo to Bar if Bar extends/implements
Foo), but I think that would require too much work, so I'm fine with
requiring the same typehint if it was present in the parent. - removing default values from the arguments. (declaring the same
argument as in the Superclass, but without default value, while the
Superclass declared it with default value)
the following cases would be allowed to change the method signature
without making the Subclass incompatible with it's Superclass:
- the Subclass could declare fewer arguments than it's Superclass.
this could sound wrong, but as I mentioned, the engine allows passing
more arguments, than it was declared, so Subclass would work
everywhere where the Superclass is expected, and the arguments can be
accessed through func_get_args. of course it would allow someone to
shot himself in the foot(if he chose not to fetch the arguments from
the method body), but that can happen already(for example. the
Subclass can return different type than what the Superclass would, or
it could throw different Exceptions, or just return some bogus value,
etc.). - the Subclass could declare more argument than it's Superclass, as
long, as the additional argument are having default values. - the Subclass could declare arguments without typehints which were
declared with typehints in the Superclass. - the Subclass could declare arguments with default values which were
declared without default values in the Superclass.
for example:
class super{
public function init(array $a=null){
}
}
could be extended like:
// one additional argument, but it has default value, so it is fine
class sub extends super{
public function init(array $a=null, $b=false){
}
}
// same argument but without the typehint
class sub extends super{
public function init($a=null){
}
}
// fewer arguments
class sub extends super{
public function init(){
}
}
// remove the typehint
class sub extends super{
public function init($a=null){
}
}
but the followings are all invalid:
// invalid, because it removes the default value
class sub extends super{
public function init(array $a){
}
}
// invalid, because the additional argument doesn't have a default value
class sub extends super{
public function init(array $a, $b){
}
}
// invalid, because changes the typehint
class sub extends super{
public function init(StdClass $a, $b){
}
}
ps: the invalid signature should generate catchable fatal error, not
fatal error imo, we are using fatal error many places where it would
be reasonable to use Catchable fatal error, as Fatals should be only
used where the Engine is left in an unstable state, or which cannot be
recovered from.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }=> PHP Strict Standards: Declaration of B::init() should be compatible
with
that of A::init()
do you know any reason for this?The reason for this is simply that B must act like A since every B object is
also an object of A.
This is not a real reason, it's just repeating it. All B is saying in
fact "I'm going to ignore everything but the first parameter", and
there's no real reason not to allow it to do that. There's no "act" that
makes B incompatible with A here, this warning is completely useless.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Am 17.09.2011 20:08, schrieb Stas Malyshev:
Hi!
class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }=> PHP Strict Standards: Declaration of B::init() should be compatible
with
that of A::init()
do you know any reason for this?The reason for this is simply that B must act like A since every B object is
also an object of A.This is not a real reason, it's just repeating it. All B is saying in fact "I'm going to ignore everything but the
first parameter", and there's no real reason not to allow it to do that. There's no "act" that makes B incompatible
with A here, this warning is completely useless
in this case i agree
practical sample:
class a
{
public function whatever($param, $no_cache=0)
{
}
}
class b extends a
{
public function whatever($param)
{
}
}
think about $no_cache is a new param of a library class with a default value
currently you have to "fix" all classes which are extend this class everywhere
as long you are using E_STRICT
which we are using on all production machines
in combination with display_errors off and a global error-log for all hosts
to make sure we are dealing with really clean code
the same if b has an additonal parameter
as long a has lesser params this is technical OK and compatible
Hi!
class A { public function init($a, $b) { } }
class B extends A { public function init($a) { } }
=> PHP Strict Standards: Declaration of B::init() should be compatible
with
that of A::init()
do you know any reason for this?
The reason for this is simply that B must act like A since every B object
is
also an object of A.This is not a real reason, it's just repeating it. All B is saying in fact
"I'm going to ignore everything but the first parameter", and there's no
real reason not to allow it to do that. There's no "act" that makes B
incompatible with A here, this warning is completely useless.
Right, In this very specific case the error should not be outputted, the
check could be refined to let that pass. Just it it may be refined to handle
contravariant typehints correctly
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
--
Etienne Kneuss
http://www.colder.ch
maybe Richard referring to https://bugs.php.net/bug.php?id=55085 ?
but those change only affects the abstract classes.
Tyrael
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi:
this is really a big bc break...(fatal error)
is there any reason for us to really change this? In Yaf, there
are a lot of Abstract classes, the subclass only need declared there
argument when they really need it.
and I really not think this change is good one, the Intenal class
can not be saw by userland(sure, doc, reflection), so they only get
the FATAL ERROR(you are wrong) but will never be told how should they
to do..
thanks
2011/9/17 Ferenc Kovacs tyra3l@gmail.com:
maybe Richard referring to https://bugs.php.net/bug.php?id=55085 ?
but those change only affects the abstract classes.Tyrael
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu--
--
Laruence Xinchen Hui
http://www.laruence.com/
Hi:
this is really a big bc break...(fatal error)
is there any reason for us to really change this? In Yaf, there
are a lot of Abstract classes, the subclass only need declared there
argument when they really need it.
Well, that is wrong. if you declare an abstract method with a set of
arguments, classes implementing that method should be compatible with that
set of arguments.
and I really not think this change is good one, the Intenal class
can not be saw by userland(sure, doc, reflection), so they only get
the FATAL ERROR(you are wrong) but will never be told how should they
to do..
Given that we have been quite flexible with this in the past, I don't
believe a E_FATAL is warranted here, might be better suited as a E_STRICT,
as there is no apparent reason why this would prevent the engine to
continue.
thanks
2011/9/17 Ferenc Kovacs tyra3l@gmail.com:
maybe Richard referring to https://bugs.php.net/bug.php?id=55085 ?
but those change only affects the abstract classes.Tyrael
On Sat, Sep 17, 2011 at 3:43 PM, Nikita Popov nikita.ppv@googlemail.com
wrote:Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu--
--
Laruence Xinchen Hui
http://www.laruence.com/--
--
Etienne Kneuss
http://www.colder.ch
2011/9/17 Etienne Kneuss colder@php.net:
Hi:
this is really a big bc break...(fatal error)
is there any reason for us to really change this? In Yaf, there
are a lot of Abstract classes, the subclass only need declared there
argument when they really need it.Well, that is wrong. if you declare an abstract method with a set of
arguments, classes implementing that method should be compatible with that
set of arguments.and I really not think this change is good one, the Intenal class
can not be saw by userland(sure, doc, reflection), so they only get
the FATAL ERROR(you are wrong) but will never be told how should they
to do..Given that we have been quite flexible with this in the past, I don't
believe a E_FATAL is warranted here, might be better suited as a E_STRICT,
as there is no apparent reason why this would prevent the engine to
continue.
Hmm, E_STRICT
is better, I support.
Thanks
thanks
2011/9/17 Ferenc Kovacs tyra3l@gmail.com:
maybe Richard referring to https://bugs.php.net/bug.php?id=55085 ?
but those change only affects the abstract classes.Tyrael
On Sat, Sep 17, 2011 at 3:43 PM, Nikita Popov
nikita.ppv@googlemail.com wrote:Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
On Sat, Sep 17, 2011 at 3:27 PM, Richard Quadling rquadling@gmail.com
wrote:Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).--
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu--
--
Laruence Xinchen Hui
http://www.laruence.com/--
--
Etienne Kneuss
http://www.colder.ch
--
Laruence Xinchen Hui
http://www.laruence.com/
maybe Richard referring to https://bugs.php.net/bug.php?id=55085 ?
but those change only affects the abstract classes.Tyrael
Hi Richard!
Which change are you talking about? I just tried doing:
<?php
class A { public function __construct($a) { } }
class B extends A { public function __construct($a, $b) { } }
And it worked on 5.4 Beta 1 without errors.Nikita
Hi.
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?You can always supply them and use
func_get_args()
/func_num_args()
/
etc. to read them.It would seem that the limitation restricts the capabilities. I'm not
a purist. Software development is a compromise between purity and
getting the job done in an efficient and understandable manner.By allowing undocumented parameters to the constructor (due to the
enforced signature), this would seem to break things on a different
front (I can't docblock non defined parameters for examples).
My question was due to the documentation commit
http://news.php.net/php.doc.cvs/8818 and
http://news.php.net/php.doc.cvs/8819.
Not being an expert, so I don't know all the theory, but it would seem
an abstract class defines the idea and the concrete class defines it
more - I need a better grasp of words here.
An interface is the absolute here. Sub classes (the super class being
abstract or otherwise), should be able to define MORE parameters (with
or without default values). Otherwise they aren't extending, but
implementing - and for me that's the role of an interface.
It is only interfaces that guarantee things because the interface IS
the guarantee.
A class isn't beholden to anything it inherits. You can override
methods, completely obscuring the super class if you so want.
A subclass can completely divorce itself from the superclass.
But not with an interface.
PHP has always been a very very flexible language. I feel that if a
developer absolutely must enforce a method's signature, then an
interface is going to do that 100% and is a relatively easy way to do
things. Enforcing method signatures in subclasses (I don't know why
abstract classes are treated differently) does sort of make the
interface redundant.
Unless there is a real issue with the internal engine, then the method
signatures should be flexible. Unless an interface is used.
Richard.
Richard Quadling
Twitter : EE : Zend : PHPDoc
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY : bit.ly/lFnVea
My question was due to the documentation commit
http://news.php.net/php.doc.cvs/8818 and
http://news.php.net/php.doc.cvs/8819.
Just to clarify (as I'm not sure you got that change right): PHP has
enforced signatures for methods defined in abstract classes already
before 5.4. ("Enforce" doesn't mean that it's equal. The inheriting
class can for example define further optional arguments. Or change
default values.) But constructors were excluded from this for some
reason. As of 5.4 constructors are handled the same way as other
methods. So, in 5.4 nothing fundamental was changed, only constructors
were adjusted to be handled like any other function.
In my eyes this makes sense. Abstract methods define method
signatures, just like interfaces do. So they should be enforced as
such.
(The thing about the new E_STRICT
warning with normal classes is a
very different thing. That behavior doesn't make any sense to me.)
Nikita
Hi!
An interface is the absolute here. Sub classes (the super class being
abstract or otherwise), should be able to define MORE parameters (with
or without default values). Otherwise they aren't extending, but
implementing - and for me that's the role of an interface.
No, this is not correct. Requiring more parameters, despite seeming like
"extending", is actually not extension but additional restriction - if
you had function with 1 param, you could call it as foo(1) or foo(1,2)
(in the second case 2 would be ignored) but if you add second parameter
now only foo(1,2) works. The new method is more restrictive than the old
one, and that shouldn't happen in proper OO.
It is only interfaces that guarantee things because the interface IS
the guarantee.A class isn't beholden to anything it inherits. You can override
methods, completely obscuring the super class if you so want.
This is a wrong thing to do and should be avoided at all costs. Or,
putting it other way, if you want to do this, do not use inheritance,
it's not for that. By using inheritance, you agree to a contract that
says "this class is at least as good as the base class, but maybe also
better". Violating this contract is setting yourself up for all kinds of
trouble.
A subclass can completely divorce itself from the superclass.
It "can", technically, but it should not. Of course, we can not control
it, except for most basic cases, but at least in basic cases we can.
Now, I have absolutely no idea where separate treatment of abstract and
non-abstract classes came from - can anybody explain that (preferably
people that actually implemented that)?
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
hi,
My two cents:
For one I do think that enforcing strictness, or as I see it, clean
code and implementation, should be done in php. We did not do that in
the past and it has created more issues than anything else in the long
run.
One solution to solve the need of having different methods or
constructors signatures is to support multiple signature per method or
constructor, as long as the signature of the parent class or interface
is available. This is something becoming more and more common in other
languages and is actually very handy. Having such things would have
also avoided bad (per design while necessary) sutff like the recent
reflection function to create objects without invoking the
constructor.
A subclass can completely divorce itself from the superclass.
But not with an interface.
PHP has always been a very very flexible language.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi !
For one I do think that enforcing strictness, or as I see it, clean
code and implementation, should be done in php. We did not do that in
the past and it has created more issues than anything else in the long
run.
+1.
One solution to solve the need of having different methods or
constructors signatures is to support multiple signature per method or
constructor, as long as the signature of the parent class or interface
is available. This is something becoming more and more common in other
languages and is actually very handy. Having such things would have
also avoided bad (per design while necessary) sutff like the recent
reflection function to create objects without invoking the
constructor.
+1.
The possibility to defined multiple signature per method will be a great improvement.
A class like this :
class foo
{
function barForArray(array $array) {...}
function barForACall(aCall $objectClass) {...}
function barForCallable(Callable $callable) {...}
}
foreach ($arrayOfUndeterminedThings as $data)
{
switch (true)
{
case is_array($data):
$foo->barForArray($data);
break;
case $data instanceof aCall:
$foo->barForACall($data);
break;
case $data instanceof Callable:
$foo->barForCallable($data);
break;
}
}
will become :
class foo
{
function bar(array $array) {...}
function bar(aCall $objectClass) {...}
function bar(Callable $callable) {...}
}
foreach ($arrayOfUndeterminedThings as $data)
{
$foo->bar($data);
}
Best regards,
Fred
========================================================================
Frédéric Hardy : Architecte d'application/Admin. système/Ergonome
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy
Hi:
if this change will not revert, I will ask a more clearly error
message for this,
I report a req, https://bugs.php.net/bug.php?id=55719,
which is, the error message should told user what the correct
argument list is.
and also submitted a patch for this req.
thanks
2011/9/18 Frédéric Hardy frederic.hardy@mageekbox.net:
Hi !
For one I do think that enforcing strictness, or as I see it, clean
code and implementation, should be done in php. We did not do that in
the past and it has created more issues than anything else in the long
run.+1.
One solution to solve the need of having different methods or
constructors signatures is to support multiple signature per method or
constructor, as long as the signature of the parent class or interface
is available. This is something becoming more and more common in other
languages and is actually very handy. Having such things would have
also avoided bad (per design while necessary) sutff like the recent
reflection function to create objects without invoking the
constructor.+1.
The possibility to defined multiple signature per method will be a great improvement.A class like this :
class foo
{
function barForArray(array $array) {...}
function barForACall(aCall $objectClass) {...}
function barForCallable(Callable $callable) {...}
}foreach ($arrayOfUndeterminedThings as $data)
{
switch (true)
{
case is_array($data):
$foo->barForArray($data);
break;case $data instanceof aCall:
$foo->barForACall($data);
break;case $data instanceof Callable:
$foo->barForCallable($data);
break;
}
}will become :
class foo
{
function bar(array $array) {...}
function bar(aCall $objectClass) {...}
function bar(Callable $callable) {...}
}foreach ($arrayOfUndeterminedThings as $data)
{
$foo->bar($data);
}Best regards,
Fred========================================================================
Frédéric Hardy : Architecte d'application/Admin. système/Ergonome
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy--
--
Laruence Xinchen Hui
http://www.laruence.com/
Hi!
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?
I think the whole "strict parameters" thing has gone into somewhat wrong
direction and now we seem to have tons of useless errors and
restrictions. For example, handling of bug #51421 seems to be wrong -
there should be no problem if child method's signature is more
permissive than the parent's. I do not see any point in it except
puristic "enforcement" for the sake of enforcement, which doesn't seem
to have any practical purpose.
As for signature being more restrictive, in general case this should not
be allowed as it is a LSP violation. The case can be made that ctor is
not actually called on a real object but instead are used directly and
statically, so more restrictive ctors are OK, but this is not true in
the case of factories - i.e., if you do something like this:
abstract class Base {
function __construct($foo, $bar) { /* blah */ }
function createBase($concrete, $foo, $bar) {
if(!is_a($concrete, CLASS)) { throw new Exception("$concrete
is not my descendant, can not create!"); }
return new $concrete($foo, $bar);
}
This would fail if some concrete descendant of Base has additional
restrictions on its ctor. So in this case the prohibition of additional
restrictions is appropriate as well.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?I think the whole "strict parameters" thing has gone into somewhat wrong
direction and now we seem to have tons of useless errors and restrictions.
If you don't want those warnings, turn them off! That's why there is a
specific error level for it.
Derick
With the recent BC with regard the locking of the constructor's
signature for subclasses, what is the expected mechanism for allowing
a subclass to have additional parameters?I think the whole "strict parameters" thing has gone into somewhat wrong
direction and now we seem to have tons of useless errors and restrictions.If you don't want those warnings, turn them off! That's why there is a
specific error level for it.
Well, that won't help for fatal error, which is what's this discussion
is all about, or almost :)
--
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi!
If you don't want those warnings, turn them off! That's why there is a
specific error level for it.
-
It's not possible to turn these specific warnings off. The error
level turns off all warnings on the level, not just specific ones. -
These warnings don't warn about anything useful. There's no problem
there to warn about. -
Some of them aren't actually a warnings but fatal errors. Example:
abstract class BaseClass {
abstract public function foo(Type1 $foo, Type2 $bar);
}
class ExtendedClass extends BaseClass {
public function foo() {
}
}
This doesn't make sense on two levels: there's no reason why I can't
extend foo() this way and there's no reason why abstracts are treated
differently from non-abstracts.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
- Some of them aren't actually a warnings but fatal errors. Example:
abstract class BaseClass {
abstract public function foo(Type1 $foo, Type2 $bar);
}class ExtendedClass extends BaseClass {
public function foo() {
}
}
Just to make sure that we all got the situation right: The above code
throws the very same error on 5.3, on 5.2 and probably every other PHP
5 version before that (http://codepad.viper-7.com/Mb9Syu,
http://codepad.viper-7.com/lwmlaQ). 5.4 only made the behavior
consistent for constructors.
I didn't see any complaints about this "feature", so I don't really
see a reason why we should break BC with 5.3 here and drop the error
message.
This doesn't make sense on two levels: there's no reason why I can't extend
foo() this way and there's no reason why abstracts are treated differently
from non-abstracts.
I already tried to explain why it makes sense: An abstract method is
much like an interface. Both define signatures without implementation.
If their signatures weren't enforces, what would abstract methods be
good for? An abstract method (for me) is a way for an abstract class
to tell you: I need this and that method accepting these and these
arguments to work correctly. If the signature isn't enforced, this
doesn't make sense anymore. You could just as well drop the signature
from abstract method definitions, as it's pointless then.
Nikita
Hi!
I didn't see any complaints about this "feature", so I don't really
see a reason why we should break BC with 5.3 here and drop the error
message.
There's no matter of "breaking BC" - there's no BC issues with dropping
error messages, nobody's code relies on generating fatal errors for
specific code.
I already tried to explain why it makes sense: An abstract method is
much like an interface. Both define signatures without implementation.
If their signatures weren't enforces, what would abstract methods be
good for? An abstract method (for me) is a way for an abstract class
What's the point of "enforcing" such signatures? The only point I know
is that you could call these methods with specific parameters and have
it work. In the example it is possible. Enforcing for the sake of
enforcing is meaningless purism, which does not bear any practical purpose.
Abstract methods define a contract between designer of the base class
and implemetor of the concrete class. However, I do not see why this
contract should state that you can never relax restrictions on your
methods or ignore parameters or accept more than base class would allow
you to accept.
to tell you: I need this and that method accepting these and these
arguments to work correctly. If the signature isn't enforced, this
doesn't make sense anymore. You could just as well drop the signature
from abstract method definitions, as it's pointless then.
No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen
preconditions on my methods".
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
hi,
There's no matter of "breaking BC" - there's no BC issues with dropping
error messages, nobody's code relies on generating fatal errors for specific
code.
Btw, afair there is and was no BC as it was introduced with the
abstract support.
I already tried to explain why it makes sense: An abstract method is
much like an interface. Both define signatures without implementation.
If their signatures weren't enforces, what would abstract methods be
good for? An abstract method (for me) is a way for an abstract classWhat's the point of "enforcing" such signatures?
The key word missing here is "compatible". We enforce (or should)
compatible signatures. And that makes totally sense.
The only point I know is
that you could call these methods with specific parameters and have it work.
In the example it is possible. Enforcing for the sake of enforcing is
meaningless purism,
Are you saying that extending a class and making the constructor
incompatible with the extended class is a good thing to allow and to
do? If yes then I'd to strongly disagree with this idea as it allows
very bad practices (and we have enough opportunities already to shoot
ourselves in the head).
No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen preconditions
on my methods".
And finally, the reason why abstract differs from the other areas is
that they are newer. It was decided not to break BC for the other
cases where we should have done the same. While Marcus wanted to do it
everywhere, with the same good reasons.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
hi,
There's no matter of "breaking BC" - there's no BC issues with dropping
error messages, nobody's code relies on generating fatal errors for specific
code.Btw, afair there is and was no BC as it was introduced with the
abstract support.I already tried to explain why it makes sense: An abstract method is
much like an interface. Both define signatures without implementation.
If their signatures weren't enforces, what would abstract methods be
good for? An abstract method (for me) is a way for an abstract classWhat's the point of "enforcing" such signatures?
The key word missing here is "compatible". We enforce (or should)
compatible signatures. And that makes totally sense.The only point I know is
that you could call these methods with specific parameters and have it work.
In the example it is possible. Enforcing for the sake of enforcing is
meaningless purism,Are you saying that extending a class and making the constructor
incompatible with the extended class is a good thing to allow and to
do? If yes then I'd to strongly disagree with this idea as it allows
very bad practices (and we have enough opportunities already to shoot
ourselves in the head).No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen preconditions
on my methods".And finally, the reason why abstract differs from the other areas is
that they are newer. It was decided not to break BC for the other
cases where we should have done the same. While Marcus wanted to do it
everywhere, with the same good reasons.
What do you think about finding consensus and fixing it for trunk?
I think that consistent behavior between "normal" and abstract classes
would be good.
We could also improve the current method signature restrictions(many
of us agree that it is too strict in some cases) and changing the
E_FATAL something less grave was also mentioned.
I could write up an RFC from the thread and my previous email for the
allowed and restricted method signature overriding, and maybe Pierre
also want to create an RFC for the method overloading.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
The key word missing here is "compatible". We enforce (or should)
compatible signatures. And that makes totally sense.
foo() is compatible with foo($a, $b) - since anywhere you can call
function declared as foo($a, $b) you can also successfully call one
declared as foo(), it'd just ignore extra parameters - but it is not
allowed (I gave an example). It was bad when it was a useless E_STRICT,
it is much worse when it's a fatal error.
Are you saying that extending a class and making the constructor
incompatible with the extended class is a good thing to allow and to
do? If yes then I'd to strongly disagree with this idea as it allows
I never said anything about constructor being "incompatible" with
extended class and it being good thing. I am saying rejecting compatible
but not matching signatures - and I gave example of foo() vs foo($a, $b)
- makes no sense. And not even with
E_STRICT
- it's a fatal error now!
And finally, the reason why abstract differs from the other areas is
that they are newer. It was decided not to break BC for the other
cases where we should have done the same. While Marcus wanted to do it
everywhere, with the same good reasons.
I guess "all abstracts do that" is better than "abstracts except for
ctors do that" but only marginally as both behaviors make little sense
to me. Being restricted to abstracts limits the BC impact (as people
could just remove the abstract keyword and thus avoid trouble relatively
easily) but in both cases it doesn't look too good.
I'm not sure if we should keep the new BC-breaking error in 5.4 for
consistency sake (I'd felt much easier if we had discussed that before -
or we did and I just forgot?), but I seriously think we need to rework
it in 5.5 and make all compatible signatures work without complaining.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
foo() is compatible with foo($a, $b) - since anywhere you can call function
declared as foo($a, $b) you can also successfully call one declared as
foo(), it'd just ignore extra parameters - but it is not allowed (I gave an
example). It was bad when it was a useless E_STRICT, it is much worse when
it's a fatal error.
It is not compatible as the two new parameters are not optional.
foo($a=null, $b=2) would be compatible. The only difference is that
(if we don't fail with the signature checks) warnings will be raised
for the missing arguments. But basically the problem is actually the
incompatible signature. Take this code:
class foo{
function __construct(){}
}
class bar extends foo{
function__construct($a, $b){}
}
if (is_a('bar', 'foo')) {
$a = new bar(); // warnings and maybe unexpected results due to the
lack of arguments
}
to me (and to the OO concepts), they should fail at compile time to
avoid any kind of confusions or bad consequences/usage. But I can
understand that a fatal error could be seen as too extreme.
Are you saying that extending a class and making the constructor
incompatible with the extended class is a good thing to allow and to
do? If yes then I'd to strongly disagree with this idea as it allowsI never said anything about constructor being "incompatible" with extended
class and it being good thing. I am saying rejecting compatible but not
matching signatures - and I gave example of foo() vs foo($a, $b) - makes no
sense. And not even withE_STRICT
- it's a fatal error now!
Given that your example is not compatible, it is then fine to reject them.
And finally, the reason why abstract differs from the other areas is
that they are newer. It was decided not to break BC for the other
cases where we should have done the same. While Marcus wanted to do it
everywhere, with the same good reasons.I guess "all abstracts do that" is better than "abstracts except for ctors
do that" but only marginally as both behaviors make little sense to me.
Being restricted to abstracts limits the BC impact (as people could just
remove the abstract keyword and thus avoid trouble relatively easily) but in
both cases it doesn't look too good.
Where is the BC impact with abstract? It was always like that. It
happens to affect some newly written codes now but the behaviors is
not new.
I'm not sure if we should keep the new BC-breaking error in 5.4 for
consistency sake (I'd felt much easier if we had discussed that before - or
we did and I just forgot?), but I seriously think we need to rework it in
5.5 and make all compatible signatures work without complaining.
Which BC break fix? afair there is one bug fix Gustavo made which
creates bad side effects (about parent not being called or something
like that). Etienne was planning to work on that to find a better
solution. I don't have the bug # at hand but either Etienne or Gustavo
could explain it better,
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi!
class foo{
function __construct(){}
}
class bar extends foo{
function__construct($a, $b){}
}
Come on. This is not my example. My example was:
class foo{
function __construct($a, $b){}
}
class bar extends foo{
function__construct(){}
}
I would expect people at least read what I write before replying - is it
too much? I wrote "foo() is compatible with foo($a, $b)" not the other
way around and in case it wasn't clear I gave specific example.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
class foo{
function __construct(){}
}
class bar extends foo{
function__construct($a, $b){}
}Come on. This is not my example. My example was:
class foo{
function __construct($a, $b){}
}
class bar extends foo{
function__construct(){}
}I would expect people at least read what I write before replying - is it too
much?
You never misread something right? Can you please cool? Such comments
do not help, at all. Thanks.
I wrote "foo() is compatible with foo($a, $b)" not the other way
around and in case it wasn't clear I gave specific example.
But this exact example works, only the similar case using abstract
will fail, and it makes to fail here as an abstract method is only the
declaration, the implementation being done in the child class (bar
extends foo). This is the concept of 'abstract', see it like the
declaration and implementation in C. The PHP documentation is also
clear about that:
"Methods defined as abstract simply declare the method's signature"
For the record again here, the abstract example:
abstract class foo{
abstract public function foo(Type1 $foo, Type2 $bar);
}
class bar extends foo{
public function foo() {
}
}
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
But this exact example works, only the similar case using abstract
will fail, and it makes to fail here as an abstract method is only the
declaration, the implementation being done in the child class (bar
extends foo). This is the concept of 'abstract', see it like the
declaration and implementation in C. The PHP documentation is also
clear about that:"Methods defined as abstract simply declare the method's signature"
I forgot to mention that abstract are per se more restrictive than
interface, given their nature (declaration of methods with their
signature, which must be respected). A rather abstract concept (if I
may say so :) ) but well defined and documented (be in php's doc or OO
books).
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi!
But this exact example works, only the similar case using abstract
will fail, and it makes to fail here as an abstract method is only the
It produces E_STRICT
for regular functions, but for some reason not for
ctors, but fatal error for abstract ctors. Quite weird.
declaration, the implementation being done in the child class (bar
extends foo). This is the concept of 'abstract', see it like the
declaration and implementation in C. The PHP documentation is also
No, it's not at all like declaration and implementation in C. In C,
declaration and implementation relate to the SAME entity. In PHP,
abstract method and its (multiple, inependent) implementations are
different entities. Moreover, this is prohibited too:
abstract class BaseClass {
abstract public function foo(Type1 $foo, Type2 $bar);
}
class ExtendedClass extends BaseClass {
public function foo(Type1 $foo, Type2 $bar) {}
}
class MoreExtendedClass extends ExtendedClass {
public function foo() { }
}
even though it is clear that ExtendedClass::foo() and
MoreExtendedClass::foo() are two different concrete functions, so your
"abstract is declaration only" argument does not work either.
This makes absolutely no sense, as there's no reason why
MoreExtendedClass can't extend domain of ExtendedClass. However, for
some reason it is prohibited.
The example without abstract works, but produces E_STRICT
which is
useless too.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Am 19.09.2011 03:00, schrieb Stas Malyshev:
The example without abstract works, but produces
E_STRICT
which is useless too
right - because the following code is totally valid and reasonable even if
"my_function" will later get a fourth param $d='default' and the strict warnings
here forcing you to disable E_STRICT
with the negative impact losing usefull
strict-warinings or rewrite tons of code after changes in a extended class with a
new optional param
someone who is using "my_b" needs not to know anything about "my_a" which usually
is in a foreign file and often an external library
<?php
class my_a
{
public function my_function($a, $b, $c)
{
echo "$a, $b, $c";
}
}
class my_b extends my_a
{
public function my_function($a)
{
parent::my_function($a, 'internal default b', 'internal default c');
}
}
$instance = new my_b();
$instance->my_function('test');
?
Hi !
What is the utility of abstract method if implementation can not follow
the signature (constraints ?) of the abstract method ?
In this case, abstract methods are totaly useless !
Moreover, i think that it's not compatible with Liskov substitution
principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle).
Best regards,
Fred
========================================================================
Frédéric Hardy : Architecte d'application/Admin. système/Ergonome
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy
Hi !
What is the utility of abstract method if implementation can not follow
the signature (constraints ?) of the abstract method ?
In this case, abstract methods are totaly useless !
Moreover, i think that it's not compatible with Liskov substitution
principle.
Best regards,
Fred
========================================================================
Frédéric Hardy : Architecte d'application/Admin. système/Ergonome
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy
Hi,
2011/9/19 Frédéric Hardy frederic.hardy@mageekbox.net
Hi !
What is the utility of abstract method if implementation can not follow
the signature (constraints ?) of the abstract method ?
In this case, abstract methods are totaly useless !
Moreover, i think that it's not compatible with Liskov substitution
principle.
Subclasses can loosen the preconditions, that's eactly what happens here and
it is perfectly fine in theory.
Best regards,
Fred============================================================
Frédéric Hardy : Architecte d'application/Admin. système/Ergonome
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdfhttp://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy
============================================================**--
--
Etienne Kneuss
http://www.colder.ch
Hi,
2011/9/19 Frédéric Hardy frederic.hardy@mageekbox.net
Hi !
What is the utility of abstract method if implementation can not follow
the signature (constraints ?) of the abstract method ?In this case, abstract methods are totaly useless !
Moreover, i think that it's not compatible with Liskov substitution
principle.
Subclasses can loosen the preconditions, that's eactly what happens here and
it is perfectly fine in theory.
WOW. I really didn't expect this as a response to what, for me, was
quite an innocent question. Normally I get a 3 comments pointing me in
the right direction and I'm happy.
Maybe its me. But something that is abstract certain implies to me a
loose idea. The keywords have inherent meaning. At least in English.
If a class, abstract or otherwise, has the requirement to have an
enforced parameter list for a method, then would seem to be the role
of an interface. An abstract class and an method declared in an
interface, both require implementation. But with an interface, you are
not able to change the parameters. would seem to satisfy LSP.
Unless you use an interface to enforce the parameter order, how else
do you guarantee the contract?
If you also enforce the parameters for other methods (abstract or
otherwise - I'm still not 100% on the reason for the difference - or,
at this stage, if there even is a difference), then interfaces are
seemingly redundant.
Take the following code ...
<?php
abstract class abstractBase {
abstract public function __construct($a, $b);
}
class ConcreteBase extends abstractBase {
public function __construct($a, $b, $c = null) {
}
}
class SuperCrete extends ConcreteBase {
public function __construct($a, $b, $c, $d = null) {
parent::__construct($a, $b, $c);
}
}
$cb = new ConcreteBase(1,2);
$sc = new SuperCrete(1,2,3);
?>
The signatures here feel completely right, though MAYBE one could
argue that SuperCrete's constructor is off because $c is not optional.
I don't think $a and $b can ever become optional or missing from any
sub-class' definition.
As things stand, 5.4.0-beta doesn't report any problem with this code.
Have I made a mountain out of a mole hill?
(http://en.wikipedia.org/wiki/Make_a_mountain_out_of_a_molehill for
those who don't know the English idiom).
--
Richard Quadling
Twitter : EE : Zend : PHPDoc
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY : bit.ly/lFnVea
Hi!
But this exact example works, only the similar case using abstract
will fail, and it makes to fail here as an abstract method is only theIt produces
E_STRICT
for regular functions, but for some reason not for
ctors, but fatal error for abstract ctors. Quite weird.declaration, the implementation being done in the child class (bar
extends foo). This is the concept of 'abstract', see it like the
declaration and implementation in C. The PHP documentation is alsoNo, it's not at all like declaration and implementation in C. In C,
declaration and implementation relate to the SAME entity. In PHP, abstract
method and its (multiple, inependent) implementations are different
entities. Moreover, this is prohibited too:
It is the same in C. A class extending an abstract class (or method)
does not extend it per se but implement it. Just like the foo(int a,
float b); is the declaration (abstract) and foo(int a, float b) {
return a * b;= the extended class. This is the basic of abstract
methods/classes.
This makes absolutely no sense, as there's no reason why MoreExtendedClass
can't extend domain of ExtendedClass. However, for some reason it is
prohibited.
Not willing to understand the declaration idea will only block this
thread forever.
--
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi!
It is the same in C. A class extending an abstract class (or method)
does not extend it per se but implement it. Just like the foo(int a,
float b); is the declaration (abstract) and foo(int a, float b) {
return a * b;= the extended class. This is the basic of abstract
methods/classes.
There can be many implementations of the same protocol, that's the point
of OOP (which has nothing to do with C or C function declarations, which
and not overridable and as such do not define any protocols but just
functions themselves). Your idea is that somehow only identically
copying signatures instead of extending them is possible, and you intend
to prove it referring to C as an example of object-oriented paradigms?
Really.
This makes absolutely no sense, as there's no reason why MoreExtendedClass
can't extend domain of ExtendedClass. However, for some reason it is
prohibited.Not willing to understand the declaration idea will only block this
thread forever.
Please do not assume I disagree with you because I am unable to
understand you. I understand you perfectly well and you are wrong. You
are claiming that only way in OO to implement contacts is have the whole
hierarchy enforce exactly the same function signatures based on
non-extendable "declaration" and overriding functions must have exactly
identical arguments, domains and signatures. This is plain wrong -
domains can be extended and overriding functions can accept more than
the base protocol requires (and also this protocol is not the same as C
declaration). Obsessive insistence on formal "enforcement" without
understanding why this enforcement exists and what purpose it was
supposed to serve just makes lives of programmers in PHP harder because
they have to work around something that was supposed to help them, but
instead impedes them. PHP is not C, and blindly applying C concepts to
PHP OO constructs would not work.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Please do not assume I disagree with you because I am unable to understand
you. I understand you perfectly well and you are wrong.
Sorry but your constantly rejecting any logical, documented, known
principles for the abstract concept is killing me.
You are claiming that only way in OO to implement contacts is have the whole hierarchy enforce exactly
the same function signatures
You miss one word, abstract. And that's what you actually do not
understand or does not want to.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi!
Sorry but your constantly rejecting any logical, documented, known
principles for the abstract concept is killing me.
I didn't see any documented principle that say the code I cited is
prohibited. Only example you brought so far is C function declaration
that has nothing to do with OO. You want to prove you point - prove it,
not repeat it.
You are claiming that only way in OO to implement contacts is have the whole hierarchy enforce exactly
the same function signaturesYou miss one word, abstract. And that's what you actually do not
understand or does not want to.
-
Non-abstract methods have the same problem, as I have already shown you.
-
Abstract has nothing to do with it, abstract just says there's no
default implementation provided so protocol implementor must always
override it. The fact that abstract in PHP works differently is an
artifact of supporting BC, as you yourself recently explained. I don't
know where the notion that abstract methods must have different
signature enforcement and inheritance rules and it's somehow "known
principle" comes from. Its totally not what abstract means in all OO
languages known to me. Could you please cite OO languages that have
different signature enforcement rules for abstracts and non-abstracts?
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
Sorry but your constantly rejecting any logical, documented, known
principles for the abstract concept is killing me.I didn't see any documented principle that say the code I cited is
prohibited. Only example you brought so far is C function declaration that
has nothing to do with OO. You want to prove you point - prove it, not
repeat it.
With the risk to sound harsh, please do your home work and stop to ask
me (or other) to go fetch quotes from very well kown OO programming
reference and paste them here. Thanks for your understanding.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi,
On Mon, Sep 19, 2011 at 11:12 AM, Stas Malyshev smalyshev@sugarcrm.com
wrote:Hi!
Sorry but your constantly rejecting any logical, documented, known
principles for the abstract concept is killing me.I didn't see any documented principle that say the code I cited is
prohibited. Only example you brought so far is C function declaration
that
has nothing to do with OO. You want to prove you point - prove it, not
repeat it.With the risk to sound harsh, please do your home work and stop to ask
me (or other) to go fetch quotes from very well kown OO programming
reference and paste them here. Thanks for your understanding.
There is a precondition that the abstract method enforces in such
prototypes, and it is:
- I will require at least X arguments
if a method implements this and defines the prototype with:
- I will require at least Y arguments with Y < X
The procondition of the subclass method is looser. And this is perfectly
fine.
You may debate whether it is "at least X" or not that the prototype defines,
but that's exactly what it does and how PHP w.r.t. to function calls work,
we have always allowed more arguments in the call than functions declares.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
--
--
Etienne Kneuss
http://www.colder.ch
Hi,
Hi,
On Mon, Sep 19, 2011 at 11:12 AM, Stas Malyshev smalyshev@sugarcrm.com
wrote:Hi!
Sorry but your constantly rejecting any logical, documented, known
principles for the abstract concept is killing me.I didn't see any documented principle that say the code I cited is
prohibited. Only example you brought so far is C function declaration
that
has nothing to do with OO. You want to prove you point - prove it, not
repeat it.With the risk to sound harsh, please do your home work and stop to ask
me (or other) to go fetch quotes from very well kown OO programming
reference and paste them here. Thanks for your understanding.There is a precondition that the abstract method enforces in such
prototypes, and it is:
- I will require at least X arguments
if a method implements this and defines the prototype with:
- I will require at least Y arguments with Y < X
The procondition of the subclass method is looser. And this is perfectly
fine.You may debate whether it is "at least X" or not that the prototype
defines, but that's exactly what it does and how PHP w.r.t. to function
calls work, we have always allowed more arguments in the call than functions
declares.
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.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
--
--
Etienne Kneuss
http://www.colder.ch
--
Etienne Kneuss
http://www.colder.ch
There is a precondition that the abstract method enforces in such
prototypes, and it is:
- I will require at least X arguments
if a method implements this and defines the prototype with:- I will require at least Y arguments with Y < X
The procondition of the subclass method is looser. And this is perfectly
fine.
You may debate whether it is "at least X" or not that the prototype
defines, but that's exactly what it does and how PHP w.r.t. to function
calls work, we have always allowed more arguments in the call than functions
declares.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.
Exactly. Abstract is a different than interface or simple extended
classes. That's something I totally fail to make clear in this lengthy
thread.
Cheers,
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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.
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:
This is the most common scenario for implementations of this interface
(see the other search results).
--
Gustavo Lopes
This is the most common scenario for implementations of this interface (see
the other search results).
If we talk about implementing the abstract concept and we use the
interface model to do it, then we do it wrong. I'm out of other
arguments, or maybe one new one, if we implement abstract like
interface, then let kill abstract support, we don't need that.
--
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
This is the most common scenario for implementations of this interface (see
the other search results).If we talk about implementing the abstract concept and we use the
interface model to do it, then we do it wrong. I'm out of other
arguments, or maybe one new one, if we implement abstract like
interface, then let kill abstract support, we don't need that.
how would you have concrete methods in your interfaces?
you can do that with abstract, but you can't do that with interface.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Em Mon, 19 Sep 2011 11:20:56 +0100, Pierre Joye pierre.php@gmail.com
escreveu:
On Mon, Sep 19, 2011 at 12:18 PM, Gustavo Lopes glopes@nebm.ist.utl.pt
wrote:
http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36This is the most common scenario for implementations of this interface
(see the other search results).If we talk about implementing the abstract concept and we use the
interface model to do it, then we do it wrong. I'm out of other
arguments, or maybe one new one, if we implement abstract like
interface, then let kill abstract support, we don't need that.
There's no significant difference between interfaces and abstract classes
here. Abstract classes frequently have protected hook methods you can
override/implement take more arguments that you need. For instance, you
could have:
class Form {
...
abstract protected function buildHTML($page, $errors, $params, $command);
...
}
and reasonably not need all the arguments.
--
Gustavo Lopes
Hi!
If we talk about implementing the abstract concept and we use the
interface model to do it, then we do it wrong. I'm out of other
arguments, or maybe one new one, if we implement abstract like
interface, then let kill abstract support, we don't need that.
Interface defines protocol without any implementation. Abstract class
defines both class template and protocol with partial implementation.
These are different things, and regardless of signature discussion -
which really has nothing to do with the difference between abstracts and
interfaces - one of them does not replace the other in any way. You
could simulate abstracts with interfaces, but that would be abuse of the
interface idea.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi,
Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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.
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=**36http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36This is the most common scenario for implementations of this interface (see
the other search results).--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch
Hi,
Hi,
On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes glopes@nebm.ist.utl.ptwrote:
Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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.
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=**36http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36This is the most common scenario for implementations of this interface
(see the other search results).--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch
--
Etienne Kneuss
http://www.colder.ch
2011/9/19 Etienne Kneuss colder@php.net:
Hi,
Hi,
On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes glopes@nebm.ist.utl.ptwrote:
Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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=**36http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36This is the most common scenario for implementations of this interface
(see the other search results).--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch--
Etienne Kneuss
http://www.colder.ch
--
Laruence Xinchen Hui
http://www.laruence.com/
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
2011/9/19 Etienne Kneuss colder@php.net:
Hi,
Hi,
On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes glopes@nebm.ist.utl.ptwrote:
Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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=**36http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36This is the most common scenario for implementations of this interface
(see the other search results).--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch--
Etienne Kneuss
http://www.colder.ch--
Laruence Xinchen Hui
http://www.laruence.com/
Hi,
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_problemAs 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.
Uh unless I'm misunderstanding you here there is something that looks
terribly wrong:
Overriding methods with a different signature, e.g. allowing more sets of
arguments(contra variant args), or returning a more precise type (covariant
return type) definitely works with LSP, and it is fine in OO. LSP only
states about substitution between one parent and its child, not one child
with another...
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.
Well, we have no way of declaring that we accept an undefined number of
arguments. So there is simply no choice here.
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)...
By doing that, you also do exactly what you describe earlier, you push the
args checks in the code itself, as you could always pass an incomplete
array, and you could error out earlier.
Var-args functions have been quite used for many things, there is no reason
why we should flag that as bad practice now... is there?
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
2011/9/19 Etienne Kneuss colder@php.net:
Hi,
Hi,
On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes <glopes@nebm.ist.utl.pt
wrote:Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss colder@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=36This is the most common scenario for implementations of this interface
(see the other search results).--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch--
Etienne Kneuss
http://www.colder.ch--
Laruence Xinchen Hui
http://www.laruence.com/--
--
Etienne Kneuss
http://www.colder.ch
First of all, Anthony, thanks for joining into the discussion!
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.Well, we have no way of declaring that we accept an undefined number of
arguments. So there is simply no choice here.
maybe we should consider adding support for that, as I agree that it
would make the contract more clear.
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)...By doing that, you also do exactly what you describe earlier, you push the
args checks in the code itself, as you could always pass an incomplete
array, and you could error out earlier.Var-args functions have been quite used for many things, there is no reason
why we should flag that as bad practice now... is there?
not that I know of, except that we have no explicit way to declare signature.
to reflect to the original points by Anthony:
bringing up the Square - Rectangle example was a bad one, but to quote
from the wikipedia entry: "Violations of LSP, like this one, may or
may not be a problem in practice, depending on the postconditions or
invariants that are actually expected by the code that uses classes
violating LSP. Mutability is a key issue here. If Square and Rectangle
had only getter methods (i.e. they were immutable objects), then no
violation of LSP could occur."
So while it can cause violation, it isn't a violation by itself.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
I followed the whole discussion, still it is not clear which one is
considered good and which bad practice...
In ZF 1 and 2, ZendRegistry::__construct() has the following signature with
2 parameters:
function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS)
while SPL ArrayObject (its parent) has this one with 3 parameters:
ArrayObject::__construct() ([ mixed $input [, int $flags [, string
$iterator_class]]] )
from
https://github.com/zendframework/zf2/blob/master/library/Zend/Registry.phpand
http://www.php.net/manual/en/arrayobject.construct.php
There are other similar examples, e.g.
ReflectionClass::getParentClass()
and
ZF Zend_Reflection_Class::getParentClass($reflectionClass =
'Zend_Reflection_Class')
I don't know if being ArrayObject an SPL makes any difference, I hope
not... Is the example relevant for the discussion ?
I always found PHP flexibility one of its strong points, and since we don't
have method overloading, limiting the signature extension or contraction
doesn't sound very useful to developers.
bye!
Devis
First of all, Anthony, thanks for joining into the discussion!
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.Well, we have no way of declaring that we accept an undefined number of
arguments. So there is simply no choice here.maybe we should consider adding support for that, as I agree that it
would make the contract more clear.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)...By doing that, you also do exactly what you describe earlier, you push
the
args checks in the code itself, as you could always pass an incomplete
array, and you could error out earlier.Var-args functions have been quite used for many things, there is no
reason
why we should flag that as bad practice now... is there?not that I know of, except that we have no explicit way to declare
signature.to reflect to the original points by Anthony:
bringing up the Square - Rectangle example was a bad one, but to quote
from the wikipedia entry: "Violations of LSP, like this one, may or
may not be a problem in practice, depending on the postconditions or
invariants that are actually expected by the code that uses classes
violating LSP. Mutability is a key issue here. If Square and Rectangle
had only getter methods (i.e. they were immutable objects), then no
violation of LSP could occur."
So while it can cause violation, it isn't a violation by itself.--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
I followed the whole discussion, still it is not clear which one is
considered good and which bad practice...In ZF 1 and 2, ZendRegistry::__construct() has the following signature
with
2 parameters:function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS)
while SPL ArrayObject (its parent) has this one with 3 parameters:
ArrayObject::__construct() ([ mixed $input [, int $flags [, string
$iterator_class]]] )from
https://github.com/zendframework/zf2/blob/master/library/Zend/Registry.phpand
That's exactly why I think constructors should be exempted from "strict"
signature checking.
When you pass objects as a function parameter, the signature should match
even though you pass an instance of a child class. That has been well
established in some of the previous messages.
However, I don't think this should apply to constructors in the same way.
They can be 'simplified' as the child class becomes more specific.
There are other similar examples, e.g.
ReflectionClass::getParentClass()
and
ZF Zend_Reflection_Class::getParentClass($reflectionClass =
'Zend_Reflection_Class')I don't know if being ArrayObject an SPL makes any difference, I hope
not... Is the example relevant for the discussion ?I always found PHP flexibility one of its strong points, and since we
don't
have method overloading, limiting the signature extension or contraction
doesn't sound very useful to developers.bye!
DevisFirst of all, Anthony, thanks for joining into the discussion!
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.Well, we have no way of declaring that we accept an undefined number
of
arguments. So there is simply no choice here.maybe we should consider adding support for that, as I agree that it
would make the contract more clear.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)...By doing that, you also do exactly what you describe earlier, you push
the
args checks in the code itself, as you could always pass an incomplete
array, and you could error out earlier.Var-args functions have been quite used for many things, there is no
reason
why we should flag that as bad practice now... is there?not that I know of, except that we have no explicit way to declare
signature.to reflect to the original points by Anthony:
bringing up the Square - Rectangle example was a bad one, but to quote
from the wikipedia entry: "Violations of LSP, like this one, may or
may not be a problem in practice, depending on the postconditions or
invariants that are actually expected by the code that uses classes
violating LSP. Mutability is a key issue here. If Square and Rectangle
had only getter methods (i.e. they were immutable objects), then no
violation of LSP could occur."
So while it can cause violation, it isn't a violation by itself.--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
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.Well, we have no way of declaring that we accept an undefined number of
arguments. So there is simply no choice here.
foo() is accepting undefined number of args. PHP has always been
tolerant to passing more arguments than signature states and blank
signatures were used for such things since forever.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
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
Here you lost me. Why exactly you can not accept more? If you implement
some interface, and fulfill all obligations about this interface, why
can not you also accept more than this interface requires? This is
certainly not the current practice - but even in theory, why would that
be a requirement? LSP certainly does not require that - otherwise it
would also say "any property that is not true for q(x) should also be
not true for q(y)" - which btw would severely reduce what you could do
in OO.
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).
I do not see why this would be true. You use interface for the protocol
it defines, and it's useful for that, but you can also use extended
protocol when you know particular implementation provides extended
features. Yes, that would mean that this specific code would not work
with all implementations of the base protocol - but that's exactly what
you want, have guaranteed base and optional extension. Yes, that would
introduce coupling - but sometimes you have no other way (think about
something like PDO and having to support both base features and give
access to additional DB-specific features for performance, etc.) I agree
that it may be not ideal from purist point of view but practically I've
seen many situations where it's required - especially when you can't
really change the interface.
Having said that, I could begrudgingly (very begrudgingly) accept the
case for doing this with interfaces. The problem we have however is not
with interfaces but with abstracts.
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
Unfortunately, it does not - in fact, as you yourself noted, abstract
should allow child classes to generalize, but right now not only this is
not allowed, but the presence of an abstract anywhere in the hierarchy
blocks generalization for the whole hierarchy. So not only you have to
work around this particular problem when you use abstract classes - you
have to work around the case where somebody refactored base classes you
relied on and introduced abstract signature there - and now your
perfectly good and working generalization produces fatal errors!
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
foo() is compatible with foo($a, $b) - since anywhere you can call function
declared as foo($a, $b) you can also successfully call one declared as
foo(), it'd just ignore extra parameters - but it is not allowed (I gave an
example). It was bad when it was a useless E_STRICT, it is much worse when
it's a fatal error.It is not compatible as the two new parameters are not optional.
foo($a=null, $b=2) would be compatible. The only difference is that
(if we don't fail with the signature checks) warnings will be raised
for the missing arguments. But basically the problem is actually the
incompatible signature. Take this code:class foo{
function __construct(){}
}
class bar extends foo{
function__construct($a, $b){}
}if (is_a('bar', 'foo')) {
$a = new bar(); // warnings and maybe unexpected results due to the
lack of arguments
}to me (and to the OO concepts), they should fail at compile time to
avoid any kind of confusions or bad consequences/usage. But I can
understand that a fatal error could be seen as too extreme.Are you saying that extending a class and making the constructor
incompatible with the extended class is a good thing to allow and to
do? If yes then I'd to strongly disagree with this idea as it allowsI never said anything about constructor being "incompatible" with extended
class and it being good thing. I am saying rejecting compatible but not
matching signatures - and I gave example of foo() vs foo($a, $b) - makes no
sense. And not even withE_STRICT
- it's a fatal error now!Given that your example is not compatible, it is then fine to reject them.
And finally, the reason why abstract differs from the other areas is
that they are newer. It was decided not to break BC for the other
cases where we should have done the same. While Marcus wanted to do it
everywhere, with the same good reasons.I guess "all abstracts do that" is better than "abstracts except for ctors
do that" but only marginally as both behaviors make little sense to me.
Being restricted to abstracts limits the BC impact (as people could just
remove the abstract keyword and thus avoid trouble relatively easily) but in
both cases it doesn't look too good.Where is the BC impact with abstract? It was always like that. It
happens to affect some newly written codes now but the behaviors is
not new.I'm not sure if we should keep the new BC-breaking error in 5.4 for
consistency sake (I'd felt much easier if we had discussed that before - or
we did and I just forgot?), but I seriously think we need to rework it in
5.5 and make all compatible signatures work without complaining.Which BC break fix? afair there is one bug fix Gustavo made which
creates bad side effects (about parent not being called or something
like that). Etienne was planning to work on that to find a better
solution. I don't have the bug # at hand but either Etienne or Gustavo
could explain it better,
the __construct was handled differently, as the other methods, so this
was valid before 5.4:
<?php
abstract class BaseClass {
abstract public function __construct(Type1 $foo, Type2 $bar);
}
class ExtendedClass extends BaseClass {
public function __construct() {
}
}
now the constructor also uses the same restrictions for abstract
classes as the other methods.
it's not a problem in itself, I'm just answering the question that
which bugfix is Stas referring to.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Em Sun, 18 Sep 2011 20:06:31 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:
to tell you: I need this and that method accepting these and these
arguments to work correctly. If the signature isn't enforced, this
doesn't make sense anymore. You could just as well drop the signature
from abstract method definitions, as it's pointless then.No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen
preconditions on my methods".
While this is true, we should be a bit pragmatic here.
If the subclass method specifies less parameters, it is very likely a
mistake. It's not usual (certainly it's not more frequent than a mistake
being the case) for arguments to be simply intentionally ignored. In fact,
I don't know of any popular language that implements this type of
contravariance. Plus, consider the case in which the superclass takes an
argument by reference:
function increment(&$foo) { $foo = $foo + 1; }
It is obvious that in this case a subclass override that takes no
arguments cannot possibly satisfy the contract of the original method.
That is to say, by ref arguments are also used for postconditions.
That said, I think the only rules worth implementing are:
- Allow arguments with a supertype. For instance, this should give no
warning:
<?php
class A {}
class B extends A {}
class C { function g(B $b) {} }
class D extends C { function g(A $a) { } }
- Allow extra parameters with a default value (implemented):
<?php
class C { function g($a) {} }
class D extends C { function g($a,$b=true) { } }
--
Gustavo Lopes
Hi,
Em Sun, 18 Sep 2011 20:06:31 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:to tell you: I need this and that method accepting these and these
arguments to work correctly. If the signature isn't enforced, this
doesn't make sense anymore. You could just as well drop the signature
from abstract method definitions, as it's pointless then.No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen preconditions
on my methods".While this is true, we should be a bit pragmatic here.
If the subclass method specifies less parameters, it is very likely a
mistake. It's not usual (certainly it's not more frequent than a mistake
being the case) for arguments to be simply intentionally ignored. In fact, I
don't know of any popular language that implements this type of
contravariance. Plus, consider the case in which the superclass takes an
argument by reference:function increment(&$foo) { $foo = $foo + 1; }
It is obvious that in this case a subclass override that takes no arguments
cannot possibly satisfy the contract of the original method. That is to say,
by ref arguments are also used for postconditions.
Sure, but you mix two things here, references would have to be handled
specifically, and we would not allow to specify less args by ref.
There is simply no reason for disallowing this with nornal args though.
Imagine the following code:
interface Foo { public function plop ($a,$b,$c,$d) }
class A implements Foo {
public function plop() {
$args = func_get_args()
;
// ...
}
}
This is perfectly valid PHP code, there is no valid usage of Foo that will
not work with A... So to me this restriction seems really arbitrary.
And I don;t believe missing an argument in the declaration of a method,
using it in the body of the method and not realising it is so much of a
common error that it would warrant this.
That said, I think the only rules worth implementing are:
- Allow arguments with a supertype. For instance, this should give no
warning:
<?php
class A {}
class B extends A {}
class C { function g(B $b) {} }
class D extends C { function g(A $a) { } }
- Allow extra parameters with a default value (implemented):
<?php
class C { function g($a) {} }
class D extends C { function g($a,$b=true) { } }--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch
Em Mon, 19 Sep 2011 10:01:32 +0100, Etienne Kneuss colder@php.net
escreveu:
Sure, but you mix two things here, references would have to be handled
specifically, and we would not allow to specify less args by ref.There is simply no reason for disallowing this with nornal args though.
Imagine the following code:interface Foo { public function plop ($a,$b,$c,$d) }
class A implements Foo {
public function plop() {
$args =func_get_args()
;
// ...
}
}This is perfectly valid PHP code, there is no valid usage of Foo that
will not work with A... So to me this restriction seems really arbitrary.
And I don;t believe missing an argument in the declaration of a method,
using it in the body of the method and not realising it is so much of a
common error that it would warrant this.
Arbitrary as it may be, it's nevertheless reasonably arbitrated given how
little useful it is to just ignore arguments and how likely it is to a
mistake.
And I don't buy the func_get_args()
argument. Why would anyone use
func_get_args for anything other than variadic functions?... I certainly
don't.
--
Gustavo Lopes
Hi!
Arbitrary as it may be, it's nevertheless reasonably arbitrated given how
little useful it is to just ignore arguments and how likely it is to a
mistake.
It is not little useful and it is not likely to make such mistake
without immediately being notified and corrected. I don't know how you
make these assumptions or what they are based on - could you please
bring some evidence?
And I don't buy the
func_get_args()
argument. Why would anyone use
func_get_args for anything other than variadic functions?... I certainly
don't.
So you say if you personally don't use something in PHP nobody should
use it?
And, BTW, what's wrong with variadic functions (i.e., ones accepting
variable number of arguments)? Such functions are commonplace in PHP.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Em Mon, 19 Sep 2011 10:18:50 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:
Arbitrary as it may be, it's nevertheless reasonably arbitrated given
how little useful it is to just ignore arguments and how likely it is
to a
mistake.It is not little useful and it is not likely to make such mistake
without immediately being notified and corrected. I don't know how you
make these assumptions or what they are based on - could you please
bring some evidence?
It would obviously be hard to produce such evidence. Measuring how common
a mistake is very difficult without some study to that effect since those
mistakes are steeply underrepresented in the public PHP code corpus (i.e.
they get fixed). And measuring how common overrides with less arguments
are would still require relatively complex code analysis.
So, no, I cannot prove the assertions behind my argument, I can only say I
believe them to be plausible.
And I don't buy the
func_get_args()
argument. Why would anyone use
func_get_args for anything other than variadic functions?... I certainly
don't.So you say if you personally don't use something in PHP nobody should
use it?
And, BTW, what's wrong with variadic functions (i.e., ones accepting
variable number of arguments)? Such functions are commonplace in PHP.
The thing if you introduce func_get_args()
to the argument, any discussion
about enforcing signatures becomes meaningless. One could argue this
should be allowed:
class A {
function foo() {}
}
class B extends A {
function foo($a) {}
}
Because for all we know the implementation of A::foo() could have:
function foo() {
$a = func_get_args()
[0];
}
--
Gustavo Lopes
Em Mon, 19 Sep 2011 10:18:50 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:Arbitrary as it may be, it's nevertheless reasonably arbitrated given how
little useful it is to just ignore arguments and how likely it is to a
mistake.It is not little useful and it is not likely to make such mistake without
immediately being notified and corrected. I don't know how you make these
assumptions or what they are based on - could you please bring some
evidence?It would obviously be hard to produce such evidence. Measuring how common
a mistake is very difficult without some study to that effect since those
mistakes are steeply underrepresented in the public PHP code corpus (i.e.
they get fixed). And measuring how common overrides with less arguments
are would still require relatively complex code analysis.So, no, I cannot prove the assertions behind my argument, I can only say I
believe them to be plausible.And I don't buy the
func_get_args()
argument. Why would anyone use
func_get_args for anything other than variadic functions?... I certainly
don't.So you say if you personally don't use something in PHP nobody should use
it?
And, BTW, what's wrong with variadic functions (i.e., ones accepting
variable number of arguments)? Such functions are commonplace in PHP.The thing if you introduce
func_get_args()
to the argument, any discussion
about enforcing signatures becomes meaningless. One could argue this
should be allowed:class A {
function foo() {}
}class B extends A {
function foo($a) {}
}Because for all we know the implementation of A::foo() could have:
function foo() {
$a =func_get_args()
[0];
}
yeah, but you can't know from the method signature that there is, or
isn't any place, where the parent method is called without arguments,
but your class would be incompatible there.
You aren't supposed to know at all time that what does the Superclass
class/method internally or how is it used at all time(where it is
called, with how many arguments, etc), but you get a guarantee that
all call for that method has to be compatible with it's method
signature, so as long as your method signature also compatible with
it, you should be fine to change it.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
The thing if you introduce
func_get_args()
to the argument, any discussion
about enforcing signatures becomes meaningless. One could argue this
should be allowed:class A {
function foo() {}
}class B extends A {
function foo($a) {}
}
No, this can not be allowed because A's signature accepts any call,
including foo(), but B's only accepts one with at least one parameters,
so LSP is broken here. That is regardless of what A::foo() is doing
inside, since arguments are going to be checked first.
Having said that, even in this case I would probably not go as far as
producing a fatal error - though E_STRICT
would be OK here.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
but it is not logical
foo() in B can do anything with $a before or after parent::foo()
and the caller does not need to know at any point that B
has anything to do with the class A
Am 20.09.2011 19:40, schrieb Stas Malyshev:Hi!
The thing if you introduce
func_get_args()
to the argument, any discussion
about enforcing signatures becomes meaningless. One could argue this
should be allowed:class A {
function foo() {}
}class B extends A {
function foo($a) {}
}
No, this can not be allowed because A's signature accepts any call, including foo(), but B's only accepts one with
at least one parameters, so LSP is broken here. That is regardless of what A::foo() is doing inside, since
arguments are going to be checked first.
Having said that, even in this case I would probably not go as far as producing a fatal error - though E_STRICT
would be OK here
--
Mit besten Grüßen, Reindl Harald
the lounge interactive design GmbH
A-1060 Vienna, Hofmühlgasse 17
CTO / software-development / cms-solutions
p: +43 (1) 595 3999 33, m: +43 (676) 40 221 40
icq: 154546673, http://www.thelounge.net/
Hi!
but it is not logical
foo() in B can do anything with $a before or after parent::foo()
and the caller does not need to know at any point that B
has anything to do with the class A
Consider this code:
function doFoo(A $a) {
$a->foo();
}
It's fine, right? Since A::foo() signature fits.
Now consider this code:
$a = new B(); // B extends A, it's OK, right?
doFoo($a); // oops, $a->foo() breaks!
This code would break since B::foo() requires a parameter.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
but it is not logical
foo() in B can do anything with $a before or after parent::foo()
and the caller does not need to know at any point that B
has anything to do with the class AConsider this code:
function doFoo(A $a) {
$a->foo();
}It's fine, right? Since A::foo() signature fits.
Now consider this code:$a = new B(); // B extends A, it's OK, right?
doFoo($a); // oops, $a->foo() breaks!This code would break since B::foo() requires a parameter.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227--
A little bit off-topic, but maybe we could also discuss/fix this:
https://bugs.php.net/bug.php?id=43200
http://groups.google.com/group/symfony-devs/browse_thread/thread/3fc16ba601045551
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi
2011/9/23 Ferenc Kovacs tyra3l@gmail.com:
A little bit off-topic, but maybe we could also discuss/fix this:
https://bugs.php.net/bug.php?id=43200
http://groups.google.com/group/symfony-devs/browse_thread/thread/3fc16ba601045551
I don't see the bug in that matter, because the interface defines the
prototype of methods that classes who inherit must implement. Abstract
classes who implement the interface but not all the methods don't need
to say to the child class that the method must be implemented twice,
as the implementation requirement is inherited in the child class
since its parent is abstract.
Sure the warning could go away if both the interface and abstract
class signatures match, but that would seem inconsistent to me.
--
regards,
Kalle Sommer Nielsen
kalle@php.net
Em Mon, 19 Sep 2011 10:18:50 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:
Arbitrary as it may be, it's nevertheless reasonably arbitrated given
how little useful it is to just ignore arguments and how likely it is
to a
mistake.It is not little useful and it is not likely to make such mistake
without immediately being notified and corrected.
In the cases where you do want to ignore arguments (say overgenerous
interfaces that give you more information than you need to make a
decision), relaxing the parameter checks would not be very helpful:
- It had to be case that the parameters you want to ignore are the last
- You could just put the arguments in the signature and still ignore them
(perhaps also giving them a dummy default value so that they don't have to
be passed).
So there's little to be gained here.
--
Gustavo Lopes
Em Mon, 19 Sep 2011 10:18:50 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:Arbitrary as it may be, it's nevertheless reasonably arbitrated given how
little useful it is to just ignore arguments and how likely it is to a
mistake.It is not little useful and it is not likely to make such mistake without
immediately being notified and corrected.In the cases where you do want to ignore arguments (say overgenerous
interfaces that give you more information than you need to make a
decision), relaxing the parameter checks would not be very helpful:
- It had to be case that the parameters you want to ignore are the last
- You could just put the arguments in the signature and still ignore them
(perhaps also giving them a dummy default value so that they don't have to
be passed).So there's little to be gained here.
There is surely little to be gained here, but that's not really the point.
The point is that we are currently diverging from the theory, so we should
have a BIG gain of doing so, not the opposite.
--
Gustavo Lopes--
--
Etienne Kneuss
http://www.colder.ch
Hi!
- It had to be case that the parameters you want to ignore are the last
Of course, but that's a very common scenario - important parameters go
first, unimportant go last. Or, if I wanted, I could just have blank
signature foo() and figure it out in the function. There are many ways
to do this, and I see no reason why we must prohibit some of them - it
only makes the life of the developers harder.
- You could just put the arguments in the signature and still ignore them
(perhaps also giving them a dummy default value so that they don't have to
be passed).
Yes, I could - but why would I want do that? Do you see the situation
here: we introduce a check that is supposed to help me, and now you
say I have to work around it to actually do what I want to do? Doesn't
it sound like contrary to the purpose of helping me? I want the language
to assist me in doing what I want to do, not having to wrestle with it
by shutting off warnings and introducing useless parameters.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Em Mon, 19 Sep 2011 10:01:32 +0100, Etienne Kneuss colder@php.net
escreveu:Sure, but you mix two things here, references would have to be handled
specifically, and we would not allow to specify less args by ref.There is simply no reason for disallowing this with nornal args though.
Imagine the following code:interface Foo { public function plop ($a,$b,$c,$d) }
class A implements Foo {
public function plop() {
$args =func_get_args()
;
// ...
}
}This is perfectly valid PHP code, there is no valid usage of Foo that will
not work with A... So to me this restriction seems really arbitrary.
And I don;t believe missing an argument in the declaration of a method,
using it in the body of the method and not realising it is so much of a
common error that it would warrant this.Arbitrary as it may be, it's nevertheless reasonably arbitrated given how
little useful it is to just ignore arguments and how likely it is to a
mistake.And I don't buy the
func_get_args()
argument. Why would anyone use
func_get_args for anything other than variadic functions?... I certainly
don't.
Lester mentioned another use-case, when the Subclass will call the
parent method with constant values as arguments.
For example I have Quadrilateral Superclass with the public function
__construct($width, $height){}
If I create a more special Subclass: Square, I can omit the second
parameter, as I know that for Square, the $width and $height is equal.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi!
If the subclass method specifies less parameters, it is very likely a
mistake. It's not usual (certainly it's not more frequent than a mistake
Why would it always be? I had code where overriding class had methods
that need less arguments and substituted defaults or overrode values for
some parameters. I never had a code (which survived longer than 2
minutes without editing) where one just forgot to specify arguments to a
method that's using them - any half-decent IDE would discover that in
seconds and alert you. I don;t think it's a place of the engine to
decide perfectly valid language use is a "mistake" and throw errors just
because somebody dislikes it.
being the case) for arguments to be simply intentionally ignored. In fact,
I don't know of any popular language that implements this type of
contravariance. Plus, consider the case in which the superclass takes an
Most languages that check signatures are compiled languages, in which
variable arguments are implemented differently than in PHP. For example,
Python doesn't have this kind of signature enforcement at all (so you
could claim it allows this type of contravariance, too) - and yet you
don't think this is the reason we have to abandon it, right?
argument by reference:
function increment(&$foo) { $foo = $foo + 1; }
It is obvious that in this case a subclass override that takes no
arguments cannot possibly satisfy the contract of the original method.
In this particular case it would not be right, but it's not the reason
to prohibit it in many other cases where it is. The fact that you can
write wrong code if you try really hard shouldn't be a reason to prevent
perfectly correct code from working.
- Allow arguments with a supertype. For instance, this should give no
warning:- Allow extra parameters with a default value (implemented):
- Allow ignoring parameters or introducing defaults
- Remove fatal errors. Fatal errors should be when the engine can not
continue, not when the code is perfectly workable but we frown upon such
use of the language. This is especially true in non-compiled language
where all fatal errors break application in runtime.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Em Sun, 18 Sep 2011 20:06:31 +0100, Stas Malyshev smalyshev@sugarcrm.com
escreveu:to tell you: I need this and that method accepting these and these
arguments to work correctly. If the signature isn't enforced, this
doesn't make sense anymore. You could just as well drop the signature
from abstract method definitions, as it's pointless then.No it is not. The signature tells "this method would accept certain
arguments". If you call it with these arguments, it would work. However,
there's no promise to never extend the cases where it works - it goes
contrary to the whole point of OOP to say "I will never loosen preconditions
on my methods".While this is true, we should be a bit pragmatic here.
If the subclass method specifies less parameters, it is very likely a
mistake. It's not usual (certainly it's not more frequent than a mistake
being the case) for arguments to be simply intentionally ignored. In fact, I
don't know of any popular language that implements this type of
contravariance. Plus, consider the case in which the superclass takes an
argument by reference:function increment(&$foo) { $foo = $foo + 1; }
It is obvious that in this case a subclass override that takes no arguments
cannot possibly satisfy the contract of the original method. That is to say,
by ref arguments are also used for postconditions.That said, I think the only rules worth implementing are:
- Allow arguments with a supertype. For instance, this should give no
warning:<?php
class A {}
class B extends A {}
class C { function g(B $b) {} }
class D extends C { function g(A $a) { } }
- Allow extra parameters with a default value (implemented):
<?php
class C { function g($a) {} }
class D extends C { function g($a,$b=true) { } }
could you check my second(lengthy) mail on this thread?
I also tried to come up with the valid, and invalid signature changes.
I forget to take references into account, so thanks for pointing that out.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu