php - tipos - Comportamiento extraño al anular métodos privados
tipos de metodos en php (3)
Considere la siguiente pieza de código:
class foo {
private function m() {
echo ''foo->m() '';
}
public function call() {
$this->m();
}
}
class bar extends foo {
private function m() {
echo ''bar->m() '';
}
public function callbar() {
$this->m();
}
}
$bar = new bar;
$bar->call();
$bar->callbar();
Ahora, cambiando la visibilidad del método m()
, obtengo:
( +
para public
, -
para private
)
Visibility bar->call() bar->callbar()
======================================================
-foo->m(), -bar->m() foo->m() bar->m()
-foo->m(), +bar->m() foo->m() bar->m()
+foo->m(), -bar->m() ERROR ERROR
+foo->m(), +bar->m() bar->m() bar->m()
( protected
parece comportarse como public
).
Esperaba que todo se comporte como lo hace cuando ambos se declaran public
. Pero aunque foo->call()
y bar->callbar()
son esencialmente lo mismo, producen resultados diferentes dependiendo de la visibilidad de m()
en foo
y bar
. ¿Por qué pasó esto?
Heredar / anular métodos privados
En PHP, los métodos (incluidos los privados) en las subclases son:
- Copiado Se mantiene el alcance de la función original.
- Sustituido ("anulado", si lo desea).
Puedes ver esto con este código:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
//not necessary; just to make explicit what''s happening
function callH() { parent::callH(); }
}
$b = new B;
$b->callH();
Ahora, si reemplaza el método privado, su nuevo alcance no será A, será B y la llamada fallará porque A::callH()
ejecuta en el alcance A
:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context ''A''
Métodos de llamada
Aquí las reglas son las siguientes:
- Busque en la tabla de métodos de la clase real del objeto (en su caso,
bar
).- Si esto da lugar a un método privado :
- Si el alcance donde se definió el método es el mismo que el alcance de la función que llama y es el mismo que la clase del objeto, utilícelo.
- De lo contrario, busque en las clases primarias un método privado con el mismo alcance que el de la función de llamada y con el mismo nombre.
- Si no se encuentra ningún método que satisfaga uno de los requisitos anteriores, falle.
- Si esto produce un método público / protegido :
- Si el alcance del método está marcado como cambiado, es posible que hayamos anulado un método privado con un método público / protegido. Entonces, en ese caso, y si, adicionalmente, hay un método con el mismo nombre que es privado tal como se define para el alcance de la función de llamada, use ese en su lugar.
- De lo contrario, utilice el método encontrado.
- Si esto da lugar a un método privado :
Conclusión
- (Ambos privados) Para
bar->call()
, el alcance de lacall
esfoo
. Llamar a$this->m()
provoca una búsqueda en la tabla de métodos de labar
param
, produciendo unabar::m()
privadabar::m()
. Sin embargo, el alcance debar::m()
es diferente del alcance de la llamada, quefoo
. El métodofoo:m()
se encuentra al atravesar la jerarquía y se usa en su lugar. - (Privado en
foo
, público enbar
) El alcance de lacall
sigue siendofoo
. La búsqueda produce unabar::m()
públicabar::m()
. Sin embargo, su ámbito está marcado como cambiado, por lo que se realiza una búsqueda en la tabla de funciones del ámbito de llamadafoo
para el métodom()
. Esto produce un método privadofoo:m()
con el mismo alcance que el alcance de llamada, por lo que se usa en su lugar. - No hay nada que ver aquí, error porque la visibilidad se redujo.
- (Ambos públicos) El alcance de la
call
sigue siendofoo
. La búsqueda produce unabar::m()
públicabar::m()
. Su alcance no está marcado como cambiado (ambos son públicos), por lo que se utilizabar::m()
.
Según el manual de PHP:
Los miembros declarados como privados solo pueden ser accedidos por la clase que define al miembro.
http://www.php.net/manual/en/language.oop5.visibility.php
EDITAR
dan resultados diferentes dependiendo de la visibilidad de m () en foo y bar. ¿Por qué pasó esto?
Si m()
en foo
es público, es anulable. Cuando este es el caso, m()
de la bar
anula m()
en foo
.
Un método privado no es anulable, ya que un método privado no es visible incluso para sus subclases. Definir un método como protegido significa que no es visible fuera de la propia clase o sus subclases.
Si tiene un método que desea usar de su clase principal pero desea que los niños puedan modificar su comportamiento y no quiere que este método esté disponible externamente, use protected
. Si desea una funcionalidad en su clase principal que no pueda ser modificada de ninguna manera por subclases, defina el método como private
.
EDITAR: para aclarar aún más, si tiene dos métodos con el mismo nombre en un padre y una subclase, y estos métodos se definen como privados, esencialmente el método de la subclase no tiene absolutamente ninguna relación con el método principal. Como se indicó, un método privado es COMPLETAMENTE INVISIBLE para la subclase.
Considera esto:
class foo {
private function m() {
echo ''foo->m() '';
}
private function z() { echo "foo->z();"; }
public function call() {
$this->m();
}
}
class bar extends foo {
private function m() {
echo ''bar->m() '';
}
public function callbar() {
$this->m();
}
public function callz()
{
$this->z();
}
}
Llamando a $bar->callz()
; producirá un ERROR, porque z no existe en la subclase en absoluto, ni siquiera como un método heredado.