The PropertyAccess Component

The PropertyAccess component provides functions to read and write from/to an object or array using a simple string notation.

PropertyAccess コンポーネントは、単純な文字列表記を使用して、オブジェクトまたは配列に対して読み書きする関数を提供します。

Installation

1
$ composer require 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.

このコンポーネントを Symfony アプリケーションの外部にインストールする場合は、Composer が提供するクラス自動ロード メカニズムを有効にするために、コード内に vendor/autoload.php ファイルを必要とする必要があります。詳細については、この記事をお読みください。

Usage

The entry point of this component is the createPropertyAccessor() factory. This factory will create a new instance of the PropertyAccessor class with the default configuration:

このコンポーネントのエントリ ポイントは createPropertyAccessor() ファクトリです。このファクトリは、PropertyAccessor クラスの新しいインスタンスをデフォルト設定で作成します。
1
2
3
use Symfony\Component\PropertyAccess\PropertyAccess;

$propertyAccessor = PropertyAccess::createPropertyAccessor();

Reading from Arrays

You can read an array with the getValue() method. This is done using the index notation that is used in PHP:

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

As you can see, the method will return null if the index does not exist. But you can change this behavior with the enableExceptionOnInvalidIndex() method:

ご覧のとおり、インデックスが存在しない場合、このメソッドは 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',
];

// instead of returning null, the code now throws an exception of type
// Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
$value = $propertyAccessor->getValue($person, '[age]');

// You can avoid the exception by adding the nullsafe operator
$value = $propertyAccessor->getValue($person, '[age?]');

You can also use multi dimensional arrays:

多次元配列も使用できます。
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'

Reading from Objects

The getValue() method is a very robust method, and you can see all of its features when working with objects.

getValue() メソッドは非常に堅牢なメソッドであり、オブジェクトを操作するときにすべての機能を確認できます。

Accessing public Properties

To read from properties, use the "dot" notation:

プロパティから読み取るには、「ドット」表記を使用します。
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

注意

Accessing public properties is the last option used by PropertyAccessor. It tries to access the value using the below methods first before using the property directly. For example, if you have a public property that has a getter method, it will use the getter.

パブリック プロパティへのアクセスは、PropertyAccessor によって使用される最後のオプションです。プロパティを直接使用する前に、まず以下のメソッドを使用して値にアクセスしようとします。たとえば、getter メソッドを持つパブリック プロパティがある場合、getter が使用されます。

Using Getters

The getValue() method also supports reading using getters. The method will be created using common naming conventions for getters. It transforms the property name to camelCase (first_name becomes FirstName) and prefixes it with get. So the actual method becomes getFirstName():

getValue() メソッドは、getter を使用した読み取りもサポートしています。メソッドは、ゲッターの一般的な命名規則を使用して作成されます。プロパティ名をキャメルケースに変換し (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'

Using Hassers/Issers

And it doesn't even stop there. If there is no getter found, the accessor will look for an isser or hasser. This method is created using the same way as getters, this means that you can do something like this:

そしてそれだけではありません。 getter が見つからない場合、アクセサーは isser または hasser を探します。このメソッドは asgetters と同じ方法で作成されます。つまり、次のようなことができます。
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 = [];

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

    public function hasChildren()
    {
        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 will produce: This person is an author

これにより、次が生成されます。この人は著者です

Accessing a non Existing Property Path

By default a NoSuchPropertyException is thrown if the property path passed to getValue() does not exist. You can change this behavior using the disableExceptionOnInvalidPropertyPath() method:

デフォルトでは、getValue() に渡されたプロパティ パスが存在しない場合、NoSuchPropertyException がスローされます。この動作は、disableExceptionOnInvalidPropertyPath() メソッドを使用して変更できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
class Person
{
    public $name;
}

$person = new Person();

$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->disableExceptionOnInvalidPropertyPath()
    ->getPropertyAccessor();

// instead of throwing an exception the following code returns null
$value = $propertyAccessor->getValue($person, 'birthday');

Accessing Nullable Property Paths

Consider the following PHP code:

次の 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';

Given that $person is nullable, an object graph like comment.person.profile will trigger an exception when the $person property is null. The solution is to mark all nullable properties with the nullsafe operator (?):

$person が null 可能であることを考えると、comment.person.profile のようなオブジェクト グラフは、$person プロパティが null の場合に例外をトリガーします。解決策は、すべての null 許容プロパティを nullsafe 演算子 (?) でマークすることです。
1
2
3
4
5
6
7
// This code throws an exception of type
// Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
var_dump($propertyAccessor->getValue($comment, 'person.firstname'));

// If a property marked with the nullsafe operator is null, the expression is
// no longer evaluated and null is returned immediately without throwing an exception
var_dump($propertyAccessor->getValue($comment, 'person?.firstname')); // null

6.2

6.2

The ? nullsafe operator was introduced in Symfony 6.2.

? nullsafe 演算子は Symfony 6.2 で導入されました。

Magic __get() Method

The getValue() method can also use the magic __get() method:

getValue() メソッドは、魔法の __get() メソッドも使用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ...
class Person
{
    private $children = [
        'Wouter' => [...],
    ];

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

$person = new Person();

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

Note

ノート

The __get() method support is enabled by default. See Enable other Features if you want to disable it.

__get() メソッドのサポートはデフォルトで有効になっています。無効にする場合は、他の機能を有効にするを参照してください。

Magic __call() Method

Lastly, getValue() can use the magic __call() method, but you need to enable this feature by using PropertyAccessorBuilder:

最後に、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 $children = [
        'wouter' => [...],
    ];

    public function __call($name, $args)
    {
        $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();

// enables PHP __call() magic method
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

var_dump($propertyAccessor->getValue($person, 'wouter')); // [...]

Caution

注意

The __call() feature is disabled by default, you can enable it by calling enableMagicCall() see Enable other Features.

__call() 機能はデフォルトで無効になっています。enableMagicCall() を呼び出すことで有効にできます。他の機能を有効にするを参照してください。

Writing to Arrays

The PropertyAccessor class can do more than just read an array, it can also write to an array. This can be achieved using the setValue() method:

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'
// or
// var_dump($person['first_name']); // 'Wouter'

Writing to Objects

The setValue() method has the same features as the getValue() method. You can use setters, the magic __set() method or properties to set values:

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 = [];

    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', [new Person()]); // __set is called

var_dump($person->firstName); // 'Wouter'
var_dump($person->getLastName()); // 'de Jong'
var_dump($person->getChildren()); // [Person()];

You can also use __call() to set values but you need to enable the feature, see Enable other Features:

__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 $children = [];

    public function __call($name, $args)
    {
        $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();

// Enable magic __call
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

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

var_dump($person->getWouter()); // [...]

Note

ノート

The __set() method support is enabled by default. See Enable other Features if you want to disable it.

__set() メソッドのサポートはデフォルトで有効になっています。無効にする場合は、他の機能を有効にするを参照してください。

Writing to Array Properties

The PropertyAccessor class allows to update the content of arrays stored in properties through adder and remover methods:

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 = [];

    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']

The PropertyAccess component checks for methods called add<SingularOfThePropertyName>() and remove<SingularOfThePropertyName>(). Both methods must be defined. For instance, in the previous example, the component looks for the addChild() and removeChild() methods to access the children property. The String component inflector is used to find the singular of a property name.

PropertyAccess コンポーネントは、add() および remove() と呼ばれるメソッドをチェックします。両方のメソッドを定義する必要があります。たとえば、前の例では、コンポーネントは子プロパティにアクセスするために addChild() および removeChild() メソッドを探します。String コンポーネント インフレクタは、プロパティ名の単数形を見つけるために使用されます。

If available, adder and remover methods have priority over a setter method.

利用可能な場合、adder メソッドと remover メソッドは、setter メソッドよりも優先されます。

Using non-standard adder/remover methods

Sometimes, adder and remover methods don't use the standard add or remove prefix, like in this example:

次の例のように、adder メソッドと remover メソッドが標準の 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']

Instead of calling add<SingularOfThePropertyName>() and remove<SingularOfThePropertyName>(), the PropertyAccess component will call join<SingularOfThePropertyName>() and leave<SingularOfThePropertyName>() methods.

add() および remove() を呼び出す代わりに、PropertyAccess コンポーネントは join() および leave() メソッドを呼び出します。

Checking Property Paths

When you want to check whether getValue() can safely be called without actually calling that method, you can use isReadable() instead:

実際にそのメソッドを呼び出さずに getValue() を安全に呼び出すことができるかどうかを確認したい場合は、代わりに isReadable() を使用できます。
1
2
3
4
5
$person = new Person();

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

The same is possible for setValue(): Call the isWritable() method to find out whether a property path can be updated:

プロパティ パスを更新できるかどうかを調べるには、setValue():Call the isWritable() メソッドでも同じことが可能です。
1
2
3
4
5
$person = new Person();

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

Mixing Objects and Arrays

You can also mix objects and arrays:

オブジェクトと配列を混在させることもできます:
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 = [];

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

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

$person = new Person();

$propertyAccessor->setValue($person, 'children[0]', new Person);
// equal to $person->getChildren()[0] = new Person()

$propertyAccessor->setValue($person, 'children[0].firstName', 'Wouter');
// equal to $person->getChildren()[0]->firstName = 'Wouter'

var_dump('Hello '.$propertyAccessor->getValue($person, 'children[0].firstName')); // 'Wouter'
// equal to $person->getChildren()[0]->firstName

Enable other Features

The PropertyAccessor can be configured to enable extra features. To do that you could use the PropertyAccessorBuilder:

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(); // enables magic __call
$propertyAccessorBuilder->enableMagicGet(); // enables magic __get
$propertyAccessorBuilder->enableMagicSet(); // enables magic __set
$propertyAccessorBuilder->enableMagicMethods(); // enables magic __get, __set and __call

$propertyAccessorBuilder->disableMagicCall(); // disables magic __call
$propertyAccessorBuilder->disableMagicGet(); // disables magic __get
$propertyAccessorBuilder->disableMagicSet(); // disables magic __set
$propertyAccessorBuilder->disableMagicMethods(); // disables magic __get, __set and __call

// checks if magic __call, __get or __set handling are enabled
$propertyAccessorBuilder->isMagicCallEnabled(); // true or false
$propertyAccessorBuilder->isMagicGetEnabled(); // true or false
$propertyAccessorBuilder->isMagicSetEnabled(); // true or false

// At the end get the configured property accessor
$propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();

// Or all in one
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

Or you can pass parameters directly to the constructor (not the recommended way):

または、パラメーターをコンストラクターに直接渡すこともできます (推奨される方法ではありません)。
1
2
// enable handling of magic __call, __set but not __get:
$propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_CALL | PropertyAccessor::MAGIC_SET);