PHP Conference Japan 2024

Visibilidade

A visibilidade de uma propriedade ou método ou constante pode ser definida prefixando a declaração com as palavras-chave public, protected ou private. Membros de classes declarados como públicos podem ser acessados de qualquer lugar. Membros declarados como protegidos pode ser acessados na própria classe ou de classes referenciadas por herança acima. Membros declarados como privados só podem ser acessados por classes que definem esse membro.

Visibilidade de propriedades

Propriedades de classes podem ser declarados como públicas, privadas ou protegidas. Propriedades declaradas sem visibilidade são definidas como públicas.

Exemplo #1 Declaração de propriedade

<?php
/**
* Define MinhaClasse
*/
class MinhaClasse
{
public
$publica = 'Public';
protected
$protegida = 'Protected';
private
$privada = 'Private';

function
imprimeAlo()
{
echo
$this->publica;
echo
$this->protegida;
echo
$this->privada;
}
}

$obj = new MinhaClasse();
echo
$obj->publica; // Funciona
echo $obj->protegida; // Erro Fatal
echo $obj->privada; // Erro Fatal
$obj->imprimeAlo(); // Mostra Public, Protected e Private


/**
* Define MinhaClasse2
*/
class MinhaClasse2 extends MinhaClasse
{
// Nós podemos redeclarar as propriedades públicas e protegidas mas não as privadas
public $publica = 'Public2';
protected
$protegida = 'Protected2';

function
imprimeAlo()
{
echo
$this->publica;
echo
$this->protegida;
echo
$this->privada;
}
}

$obj2 = new MinhaClasse2();
echo
$obj2->publica; // Funciona
echo $obj2->protegida; // Erro fatal
echo $obj2->privada; // Não definida
$obj2->imprimeAlo(); // Mostra Public, Protected2, Undefined

?>

Visibilidade de propriedade assimétrica

A partir do PHP 8.4, as propriedades também podem ter sua visibilidade definida de forma assimétrica, com escopo diferente para leitura (get) e escrita (set). Especificamente, a visibilidade set pode ser especificada separadamente, desde que não seja mais permissiva que a visibilidade padrão.

Exemplo #2 Visibilidade de propriedade assimétrica

<?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; // Erro fatal
}
}

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

echo
$b->title; // Funciona
echo $b->author; // Funciona
echo $b->pubYear; // Erro fatal

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

Existem algumas advertências em relação à visibilidade assimétrica:

  • Somente propriedades tipadas podem ter uma visibilidade set separada.
  • A visibilidade set deve ser a mesma de get ou mais restritiva. Ou seja, public protected(set) e protected protected(set) são permitidos, mas protected public(set) causará um erro de sintaxe.
  • Se uma propriedade for public, a visibilidade principal poderá ser omitida. Ou seja, public private(set) e private(set) terão o mesmo resultado.
  • Uma propriedade com visibilidade private(set) é automaticamente final e não pode ser declarada novamente em uma classe filha.
  • A obtenção de uma referência para uma propriedade segue a visibilidade set, não get. Isso ocorre porque uma referência pode ser usada para modificar o valor da propriedade.
  • Da mesma forma, tentar escrever em uma propriedade de array envolve uma operação get e set internamente e, portanto, seguirá a visibilidade set, como é sempre a mais restritiva.

Nota: Espaços não são permitidos na declaração de visibilidade "set". private(set) está correto. private( set ) não está correto e resultará em erro de análise.

Quando uma classe estende outra, a classe filha pode redefinir qualquer propriedade que não seja final. Ao fazer isso, pode ampliar a visibilidade principal ou a visibilidade set, desde que a nova visibilidade seja igual ou mais ampla que a classe pai. Entretanto, esteja ciente de que se uma propriedade private for substituída, ela na verdade não altera a propriedade da classe pai, mas cria uma nova propriedade com um nome interno diferente.

Exemplo #3 Herança de propriedade assimétrica

<?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, pois a leitura é mais ampla e a escrita igual.
public string $author; // OK, pois a leitura é a mesma e a escrita é mais ampla.
public protected(set) int $pubYear; // Erro fatal. propriedades private(set) são finais.
}
?>

Visibilidades de métodos

Métodos de classe podem ser definidos como público, privado ou protegido. Métodos sem qualquer declaração explícita serão definidos como público.

Exemplo #4 Declaração de método

<?php
/**
* Define MinhaClasse
*/
class MinhaClasse
{
// Declara um construtor público
public function __construct() { }

// Declara um método público
public function MeuPublico() { }

// Declara um método protegido
protected function MeuProtegido() { }

// Declara um método privado
private function MeuPrivado() { }

// Esse é público
function Foo()
{
$this->MeuPublico();
$this->MeuProtegido();
$this->MeuPrivado();
}
}

$minhaclasse = new MinhaClasse;
$minhaclasse->MeuPublico(); // Funciona
$minhaclasse->MeuProtegido(); // Erro Fatal
$minhaclasse->MeuPrivado(); // Erro Fatal
$minhaclasse->Foo(); // Public, Protected e Private funcionam


/**
* Define MinhaClasse2
*/
class MinhaClasse2 extends MinhaClasse
{
// Esse é public
function Foo2()
{
$this->MeuPublico();
$this->MeuProtegido();
$this->MeuPrivado(); // Erro Fatal
}
}

$minhaclasse2 = new MinhaClasse2;
$minhaclasse2->MeuPublico(); // Funciona
$minhaclasse2->Foo2(); // Public e Protected funcionam, Private não

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
?>

Visibilidade de constantes

A partir do PHP 7.1.0, constantes de classe podem ser definidas como públicas, protegidas ou privadas. Constantes declaradas sem visibilidade explícita são definidas como públicas.

Exemplo #5 Declaração de constantes (desde o PHP 7.1.0)

<?php
/**
* Define MyClass
*/
class MyClass
{
// Declara uma constante pública
public const MY_PUBLIC = 'public';

// Declara uma constante protegida
protected const MY_PROTECTED = 'protected';

// Declara uma 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; // Erro fatal
MyClass::MY_PRIVATE; // Erro fatal
$myclass->foo(); // Todos os acessos funcionam dentro do método


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// Isto é público
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Fatal Error
}
}

$myclass2 = new MyClass2;
echo
MyClass2::MY_PUBLIC; // Funciona
$myclass2->foo2(); // Public e Protected funcionam, mas não Private
?>

Visibilidade de outros objetos

Objetos do mesmo tipo terão acesso a outros mementos privados e protegidos mesmo que não sejam da mesma instância. Isso acontece por que os detalhes específicos de implementação já são conhecidos dentro destes objetos.

Exemplo #6 Acessando membros privados entre objetos do mesmo tipo

<?php
class Test
{
private
$foo;

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

private function
bar()
{
echo
'Acessou o método privado.';
}

public function
baz(Test $other)
{
// Pode-se alterar a propriedade privada:
$other->foo = 'hello';
var_dump($other->foo);

// Pode-se chamar método privado:
$other->bar();
}
}

$test = new Test('test');

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

O exemplo acima produzirá:

string(5) "hello"
Acessou o método privado.
adicione uma nota

Notas Enviadas por Usuários (em inglês) 6 notes

up
62
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
4
alperenberatdurmus at gmail dot com
1 year 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
13
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
7
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
8
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
1
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