Область видимости свойства, метода или константы (начиная c PHP 7.1.0) определяют,
добавляя перед объявлением ключевое слово: public
,
protected
или private
. Доступ к свойствам
и методам класса, объявленным как public (общедоступный), разрешён отовсюду.
Модификатор protected (защищённый) разрешает доступ самому классу, наследующим его классам и родительским классам.
Модификатор private (закрытый) ограничивает область видимости так, что только класс,
где объявлен сам элемент, имеет к нему доступ.
Свойства класса могут быть определены как public, private или protected. Свойства, объявленные без явного ключевого слова области видимости, определяются как общедоступные (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->private; // Неопределён
echo $obj2->protected; // Неисправимая ошибка
$obj2->printHello(); // Выводит Public2, Protected2, Undefined
?>
Начиная с 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; // Критическая ошибка
}
}
$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
,
поскольку она всегда является более строгой.
При наследовании класса, дочерний класс может переопределить любое свойство, которое не является окончательным (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. Методы, объявленные без указания области видимости, определяются как 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, private или protected. Константы, объявленные без указания области видимости, определяются как 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
?>
Объекты, которые имеют общий тип (наследуются от одного класса), имеют доступ к элементам с модификаторами private и protected друг друга, даже если не являются одним и тем же экземпляром. Это объясняется тем, что реализация видимости элементов известна внутри этих объектов.
Пример #6 Доступ к элементам с модификатором private из объектов одного типа
<?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) "привет" Доступ к закрытому методу.