php中实例访问属性需遵循可见性规则:public属性可用->直接读写,protected/private属性在类外访问会报致命错误;__get/__set仅拦截不可见属性,反射可绕过限制但代价高。

PHP中实例访问属性的语法和常见错误
直接用 -> 操作符访问对象属性,前提是该属性不是 private 或未被 __get() 拦截。比如 $obj->name 能读取 public $name,但对 private $id 会报 Fatal error: Cannot access private property。
- 属性可见性决定能否直接访问:只有
public属性支持->直接读写 -
protected和private属性在类外部不可见,强行访问会触发致命错误 - 如果类定义了
<strong>get()</strong>或set(),即使属性是public,也可能被拦截(取决于实现逻辑) - 动态属性(如
$obj->$key)只对public生效,且变量$key必须有值,否则报Notice: Trying to get property '...' of non-object
修改实例属性时必须注意的赋值边界
赋值本身很简单:$obj->prop = 'new value';,但实际中容易踩坑的是类型、作用域和引用行为。
- 对
object类型属性赋值默认是对象引用(不是深拷贝),改副本会影响原对象 - 对
array或string等标量类型赋值是值拷贝,互不影响 - 如果属性是
readonly(PHP 8.2+),首次赋值后再次写入会抛出Error: Cannot modify readonly property - 使用
unset($obj->prop)只能删除动态添加的public属性,不能删类中声明的属性(会静默失败)
通过魔术方法控制属性访问的真实场景
<strong>get()</strong> 和 set() 不是“让私有属性变公开”的捷径,而是为封装逻辑服务的——比如日志记录、数据转换、懒加载。
-
__get()在读取不可见属性时触发,返回值会作为读取结果;若不 return 或 return null,表达式结果就是null -
__set()在写入不可见属性时触发,参数是属性名和值,内部可做校验或转换(如自动 trim 字符串) - 注意:这两个方法不会对
public属性生效,除非你显式删掉该属性(unset($this->prop))再触发 - 示例:
$obj = new class { private $data = []; public function __get($name) { return $this->data[$name] ?? null; } public function __set($name, $value) { $this->data[$name] = filter_var($value, FILTER_SANITIZE_STRING); } }; $obj->title = " <script>alert(1)</script> "; echo $obj->title; // 输出:<script>alert(1)</script>
反射(ReflectionProperty)用于绕过访问限制的代价
真需要读写 private 或 protected 属性(比如测试、序列化、调试),得用 ReflectionProperty,但它不是常规开发手段。
- 必须先调用
setAccessible(true)才能操作,否则getValue()/setValue()报错 - 反射破坏封装性,生产环境滥用会导致维护困难和意外副作用
- 性能比直接访问低一个数量级,频繁调用会影响吞吐(尤其在循环里)
- PHP 8.1+ 的
readonly属性即使通过反射也无法修改,会抛出ReflectionException
属性访问看似简单,但 visibility、magic method、reflection 三层机制叠加后,实际行为取决于类定义细节和 PHP 版本。别假设“能读就能写”,也别把 __get 当成通用 getter —— 它只响应未定义/不可见属性的访问。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











