PropertyAccess组件

3.3 版本
维护中的版本

PropertyAccess组件提供的是利用简单字符串标记来对一个对象或数组进行读/写的功能

安装 

你可以通过下述两种方式安装:

然后,包容vendor/autoload.php文件,以开启Composer提供的自动加载机制。否则,你的程序将无法找到这个Symfony组件的类。

用法 

本组件的入口点是PropertyAccess::createPropertyAccessor factory(工厂)。这个工厂创建的是默认配置的PropertyAccessor实例。

1
2
3
use Symfony\Component\PropertyAccess\PropertyAccess;
 
$accessor = PropertyAccess::createPropertyAccessor();

从数组读取 

你可以用PropertyAccessor::getValue方法来读取一个数组。它使用了PHP中常用的索引标记(index notation):

1
2
3
4
5
6
7
// ...
$person = array(
    'first_name' => 'Wouter',
);
 
var_dump($accessor->getValue($person, '[first_name]')); // 'Wouter'
var_dump($accessor->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($accessor->getValue($persons, '[0][first_name]')); // 'Wouter'
var_dump($accessor->getValue($persons, '[1][first_name]')); // 'Ryan'

从对象读取 

getValue()方法非常强大,在操作对象时,你可以领教它的全部威力。

访问public属性 

读取属性时,使用“dot(.)”标记:

1
2
3
4
5
6
7
8
9
10
11
// ...
$person = new Person();
$person->firstName = 'Wouter';
 
var_dump($accessor->getValue($person, 'firstName')); // 'Wouter'
 
$child = new Person();
$child->firstName = 'Bar';
$person->children = array($child);
 
var_dump($accessor->getValue($person, 'children[0].firstName')); // 'Bar'

访问public属性是PropertyAccess所使用的最后一招。它尝试通过以下方法先行访问(并得到属性的值)而不是直接使用属性(来得到值)。

使用Getters 

getValue()方法支持使用getters进行读取。对getters使用命名约定即可创建(相应的)方法。它把属性名字驼峰化(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($accessor->getValue($person, 'first_name')); // 'Wouter'

使用Hassers/Issers 

getValue并未止步。如果没找到getter,accessor将寻找isser或hasser。这些方法的创建方式则与getters的相同,这意味着你可以做下面这样的操作:

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 ($accessor->getValue($person, 'author')) {
    var_dump('He is an author');
}
if ($accessor->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($accessor->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();
 
// Enable magic __call 开启魔术方法 __call
$accessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();
 
var_dump($accessor->getValue($person, 'wouter')); // array(...)

_call功能默认是关闭的,若要开启它需要调用PropertyAccessorBuilder::enableMagicCall,参见开启其他功能

写入数组 

PropertyAccessor类可以做到的不只是从数组中读取,它也能向数组写入。要实现这个需要使用PropertyAccessor::setValue方法:

1
2
3
4
5
6
7
8
// ...
$person = array();
 
$accessor->setValue($person, '[first_name]', 'Wouter');
 
var_dump($accessor->getValue($person, '[first_name]')); // 'Wouter'
// or
// var_dump($person['first_name']); // 'Wouter'

写入对象 

setValue()方法有和getValue()方法一样的功能。你可以使用setters,__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
// ...
class Person
{
    public $firstName;
    private $lastName;
    private $children = array();
 
    public function setLastName($name)
    {
        $this->lastName = $name;
    }
 
    public function __set($property, $value)
    {
        $this->$property = $value;
    }
 
    // ...
}
 
$person = new Person();
 
$accessor->setValue($person, 'firstName', 'Wouter');
$accessor->setValue($person, 'lastName', 'de Jong');
$accessor->setValue($person, 'children', array(new Person()));
 
var_dump($person->firstName); // 'Wouter'
var_dump($person->getLastName()); // 'de Jong'
var_dump($person->children); // 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();
 
// Enable magic __call
$accessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();
 
$accessor->setValue($person, 'wouter', array(...));
 
var_dump($person->getWouter()); // array(...)

检查property路径 

当你要检查PropertyAccessor::getValue是否能被安全地调用而不是要真正调用该方法时,你可以使用PropertyAccessor::isReadable来代替它:

1
2
3
4
5
$person = new Person();
 
if ($accessor->isReadable($person, 'firstName')) {
    // ...
}

类似的情况是对PropertyAccessor::setValue:调用PropertyAccessor::isWritable方法来找到“是否有一个属性的路径可以被更新”:

1
2
3
4
5
$person = new Person();
 
if ($accessor->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();
 
$accessor->setValue($person, 'children[0]', new Person);
// equal to $person->getChildren()[0] = new Person()
 
$accessor->setValue($person, 'children[0].firstName', 'Wouter');
// equal to $person->getChildren()[0]->firstName = 'Wouter'
 
var_dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter'
// equal to $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
// ...
$accessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
 
// Enable magic __call 开启魔术方法__call
$accessorBuilder->enableMagicCall();
 
// Disable magic __call 关闭魔术方法__call
$accessorBuilder->disableMagicCall();
 
// Check if magic __call handling is enabled
// 检查魔术方法__call功能是否开启
$accessorBuilder->isMagicCallEnabled(); // true or false
 
// At the end get the configured property accessor
// 最终得到配置好的property accessor
$accessor = $accessorBuilder->getPropertyAccessor();
 
// Or all in one 或者全部得到
$accessor = PropertyAccess::createPropertyAccessorBuilder()
    ->enableMagicCall()
    ->getPropertyAccessor();

或者你直接传入参数到其构造器中(此法并非推荐):

1
2
3
// ...
$accessor = new PropertyAccessor(true); // this enables handling of magic __call
// 开启__call魔术方法

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)