Поздняя статическая привязка php и вызовы self

Я видел тему об этом, и я понимаю, как это работает. Но разве это не очень запутанно? С помощью 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" может быть полезным и безопасным?

0 ответов

Другие вопросы по тегам