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;
}
?>
Область видимости свойства, метода или начиная 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
,
поскольку ограничение области видимости для записи сильнее.
При наследовании класса дочернему классу доступно переопределение свойств родительского класса,
которые не обозначили окончательными ключевым словом 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) "привет" Доступ к закрытому методу.
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;
}
?>
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"
?>
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
?>
> 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.
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
?>
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
}
?>