PHP中的__set魔术方法属于类的魔术方法范畴,主要用于在给不可访问的属性赋值时自动触发执行,是处理对象属性赋值逻辑的常用工具。
__set魔术方法的基本语法
__set方法需要定义在类的内部,方法名固定为__set,且必须带有两个参数,第一个参数是要赋值的属性名称,第二个参数是要赋的属性值,方法不需要手动调用,当触发对应场景时会自动执行。
基本定义格式如下:
<?php
class Demo {
// __set魔术方法定义
public function __set($name, $value) {
// 方法内部逻辑
}
}
?>
__set魔术方法的核心作用
__set方法主要有两个核心作用:
- 处理类中不存在的属性的赋值操作,避免直接赋值报错
- 处理类中私有或者受保护属性的外部赋值,统一做赋值校验或者额外逻辑处理
当我们在类的外部给一个不存在的属性,或者没有访问权限的属性赋值时,PHP不会直接报错,而是会自动调用当前类的__set方法,把属性名和要赋的值作为参数传入方法内部。
__set方法的使用实例
实例1:处理不存在的属性赋值
下面的示例中,Demo类没有定义任何属性,外部给对象赋值不存在的属性时,会触发__set方法,我们可以在方法内部记录赋值信息。
<?php
class Demo {
public function __set($name, $value) {
echo "正在给不存在的属性 {$name} 赋值,值为 {$value}" . PHP_EOL;
}
}
$obj = new Demo();
// 给不存在的属性赋值,触发__set方法
$obj->test = "测试值";
$obj->age = 20;
?>
上述代码执行后会输出:
正在给不存在的属性 test 赋值,值为 测试值 正在给不存在的属性 age 赋值,值为 20
实例2:私有属性的赋值校验
当类中有私有属性,外部无法直接赋值时,可以通过__set方法统一处理赋值逻辑,比如校验值的合法性。
<?php
class User {
// 私有属性
private $age;
private $name;
public function __set($name, $value) {
// 判断属性名是age时做校验
if ($name == "age") {
if ($value < 0 || $value > 120) {
echo "年龄值不合法,必须在0到120之间" . PHP_EOL;
return;
}
}
// 存储属性值到类的内部数组
$this->$name = $value;
echo "属性 {$name} 赋值成功,值为 {$value}" . PHP_EOL;
}
// 获取属性值的方法
public function getInfo() {
return "姓名:{$this->name},年龄:{$this->age}";
}
}
$user = new User();
$user->name = "张三";
$user->age = 150; // 年龄不合法,赋值失败
$user->age = 25; // 年龄合法,赋值成功
echo $user->getInfo();
?>
上述代码执行后输出:
属性 name 赋值成功,值为 张三 年龄值不合法,必须在0到120之间 属性 age 赋值成功,值为 25 姓名:张三,年龄:25
实例3:结合数组存储属性
我们也可以把__set方法赋值的属性统一存储到一个类的内部数组中,方便后续统一管理。
<?php
class Data {
private $data = [];
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function get($name) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
}
$obj = new Data();
$obj->title = "PHP教程";
$obj->author = "李四";
echo "标题:" . $obj->get("title") . PHP_EOL;
echo "作者:" . $obj->get("author") . PHP_EOL;
?>
执行上述代码会输出:
标题:PHP教程 作者:李四
使用注意事项
使用__set魔术方法时需要注意以下几点:
- __set方法必须是public访问权限,否则无法自动触发
- 如果类中定义了__set方法,给不可访问属性赋值时不会报错,而是执行方法内的逻辑,需要在方法内部做好异常处理
- __set方法只会在给不可访问属性赋值时触发,给public属性赋值不会触发该方法