php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49328 __call is not able to handle private functions in extended classes
Submitted: 2009-08-22 12:25 UTC Modified: 2009-08-23 11:54 UTC
From: rayro at gmx dot de Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 5.3.0 OS: Windows XP
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
43 - 18 = ?
Subscribe to this entry?

 
 [2009-08-22 12:25 UTC] rayro at gmx dot de
Description:
------------
While public functions will be called on the extended class, private will not. If you define a "test" function in the Foo class, the function on the extended class will be called like expected. Code is self-explaining this "problem"...

Reproduce code:
---------------
abstract class Foo {
	function __call($f, $a) {
		return call_user_func_array(array($this, $f), $a);
	}
	// uncomment this: private function test() { }
}
class Bar extends Foo {
	private function test($a) {
		var_dump($a);
	}
}
$bar = new Bar();
$bar->test('hello');

Expected result:
----------------
string(5) "hello"

Actual result:
--------------
endless loop

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-08-22 13:26 UTC] colder@php.net
Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  Due to the volume
of reports we can not explain in detail here why your report is not
a bug.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.

Foo::__call() doesn't have access to Bar::test() since its private. I can't see see the "problem" here.
 [2009-08-22 13:34 UTC] rayro at gmx dot de
Well, does Foo::__call has access to Bar::test() if the private function is defined in the abstract class?
Although, since 5.3 __call can handle private functions (place __call in Bar), this _should_ be argumented as a bug i think?
The access level should not matter while extending?
 [2009-08-22 13:47 UTC] rayro at gmx dot de
Addition:
The following __call function will also generate an endless loop, and there will be _no output_ if you uncomment Foo::test() here:

abstract class Foo {
	function __call($f, $a) {
		return $this->{$f}($a[0]);
	}
//	private function test() { }
}

class Bar extends Foo {
	private function test($a) {
		var_dump($a);
	}
}
$bar = new Bar();
$bar->test('hello');
 [2009-08-22 15:00 UTC] colder@php.net
There 2 different things here

1) conditions required to call __call
2) requirements to call private methods

So:
1) in 5.3, calling private methods from invalid context will trigger __call if available.

2) You can only call private methods of the class defining the function you're in.

in this case, __call is defined in Foo, so it cannot call private methods that are not defined in Foo. Since it cannot call it, it will trigger __call again, causing the loop.
 [2009-08-22 20:57 UTC] rayro at gmx dot de
Spending time sorting my brain, i agree with the conclusion that the context is not the same while extending a _class_, and i have to use protected methods here, but this is an _abstract class_?
It is not really intelligible for me that abstract classes gaining a context while their not instantiable, nor i'm convinced of the abstraction design over all. :/
In my opinion, the context of the abstracted class should "fully" relate to the context of the extending class. Furthermore statically definitions should render in the class scope/context, and none static calls should result in an error if called on abstracted class (as it is with class vars).
The definition of (not abstracted) functions in a abstract class should ever run in the context of the extended class, so theres no lsb necessary (e.g. __CLASS__ resolves to the classname of the extended class).
Surely a topic to talk about?

The following abstract class predefines the static property and method, but the result should vary towards a normal class definition.

Reproduce code:
---------------
abstract class Foo {
	public static $text;
	public static function test() {
		var_dump('get_called_class(): '.get_called_class());
		var_dump(static::$text);
	}
}
class Bar extends Foo { }
Foo::$text = '1';
Foo::test();
Bar::test();
Bar::$text = '2';
Foo::test();
Bar::test();

Expected result:
----------------
string(23) "get_called_class(): Foo"
string(1) "1"
string(23) "get_called_class(): Bar"
NULL
string(23) "get_called_class(): Foo"
string(1) "1"
string(23) "get_called_class(): Bar"
string(1) "2"

Actual result:
--------------
string(23) "get_called_class(): Foo"
string(1) "1"
string(23) "get_called_class(): Bar"
string(1) "1"
string(23) "get_called_class(): Foo"
string(1) "2"
string(23) "get_called_class(): Bar"
string(1) "2"
 [2009-08-22 22:51 UTC] colder@php.net
Well, with your last example you're pointing to a totally different problem composed of 3 PHP facts:

1) you cannot dynamically define static properties
2) static properties are strictly bound to the class in which they are defined
3) Access to undefined static properties fallback to their parent class.

IMO, (3) is highly arguable, but it has nothing to do with the problem at hand.
 [2009-08-23 11:54 UTC] rayro at gmx dot de
Ok for now, this is more a feature request than a bug i think... ^^
Just gimme a try to explain it, thanks :)

i talk about how class abstraction can be extended with such nice features like the __call->private issue described in this submission.

My idea was that every method/property defined not abstracted in an abstract class should be a template/pattern for the extended class, giving the full control to redefine it but acting like a default without loosing the context in the extended class.

With this change it would be possible to define private methods in abstract classes that are redefineable, except for methods defined as final.
All magic methods defined in the abstract class should only run in context of the extended class, since it does never make sense for me that they got context of the abstract class. With this change it will be possible to e.g. create magic methods or normal methods that "act like defined" in the extended class.

to 3) on one side, it is a nice feature to build a object tree having control to the higher static properties from the class, on the other side it will create negative side effects if setting this variable in the extended class. I think that this behaviour should be removed, since it makes much more sense to talk to the parent::$static, not to the self::$static... Here too, it makes sense to allow the abstract definition of statics, so the owner is requestet to redefine the variable. Same for class constant like self::VERSION, that should be redefined in the extended class...
Look at this example that uses references, where the static reference is lost, as it should work in abstraction. It is much like the last example, except the reference to a global variable here:

Reproduce code:
---------------
abstract class Foo {
	static public $test;
}
class Bar extends Foo {

}
$my_variable = 'test';
Foo::$test =& $my_variable;
var_dump(Foo::$test);
var_dump(Bar::$test);
Bar::$test = 'baz';
var_dump(Foo::$test);
var_dump(Bar::$test);

Expected result:
----------------
string(4) "test"
string(4) "test"
string(3) "baz"
string(3) "baz"

Actual result:
--------------
string(4) "test"
NULL
string(4) "test"
string(3) "baz"
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 18:01:28 2024 UTC