首先上来就贴个代码:
- <?php
- class Test {
- private $a;
- public function __set($key, $value) { //必须是两个参数,第一个是属性,第二个是设置属性的值。
- $this->$key = $value;
- //这里不明白为什么是$this->$key ?? 一般不是这样写吗? $this->key ? 请看我下面的分解。
- }
- public function __get($key) { //一个参数,要获得的属性
- return $this->$key;
- }
- }
- $test = new Test();
- $test->__set('a', '我是A'); //给属性a 设置一个值
- echo $test->__get('a'); //打印a 输出 我是A
- ?>
你可以注意到,$key只是我们需要传进来的一个内部属性,不是我们真正需要的,如果$this->key这样就会编程$key这个属性的值了,而我们需要设置的是$this->a的值,所以这里就需要使用变量的变量了。
在实践中,往往内部属性的个数是不定的,你想到了什么解决办法?对,就是数组,PHP中数组的长度是不定的,所以我们就可以给他无限增加元素。如果是注册的页面,我们就可以灵活的获得和设置属性了。但是在实际运用中,我们知道传过来的值是不可靠的,我们得需要HTML转义,Mysql转义等等,所以我们可以写一个简单的回调函数,做数据验证的作用。
- <?php
- class Test {
- public $values = array('name' => '名字', 'passwd' => '密码'); //提前规定私有属性只有name和passwd
- public function __get($name){
- if (isset($this->values[$name])) { //判断一下
- return $this->values[$name];
- } else {
- echo '没有此属性!';
- }
- }
- public function __set($name, $value){
- if (isset($this->values[$name])) { //这里也判断一下
- $this->values[$name] = $this->validate($value);
- } else {
- echo '没有此属性!';
- }
- }
- private function validate($value){
- return htmlspecialchars(addslashes($value));
- //等等
- }
- }
- $test = new Test();
- $test->__set('name', '<a>我是name</a>'); //设置name 无输出
- $test->__set('我也不知道这个是什么', '<a>看看是我的值是多少</a>'); //输出没有此属性!
- echo $test->__get('name'); //输出 <a>我是a</a>
- echo $test->__get('随便看看'); //输出没有此属性!
- ?>
我们来看看 __isset() 和 __unset() 。
其实__isset() 和 __unset() 与 __sleep()和__wakeup() 是有点像的,__isset() 和 __unset() 是在触发isset()和unset()函数前的回调函数。我们继续来做例子。
- <?php
- class Test {
- public $a;
- public function __construct($value) {
- $this->a = $value;
- }
- public function __isset($name) {
- echo "你小子偷看我??";
- }
- public function __unset($name) {
- echo "你没权利宰了我!";
- }
- }
- $test = new Test("我是a");
- isset($test->a); //无任何输出
- ?>
执行上面的代码,结果无任何输出,但是刚才我说了,只要执行isset()就要执行__isset()这个回调函数的啊,回调函数里面输出 “你小子偷看我??” ,但是为什么结果却没有输出呢?难道是我说错了??我不能欺骗大家啊,所以我得给大家说清楚,不是我错了,也不是程序错了,是PHP的问题!
在PHP中关于 __isset() 和 __unset() 有个约定,就是当用 isset() 函数测试成员属性的时候,如果这个属性为public属性或者默认属性时,就不回调,当为protected或者private时就回调。我语言表达能力差,还是用代码表示吧。
- <?php
- class Test {
- var $a; //这样就不输出
- public $a; //这样也不输出
- protected $a; //输出你小子偷看我??
- private $a; //输出你小子偷看我??
- public function __construct($value) {
- $this->a = $value;
- }
- public function __isset($name) {
- echo "你小子偷看我??";
- return isset($this->a); //返回真是的信息
- }
- public function __unset($name) {
- echo "你没权利宰了我!";
- }
- }
- $test = new Test("我是a");
- isset($test->a);
- ?>
下面有个例子,没事的时候可以研究一下,有个怪物类中有个属性是怪物的血,你不能想unset()就unset(),也就是说你不能等怪物的血没完了就使他死了
- <?php
- class Monster {
- /**
- * 怪物的属性表
- *
- * @var array
- */
- private $state = array('hp' => 100, 'mp' => 100);
- /**
- * 打击怪物
- *
- * @access public
- * @return void
- *
- */
- public function hit() {
- $this->state['hp'] = $this->state['hp'] - rand(1, 10);
- }
- /**
- * 设置怪物属性
- *
- * @param string $key
- * @param integer $value
- * @access public
- * @return void
- */
- public function __set($key, $value) {
- if (isset($this->state[$key])) {
- $this->state[$key] = $this->validate($value);
- } else {
- echo '没有此属性!';
- }
- }
- /**
- * 获得怪物属性
- *
- * @param string $key
- * @access public
- * @return integer
- */
- public function __get($key) {
- if (isset($this->state[$key])) {
- return $this->state[$key];
- } else {
- echo '没有此属性!';
- }
- }
- /**
- * 验证设定怪物的血是否合法
- *
- * @param integer $value
- * @access private
- * @return void
- */
- private function validate($value){
- if (is_int($value) && $value > 0) {
- return $value;
- } else {
- return 0;
- }
- }
- /**
- * 查看是否存在怪物的状态
- *
- * @param string $key
- * @return boolean
- */
- public function __isset($key) {
- return isset($this->state[$key]);
- }
- /**
- * 查看怪物的属性是否为0
- *
- * @param string $key
- * @access public
- * @return void
- */
- public function __unset($key) {
- if ($this->state[$key] > 0) {
- echo '你没权利宰了我!';
- } else {
- unset($this->state[$key]);
- echo '我死了!';
- }
- }
- }
- $monster = new Monster();
- $monster->__set('hp', 100); //设定怪物的血和蓝
- $monster->__set('mp', 100);
- $monster->__set('血', 200); //输出没有此属性!
- while (isset($monster->hp)) { //当怪物还存在的时候
- $monster->hit(); //开始打怪
- unset($monster->hp); //消灭怪物
- echo $monster->__get('hp'); //输出怪物剩余血量当怪物死后输出没有此属性!
- }