可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need an inherited static function "call" to call another static function "inner" that has been overridden. I could do this with late static binding, but my host does not have php5.3 yet and so I need to work around it.
class ClassA{
static function call()
{
return self::inner();
}
static function inner(){
return "Class A";
}
}
class ClassB extends ClassA{
static function inner(){
return "Class B";
}
}
echo "<p>Class A = " . ClassA::call();
echo "<p>Class B = " . ClassB::call();
I would like the output to be:
Class A = Class A
Class B = Class B
But what it is:
Class A = Class A
Class B = Class A
My gut tells me that I should be able to write something in call() to detect what object was referenced when "call()" was, well, called. So instead of self::inner() it would so something along the lines of calledclass::inner(). Detecting the proper version of inner() to call from the original method call.
回答1:
You can use object instances rather than classes. If you want a global symbol, you can use a global variable. Since they are rather unwieldy in PHP, one trick is to wrap it in a function. Eg.:
class ClassA {
function call() {
return $this->inner();
}
function inner() {
return "Class A";
}
}
function ClassA() {
static $instance;
return $instance ? $instance : new ClassA();
}
class ClassB extends ClassA {
function inner() {
return "Class B";
}
}
function ClassB() {
static $instance;
return $instance ? $instance : new ClassB();
}
echo "<p>Class A = " . ClassA()->call();
echo "<p>Class B = " . ClassB()->call();
But a better idea might be to avoid global symbols altogether; The reason why it works well in Ruby/Rails, is that Ruby doesn't really have static state in the same way that PHP has. A class can be rebound and added to at runtime, which allows for easy extension of the framework. In PHP, classes are always final, so referring to them in application code, is a very strong degree of coupling.
回答2:
If performance is not an issue, you can use debug_backtrace() to find the called class:
$bt = debug_backtrace();
return get_class($bt[1]['object']);
http://php.net/manual/en/function.debug-backtrace.php
回答3:
Here's a quick example.
<?php
class ClassA{
public function call(){
return $this->inner();
}
static function inner(){
return "Class A";
}
static function init($class){
return new $class();
}
}
class ClassB extends ClassA{
static function inner(){
return "Class B";
}
}
echo "<p>Class A = " . ClassA::init("ClassA")->call();
echo "<p>Class B = " . ClassB::init("ClassB")->call();
?>
If you don't like passing in the class name you could add a static init function to the child class and explicitly pass it there as well. This would allow you to do something like: ClassA::init()->call() and ClassB::init()->call() but would have some minor code duplication.
回答4:
Unfortunately there is no nice way to do it (otherwise PHPers wouldn't cheer so much for that feature).
You have to pass class name. PHP < 5.3 also doesn't have nice syntax for static calls with dynamic class name which makes whole thing even uglier:
static function call($class)
{
return call_user_func(array($class,"inner"));
}
…
ClassA::call("ClassA");
ClassB::call("ClassB");
If you can change the code (and not use static
), then singletons make it more bearable. You can make helper function to ease the syntax:
X("ClassA")->call();
X("ClassB")->call();
The X function should look up, create and return instance of a class.
回答5:
Often, late static binding is needed when a child calls back a method of the parent who, in turn, calls an abstract static method of the child. I was in such a postition, and could not use static:: as my PHP was not version 5.3 (or later). The following accomplished late static binding by way of debug_backtrace().
abstract class ParentClass
{
static function parent_method()
{
$child_class_str = self::get_child_class();
eval("\$r = ".$child_class_str."::abstract_static();");
return $r;
}// CHILD MUST OVERRIDE TO PUT ITSELF INTO TRACE
protected abstract static function abstract_static(); // THIS NEEDS LATE STATIC BINDING
private static function get_child_class()
{
$backtrace = debug_backtrace();
$num = count($backtrace);
for($i = 0; $i < $num; $i++)
{
if($backtrace[$i]["class"] !== __CLASS__)
return $backtrace[$i]["class"];
}
return null;
}
}
class ChildClass extends ParentClass
{
static function parent_method(){ return parent::parent_method(); }
protected static function abstract_static()
{
return __METHOD__."()";
}// From ParentClass
}
print "The call was: ". ChildClass::parent_method();
回答6:
Since you cant use static:: , or get_called_class(), or __callStatic,
you'll have to call the inner() function with some indication with
the called class (as mentioned in the other answers). An instance
of the called class would be fine.
You can add "Pseudo Static" methods to mimick every static method
you need to overwrite. This doubles your code, but
by doing this as below, I hope the code is easier to upgrade once
php5.3 comes around: just remove all the ps methods and all the
functions referencing them (and change "self" to "static" where needed ..)
class ClassA{
static function call()
{
return self::inner();
}
static function inner(){
return "Class A";
}
public function _ps_call()
{
return $this->_ps_inner();
}
}
class ClassB extends ClassA{
public static function getInstance()
{
return new self();
}
public static function call()
{
return self::getInstance()->_ps_call();
}
static function inner()
{
return "Class B";
}
public function _ps_inner()
{
return self::inner();
}
}
echo "<p>Class A = " . ClassA::call();
echo "<p>Class B = " . ClassB::call();