Компонента PropertyAccess (доступ к свойству)

Компонент PropertyAccess предоставляет функцию для чтения и написания из/в объект или массив, используя простую нотацию строки.

Установка

1
$ composer require symfony/property-access

Также вы можете клонировать репозиторий https://github.com/symfony/property-access.

Note

If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer. Read this article for more details.

Использование

Входная точка этого компонента - это фабрика PropertyAccess::createPropertyAccessor. Это фабрика создаст новый экземпляр класса PropertyAccessor с конфигурацией по умолчанию:

1
2
3
use Symfony\Component\PropertyAccess\PropertyAccess;

$propertyAccessor = PropertyAccess::createPropertyAccessor();

Чтение из массивов

Вы можете прочитать массив с помощью метода PropertyAccessor::getValue. Это делается, используя нотации индекса, которые используются в PHP:

1
2
3
4
5
6
7
// ...
$person = array(
    'first_name' => 'Wouter',
);

var_dump($propertyAccessor->getValue($person, '[first_name]')); // 'Wouter'
var_dump($propertyAccessor->getValue($person, '[age]')); // null

Как вы можете увидеть, метод вернёт``null``, если индекс не существует.

Вы также можете использовать многомерные массивы:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ...
$persons = array(
    array(
        'first_name' => 'Wouter',
    ),
    array(
        'first_name' => 'Ryan',
    )
);

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 = array($child);

var_dump($propertyAccessor->getValue($person, 'children[0].firstName')); // 'Bar'

Caution

Доступ к публичным свойствам - это последня опция, используемая PropertyAccessor. Она пытается получить доступ к значению, используя методы описанные ниже до использования свойства напрямую. Например, если у вас есть публичное свойство, которое имеет метод геттера, то она будет использовать геттер.

Использование геттеров

Метод getValue() также поддерживает чтение, используя геттеры. Этот метод будет создан, используя общие соглашения об именовании для геттеров. Он camelizes имя свойства (first_name становится FirstName) и добавляет к нему префикс get. Поэтому настоящий метод становится getFirstName():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ...
class Person
{
    private $firstName = 'Wouter';

    public function getFirstName()
    {
        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 $author = true;
    private $children = array();

    public function isAuthor()
    {
        return $this->author;
    }

    public function hasChildren()
    {
        return 0 !== count($this->children);
    }
}

$person = new Person();

if ($propertyAccessor->getValue($person, 'author')) {
    var_dump('He is an author');
}
if ($propertyAccessor->getValue($person, 'children')) {
    var_dump('He has children');
}

Это произведёт: He is an author

Волшебный метод __get()

Метод getValue() может также использовать волшебный метод __get():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ...
class Person
{
    private $children = array(
        'Wouter' => array(...),
    );

    public function __get($id)
    {
        return $this->children[$id];
    }
}

$person = new Person();

var_dump($propertyAccessor->getValue($person, 'Wouter')); // array(...)

Волшебный метод __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
28
29
// ...
class Person
{
    private $children = array(
        'wouter' => array(...),
    );

    public function __call($name, $args)
    {
        $property = lcfirst(substr($name, 3));
        if ('get' === substr($name, 0, 3)) {
            return isset($this->children[$property])
                ? $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')); // array(...)

Caution

Функция __call() отключена по умолчанию, вы можете включить её вызвав PropertyAccessorBuilder::enableMagicCall см. Включение других функций.

Написание в массивы

Класс PropertyAccessor может делать больше, чем просто читать массивы, он может также писать в массив. Этого можно достичь, используя метод PropertyAccessor::setValue:

1
2
3
4
5
6
7
8
// ...
$person = array();

$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 $firstName;
    private $lastName;
    private $children = array();

    public function setLastName($name)
    {
        $this->lastName = $name;
    }

    public function getLastName()
    {
        return $this->lastName;
    }

    public function getChildren()
    {
        return $this->children;
    }

    public function __set($property, $value)
    {
        $this->$property = $value;
    }
}

$person = new Person();

$propertyAccessor->setValue($person, 'firstName', 'Wouter');
$propertyAccessor->setValue($person, 'lastName', 'de Jong'); // setLastName is called
$propertyAccessor->setValue($person, 'children', array(new Person())); // __set is called

var_dump($person->firstName); // 'Wouter'
var_dump($person->getLastName()); // 'de Jong'
var_dump($person->getChildren()); // array(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
29
30
// ...
class Person
{
    private $children = array();

    public function __call($name, $args)
    {
        $property = lcfirst(substr($name, 3));
        if ('get' === substr($name, 0, 3)) {
            return isset($this->children[$property])
                ? $this->children[$property]
                : null;
        } elseif ('set' === substr($name, 0, 3)) {
            $value = 1 == count($args) ? $args[0] : null;
            $this->children[$property] = $value;
        }
    }

}

$person = new Person();

// Включить волшебный __call
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

$propertyAccessor->setValue($person, 'wouter', array(...));

var_dump($person->getWouter()); // array(...)

Написание в массив свойств

Класс 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 $children = array();

    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', array('kevin', 'wouter'));

var_dump($person->getChildren()); // array('kevin', 'wouter')

Компонент PropertyAccess ищет методы под названием add<SingularOfThePropertyName>() и remove<SingularOfThePropertyName>(). Оба метода должны быть определены. Например, в предыдущем примере, компонент щет методы addChild() и removeChild(), чтобы получить доступ к свойству children. Компонент Инфлектор используется, чтобы найти сингуляр имени свйоства.

Если они доступны, то методы добавления и удаления имеют приоритет перед методом сеттера.

Проверка путей свойства

Если вы хотите проверить может ли безопасно быть вызван, PropertyAccessor::getValue на самом деле не вызывая этот метод, то вы можете вместо этого использовать PropertyAccessor::isReadable:

1
2
3
4
5
$person = new Person();

if ($propertyAccessor->isReadable($person, 'firstName')) {
    // ...
}

То же самое возможно для PropertyAccessor::setValue: Вызовите метод PropertyAccessor::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 $firstName;
    private $children = array();

    public function setChildren($children)
    {
        $this->children = $children;
    }

    public function getChildren()
    {
        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
// ...
$propertyAccessorBuilder = PropertyAccess::createPropertyAccessorBuilder();

// включает волшебный __call
$propertyAccessorBuilder->enableMagicCall();

// включает волшебный __call
$propertyAccessorBuilder->disableMagicCall();

// проверяет, включена ли обработка волшебного __call
$propertyAccessorBuilder->isMagicCallEnabled(); // true or false

// В конце получить сконфигурированый доступ к свойству
$propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();

// Или всё за раз
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

Или вы можете передать параметры напрямую в конструктор (не рекомендуется):

1
2
// ...
$propertyAccessor = new PropertyAccessor(true); // это включит обработку волшебного __call

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.