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('Правильный 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) "привет" Доступ к закрытому методу.
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;
}
?>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
?>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
?>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"
?>> 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 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
}
?>