PHP 8.6.0 Beta 1 is available for testing

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

Область видимости свойства, метода или начиная c PHP 7.1.0 константы определяют путём добавления перед объявлением ключевого слова: public, protected или private. Доступ к общедоступным членам класса, — которые объявили с ключевым словом public, — разрешается из любой области видимости. Доступ к защищённым членам класса, которые объявили с ключевым словом protected, — возможен только внутри самого класса, и в производных классах-наследниках или родительских классах. Доступ к закрытым членам класса, — которые объявили с ключевым словом private, — открывается только для самого класса, в котором их определили.

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

Свойства класса разрешается определять как открытые — public, защищённые — protected или закрытые — private. Свойства, которые объявили без модификатора области видимости, определяются как открытые, как будто свойство объявили с ключевым словом public.

Пример #1 Объявление свойства класса

<?php
/**
 * Определение класса 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; // Работает
echo $obj->protected; // Фатальная ошибка
echo $obj->private; // Фатальная ошибка
$obj->printHello(); // Выводит 'Public', 'Protected' и 'Private'
/**
 * Определение MyClass2
 */
class MyClass2 extends MyClass
{
 // Разрешается переопределять открытые и защищённые свойства, но не закрытые
 public $public = 'Public2';
 protected $protected = 'Protected2';
 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(); // Выводит 'Public2', 'Protected2' и предупреждение о неопределённом свойстве

Асимметричная область видимости свойств

Начиная с PHP 8.4 свойства объекта дополнительно стали поддерживать установку асимметричной области видимости: отдельно для чтения и записи. Видимость свойства для записи определяют спецификатором (set), который указывают сразу после модификатора видимости, тогда как спецификатор (get) после модификатора видимости для чтения не указывают, а подразумевают. Спецификатором 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; // Критическая ошибка
 }
}
$b = new Book('Правильный PHP-код', 'Peter H. Peterson', 2024);
echo $b->title; // Всё хорошо
echo $b->author; // Всё хорошо
echo $b->pubYear; // Критическая ошибка
$b->title = 'Неправильный PHP-код'; // Критическая ошибка
$b->author = 'Pedro H. Peterson'; // Критическая ошибка
$b->pubYear = 2023; // Критическая ошибка

Начиная с PHP 8.5 статические свойства класса тоже стали поддерживать установку отдельной области видимости для записи через спецификатор set.

Пример #3 Asymmetric Static Property Visibility

<?php
class Manager
{
 public private(set) static int $calls = 0;
 public function doAThing(): string
 {
 self::$calls++;
 // Выполняем другую работу
 return "Произвольная строка";
 }
}
$m = new Manager();
$m->doAThing(); // Работает
echo Manager::$calls; // Работает
Manager::$calls = 5; // Фатальная ошибка

Результат выполнения приведённого примера:

1
Fatal error: Uncaught Error: Cannot modify private(set) property Manager::$calls from global scope in /some/file.php

Относительно асимметричной области видимости определили ряд условий:

  • Отдельную область видимости 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, поскольку ограничение области видимости для записи сильнее.

Замечание: Пробелы в объявлении области видимости для записи не допускаются. Правильно: private(set). Неправильно и вызовет ошибку синтаксического анализа: private( set ).

При наследовании класса дочернему классу доступно переопределение свойств родительского класса, которые не обозначили окончательными ключевым словом final. При этом дочернему классу разрешается ослабить либо основную видимость — для чтения, либо видимость set, если только новая видимость останется такой же или станет слабее, чем у родительского класса. Однако имейте в виду, что при переопределении свойства с модификатором private новая область видимости не изменяет видимость родительского свойства, а создаёт новое свойство с другим внутренним именем.

Пример #4 Наследование свойств с асимметричной областью видимости

<?php
class Book
{
 protected string $title;
 public protected(set) string $author;
 protected private(set) int $pubYear;
}
class SpecialBook extends Book
{
 public protected(set) string $title; // Всё хорошо, поскольку ограничение на чтение слабее, а на запись – такое же
 public string $author; // Всё хорошо, поскольку ограничение на чтение такое же, а на запись – слабее
 public protected(set) int $pubYear; // Критическая ошибка. Свойства с видимостью private(set) — окончательны!
}

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

Методы класса разрешается определять как открытые — public, защищённые — protected или закрытые — private. Методы, которые объявили без явного ключевого слова области видимости, определяются как открытые, как будто метод объявили с ключевым словом public.

Пример #5 Объявление метода

<?php
/**
 * Определение класса MyClass
 */
class MyClass
{
 // Объявление общедоступного конструктора
 public function __construct() {}
 // Объявление общедоступного метода
 public function MyPublic() {}
 // Объявление защищённого метода
 protected function MyProtected() {}
 // Объявление закрытого метода
 private function MyPrivate() {}
 // Это общедоступный метод
 function Foo()
 {
 $this->MyPublic();
 $this->MyProtected();
 $this->MyPrivate();
 }
}
$myclass = new MyClass();
$myclass->MyPublic(); // Работает
$myclass->MyProtected(); // Фатальная ошибка
$myclass->MyPrivate(); // Фатальная ошибка
$myclass->Foo(); // Общедоступный, защищённый и закрытый методы работают
/**
 * Определение класса MyClass2
 */
class MyClass2 extends MyClass
{
 // Это общедоступный метод
 function Foo2()
 {
 $this->MyPublic();
 $this->MyProtected();
 $this->MyPrivate(); // Фатальная ошибка
 }
}
$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Работает
$myclass2->Foo2(); // Общедоступный и защищённый методы работают, закрытый — не работает
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, защищённые — protected или закрытые — private. Константы, которые объявили без явного ключевого слова области видимости, определяются как открытые, как будто константу объявили с ключевым словом public.

Пример #6 Пример объявления констант с PHP 7.1.0

<?php
/**
 * Объявление класса 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

Видимость из других объектов

Объектам одного и того же типа доступны защищённые и закрытые члены друг друга, даже если это разные экземпляры. Это связано с тем, что внутри таких объектов уже известны конкретные детали реализации.

Пример #7 Доступ к закрытым членам объекта того же типа

<?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(6) "привет"
Доступ к закрытому методу.

Нашли ошибку?

ИнструкцияИсправлениеСообщение об ошибке
+Добавить

Примечания пользователей 6 notes

up
60
pgl at yoyo dot org
11 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
11
Joshua Watt
19 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
7
jc dot flash at gmail dot com
14 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
3
alperenberatdurmus at gmail dot com
3 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
5
bishop at php dot net
9 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
2
kostya at eltexsoft dot com
4 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
}
?>
+Добавить

AltStyle によって変換されたページ (->オリジナル) /