题目
class User { public $name; public $age; }
$User = new User();
$User->name = "Kingmax";
$User->age = 30;
foreach($User as $key=>$value) {
echo $key.":".$value."\n";
}
输出
name:Kingmax
age:30
如果我要以上例程输出的结果变成:
姓名:Kingmax
年龄:30
解法过程
习惯于在各种框架上进行model
操作的我们乍一看这不是根据获取器和拦截器简单安排嘛。例如来laravel
上面我们对UserModel
的拦截器getNameAttribute()
做出修改就能够得到年龄
这个值。
emmm不太对,获取器修改得到的是值,没办法改动到键名
仔细看一下其实是遍历然后修改键名,所以我们有必要对键名做一个中文映射于是定义了$data
私有属性。显而易见的可以想得到通过魔术方法做到这件事情。我们通过__get()
和__set()
两个魔术方法可以轻易的做到映射维护。题目要求我们仅能修改class user{}
作出修改从而兼容客户端,便有了以下代码。
class User implements Iterator
{
private array $data = [
'name' => [
'cn' => '姓名',
'value' => null,
],
'age' => [
'cn' => '年龄',
'value' => null,
],
];
public function current()
{
return $this->data[key($this->data)]['value'];
}
public function next()
{
return next($this->data);
}
public function key()
{
return $this->data[key($this->data)]['cn'];
}
public function valid(): bool
{
return key($this->data) !== null;
}
public function rewind(): void
{
reset($this->data);
}
public function __set(string $name, $value): void
{
if (!isset($this->data[$name])) {
throw new InvalidArgumentException(__CLASS__ . " 设置 $name 属性失败");
}
$this->data[$name]['value'] = $value;
}
public function __get(string $name)
{
if (!isset($this->data[$name])) {
throw new InvalidArgumentException(__CLASS__ . " 获取 $name 属性不存在");
}
return $this->data[$name]['value'];
}
}
输出之后得到预期结果
姓名:Kingmax
年龄:30
下面我们来复盘几个知识点
foreach
的基础知识点- php的
魔术方法
- 什么是迭代器
foreach
的基础知识点
foreach
是 PHP 的一个内置函数,它用于遍历数组中的每个元素并对其进行操作。一般语法为:
$array = [];
foreach ($array as $key => $value) { }
foreach
也支持对对象的非空公有属性
进行遍历。对于对象,遍历的顺序是按照属性定义的顺序进行的。例如题目User{}
中的$name
属性就是一个公共属性。至于要强调非空
是因为在PHP中null
,''
,0
,false
都可以定义为空。null
是一种特殊数据结构,而下面的例子的属性则不会被遍历出来,因为没有未被赋值。
class User{
public $name;
public $age;
}
$user = new User();
$user->name = 'tty199';
foreach($User as $key=>$value) {
echo $key.":".$value."\n";
}
#输出结果中并没有age
#输出
> name : tty199
foreach
也可以遍历实现了Interator
即迭代器接口的类。迭代器在不同的语言中有实现差异。
php的魔术方法
- php提供了一组魔术方法能够在对象生命周期中自动调用以方便我们做一些特殊处理,例如构造函数
__construct()
可以在函数创建时被调用。其功能和java的类同名方法
功能相似。我们前面使用了__set($name,$value)
是PHP提供的功能是当设置的属性在类实例中不存在时会被调用到。其中$name
为属性名$value
为具体任何类型值。
在上述改造后的user{}
中我定义了一个私有属性$data
用于存储或获取我需要的两个属性并且做了中文映射。下面是PHP提供的全部魔术方法。
方法 | 作用 |
---|---|
__construct() | 构造函数,在对象创建时自动调用 |
__destruct() | 析构函数,在对象销毁时自动调用 |
__get() | 获取不存在或不可访问属性时自动调用 |
__set() | 设置不存在或不可访问属性时自动调用 |
__isset() | 检查不存在或不可访问属性是否已设置时自动调用 |
__unset() | 取消设置不存在或不可访问属性时自动调用 |
__call() | 调用不存在或不可访问方法时自动调用 |
__callStatic() | 调用不存在或不可访问静态方法时自动调用 |
__toString() | 将对象转换为字符串时自动调用 |
__invoke() | 将对象作为函数调用时自动调用 |
__set_state() | 当 var_export() 导出类时调用 (php8已放弃) |
__clone() | 对象被克隆时自动调用 |
什么是迭代器
- 它提供了一种统一的方式来遍历集合中的元素。
foreach
内置函数会调用实现了迭代器接口的实例的五个方法。前面的例子中我们使用了Iterator
接口来定义自定义user
迭代器。该接口定义了rewind()
、valid()
、key()
、current()
和next()
方法,分别用于重置迭代器指针、检查是否还有元素可遍历、返回当前元素的键和值,以及将迭代器指针指向下一个元素。
显然,我们通过编写key()
这个函数配合$data
实现了对键名的映射。
...
private array $data = [
'name' => [
'cn' => '姓名',
'value' => null,
],
'age' => [
'cn' => '年龄',
'value' => null,
],
];
...
public function key()
{
return $this->data[key($this->data)]['cn'];
}