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

Область видимости свойства, метода или начиная 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('How to PHP', 'Peter H. Peterson', 2024);

echo
$b->title; // Всё хорошо
echo $b->author; // Всё хорошо
echo $b->pubYear; // Критическая ошибка

$b->title = 'How not to PHP'; // Критическая ошибка
$b->author = 'Pedro H. Peterson'; // Критическая ошибка
$b->pubYear = 2023; // Критическая ошибка

?>

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

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

Пример #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, защищённые — protected или закрытые — private. Методы, которые объявили без явного ключевого слова области видимости, определяются как открытые, как будто метод объявили с ключевым словом public.

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

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

Пример #5 Пример объявления констант с 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
?>

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

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

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