It is currently impossible to call a true static method from within a class method. It lets a class method call any object method statically even when not defined as a static method. The static method being called inherits the $this from the encapsulating object. This may not be a big deal with userspace objects, but it is impossible to handle for internal objects.
Within the dom extension there are a few methods which can be called both statically and off an instantiated object. Calling them statically works fine until they are called within a class method as the function either expects this to be defined (meaning an instantiated object) or it to be null (when it is called statically). Since within the class method, it magically inherits the this of the object calling it, it currently crashes.
The following is an example using simplexml to illustrate the problem (simplexml's code base is much smaller so easier to follow).
Class AClass {
function tester() {
print simplexml_element::asXML(); // segfaults as this should not be allowed to be called statically
}
}
$test = new AClass();
$test->tester();
And before someone says that the object type should be tested using zend_parse_method_parameters as a "workaround", it still wouldnt work correctly:
Class BClass extends simplexml_element {
function tester() {
print simplexml_element::asXML();
}
}
$test = new BClass("<root/>");
$test->tester();
This doesnt crash, but because the static call is being called from within an object which extends simplexml_element, it would pass and act as if $this->asXML() was called, which also is incorrect. It should be issuing the same non-static method cannot be called statically error as when it is called outside a class. A better example of why it also would be incorrect would be using a domdocument:
Class BClass extends domdocument {
var $newdoc = NULL;
function tester() {
$this->newdoc = domdocument::loadXML("<root/>");
}
}
$test = new BClass();
$test->loadXML("<root>abc</root>");
print $test->saveXML()."\n";
$test->tester();
var_dump($test->newdoc);
print $test->saveXML()."\n";
newdoc ends up as a boolean rather than a new document as $test is actually changed from the static call as it believes it is being called as $test's method and not as a static method, which should produce a new document rather than replacing the current document.
Hi,
Rob Richards wrote:
Within the dom extension there are a few methods which can be called both statically and off an instantiated object.
The PEAR base class also has this design flaw in several methods. It
has caused nothing but annoying and unfixable bugs. The only good
solution is to redesign by splitting the methods into two methods, one
static, one not. It's best to do this before the stable label is
applied, unless you like getting the same valid bugs reported over and
over with different permutations :)
Greg
From: Greg Beaver
The PEAR base class also has this design flaw in several methods. It
has caused nothing but annoying and unfixable bugs. The only good
solution is to redesign by splitting the methods into two methods, one
static, one not. It's best to do this before the stable label is
applied, unless you like getting the same valid bugs reported over and
over with different permutations :)
Say dom load were split into 2 methods (this is not something that should be
required to do). One static and one a regular public function. As shown with
the simplexml example, the regular function would still not work correctly.
The method I used in that example, is not a static method, but if called
within another class method, it is allowed to be called using the static
syntax (which is not allowed when called that way outside of a class scope),
and it also picks up the scope of the object that is is called from (which
is wrong). To top it off, behavior of the statically called method is also
affected by the type of the class calling it (also shown there).
Rob
Hi Rob,
Rob Richards wrote:
From: Greg Beaver
The PEAR base class also has this design flaw in several methods. It
has caused nothing but annoying and unfixable bugs. The only good
solution is to redesign by splitting the methods into two methods, one
static, one not. It's best to do this before the stable label is
applied, unless you like getting the same valid bugs reported over and
over with different permutations :)Say dom load were split into 2 methods (this is not something that should be
required to do). One static and one a regular public function. As shown with
the simplexml example, the regular function would still not work correctly.
The method I used in that example, is not a static method, but if called
within another class method, it is allowed to be called using the static
syntax
To quote Microsoft, "this is a feature, not a bug." :) Of course, if
someone tries to call a non-static method from another completely
unrelated class, that's just stupid. But there are cases where you
might want to call a method from a parent class 2 levels up.
class one {
function doSomething() {}
}
class two {
function doSomething() {}
}
class three {
function doSomething()
{
$a = one::doSomething();
}
}
Here, it's perfectly logical to allow :: as it is in fact not a static
method call. If PHP removes this feature, it will break a lot of code
I've written that has been working in a production environment for the
past 2 years without a single glitch.
I've also written code to emulate runtime multiple inheritance that
relies upon using a method from another class that expects certain
properties to be available in the class, and this works great.
There is a certain amount of control that PHP can enforce, but in this
case, I think it's up to the programmer's intelligence to understand how
class hierarchies work, and not write code that fails. One point you
raised that is significant to me is the crash, I don't think it's all
that good that PHP 5 crashes instead of failing with some kind of fatal
error.
Greg
To quote Microsoft, "this is a feature, not a bug." :) Of course, if
someone tries to call a non-static method from another completely
unrelated class, that's just stupid. But there are cases where you
might want to call a method from a parent class 2 levels up.
I can see calling the method from a parent as valid, but from an unrelated
class I see it as a bug.
When calling a non-static method, it is allowed which it shouldnt be and
when calling a method which can be either, it should be called as a true
static method and not as a method of this unrelated object.
The crashes can be fixed, but imo it should be fixed on the engine level
rather than writing around the engine in the extensions as this behavior is
just wrong.
Rob