Calling methods of parent/ancestor classes in PHP5

php.internals

Brad Fisher

22 years ago
Since you can no longer assign to $this in PHP5, how would one call a method defined in a particular ancestor class that has been overridden by the current class or a more recent ancestor? In PHP4 doing this was hackish, but worked. For example: class A { function foo() { echo "A::foo"; // Do stuff with $this (not a static method) } } class B extends A { function foo() { echo "B::foo"; } } class C extends B { function bar() { // Inside C, i should be able to do the following since $this is an instance of A: A::foo(); } } class D { function bar() { $c = new C; // I'd like to call the A::foo method of C here... // In PHP4, I could do something like: $othis = &$this; $this = &$c; A::foo(); $this = &$othis; } } How would one emulate the same sort of functionality as in D::bar with PHP5? The best I've been able to figure out is to build the appropriate code in a method and eval it: (add the following method to class A) function call_user_method_array($method, $args = null) { if (is_array($method)) $method = $method[0] .'::'. $method[1]; else $method = '$this->'. $method; $code = 'return '. $method .'('; if (isset($args)) { $n = count($args); if ($n >= 1) { $code .= '$args[0]'; for ($i = 1; $i < $n; ++$i) { $code .= ',$args['.$i.']'; } } } $code .= ');'; return eval($code); } Then D::bar would be more like: function bar() { $c = new C; $c->call_user_method_array("A::foo"); } I'd assume this has big performance penalties compared to the old PHP4 way. It would be nice if call_user_func/call_user_func_array could be used instead of an eval, but doing so results in $this not being set in the called method. For example, the following does not work as I'd expect it to when $method is given as Array(ancestor_class_name, method_name) since it seems to treat it as a static method call ($this is not set within the called method): function call_user_method_array($method, $args = null) { if (!is_array($method)) $method = Array(&$this, $method); return call_user_func_array($method, $args); } Also, it is not possible to do something like the following because PHP5 dies on the assignment to $this during parsing, and not execution: function call_user_method_array($method, $args = null) { if (preg_match('/^4/', PHP_VERSION)) { $othis = &$this; $this = &$c; A::foo(); $this = &$othis; } else { ... PHP 5 code with eval here ... } } The only way to make the above example work is to eval the PHP4 version (or perhaps include it?), thus incurring a performance penalty yet again. Any suggestions how this could be more efficient? Personally, I'd like to see syntax directly in the engine to support this: $c = new C; $c->A::foo(); Though the static version "C::A::foo()" looks a little wierd.... -Brad Fisher

Ferdinand Beyer

22 years ago
On 17 Feb 2004 at 14:01, Brad Fisher wrote:
> class D { > function bar() { > $c = new C; > // I'd like to call the A::foo method of C here... > // In PHP4, I could do something like:
Why should one want to do that? Why should PHP allow this? $c is not an A, it is a C. Of course C is a special version of A, so it is compatible to A. But it still is a C. There will be a reason why C has overridden the A implementation of foo(). It is possible that the original implementation is not compatible to C, so it could be a risk to call the old method. The feature you are asking for is not available in any OO language and it even conflicts with the OOP ideology!
-- Ferdinand Beyer <fb@fbeyer.com>

Brad Fisher

22 years ago
Ferdinand Beyer wrote:
> On 17 Feb 2004 at 14:01, Brad Fisher wrote: > > > class D { > > function bar() { > > $c = new C; > > // I'd like to call the A::foo method of C here... > > // In PHP4, I could do something like: > > Why should one want to do that? Why should PHP allow this?
For one because PHP doesn't allow method overloading. If I have a method A::foo($a), and I want to change the prototype in B to B::foo($x, $y), then there is no way for me to call the original A::foo. I could use optional params to emulate this, and I do as often as possible, so it typically isn't a problem. Also if I know A::foo does something I need/require, and C::foo does the something a little different, and I also know A::foo is compatible with C, why shouldn't I be able to do this? Or are you saying I should add a method to C (like C::A_foo) which then calls A::foo internally? I'd hate to have to add such wrappers in all cases, though I suppose it could work as well. I guess I just like the flexibility. And of course it's a BC break, but it is a major version...
> $c is not an A, it is a C. Of course C is a special version of A, so it is > compatible to A. But it still is a C.
> There will be a reason why C has overridden the A implementation of > foo(). It is possible that the original implementation is not compatible > to C, so it could be a risk to call the old method.
That is potentially true, but if I intentionally coded the objects to ensure compaitibility, then where's the problem? As mentioned above, it helps with getting around the missing method overloading. In my opinion, a programming language should enable the programmer to solve problems in ways that make sense for the situation. One of the great strengths of PHP is that it allows a lot of flexiblity. While it's true that such flexibility can be abused, it can also be put to uses the original designers never anticipated.
> The feature you are asking for is not available in any OO language > and it even conflicts with the OOP ideology!
Well, I guess I'd argue that Perl supports such calls (via $obj->SomeClass::method() syntax)... Not that I'm saying that PHP should be Perl, I much prefer coding in PHP! Anyway, the "feature" was in PHP4, and people have used such constructs. Just spend a little while looking through the comments for the Class/Object functions in the PHP manual and you'll see examples that others have posted to do this for PHP4.
> -- > Ferdinand Beyer > <fb@fbeyer.com>
-Brad

Ferdinand Beyer

22 years ago
On 18 Feb 2004 at 11:02, Brad Fisher wrote:
> For one because PHP doesn't allow method overloading. If I have
a method
> A::foo($a), and I want to change the prototype in B to B::foo($x,
$y), then
> there is no way for me to call the original A::foo. I could use
optional
> params to emulate this, and I do as often as possible, so it typically
isn't a
> problem. > > Also if I know A::foo does something I need/require, and C::foo
does the
> something a little different, and I also know A::foo is compatible
with C, why
> shouldn't I be able to do this? Or are you saying I should add a
method to C
> (like C::A_foo) which then calls A::foo internally? I'd hate to have
to add
> such wrappers in all cases, though I suppose it could work as well.
Sounds very weird to me. When C overrides a method, the new one should essentially do the same as the old one. In order to emulate method overloading, I think overriding is the wrong way. What about using a variable amount of arguments: class A { function foo($a) { } } class B extends A { function foo() { $args = func_get_args(); switch (count($args)) { case 1: return A::foo($args[0]); case 2: // New implementation... } } class C extends B { function foo() { $args = func_get_args(); switch (count($args)) { case 1: return B::foo($args[0]); case 2: return B::foo($args[0], $args[1]); case 3: // ... } }
> That is potentially true, but if I intentionally coded the objects to
ensure
> compaitibility, then where's the problem? As mentioned above, it
helps with
> getting around the missing method overloading. In my opinion, a
programming
> language should enable the programmer to solve problems in
ways that make
> sense for the situation. One of the great strengths of PHP is that it
allows
> a lot of flexiblity. While it's true that such flexibility can be abused,
it
> can also be put to uses the original designers never anticipated.
Nevertheless even PHP should have some fundamental rules... Programmers should try to avoid "dirty" hacks and work arounds whereever possible.
> Well, I guess I'd argue that Perl supports such calls (via > $obj->SomeClass::method() syntax)... Not that I'm saying that
PHP should be
> Perl, I much prefer coding in PHP!
Well, I must confess that I do not know Perl (perhaps I'm to young). But I said "not available in any OO language" - neither Perl nor PHP are OO languages in my opinion (like Java, C++) so we should not use Perl as a role model here :-)
> Anyway, the "feature" was in PHP4, and people have used such
constructs. Just
> spend a little while looking through the comments for the
Class/Object
> functions in the PHP manual and you'll see examples that others
have posted to
> do this for PHP4.
This is not even a "feature". I think it was a bug which some guys used for dirty hacks and work arounds (Often the difference between a bug and a feature is very small). Fortunately this bug has been fixed in PHP5.
-- Ferdinand Beyer <fb@fbeyer.com>

Michael Walter

22 years ago
Ferdinand Beyer wrote:
> [...] > Well, I must confess that I do not know Perl (perhaps I'm to young). > But I said "not available in any OO language" - neither Perl nor PHP > are OO languages in my opinion (like Java, C++) so we should not > use Perl as a role model here :-)
What is your criterium of a language being an "OO language"? Why is it that you consider C++ as an OO language, but not PHP? Cheers, Michael

Ferdinand Beyer

22 years ago
On 18 Feb 2004 at 20:21, Michael Walter wrote:
> What is your criterium of a language being an "OO language"?
Why is it
> that you consider C++ as an OO language, but not PHP?
C++ is difficult since it includes C. Nevertheless every C++ programmer is 100% coding (and thinking) object-oriented - even basic data types like strings are usually objects. I don't think I have to explain why I consider Java an OO language, do I? PHP is a procedural language with OOP being an "add-on". The API (some extensions form the exception of the rule) is completely provided by functions. Moreover, OOP still is expensive / slower than procedural coding. I think most people in this list (especially the core people) will agree.
-- Ferdinand Beyer <fb@fbeyer.com>

Michael Walter

22 years ago
Ferdinand Beyer wrote:
> [...] > C++ is difficult since it includes C. Nevertheless every C++ > programmer is 100% coding (and thinking) object-oriented - even > basic data types like strings are usually objects. >
This is rather wrong - one of C++' main strengths is that it allows multiple paradigms of programming (procedural, object-oriented, generic, generative, functional, ...) which can also be mixed etc. Instead of ideologically focusing on only one paradigm (as Java seems/ed to try to), it provides a multitude of possibilities to tackle a problem and design a solution. You can clearly see that when you, for instance, consider strings (which you mentioned): There is no built-in string type, the standard library introduces a std::string class, but it's absolutely not mandatory to use it. In contrast, many standard library classes rather accept a pointer to a zero-terminated (w)char(_t) array instead of a string, so that you can use your own, "domain-adequate" string type which suits your needs most (note that we're not discussing whether that would be a good thing to have for a scripting language like PHP -- I'm merely pointing out that it doesn't make sense to say that "every C++ programmer ist 100% coding object-oriented". Another example would be if you consider the <algorithm> part of the standard library -- although classes are used for the "physical" architecture of that library part, it's rather the paradigm of functional programming/style which is applied there. C++ is very pragmatic in its use of paradigms, and I think that's a good thing.
> I don't think I have to explain why I consider Java an OO language, > do I?
No, but I'm still missing your definition of an "OO language". Is it a language that only provides you with the means of developing in the OO paradigm? That seems rather restrictive, and more of a problem than a feature.
> PHP is a procedural language with OOP being an "add-on".
I don't think that makes PHP inferior for using OO techniques. In contrast, consider the CLOS (Common Lisp Object System) -- you can implement it *inside* plain Common Lisp, but still it's the most sophisticated object system I'm used to (IIRC it's the only object system that satisfies certain criteria of the OMG).
> The API > (some extensions form the exception of the rule) is completely > provided by functions.
There are quite some parts of the "API" which provide an OO interface, and with PHP5 that development continues. Also, you could always wrap parts of the "API" in your own classes, if you felt that made sense. Maybe the more pragmatic approach (resembling C++' standard library) is more adequate, though (but you still have the freedom to built your own string class providing ->length() member functions etc).
> Moreover, OOP still is expensive / slower than > procedural coding.
That is rather a problem of the implementation, and changing from PHP4 to PHP5, doesn't it? Cheers, Michael

Ferdinand Beyer

22 years ago
On 18 Feb 2004 at 21:02, Michael Walter wrote:
> This is rather wrong - one of C++' main strengths is that it allows > multiple paradigms of programming (procedural, object-oriented,
generic,
> generative, functional, ...) which can also be mixed etc. Instead of > ideologically focusing on only one paradigm (as Java seems/ed to
try
> to), it provides a multitude of possibilities to tackle a problem and > design a solution.
C++ is very flexible, yes. But everytime I came in touch with C++ the convention was to "think object-oriented" and use OOP as much as possible: MFC programming, for example. Some books I read showed up examples of "bad style" C++ programming (essentially procedural, C-like code) and "the correct way" (object-oriented). I'm sure you are much more experienced than me so maybe I'm wrong here. Don't you see a difference in the role and power of OOP in PHP and other languages like C++ or Java?! But let's get back to the thread's topic: I only said that re-assigning $this is a bad. In fact it does not even matter what language I consider to be OO...
-- Ferdinand Beyer <fb@fbeyer.com>

Brad Fisher

22 years ago
Ferdinand Beyer wrote:
> On 18 Feb 2004 at 11:02, Brad Fisher wrote: > > > For one because PHP doesn't allow method overloading. If I have > a method > > A::foo($a), and I want to change the prototype in B to B::foo($x, > $y), then
...snip...
> > (like C::A_foo) which then calls A::foo internally? I'd hate to have > to add > > such wrappers in all cases, though I suppose it could work as well. > > Sounds very weird to me. When C overrides a method, the new one > should essentially do the same as the old one. > > In order to emulate method overloading, I think overriding is the > wrong way. What about using a variable amount of arguments:
Sure... like I said my previous message, I can live without it in most cases.
> > That is potentially true, but if I intentionally coded the objects to > ensure > > compaitibility, then where's the problem? As mentioned above, it > helps with > > getting around the missing method overloading. In my opinion, a > programming > > language should enable the programmer to solve problems in > ways that make > > sense for the situation. One of the great strengths of PHP is that it > allows > > a lot of flexiblity. While it's true that such flexibility can be abused, > it > > can also be put to uses the original designers never anticipated. > > Nevertheless even PHP should have some fundamental rules... > Programmers should try to avoid "dirty" hacks and work arounds > whereever possible.
So one should never use "dirty" hacks even though it appears to be the only way to solve a problem? Where would we be if people never did that? Anyway, that's what my original question was about: How can I do what I want without having to resort to dirty hacks? I agree that there should be some limits, but I don't think you should add artificial ones just because you can never imagine a valid reason to do it another way. On the other hand, I also realise one can't have it all ways at once. I have a work-around that works, at the expense of performance. I'd prefer not to have the performance loss, but at least I can live with what I have. I'm not interested in Holy wars... we have too many of those already. I just thought I'd point out a potential drawback to the new restrictions on assigning to $this.
> Well, I must confess that I do not know Perl (perhaps I'm to young). > But I said "not available in any OO language" - neither Perl nor PHP > are OO languages in my opinion (like Java, C++) so we should not > use Perl as a role model here :-)
I never advocated that. It was just an example. I'm sure I could find others as well.
> This is not even a "feature". I think it was a bug which some guys > used for dirty hacks and work arounds (Often the difference between > a bug and a feature is very small). Fortunately this bug has been > fixed in PHP5.
Well, it did work in PHP4, and not in PHP5, which makes it a BC issue. Granted in most cases no one will ever know, but when you have code which depends on that bug/feature, then it makes a big difference when it is "fixed". Not that I don't agree with the spirit of fixing it, it's just that if you remove that way of calling methods from ancestor classes, then what other way is there? I'd prefer to have official language support for that. If some people don't like it, they don't have to use it. I don't need to be able to assign to $this if I can call the method I want to call when I want to call it. I know it's there, I know it'll work with my object, I even inherit from the class which implements it, and I know it used to be possible.
> -- > Ferdinand Beyer > <fb@fbeyer.com>
-Brad