я видел тема об этом и я понимаю как это работает.
Но разве это не очень запутанно? С помощью self::someMethod()
мы намерены остановить полиморфное поведение и ожидаем, что больше не будем зависеть от возможных переопределений в дочерних классах. Но этот пример (довольно неестественный, но все же) показывает, что это ожидание может привести к неожиданным ошибкам. Предположим, у нас есть иерархия классов Shape-> Rectangle-> Square, и наряду с другими вещами есть статические методы для вычисления областей:
abstract class Shape {
//"abstract" `area` function which should be overriden by children
public static function area(array $args) {
throw new Exception("Child must override it");
}
//We also provide checking for arguments number.
static function areaNumArgs(){
throw new Exception("Child must override it");
}
final static function checkArgsNumber (array $args) {
return count($args) == static::areaNumArgs();
//With 'static' we use late static binding
}
}
class Rectangle extends Shape {
static function areaNumArgs(){return 2;} //Arguments are lengths of sides
static function area(array $args) {
//Algorithm is stupid, but I want to illustrate result of 'self'
$n = self::areaNumArgs();
/*With 'self' we DON'T use polymorphism so child can
override areaNumArgs() and still use this method.
That's the point of 'self' instead of 'static', right?*/
$area = 1;
for($i = 0; $i<$n; $i++) $area *= $args[$i];
return $area;
}
//Let's wrap call to 'area' with checking for arguments number
static function areaWithArgsNumberCheck (array $args)
{
if(self::checkArgsNumber($args))
return self::area($args);
//We use 'self' everywhere, so again no problem with
//possible children overrides?
else
return false;
}
}
var_dump(Rectangle::areaWithArgsNumberCheck(array(3,2)));
//output is 6, everything OK.
//Now we have Square class.
class Square extends Rectangle {
static function areaNumArgs(){return 1;}
//For square we only need one side length
static function area(array $args) {
//We want to reuse Rectangle::area. As we used 'self::areaNumArgs',
//there is no problem that this method is overriden.
return parent::area(array($args[0], $args[0]));
}
static function areaAnotherVersion(array $args) {
//But what if we want to reuse Rectangle::areaWithArgsNumberCheck?
//After all, there we also used only 'self', right?
return parent::areaWithArgsNumberCheck(array($args[0], $args[0]));
}
}
var_dump(Square::area(array(3))); //Result is 9, again everything OK.
var_dump(Square::areaAnotherVersion(array(3))); //Oops, result is FALSE
Это потому, что хотя Rectangle::areaWithArgsNumberCheck
использовал только «я», он сделал вызов Shape::checkArgsNumber
, который использовал static::areaNumArgs
, И этот последний звонок был решен Square
класс, потому что self::
вызов перенаправлен статической привязкой.
Проблема легко решается с помощью имени класса (Rectangle::
) вместо self::
Так что для меня это выглядит, если есть немного static
вызовы, то все вызовы в этой иерархии классов должны быть сделаны static
использовать имена классов явно. Вы просто никогда не знаете где self::
Вызов будет переадресован, и какие потенциально переопределенные методы будут использованы. Прав ли я в этом или есть сценарии, в которых перенаправление статической привязки с помощью «self» может быть полезным и безопасным?
Задача ещё не решена.
Других решений пока нет …