Visibilidad

La visibilidad de una propiedad, un método o (a partir de PHP 7.1.0) una constante se puede definir anteponiendo a su declaración una de las palabras reservadas public, protected o private. A los miembros de clase declarados como 'public' se puede acceder desde donde sea; a los miembros declarados como 'protected', solo desde la misma clase, mediante clases heredadas o desde la clase padre. A los miembros declarados como 'private' únicamente se puede acceder desde la clase que los definió.

Visibilidad de propiedades

Las propiedades de clases deben ser definidas como 'public', 'private' o 'protected'. Las propiedades declaradas sin especificar explícitamente una visibilidad se consideran públicas.

Ejemplo #1 Declaración de propiedades

<?php
/**
* Definición de MyClass
*/
class MyClass
{
public
$public = 'Public';
protected
$protected = 'Protected';
private
$private = 'Private';

function
printHello()
{
echo
$this->public;
echo
$this->protected;
echo
$this->private;
}
}

$obj = new MyClass();
echo
$obj->public; // Funciona bien
echo $obj->protected; // Error Fatal
echo $obj->private; // Error Fatal
$obj->printHello(); // Muestra Public, Protected y Private


/**
* Definición de MyClass2
*/
class MyClass2 extends MyClass
{
// Se pueden redeclarar las propiedades pública y protegida, pero no la privada
public $public = 'Public2';
protected
$protected = 'Protected2';

function
printHello()
{
echo
$this->public;
echo
$this->protected;
echo
$this->private;
}
}

$obj2 = new MyClass2();
echo
$obj2->public; // Funciona bien
echo $obj2->protected; // Error Fatal
echo $obj2->private; // Undefined
$obj2->printHello(); // Muestra Public2, Protected2, Undefined

?>

Visibilidad asimétrica de propiedades

A partir de PHP 8.4, las propiedades también pueden tener su visibilidad configurada de manera asimétrica, con un alcance diferente para lectura (get) y escritura (set). Específicamente, la visibilidad de set puede especificarse por separado, siempre que no sea más permisiva que la visibilidad predeterminada.

Ejemplo #2 Visibilidad asimétrica de propiedades

<?php
class Book
{
public function
__construct(
public private(
set) string $title,
public protected(
set) string $author,
protected private(
set) int $pubYear,
) {}
}

class
SpecialBook extends Book
{
public function
update(string $author, int $year): void
{
$this->author = $author; // OK
$this->pubYear = $year; // Fatal Error
}
}

$b = new Book('How to PHP', 'Peter H. Peterson', 2024);

echo
$b->title; // Works
echo $b->author; // Works
echo $b->pubYear; // Fatal Error

$b->title = 'How not to PHP'; // Fatal Error
$b->author = 'Pedro H. Peterson'; // Fatal Error
$b->pubYear = 2023; // Fatal Error
?>

There are a few caveats regarding asymmetric visibility:

  • Only typed properties may have a separate set visibility.
  • The set visibility must be the same as get or more restrictive. That is, public protected(set) and protected protected(set) are allowed, but protected public(set) will cause a syntax error.
  • If a property is public, then the main visibility may be omitted. That is, public private(set) and private(set) will have the same result.
  • A property with private(set) visibility is automatically final, and may not be redeclared in a child class.
  • Obtaining a reference to a property follows set visibility, not get. That is because a reference may be used to modify the property value.
  • Similarly, trying to write to an array property involves both a get and set operation internally, and therefore will follow the set visibility, as that is always the more restrictive.

When a class extends another, the child class may redefine any property that is not final. When doing so, it may widen either the main visibility or the set visibility, provided that the new visibility is the same or wider than the parent class. However, be aware that if a private property is overridden, it does not actually change the parent's property but creates a new property with a different internal name.

Ejemplo #3 Asymmetric Property inheritance

<?php
class Book
{
protected
string $title;
public protected(
set) string $author;
protected private(
set) int $pubYear;
}

class
SpecialBook extends Book
{
public protected(
set) $title; // OK, as reading is wider and writing the same.
public string $author; // OK, as reading is the same and writing is wider.
public protected(set) int $pubYear; // Fatal Error. private(set) properties are final.
}
?>

Visibilidad de Métodos

Los métodos de clases pueden ser definidos como public, private, o protected. Aquellos declarados sin ninguna palabra clave de visibilidad explícita serán definidos como public.

Ejemplo #4 Declaración de métodos

<?php
/**
* Definición de MyClass
*/
class MyClass
{
// Declaración de un constructor public
public function __construct() { }

// Declaración de un método public
public function MyPublic() { }

// Declaración de un método protected
protected function MyProtected() { }

// Declaración de un método private
private function MyPrivate() { }

// Esto es public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}

$myclass = new MyClass;
$myclass->MyPublic(); // Funciona
$myclass->MyProtected(); // Error Fatal
$myclass->MyPrivate(); // Error Fatal
$myclass->Foo(); // Public, Protected y Private funcionan


/**
* Definición de MyClass2
*/
class MyClass2 extends MyClass
{
// Esto es public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Error Fatal
}
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Funciona
$myclass2->Foo2(); // Public y Protected funcionan, pero Private no

class Bar
{
public function
test() {
$this->testPrivate();
$this->testPublic();
}

public function
testPublic() {
echo
"Bar::testPublic\n";
}

private function
testPrivate() {
echo
"Bar::testPrivate\n";
}
}

class
Foo extends Bar
{
public function
testPublic() {
echo
"Foo::testPublic\n";
}

private function
testPrivate() {
echo
"Foo::testPrivate\n";
}
}

$myFoo = new Foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
?>

Visibilidad de las constantes

A partir de PHP 7.1.0, las constantes de clase se pueden definir como públicas, privadas o protegidas. Las constantes declaradas sin una visibilidad explítica serán definidas como públicas.

Ejemplo #5 Declaración de constantes a partir de PHP 7.1.0

<?php
/**
* Definir MiClase
*/
class MiClase
{
// Declarar una constante pública
public const MY_PUBLIC = 'public';

// Declarar una constante protegida
protected const MY_PROTECTED = 'protected';

// Declarar una constante privada
private const MY_PRIVATE = 'private';

public function
foo()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE;
}
}

$myclass = new MiClase();
MiClase::MY_PUBLIC; // Funciona
MiClase::MY_PROTECTED; // Error fatal
MiClase::MY_PRIVATE; // Error fatal
$myclass->foo(); // Funcionan Public, Protected y Private


/**
* Definir MiClase2
*/
class MiClase2 extends MiClase
{
// Esta es pública
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Error fatal
}
}

$myclass2 = new MiClase2;
echo
MiClase2::MY_PUBLIC; // Funciona
$myclass2->foo2(); // Funcionan Public y Protected, pero no Private
?>

Visibilidad desde otros objetos

Los objetos del mismo tipo tendrán acceso a los miembros private y protected entre ellos aunque no sean de la misma instancia. Esto es porque los detalles específicos de implementación ya se conocen cuando se encuentra dentro de estos objetos.

Ejemplo #6 Accediendo a miembros private del mismo tipo de objeto

<?php
class Test
{
private
$foo;

public function
__construct($foo)
{
$this->foo = $foo;
}

private function
bar()
{
echo
'Método private accedido.';
}

public function
baz(Test $other)
{
// Se puede cambiar la propiedad private:
$other->foo = 'hola';
var_dump($other->foo);

// También se puede invocar al método private:
$other->bar();
}
}

$test = new Test('test');

$test->baz(new Test('other'));
?>

El resultado del ejemplo sería:

string(5) "hola"
Método private accedido.