忽然之间的博客

Thoughts, stories and ideas.

PHP反思:__get() /__set()实现类属性

先贴代码

<?php

class InvalidCallException extends \Exception  {}
class UnknownPropertyException extends \Exception {}

/**
 * 定义一个对象类
 * 实现__get()
 * 实现__set()
 * 赋予一个对象属性读写的能力
 */
abstract class Object
{   
    public function __get($key)
    {   
        $method_name = 'get' . $key;
        if (method_exists($this, $method_name)) {
            return call_user_func([$this, $method_name]);
        } else if (method_exists($this, 'set' . $key)) {
            throw new \InvalidCallException("属性是只写的:" . __class__ . '::' . $key);
        } else {
            throw new \UnknownPropertyException("属性不存在:" . __class__ . '::' . $key);
        }
    }

    public function __set($key, $value)
    {   
        $method_name = 'set' . $key;
        if (method_exists($this, $method_name)) {
            return call_user_func([$this, $method_name], $value);
        } else if (method_exists($this, 'get' . $key)) {
            throw new \InvalidCallException("属性是只读的:"  . __class__ . '::' . $key);
        } else {
            throw new \UnknownPropertyException("属性不存在:" . __class__ . '::' . $key);
        }
    }
}


/**
 * Article类
 * title属性,可读写
 * author属性,只读
 * 内部定义私有成员,对外不可见,实现封装
 * 定义getter,setter,设置属性读写,实现封装
 */
class Article extends Object
{

    private $_title;
    private $_author = 'codefine';
    private $_content;

    public function getTitle()
    {
        return $this->_title;
    }

    public function setTitle($title)
    {
        $this->_title = trim($title);
    }

    public function getAuthor()
    {
        return $this->_author;
    }

    public function setContent($value)
    {
        $this->_content = $value;
        var_dump($this->_content);
    }
}

$obj = new Article();

//title 支持读写
$obj->title = '__get __set的使用';
var_dump($obj->title);

//只读
// $obj->author = '修改作者'; //Fatal error: Uncaught InvalidCallException: 属性是只读的:Object::author
var_dump($obj->author);

//只写
$obj->content = '内容属性赋值';
// var_dump($obj->content); // Fatal error: Uncaught InvalidCallException: 属性是只写的:Object::content 

总结

通过代码示例,可以发现,PHP中的类,本身没有设计getter, setter。但提供了__get() / __set()魔术方法,在访问(设置)不存在的类成员的时候,提供回调处理。

通过__get() / __set() 魔术方法,交由程序员自己发挥想象,自行定制实现规则,既可以实现成员的类型限制,也可以实现统一包装,格式化,访问控制等等功能。