PHP不知道为什么,钟爱“魔术”这个词,什么都弄魔术。其实想想也非常形象,就拿PHP类中的魔术方法来说吧,你不需要去调用
他们的任何一个方法,他们却可以执行,影响你的程序。
在学习之前我要说两句,首先你不能漠视魔术方法,不要认为你不用这世界上用的人就非常少,作用就小。其实 PHP的魔术方法是PHP的一大特征,既然你学了一种语言,连它最基本的特征都没学,还叫学习了这个语言了吗?其次,不要认为看完我这篇文章就可以深入的 理解魔术方法了,我只是带您入门,以后的路还得您自己走。最后,腾讯曾经出了个面试题,就是关于魔术方法的,如果你学习完我这篇文章,应付这道题应该不成 问题了。还有一点,学习魔术方法一个最重要的地方就是学习它的用法,也就是它在什么情况下被调用。这是重点中的重点,请在下面的学习中注意这点。
首先学习PHP4中就有的魔术方法——__sleep()和__wakeup()。
其实这两个方法用的非常非常的少,少到在我接触PHP1年半的前提下,如果不去主动学习他们根本不知道他们的存在,但是本着对
学术严谨的态度,多学点没什么坏处,用不了多少时间,所以你也可以跟着我花几分钟的时间熟悉一下这两个方法。
我刚才说他们用的非常非常的少,我用了两个非常,大家可以想想一下他们的程度。不光我们要知道他们用的少,重要的是知道为
什么用的少。我先写一段代码,你看看你是否见到过或者曾经写过,我见识比较少,至少我之前是没有见过这么写的。
[php]
class Test {
public $testStr;
public function fun() {
//搞点小事情
}
}
$test = new Test();
echo serialize($test); //输出 O:4:"Test":1:{s:7:"testStr";N;}
[/php]
它竟然把一个类的给序列化了,也就是把一个类转换成了一个字符串,可以传输或者保存下来。我以前可没有接触过这么神奇的事情。
下面我修改一下上面的代码,上面不是要搞点小事情吗?那我现在就搞给你看!
[php]
class Test {
public $testStr;
public function __construct($str) {
$this->testStr = $str;
}
}
$test = new Test("Skiyo.cn");
echo serialize($test); //输出 O:4:"Test":1:{s:7:"testStr";s:8:"Skiyo.cn";}
[/php]
大家可以看到,$testStr被设置后,序列化后也对应了相应的值,但是现在有个问题,比如我这个变量是个秘密呢?而且我又得把这个类序列化传给别的地方呢?
看下面的代码
[php]
class Test {
public $mySecret; //我的秘密不想让人知道
public function __construct($secret) {
$this->mySecret = $secret;
}
}
$test = new Test("我的心里话 我爱某某某");
echo serialize($test); //输出 O:4:"Test":1:{s:8:"mySecret";s:32:"我的心里话 我爱某某某";}
[/php]
大家可以看到,我的秘密序列化后还是存在的,可是我不想我的心里话被别人看到。这个时候PHP很贴心,她知道你的问题,所以设置了魔术方法。
__sleep() 就表示当你执行serialize()这个序列化函数之前时的事情,就像一个回调函数,所以在这个回调函数里面我们就可以做点事情,来隐藏我的秘密。
[php]
class Test {
public $mySecret; //我的秘密不想让人知道
public function __construct($secret) {
$this->mySecret = $secret;
}
public function __sleep() {
$this->mySecret = "你休想知道我的秘密!";
return array('mySecret'); //一定要返回变量,不然返回的是个空,所以序列化也就没有东西了。
}
}
$test = new Test("我的心里话 我爱某某某");
echo serialize($test); //输出 O:4:"Test":1:{s:8:"mySecret";s:28:"你休想知道我的秘密!";}
[/php]
大家看到了吧?我的心里话被加密了,这个就是__sleep()的作用。至于__wakeup()和__sleep()大同小异,只不过是反序列化之前进行的回调函数。我不详细说了,大家看下下面的代码就明白了。
[php]
class Test {
public $mySecret; //我的秘密不想让人知道
public function __construct($secret) {
$this->mySecret = $secret;
}
public function __sleep() {
$this->mySecret = "你休想知道我的秘密!";
return array('mySecret'); //一定要返回变量,不然返回的是个空,所以序列化也就没有东西了。
}
public function __wakeup() {
$this->mySecret = "我的秘密又回来了,哈哈!";
//反序列化就不用返回数组了,就是对字符串的解密,字符串已经有了不用其他的了。
}
}
$test = new Test("我的心里话 我爱某某某");
print_r(unserialize(serialize($test))); //输出 Test Object ( [mySecret] => 我的秘密又回来了,哈哈! )
[/php]
到此为止我们的__sleep()和__wakeup()两个魔术函数就学习完毕了。
即使你曾经对魔术方法没有任何接触,经过上节课的学习,那么我相信你也对魔术方法有一定的了解了。有了上节课的基础,我们的学习将会非常简单,因为魔术方法都是大同小异的东西。
今天我们要学习的是四个非常有用的魔术方法:__set() __get() __isset() __unset() ,特别是其中的 __set() 和 __get() ,在以后的面向对象的编程中经常会遇到,所以也是我们今天的重点。
我们还是从一个代码的例子出手。我们先来按照Java中类的思想写一个类。
[php]
class Test {
//私有属性
private $a;
private $b;
private $c;
private $d;
private $e;
private $f;
private $g;
private $h;
private $i;
private $j;
//还有很多很多....
//Getters & Setters
public function getA() {
return $this->a;
}
public function getB() {
return $this->b;
}
public function getC() {
return $this->c;
}
public function getD() {
return $this->d;
}
public function getE() {
return $this->e;
}
public function getF() {
return $this->f;
}
public function getG() {
return $this->g;
}
public function getH() {
return $this->h;
}
public function getI() {
return $this->i;
}
public function getJ() {
return $this->j;
}
public function setA($a) {
$this->a = $a;
}
public function setB($b) {
$this->b = $b;
}
public function setC($c) {
$this->c = $c;
}
public function setD($d) {
$this->d = $d;
}
public function setE($e) {
$this->e = $e;
}
public function setF($f) {
$this->f = $f;
}
public function setG($g) {
$this->g = $g;
}
public function setH($h) {
$this->h = $h;
}
public function setI($i) {
$this->i = $i;
}
public function setJ($j) {
$this->j = $j;
}
//还有很多很多...
}
[/php]
请不要认为我这是在凑字数,因为你得相信我,我没有稿费,所以不用故意拖这么多字数。
在一般的面向对象的编程中,这种情况是相当常见的,我曾经用Struts2写过一个音乐分享的网站,单单一个注册,因为注册要获取的字段很多,类似PHP 的$_POST,Struts2的机制就是给每个Form元素都设定一个Getter和Setter,这样下来,单单一个注册的类就有多的数不清的 Getters和Setters。这是个非常艰巨的任务,程序员是写程序的,不是抄程序的,所以强大的Eclipse就为我们提供了一个简单的方法,只要 你设定了类的属性,你就可以通过鼠标点击来生成相应的Getter和Setter,所以你不要认为上面的代码是我傻乎乎写的,我是个懒人,十分会偷懒 的:)
扯远了,PHP为了解决这个问题,而且又有魔术方法的基础,所以PHP创造了 __set() 和 __get() 。你只需要写一个方法,就可以代表上面大部分的Getters和Setters。
[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
[/php]
通过上面的例子,我们就给A设置了一个值,并且打印了出来。
现在我来说上面为什么是$this->$key。这要说明白也是个不小的话题,PHP比较神奇,她可以设置一个变量的变量,什么意思呢?来看下面的例子。
[php]
$a = 'b';
$b = 'c';
echo $$a; //输出c
[/php]
哇好神奇,竟然有连个$,好多的钱啊。下面我给你分解下。
[php]
$a = 'b';
$b = 'c';
echo ${$a}; //这样看的话是不是就明白多了呢? $a='b',所以大括号中就是b了,加上前面的$,就变成变量$b了。
[/php]
回到上面的类中,你可以注意到,$key只是我们需要传进来的一个内部属性,不是我们真正需要的,如果$this->key这样就会编程$key这 个属性的值了,而我们需要设置的是$this->a的值,所以这里就需要使用变量的变量了。明白了吗?有点绕,好好想想:)
经过这么折腾,我想你已经会一点 __get() 和 __set() 的使用方法了。但是上面的代码还不能用于实际的代码中,因为在实践中,往往内部属性的个数是不定的,你想到了什么解决办法?对,就是数组,PHP中数组的 长度是不定的,所以我们就可以给他无限增加元素。
[php]
class Test {
public $values = array(); //存放属性的数组,以键值对的形式存在的。
public function __get($name){
return $this->values[$name];
}
public function __set($name, $value){
$this->values[$name] = $value;
}
}
$test = new Test();
$test->__set('a', '我是a');
$test->__set('b', '我是b');
echo $test->__get('a'); //输出 我是a
echo $test->__get('b'); //输出 我是b
[/php]
这样的话,如果是注册的页面,我们就可以灵活的获得和设置属性了。但是在实际运用中,我们知道传过来的值是不可靠的,我们得需要HTML转义,Mysql转义等等,所以我们可以写一个简单的回调函数,做数据验证的作用。
[php]
class Test {
public $values = array();
public function __get($name){
return $this->values[$name];
}
public function __set($name, $value){
$this->values[$name] = $this->validate($value);
}
private function validate($value){
return htmlspecialchars(addslashes($value));
//等等
}
}
$test = new Test();
$test->__set('a', '<a>我是a</a>');
echo $test->__get('a'); //输出 <a>我是a</a> 被转义啦...
[/php]
但是有的人不需要这样的,比如我前面提交的页面就有用户名和密码,不想再要别的了。当然,你如果用两个私有属性也可以解决,但是我说一下用一个数组的方法。
[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('随便看看'); //输出 没有此属性!
[/php]
到这里,我们算是基本掌握了 __set() 和 __get() 了,其实要学精通还是需要你自己去努力的,我只是带你入门而已。下面我们来看看 __isset() 和 __unset() 。
(责任编辑:ken)