Компонент PropertyAccess
Дата оновлення перекладу 2024-05-03
Компонент PropertyAccess
Компонент PropertyAccess надає функцію для читання та запису з/в обʼєкт або масив, використовуючи нотацію рядку.
Установка
1
$ composer require symfony/property-access
Note
Якщо ви встановлюєте цей компонент поза додатком Symfony, вам потрібно підключити
файл vendor/autoload.php
у вашому коді для включення механізму автозавантаження
класів, наданих Composer. Детальніше можна прочитати у цій статті.
Використання
Вхідна точка цього компонента - це фабрика createPropertyAccessor(). Ця фабрика створить новий екземпляр класу PropertyAccessor з конфігурацією за замовчуванням:
1 2 3
use Symfony\Component\PropertyAccess\PropertyAccess;
$propertyAccessor = PropertyAccess::createPropertyAccessor();
Читання з масивів
Ви можете прочитати масив за допомогою методу getValue(). Це робиться з використанням нотацій індексу, які використовуються в PHP:
1 2 3 4 5 6 7
// ...
$person = [
'first_name' => 'Wouter',
];
var_dump($propertyAccessor->getValue($person, '[first_name]')); // 'Wouter'
var_dump($propertyAccessor->getValue($person, '[age]')); // null
Як ви можете побачити, метод поверне null
, якщо індекс не існує. Але ви
можете змінити цю поведінку за допомогою методу
enableExceptionOnInvalidIndex():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// ...
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableExceptionOnInvalidIndex()
->getPropertyAccessor();
$person = [
'first_name' => 'Wouter',
];
// замість повернення null, тепер код викликає виключення типу
// Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
$value = $propertyAccessor->getValue($person, '[age]');
// Ви можете уникнути цього виключення, додавши оператор nullsafe
$value = $propertyAccessor->getValue($person, '[age?]');
Ви також можете використати багатовимірні масиви:
1 2 3 4 5 6 7 8 9 10 11 12
// ...
$persons = [
[
'first_name' => 'Wouter',
],
[
'first_name' => 'Ryan',
],
];
var_dump($propertyAccessor->getValue($persons, '[0][first_name]')); // 'Wouter'
var_dump($propertyAccessor->getValue($persons, '[1][first_name]')); // 'Ryan'
Tip
Якщо ключ масиву містить крапку .
або ліву квадратну дужку [
,
ви повинні екранувати ці символи зворотним слешем. У вищенаведеному прикладі,
якщо ключем масиву було first.name
замість first_name
, то слід
отримати доступ до його значення наступним чином:
1 2
var_dump($propertyAccessor->getValue($persons, '[0][first\.name]')); // 'Wouter'
var_dump($propertyAccessor->getValue($persons, '[1][first\.name]')); // 'Ryan'
Праві квадратні дужки ]
не потребують екранування в масиві ключів.
Читання з обʼєктів
Метод getValue()
дуже великий, і ви можете побачити всі його функції
при роботі з обʼєктами.
Доступ до публічних властивостей
Щоб зчитувати з властивостей, використайте нотацію "dot":
1 2 3 4 5 6 7 8 9 10 11
// ...
$person = new Person();
$person->firstName = 'Wouter';
var_dump($propertyAccessor->getValue($person, 'firstName')); // 'Wouter'
$child = new Person();
$child->firstName = 'Bar';
$person->children = [$child];
var_dump($propertyAccessor->getValue($person, 'children[0].firstName')); // 'Bar'
Caution
Доступ до публічних властивостей - це остання опція, використовувана PropertyAccessor
.
Вона намагається отримати доступ до значення, використовуючи методи, описані нижче, до
використання властивості напряму. Наприклад, якщо у вас є публічна властивість, яка має
метод гетера, то вона використовуватиме гетер.
Використання гетерів
Метод getValue()
також підтримує читання, використовуючи гетери. Цей метод буде створено,
використовуючи загальні угоди про іменування для гетерів. Він перетворює імʼя властивості на
camelCase (first_name
стає FirstName
) і додає до нього префікс get
. Тому сам метод
стає getFirstName()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// ...
class Person
{
private string $firstName = 'Wouter';
public function getFirstName(): string
{
return $this->firstName;
}
}
$person = new Person();
var_dump($propertyAccessor->getValue($person, 'first_name')); // 'Wouter'
Використання хасерів/ісерів
На цьому все не завершується. Якщо гетер не знайдено, процес доступу шукатиме ісер або хасер. Цей метод створюється, використовуючи той же спосіб, що і гетери, що означає, що ви можете зробити щось на кшталт цього:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ...
class Person
{
private bool $author = true;
private array $children = [];
public function isAuthor(): bool
{
return $this->author;
}
public function hasChildren(): bool
{
return 0 !== count($this->children);
}
}
$person = new Person();
if ($propertyAccessor->getValue($person, 'author')) {
var_dump('This person is an author');
}
if ($propertyAccessor->getValue($person, 'children')) {
var_dump('This person has children');
}
Це призведе до: This person is an author
Доступ до неіcнуючого шляху властивості
За замовчуванням, якщо шлях властивості, переданий getValue() не існує, викликається NoSuchPropertyException. Ви можете змінити цю поведінку, використовуючи метод disableExceptionOnInvalidPropertyPath():
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// ...
class Person
{
public string $name;
}
$person = new Person();
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->disableExceptionOnInvalidPropertyPath()
->getPropertyAccessor();
// замість виклику виключення, наступний код поверне null
$value = $propertyAccessor->getValue($person, 'birthday');
Доступ до шляхів nullable властивостей
Розгляньте наступний PHP-код:
1 2 3 4 5 6 7 8 9 10 11 12
class Person
{
}
class Comment
{
public ?Person $person = null;
public string $message;
}
$comment = new Comment();
$comment->message = 'test';
Враховуючи, що $person
може бути null, граф об'єктів на кшталт comment.person.profile
викличе виключення, коли властивість $person
буде null
. Вирішення проблеми -
позначити всі властивості, що можуть бути null, оператором nullsafe (?
):
1 2 3 4 5 6 7
// Цей код викликає виключення типу
// Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
var_dump($propertyAccessor->getValue($comment, 'person.firstname'));
// Якщо властивість, позначена оператором nullsafe є null, вираз більше не оцінюється,
// а null повертається негайно, без виклику виключення
var_dump($propertyAccessor->getValue($comment, 'person?.firstname')); // null
Магічний метод __get()
Метод getValue()
може також використовувати магічний метод __get()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// ...
class Person
{
private array $children = [
'Wouter' => [...],
];
public function __get($id): mixed
{
return $this->children[$id];
}
}
$person = new Person();
var_dump($propertyAccessor->getValue($person, 'Wouter')); // [...]
Note
Підтримка методу __get()
включена за замовчуванням. Див.
Включення інших функцій , якщо ви хочете її відключити.
Магічний метод __get()
Метод getValue()
може також використовувати магічний метод __get()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// ...
class Person
{
private array $children = [
'Wouter' => [...],
];
public function __get($id): mixed
{
return $this->children[$id];
}
}
$person = new Person();
var_dump($propertyAccessor->getValue($person, 'Wouter')); // [...]
Note
Підтримка методу __get()
включена за замовчуванням. Див.
Включення інших функцій, якщо ви хочете її відключити.
Магічний метод __call()
Нарешті, getValue()
може використовувати магічний метод __call()
, але вам потрібно
включити цю функцію, використовуючи PropertyAccessorBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// ...
class Person
{
private array $children = [
'wouter' => [...],
];
public function __call($name, $args): mixed
{
$property = lcfirst(substr($name, 3));
if ('get' === substr($name, 0, 3)) {
return $this->children[$property] ?? null;
} elseif ('set' === substr($name, 0, 3)) {
$value = 1 == count($args) ? $args[0] : null;
$this->children[$property] = $value;
}
}
}
$person = new Person();
// включає магічний метод PHP __call()
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableMagicCall()
->getPropertyAccessor();
var_dump($propertyAccessor->getValue($person, 'wouter')); // [...]
Caution
Функція __call()
відключена за замовчуванням, ви можете включити її, викликавши
enableMagicCall()
див. Включення інших функцій.
Запис у масиви
Клас PropertyAccessor
може робити більше, ніж просто читати масиви, він може
також писати у масив. Цього можна досягти, використовуючи метод
setValue():
1 2 3 4 5 6 7 8
// ...
$person = [];
$propertyAccessor->setValue($person, '[first_name]', 'Wouter');
var_dump($propertyAccessor->getValue($person, '[first_name]')); // 'Wouter'
// або
// var_dump($person['first_name']); // 'Wouter'
Запис в обʼєкти
Метод setValue()
має такі ж функції, як метод getValue()
. Ви можете використати
сетери, магічний метод __set()
або властивості для встановлення значень:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// ...
class Person
{
public string $firstName;
private string $lastName;
private array $children = [];
public function setLastName($name): void
{
$this->lastName = $name;
}
public function getLastName(): string
{
return $this->lastName;
}
public function getChildren(): array
{
return $this->children;
}
public function __set($property, $value): void
{
$this->$property = $value;
}
}
$person = new Person();
$propertyAccessor->setValue($person, 'firstName', 'Wouter');
$propertyAccessor->setValue($person, 'lastName', 'de Jong'); // setLastName is called
$propertyAccessor->setValue($person, 'children', [new Person()]); // __set is called
var_dump($person->firstName); // 'Wouter'
var_dump($person->getLastName()); // 'de Jong'
var_dump($person->getChildren()); // [Person()];
Ви можете також використати __call()
, щоб встановлювати значення, але вам
потрібно включити функцію, див. Включення інших функцій:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// ...
class Person
{
private array $children = [];
public function __call($name, $args): mixed
{
$property = lcfirst(substr($name, 3));
if ('get' === substr($name, 0, 3)) {
return $this->children[$property] ?? null;
} elseif ('set' === substr($name, 0, 3)) {
$value = 1 == count($args) ? $args[0] : null;
$this->children[$property] = $value;
}
}
}
$person = new Person();
// Включити magic __call
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableMagicCall()
->getPropertyAccessor();
$propertyAccessor->setValue($person, 'wouter', [...]);
var_dump($person->getWouter()); // [...]
Note
Підтримка методу __set()
включена за замовчуванням. Див.
Включення інших функцій, якщо ви хочете відключити її.
Запис у масив властивостей
Клас PropertyAccessor
дозволяє оновлювати зміст масивів, що містяться у властивостях,
через методи додавання та видалення (adder та remover):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// ...
class Person
{
/**
* @var string[]
*/
private array $children = [];
public function getChildren(): array
{
return $this->children;
}
public function addChild(string $name): void
{
$this->children[$name] = $name;
}
public function removeChild(string $name): void
{
unset($this->children[$name]);
}
}
$person = new Person();
$propertyAccessor->setValue($person, 'children', ['kevin', 'wouter']);
var_dump($person->getChildren()); // ['kevin', 'wouter']
Компонент PropertyAccess шукає методи під назвами add<SingularOfThePropertyName>()
і
remove<SingularOfThePropertyName>()
. Обидва методи повинні бути визначені. Наприклад,
у попередньому прикладі, компонент шукає методи addChild()
і removeChild()
, щоб
отримати доступ до властивості children
. Компонент String використовується, щоб
знайти однину імені властивості.
Якщо вони доступні, то методи додавання та видалення мають пріоритет перед методом сетера.
Використання нестандартних методів додавання/видалення
Іноді методи додавання та видалення не використовують стандартний префікс
add
або remove
, як у цьому прикладі:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// ...
class PeopleList
{
// ...
public function joinPeople(string $people): void
{
$this->peoples[] = $people;
}
public function leavePeople(string $people): void
{
foreach ($this->peoples as $id => $item) {
if ($people === $item) {
unset($this->peoples[$id]);
break;
}
}
}
}
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyAccess\PropertyAccessor;
$list = new PeopleList();
$reflectionExtractor = new ReflectionExtractor(null, null, ['join', 'leave']);
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, null, $reflectionExtractor, $reflectionExtractor);
$propertyAccessor->setValue($person, 'peoples', ['kevin', 'wouter']);
var_dump($person->getPeoples()); // ['kevin', 'wouter']
Замість виклику add<SingularOfThePropertyName>()
і remove<SingularOfThePropertyName>()
,
компонент PropertyAccess викличе методи join<SingularOfThePropertyName>()
і
leave<SingularOfThePropertyName>()
.
Перевірка шляхів властивості
Якщо ви хочете перевірити, чи може бути безпечно викликаний getValue() насправді не викликаючи цей метод, то ви можете замість цього використати isReadable():
1 2 3 4 5
$person = new Person();
if ($propertyAccessor->isReadable($person, 'firstName')) {
// ...
}
Те ж саме можливо для setValue(): Викличте метод isWritable(), щоб дізнатися, чи можна оновити шлях властивості:
1 2 3 4 5
$person = new Person();
if ($propertyAccessor->isWritable($person, 'firstName')) {
// ...
}
Змішування обʼєктів та масивів
Ви можете також змішувати обʼєкти та масиви:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// ...
class Person
{
public string $firstName;
private array $children = [];
public function setChildren($children): void
{
$this->children = $children;
}
public function getChildren(): array
{
return $this->children;
}
}
$person = new Person();
$propertyAccessor->setValue($person, 'children[0]', new Person);
// дорівнює $person->getChildren()[0] = new Person()
$propertyAccessor->setValue($person, 'children[0].firstName', 'Wouter');
// дорівнює $person->getChildren()[0]->firstName = 'Wouter'
var_dump('Hello '.$propertyAccessor->getValue($person, 'children[0].firstName')); // 'Wouter'
// дорівнює $person->getChildren()[0]->firstName
Включення інших функцій
PropertyAccessor може бути сконфігурований так, щоб включати додаткові функції. Щоб зробити це, ви можете використати PropertyAccessorBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ...
$propertyAccessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
$propertyAccessorBuilder->enableMagicCall(); // включає магічний __call
$propertyAccessorBuilder->enableMagicGet(); // включає магічний __get
$propertyAccessorBuilder->enableMagicSet(); // включає магічний __set
$propertyAccessorBuilder->enableMagicMethods(); // включає магічні __get, __set та __call
$propertyAccessorBuilder->disableMagicCall(); // відключає магічний __call
$propertyAccessorBuilder->disableMagicGet(); // відключає магічний __get
$propertyAccessorBuilder->disableMagicSet(); // відключає магічний __set
$propertyAccessorBuilder->disableMagicMethods(); // відключає магічні __get, __set та __call
// перевіряє, чи включена обробка магічних __call, __get або __set
$propertyAccessorBuilder->isMagicCallEnabled(); // true або false
$propertyAccessorBuilder->isMagicGetEnabled(); // true або false
$propertyAccessorBuilder->isMagicSetEnabled(); // true або false
// Наприкінці, отримати сконфігурований аксесор властивості
$propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();
// Або все одразу
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableMagicCall()
->getPropertyAccessor();
Або ж ви можете передати параметри напряму у конструктор (не рекомендовано):
1 2
// включити обробку магічних magic __call, __set але не __get:
$propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_CALL | PropertyAccessor::MAGIC_SET);