PHP 8.4.2 Released!

Область видимості

Області видимості властивостей, методів або (починаючи з PHP 7.1.0) констант можуть визначатись через ключові слова public (загальнодоступно), protected (захищено) чи private (закрито). Доступ до членів класу, оголошених як загальнодоступні, дозволено всюди. Члени класу, оголошені як захищені, доступні лише всередині самого класу, а також через класи-нащадки та батьківські класи. Члени класу, оголошені як закриті можуть бути доступні лише в класі, де вони визначені.

Видимість властивості

Властивості класу можуть оголошуватись як загальнодоступні, захищені чи закриті. Властивість, оголошена без ключового слова видимості, вважається загальнодоступною.

Приклад #1 Оголошення властивості

<?php
/**
* Визначення MyClass
*/
class MyClass
{
public
$public = 'Загальнодоступна';
protected
$protected = 'Захищена';
private
$private = 'Закрита';

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

$obj = new MyClass();
echo
$obj->public; // Працює
echo $obj->protected; // Фатальна помилка
echo $obj->private; // Фатальна помилка
$obj->printHello(); // Виводить Загальнодоступна, Захищена та Закрита


/**
* Визначення MyClass2
*/
class MyClass2 extends MyClass
{
// Ми можемо перевизначити public- та protected-властивості, але не private
public $public = 'Public2';
protected
$protected = 'Захищена2';

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

$obj2 = new MyClass2();
echo
$obj2->public; // Працює
echo $obj2->protected; // Фатальна помилка
echo $obj2->private; // Невизначено
$obj2->printHello(); // Виводить Загальнодоступна2, Захищена2, Закрита

?>

Асиметрична видимість властивості

Починаючи з PHP 8.4, властивості можуть мати асиметричну видимість, з різними областями видимості для читання (get) та запису (set). Зокрема видимість set можна вказати окремо, якщо та не є ширшою за типову.

Приклад #2 Асиметрична видимість властивості

<?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; // Працює
$this->pubYear = $year; // Fatal Error
}
}

$b = new Book('Програмування на PHP', 'Петро Петрук', 2024);

echo
$b->title; // Працює
echo $b->author; // Працює
echo $b->pubYear; // Fatal Error

$b->title = 'Як не програмувати на PHP'; // Fatal Error
$b->author = 'Павло Петрук'; // Fatal Error
$b->pubYear = 2023; // Fatal Error
?>

Є кілька застережень щодо асиметричної видимості:

  • Тільки властивості з вказаним типом можуть мати окрему видимість set.
  • Видимість set має бути однаковою з get або вужчою. Тобто код public protected(set) та protected protected(set) працюватиме, але protected public(set) викличе помилку синтаксису.
  • Якщо властивість є загальнодоступною (public), то основну видимість можна не оголошувати. Тобто, код public private(set) і private(set) працюватиме однаково.
  • Властивість з видимістю private(set) автоматично є фінальною (final), та не може бути змінена в класі-нащадку.
  • На отримання посилання впливає видимість set, а не get. Це тому, що посилання може бути використано, щоб змінити значення властивості.
  • Відповідно, запис до властивості-масиву включає в себе як get-, так і set-операції, а отже до уваги братиметься видимість set, як більш обмежувальна.

Клас-нащадок може перевизначати властивість батьківського класу, якщо та не фінальна (final). Таким чином він може розширити основну видимість, або видимість set, за умови, що нова видимість є такою самою або ширшою, ніж в батьківського класу. Слід зважати на те, що якщо закрита (private) властивість перезаписується, то це не впливає на батьківську властивість, а створює нову властивість з іншою внутрішньою назвою.

Приклад #3 Асиметричне наслідування властивості

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

class
SpecialBook extends Book
{
public protected(
set) $title; // Все гаразд. Видимість читання ширша, а запису така сама.
public string $author; // Все гаразд. Видимість така сама, а читання ширша.
public protected(set) int $pubYear; // Фатальна помилка. властивість з видимістю private(set) є фінальною.
}
?>

Видимість метода

Методи класу можуть бути визначені як public, private чи protected. Метод, оголошений без ключового слова видимості, вважається загальнодоступним.

Приклад #4 Оголошення метода

<?php
/**
* Визначення MyClass
*/
class MyClass
{
// Оголошення загальнодоступного конструктора
public function __construct() { }

// Оголошення загальнодоступного методу
public function MyPublic() { }

// Оголошення захищеного методу
protected function MyProtected() { }

// Оголошення закритого методу
private function MyPrivate() { }

// Таке оголошення метода буде оброблятись як public метод
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}

$myclass = new MyClass;
$myclass->MyPublic(); // Працює
$myclass->MyProtected(); // Фатальна помилка
$myclass->MyPrivate(); // Фатальна помилка
$myclass->Foo(); // Працюють Public, Protected та Private методи


/**
* Визначення MyClass2
*/
class MyClass2 extends MyClass
{
// Таке оголошення методу буде оброблятись як public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Фатальна помилка
}
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Працює
$myclass2->Foo2(); // Працюють методи Public та Protected, але не 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
?>

Видимість констант

Починаючи з PHP 7.1.0, константи класу можуть оголошуватись як public, private чи protected. Константа, оголошена без ключового слова видимості, вважається загальнодоступною.

Приклад #5 Оголошення констант, починаючи з PHP 7.1.0

<?php
/**
* Define MyClass
*/
class MyClass
{
// Оголошення загальнодоступної константи
public const MY_PUBLIC = 'public';

// Оголошення захищеної константи
protected const MY_PROTECTED = 'protected';

// Оголошення закритої константи
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; // Працює
MyClass::MY_PROTECTED; // Фатальна помилка
MyClass::MY_PRIVATE; // Фатальна помилка
$myclass->foo(); // Працюють Public, Protected та Private


/**
* Визначення MyClass2
*/
class MyClass2 extends MyClass
{
// Це публічний метод
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Фатальна помилка
}
}

$myclass2 = new MyClass2;
echo
MyClass2::MY_PUBLIC; // Працює
$myclass2->foo2(); // Працюють константи Public та Protected, але не Private
?>

Видимість з інших об'єктів

Однотипні об'єкти мають доступ до методів та властивостей один одного, що оголошені як private або protected, навіть якщо ці об'єкти не є одним і тим же примірником. Це пояснюється тим, що впровадження механізму видимості відбувається відносно класів цих об'єктів, а не відносно самих об'єктів.

Приклад #6 Доступ до закритих членів класу, що мають однаковий тип об'єкта

<?php
class Test
{
private
$foo;

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

private function
bar()
{
echo
'Доступ до закритого методу.';
}

public function
baz(Test $other)
{
// Ми можемо змінити закриту властивість:
$other->foo = 'привіт';
var_dump($other->foo);

// Ми також можемо викликати закритий метод:
$other->bar();
}
}

$test = new Test('test');

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

Поданий вище приклад виведе:

string(12) "привіт"
Доступ до закритого методу.
add a note

User Contributed Notes 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