2012-06-17 4 views
3

Перейди через ниже приведенный код это от PHP ManualПозднее статическое связывание в PHP 5.3

<?php 
class A { 
    private function foo() { 
     echo "success!\n"; 
    } 
    public function test() { 
     $this->foo(); 
     static::foo(); 
    } 
} 

class B extends A { 
    /* foo() will be copied to B, hence its scope will still be A and 
    * the call be successful */ 
} 

class C extends A { 
    private function foo() { 
     /* original method is replaced; the scope of the new one is C */ 
    } 
} 

$b = new B(); 
$b->test(); 
$c = new C(); 
$c->test(); //fails 
?> 

может любой объяснить, что именно здесь происходит?

Каким образом foo() будет скопирован в B?

+0

'foo()' не будет скопирован, он унаследован родителем 'A'. Я не понимаю вашу конкретную проблему. Конечно, это терпит неудачу, потому что вы сделали свой метод 'foo' в' C' частным. Сделайте его общедоступным, и он будет работать. –

+0

Каким образом foo наследуется? ... так как это частный метод ... это пример из руководства php –

+0

Да, это просто не доступно. –

ответ

2

foo не копируется в B как таковой (он унаследован, но невидим, см. Комментарий Гордона ниже). B наследует A->foo, который вызывает A->test. Чтобы продемонстрировать, посмотрите на то, что происходит, когда вы echo __CLASS__ изнутри test и foo (и удалить static::foo() вызов, который вызывает ошибку):

class A { 
    private function foo() { 
     echo __CLASS__."->foo\n"; 
     echo "success!\n"; 
    } 
    public function test() { 
     echo __CLASS__."->test\n"; 
     $this->foo(); 
    } 
} 

Выход:

A->test 
A->foo 
success! 
A->test 
A->foo 
success! 

Это один из основы наследования, поскольку он относится к information hiding/encapsulation. Это позволяет делать такие вещи:

class ListOfThings { 
    // internal structure (top secret!) 
    private $_list = array(); 

    // add item to list 
    public function add($item) { 
     $this->_list[] = $item; 
    } 

    // return number of items in list 
    public function count() { 
     return count($this->_list); 
    } 
} 

class ListOfStates extends ListOfThings { 

    // do we have all 50 states? 
    public function allStatesListed() { 
     return $this->count() === 50; 
    } 

    // try to access internal structure of ListOfThings 
    public function accessInternalStructure() { 
     sort($this->_list); 
    } 
} 

$states = new ListOfStates; 
$states->add('ME'); 
$states->add('NH'); 
$states->add('VT'); 
$states->add('RI'); 
$states->add('CT'); 
var_dump($states->count()); 
var_dump($states->allStatesListed()); 
$states->accessInternalStructure(); 

Выход:

int(5) 
bool(false) 

Warning: sort() expects parameter 1 to be array, null given... 

Как вы можете видеть, ListOfStates может использовать всю общественную функциональность в ListOfThings, даже если эти функции все зависят от частная переменная $_list. При этом ListOfStates не может непосредственно манипулировать $_list; он может действовать только на $_list через публичные функции, определенные в ListOfThings.

Просмотрите страницу Visibility в документации PHP для получения более подробной информации о таких вещах.

+0

foo - это частный метод ... и по видимости частный метод не наследуется дочерним классом –

+1

@Poonam * * * унаследован. Но это не видно, поэтому вы не можете получить к нему доступ. См. Http://codepad.viper-7.com/B7OWyr – Gordon

+0

@Gordon. Это не очень хороший пример, потому что вы используете переменную, а не функцию. Переменные содержат данные, которые обязательно должны быть привязаны к экземпляру, поэтому есть разница. – Niko

4

Я помню, почему поздняя статическая привязка полезна. К сожалению, пример php.net плохо объяснен. Смотрите этот (модифицированный) пример:

<?php 
class A { 
    private function foo() { 
     echo __CLASS__; 
    } 
    public function test() { 
     $this->foo(); 
     static::foo(); 
    } 
} 

class B extends A { 
    public function foo() 
    { 
     echo __CLASS__; 
    } 
} 

$b = new B(); 
$b->test(); 
?> 

Если запустить приведенный выше код, вы увидите, что это работает, и вы получите AB. Зачем?

  • Это работает, потому что test() является публичным геттером для foo(), так что это не имеет значения, если вы звоните test() из объекта типа A или от объекта типа B, который наследуется от A, потому что test() всегда будут имеют доступ к частным членам класса, в котором он определен.
  • В первом случае $this->foo(); всегда будет звонить A::foo(), потому что привязка выполняется локально, внутри области A, что иногда является нежелательным. См. Этот комментарий: http://www.php.net/manual/en/language.oop5.late-static-bindings.php#83502
  • Во втором случае static::foo(); инструктирует переводчика, чтобы определить тип $b и посмотреть, в каком классе можно найти foo(). В этом случае B::foo() рассматривается как метод переопределения для A::foo(), поэтому, в принципе, если существует B::foo(), он будет вызываться, в противном случае интерпретатор будет искать A::foo().
  • Постарайтесь отметить B::foo() как конфиденциальный и запустите пример, который я предоставил, чтобы узнать, что произойдет. Я думаю, что этот тест и мои фразы выше прояснят вопрос для вас;)

Кроме того, я принимаю любые комментарии к моим вышеуказанным пунктам, потому что я не использовал PHP в течение долгого времени.

+0

Спойлер для моего вопроса: сначала может показаться немного странным, что он попытается получить доступ к B :: foo() с точки зрения A (не забывайте, что 'test()' определяется в ' A'), но в противном случае он прерывает инкапсуляцию, поэтому 'B :: foo()' должен быть общедоступным, чтобы работать как метод переопределения для 'A :: foo()'. –

+1

Второй пункт в вашем списке применим только для этого примера, а не в целом. Если A :: foo() объявлен общедоступным или защищенным, выход будет «BB». Это связано с тем, что частные функции не могут быть перезаписаны. – Niko

+0

Ну, да. Надеюсь, мой пример не предполагал такой ошибки :) Кстати, спасибо за редактирование. –

Смежные вопросы