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ó.
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
?>
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:
set
visibility.
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.
public
, then the main visibility may be
omitted. That is, public private(set)
and private(set)
will have the same result.
private(set)
visibility
is automatically final
, and may not be redeclared in a child class.
set
visibility, not get
.
That is because a reference may be used to modify the property value.
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.
}
?>
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
?>
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
?>
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.