大家等我教程等着急了吧?这几天事情比较多。
看到很多人看完上面的笔记还是有很多人不明白,不清楚,或者说有疑问的地发。不要着急,我今天绝对打消您的疑问。您瞧好吧。
今天我们利用前几天学习的知识,学习如何写一个动态类,其实是就是一个具有柔韧性的类。
什么是一个具有柔韧性的类?其实我也说不清楚,有的人更愿意称之为可以弯曲的类,因为这个类可以随你的意愿进行有特点的伸展。
我们上次提到了__set()这个魔术方法,又提到了,其实魔术方法之所以称之为魔术方法是由于我们不用主动去触发这个方法它就会自动的运行。
有的人会问了,在学习__set()的时候,我们是主动去调用它的啊。跟别的,比如__isset()方法有些不一样。其实在真正的运用中,__set()的使用方法不是上面那样的,请允许我为这篇文章埋下一个伏笔。今天我们就来学习__set()的最神奇的地方。
一个柔韧性的类的一个重要的特征应该就是属性具有柔韧度。如何制作一个具有伸缩属性的类?看下面的例子。
[php]
class Test {
private $array; //PHP数组就是具有伸缩性的这个特性
public function __set($key, $value) { //__set()魔术方法,设置$array的值。
$this->array[$key] = $value;
}
public function get() {
print_r($this->array);
}
}
$test = new Test();
$test->a = 'a'; //这样系统就会自动调用__set()这个魔术方法
$test->b = 'b'; //这样下去,我们就可以设置无数个属性。
$test->c = 'c'; //你还可以设置$test->d 。没有人拦着你,谁拦你我拦他!
$test->get(); //输出 Array ( [a] => a => b [c] => c )
[/php]
这只是个简单的例子,如果你要限制一些属性,该如何设置呢?还记得前面的课程吗?
[php]
class Test {
private $array;
public function __construct() { //在构造函数中我们可以设置一些属性。
$this->array['a'] = null;
$this->array['b'] = null;
}
public function __set($key, $value) { //__set()魔术方法,进行一下简单的判断就OK
if (array_key_exists($key, $this->array)) {
$this->array[$key] = $value;
} else {
throw new Exception("没有此属性");
}
}
public function get() {
print_r($this->array);
}
}
$test = new Test();
$test->a = 'a';
$test->b = 'b';
$test->c = 'c'; //当程序执行到这步的时候就会抛出异常。
$test->get();
[/php]
有人肯定又会说了,我靠。你耍我们玩啊,一会说要写个可以伸缩的类,然后又说要做限制,你怎么回事啊?
莫急莫急,我这就跟您慢慢解释。要写个具有伸缩性的类,上面我说了最简单的例子了,大家应该都明白了。
但是在实际的程序中,我们并不需要这样伸缩性的类,因为这样的类会变得不安全,未知因素也很多。这不是我们设计程序的初衷。
但是,在实际的运用中,我们又需要根据实际情况来设定一些变化的因素。这样就会有了矛盾了,既要伸缩性,但是在某种程度上又不允许程序具有伸缩性。
这是个非常棘手的问题,虽然我们绕弯避免。最简单的解决办法就是多申请几个属性,想用就用。但是我们既然学了魔术方法,就要使用魔术方法来做点事情。值得 注意的是,构造函数在PHP里也算是一个魔术方法。所以我们来写个要伸就伸,要缩就缩的类来。这种类的用处非常大,等下你就知道了。
[php]
class TestA {
private $array = array();
public function __construct($array) { //在父类中创建一个具有伸缩性的构造方法
foreach( $array as $key )
$this->array[ $key ] = null;
}
public function __set($key, $value) { //__set()魔术方法,进行一下简单的判断就OK
if (array_key_exists($key, $this->array)) {
$this->array[$key] = $value;
} else {
throw new Exception("没有此属性");
}
}
public function get() {
print_r($this->array);
}
}
class TestB extends TestA {
public function __construct($array) { //只要重写构造函数就可以申请自己的属性值.
parent::__construct($array); //调用父类的构造方法
}
}
$test1 = new TestB(array('a', 'b')); //想要几个属性就要几个属性
$test1->a = 'a';
$test1->b = 'b';
$test1->get(); //输出 Array ( [a] => a => b )
$test2 = new TestB(array('a', 'b', 'c')); //想要几个属性就要几个属性
$test2->a = 'a';
$test2->b = 'b';
$test2->c = 'c';
$test2->get(); //输出 Array ( [a] => a => b [c] => c )
[/php]
这下明白了吧?我们是通过继承来实现这种中性的伸缩的。这就叫伸缩自如。哈哈。
如果你体会到了其中的好处,你会发现,PHP的类在这方面会比Java要强大的多。我们下面试图用这种思路来写一个简单的数据库类。
[php]
class DB {
public function connect($dbhost, $dbuser, $dbpwd, $dbname = '', $charset = 'utf8') {
mysql_connect ( $dbhost, $dbuser, $dbpwd );
mysql_query ( "SET NAMES '$charset'" );
mysql_select_db ( $dbname );
}
public function insert($sql) { //插入数据
if (! ($query = mysql_query ( $sql ))) {
return -1;
}
return mysql_insert_id();
}
}
[/php]
上面这是个最基础的类,我写的很垃圾,你可以再修改,但是我们现在最重要的是让他可以具有伸缩性。所以我这样修改。
[php]
class DB {
private $table = ''; //定义表
private $field = array(); //表里面的字段
public function __construct($table, $field) { //在父类中创建一个具有伸缩性的构造方法
$this->table = $table;
foreach( $field as $key ) {
$this->$field[ $key ] = null;
}
}
public function connect($dbhost, $dbuser, $dbpwd, $dbname = '', $charset = 'utf8') {
mysql_connect ( $dbhost, $dbuser, $dbpwd );
mysql_query ( "SET NAMES '$charset'" );
mysql_select_db ( $dbname );
}
public function insert() { //插入一个数据
$fieldName = join(", ", array_keys($this->field)); //获得字段名
array_walk($this->field, array(&$this, 'filter')); //过滤字段的值.可以看下下面的回调函数
$fieldValue = join(", ", $this->field); //转换成**,**,**这种形式
$sql = "INSERT INTO %s(%s) VALUES(%s)";
$sql = sprintf($sql, $this->table, $fieldName, $fieldValue);
if (!(mysql_query ( $sql ))) {
return -1;
}
return mysql_insert_id();
}
public function filter(&$value) { //过滤字段的值
if (!is_numeric($value)) { //如果是数字就不管.如果是其他类型必须加入''才可以正常插入到数据库中
$value = "'".addslashes($value)."'";
}
}
}
[/php]
你看完后,肯定会说,好麻烦啊,你为什么把一个简单的插入数据搞得这么复杂啊。你傻吧你!
很客观的告诉你,我不傻,这么做是有好处的。如果我写了一个这样类,那么面向用户是非常好的,扩展性非常好,并且容易操作。我们来看下,当你把我这个类当做父类后,看下你的操作是多么的简单。
[php]
class TableA extends DB {
public function __construct() { //只要重写构造函数就可以申请自己的属性值.
parent::__construct('tablea',array('id', 'key', 'value')); //调用父类的构造方法
}
}
$tablea = new TableA();
$tablea->id = 123;
$tablea->key = '我是key';
$tablea->value = '我是value';
$tablea->insert();
[/php]
看到了吧?我们几乎可以使MySqL变成一个对象型的数据库,因为我们可以给每个表都写个类,一个类对应一个表,执行操作的时候不用写SQL语句,非常简单,不容易出错,这不是传说中大名鼎鼎的Hibernate吗?
说Hibernate有点言过其实了,主要是我们改变了PHP与MySql的一般操作方式。但是这样的问题就在于父类不好写,比较麻烦。对于一般的新手还是非常困难的,所以希望个大牛能写出个通用的父类来,感激不尽啊。
好了,拖了这么长时间,笔记终于算写完了,但是PHP中魔术方法的内容远远没有这么少,所以要学习还得靠自己。我以后还会不断的出类似这样的教程笔记,希望大家关注,谢谢,再次感谢您能看完我这么多字的胡扯。
觉得set之所以称为魔术方法应该是这个原因
<?php
class test
{
public $a;
public function __set($name,$value)
{
$this->a[$name]=$value;
}
}
$a=new test();
$a->b=2;
echo $a->a['b'];
[php]
<?php
class Test {
private $a;
public function __set($name, $value) { //必须是两个参数
$this->$name= $value;
//这里不明白为什么是$this->$name ?? 一般不是这样写吗?
}
public function __get($name) { //一个参数,要获得的属性
if( isset($this->$name))
{
echo "{$name} 的值是:";
return $this->$name;
} else {
$this->$name=0;
return "{$name} 未设置, {$name} 被初始化为0.<br />";
}
}
}
$test = new Test();
$test->__set('a', '我是A');
echo $test->__get('a');
echo '<br />';
echo $test->__get('b');
echo $test->__get('b');
?>`
[/php]
[php]
<?php
class Test {
//var $a; //这样就不输出
// public $a; //这样也不输出
//protected $a; //输出 你小子偷看我??
private $a; //输出 你小子偷看我??
public function __construct($value) {
$this->a = $value;
}
public function __isset($name) {
echo "你小子偷看我??";
return ($name); //返回真是的信息
}
public function __unset($name) {
echo "你没权利宰了我!";
unset($name); // 将name 改为$this->a 才消除a??
}
public function get(){
return $this->a;
}
}
$test = new Test("我是a");
isset($test->a);
unset($test->a); // isset 和 __isset() 之间的执行关系是怎样的????
echo $test->get();
?>
[/php]
[php]
<?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($name); // 这里需要返回一个bool类型.应该这样写 如果不想让别人知道就直接 return false;
}
public function __unset($name) {
echo "你没权利宰了我!";
unset($name); // $name为形参.当然不能删除$this->a
}
public function get(){
return $this->a;
}
}
$test = new Test("我是a");
isset($test->a);
unset($test->a); // 当执行isset()的时候.系统就去找是否存在__isset() 如果存在就去执行.就是isset()的一个回调.
echo $test->get();
?>
[/php]
(责任编辑:ken)