PHP 8.3.21 Released!

Visibilidad

La visibilidad de una propiedad, un método o (a partir de PHP 7.1.0) una constante puede ser definida prefijando su declaración con una palabra clave: public, protected, o private. Los elementos declarados como públicos son accesibles en cualquier lugar. El acceso a los elementos protegidos está limitado a la clase misma, así como a las clases que heredan de ella y a la clase padre. El acceso a los elementos privados está reservado únicamente a la clase que los ha definido.

Visibilidad de las propiedades

Las propiedades de las clases pueden ser definidas como públicas, privadas o protegidas. Las propiedades declaradas sin utilizar explícitamente una palabra clave de visibilidad serán automáticamente definidas como 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
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úblicas o protegidas, pero no las privadas
public $public = 'Public2';
protected
$protected = 'Protected2';

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

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

?>

Visibilidad Asimétrica de las Propiedades

A partir de PHP 8.4, las propiedades pueden también tener su visibilidad definida de manera asimétrica, con un campo diferente para la lectura (get) y la escritura (set). Más precisamente, la visibilidad set puede ser especificada separadamente, siempre que no sea más permisiva que la visibilidad por defecto.

Ejemplo #2 Visibilidad Asimétrica de las 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; // Error Fatal
}
}

$b = new Book('Cómo usar PHP', 'Peter H. Peterson', 2024);

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

$b->title = 'Cómo no usar PHP'; // Error Fatal
$b->author = 'Pedro H. Peterson'; // Error Fatal
$b->pubYear = 2023; // Error Fatal
?>

Hay algunas reservas concernientes a la visibilidad asimétrica:

  • Solo las propiedades tipadas pueden tener una visibilidad set separada.
  • La visibilidad set debe ser la misma que get o más restrictiva. Es decir, public protected(set) y protected protected(set) están permitidos, pero protected public(set) provocará un error de sintaxis.
  • Si una propiedad es public, entonces la visibilidad principal puede ser omitida. Es decir, public private(set) y private(set) tendrán el mismo resultado.
  • Una propiedad con una visibilidad private(set) es automáticamente final, y no puede ser redefinida en una clase hija.
  • Obtener una referencia a una propiedad sigue la visibilidad set, no get. Esto se debe a que una referencia puede ser utilizada para modificar el valor de la propiedad.
  • De igual manera, intentar escribir en una propiedad de array implica tanto una operación get como una operación set internamente, y seguirá por lo tanto la visibilidad set, ya que es siempre la más restrictiva.

Nota: Los espacios no están permitidos en la declaración de visibilidad para la modificación. private(set) es correcto. private( set ) no es correcto y resultará en un error de análisis.

Cuando una clase extiende a otra, la clase hija puede redefinir cualquier propiedad que no sea final. Al hacerlo, puede ampliar tanto la visibilidad principal como la visibilidad set, siempre que la nueva visibilidad sea la misma o más amplia que la de la clase padre. Sin embargo, tenga en cuenta que si una propiedad private es reemplazada, esto no cambia realmente la propiedad de la clase padre, sino que crea una nueva propiedad con un nombre interno diferente.

Ejemplo #3 Herencia de Propiedades Asimétricas

<?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, ya que la lectura es más amplia y la escritura es la misma.
public string $author; // OK, ya que la lectura es la misma y la escritura es más amplia.
public protected(set) int $pubYear; // Error Fatal. Las propiedades private(set) son finales.
}
?>

Visibilidad de los métodos

Los métodos de las clases pueden ser definidos como públicos, privados o protegidos. Los métodos declarados sin utilizar explícitamente una palabra clave de visibilidad serán automáticamente definidos como públicos.

Ejemplo #4 Declaración de métodos

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

// Declara un método público
public function MyPublic() { }

// Declara un método protegido
protected function MyProtected() { }

// Declara un método privado
private function MyPrivate() { }

// Este será público
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
{
// Este será público
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Error fatal
}
}

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

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 clases pueden ser definidas como públicas, privadas o protegidas. Las constantes declaradas sin una palabra clave de visibilidad explícita son definidas como públicas.

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

<?php
/**
* Declaremos MyClass
*/
class MyClass
{
// Declaremos una constante pública
public const MY_PUBLIC = 'public';

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

// Declaremos 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 MyClass();
MyClass::MY_PUBLIC; // Funciona
MyClass::MY_PROTECTED; // Error fatal
MyClass::MY_PRIVATE; // Error fatal
$myclass->foo(); // Public, Protegida y Privada funcionan

/**
* Definir MyClass2
*/
class MyClass2 extends MyClass
{
// Esto es público
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Error fatal
}
}

$myclass2 = new MyClass2;
echo
MyClass2::MY_PUBLIC; // Funciona
$myclass2->foo2(); // Public y Protegida funcionan, pero no Privada
?>

Visibilidad desde otros objetos

Los objetos de mismos tipos tienen acceso a los miembros privados y protegidos de los otros, incluso si no son la misma instancia. Esto se debe a que los detalles específicos de la implementación ya son conocidos internamente por estos objetos.

Ejemplo #6 Acceso a los miembros privados de un objeto del mismo tipo

<?php
class Test
{
private
$foo;

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

private function
bar()
{
echo
'Acceso al método privado.';
}

public function
baz(Test $other)
{
// Podemos modificar la propiedad privada:
$other->foo = 'Hola';
var_dump($other->foo);

// También podemos llamar al método privado:
$other->bar();
}
}

$test = new Test('test');

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

El resultado del ejemplo sería:

string(5) "Hola"
Acceso al método privado.
add a note

User Contributed Notes 6 notes

up
63
pgl at yoyo dot org
9 years ago
Just a quick note that it's possible to declare visibility for multiple properties at the same time, by separating them by commas.

eg:

<?php
class a
{
protected
$a, $b;

public
$c, $d;

private
$e, $f;
}
?>
up
12
jc dot flash at gmail dot com
12 years ago
if not overwritten, self::$foo in a subclass actually refers to parent's self::$foo
<?php
class one
{
protected static
$foo = "bar";
public function
change_foo($value)
{
self::$foo = $value;
}
}

class
two extends one
{
public function
tell_me()
{
echo
self::$foo;
}
}
$first = new one;
$second = new two;

$second->tell_me(); // bar
$first->change_foo("restaurant");
$second->tell_me(); // restaurant
?>
up
4
alperenberatdurmus at gmail dot com
2 years ago
Dynamic properties are "public".
<?php
class MyClass {
public function
setProperty($value) {
$this->dynamicProperty = $value;
}
}
$obj = new MyClass();
$obj->setProperty('Hello World');
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>

This usage is the same as well:
<?php
class MyClass {
}
$obj = new MyClass();
$obj->dynamicProperty = 'Hello World';
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>
up
10
Joshua Watt
17 years ago
I couldn't find this documented anywhere, but you can access protected and private member varaibles in different instance of the same class, just as you would expect

i.e.

<?php
class A
{
protected
$prot;
private
$priv;

public function
__construct($a, $b)
{
$this->prot = $a;
$this->priv = $b;
}

public function
print_other(A $other)
{
echo
$other->prot;
echo
$other->priv;
}
}

class
B extends A
{
}

$a = new A("a_protected", "a_private");
$other_a = new A("other_a_protected", "other_a_private");

$b = new B("b_protected", "ba_private");

$other_a->print_other($a); //echoes a_protected and a_private
$other_a->print_other($b); //echoes b_protected and ba_private

$b->print_other($a); //echoes a_protected and a_private
?>
up
8
bishop at php dot net
8 years ago
> Members declared protected can be accessed only within
> the class itself and by inherited classes. Members declared
> as private may only be accessed by the class that defines
> the member.

This is not strictly true. Code outside the object can get and set private and protected members:

<?php
class Sealed { private $value = 'foo'; }

$sealed = new Sealed;
var_dump($sealed); // private $value => string(3) "foo"

call_user_func(\Closure::bind(
function () use (
$sealed) { $sealed->value = 'BAZ'; },
null,
$sealed
));

var_dump($sealed); // private $value => string(3) "BAZ"

?>

The magic lay in \Closure::bind, which allows an anonymous function to bind to a particular class scope. The documentation on \Closure::bind says:

> If an object is given, the type of the object will be used
> instead. This determines the visibility of protected and
> private methods of the bound object.

So, effectively, we're adding a run-time setter to $sealed, then calling that setter. This can be elaborated to generic functions that can force set and force get object members:

<?php
function force_set($object, $property, $value) {
call_user_func(\Closure::bind(
function () use (
$object, $property, $value) {
$object->{$property} = $value;
},
null,
$object
));
}

function
force_get($object, $property) {
return
call_user_func(\Closure::bind(
function () use (
$object, $property) {
return
$object->{$property};
},
null,
$object
));
}

force_set($sealed, 'value', 'quux');
var_dump(force_get($sealed, 'value')); // 'quux'

?>

You should probably not rely on this ability for production quality code, but having this ability for debugging and testing is handy.
up
3
kostya at eltexsoft dot com
3 years ago
I see we can redeclare private properties into child class
<?php
class A{
private
int $private_prop = 4;
protected
int $protected_prop = 8;
}

class
B extends A{
private
int $private_prop = 7; // we can redeclare private property!!!
public function printAll() {
echo
$this->private_prop;
echo
$this->protected_prop;
}
}

$b = new B;
$b->printAll(); // show 78
}
?>
To Top