PHP类和对象
jopen
11年前
1、定义和创建类和对象:
定义类要使用class关键字。例如:class 类名{//属性和方法}
创建对象使用new关键字。例如: $p1 = new 类名;,可以基于一个类创建多个对象。
2、 类属性值
(1) 在类中除了声明属性外,也可以为属性赋值,但是只能以如下几种形式给属性指定常量值:
示例1: public $last_visitor = 'Donnan'; //正确 public $last_visitor = 9; //正确 public $last_visitor = array('Jesse'); //正确 public $last_visitor = pick_visitor(); //错误 public $last_visitor = 'Chris'.'9'; //错误 (2)在类中的方法为变量指定一个非常量值: 示例2: class guest_book{ public $last_visitor; public function update($comment, $visitor){ if(!empty($comments)){ array_unshift($this->comments, $comments); $this->last_visitor = $visitor; //在类中的方法为变量指定一个非常量值 } } } (3)在创建对象的过程中为变量指定非常量值,即在类的构造器中进行赋值。 构造器指的是在创建新对象时自动调用的一个方法,而其名字为__construct() 示例3: class guset_book{ public $comments; public $last_visitor; public function __consturct($user){ $dbh = mysqli_connect('localhost', 'username', 'password', 'site'); $user = mysqli_real_escape_string($dbh, $user); $sql = "select comments, last_visitor from guest_book where user ='$user'"; $r = mysqli_query($dbh, $sql); if($obj = mysqli_fetch_object($dbh, $r)){ $this->comments = $obj->comments; //为变量指定非常量值 $this->last_visitor = $obj->last_visitor; //为变量指定非常量值 } } } $gb = new guest_book('stewart'); //为变量指定非常量值
3、继承与覆盖
(1) 通过继承来扩展一个现存的类,使用关键字extends:
class 类名 extends 父类名{} (2)覆盖父类方法: 示例4: class DB{ function getResult(){ return $this->result; } function query($sql){ error_log("queryy() must bue overridden by a database-specific child"); return false; } } class MySQL extends DB{ // // MySQL类从父类DB继承了getResult()方法,但是重新实现了自己特定的MySQL方法query() function query($sql){ $this->result = mysql_query($sql); } } (3)在方法名前面前置parent::用以明确地调用一个父类的方法: 示例5: fuction escape($sql){ $safe_sql = mysql_real_escape_string($sql); $safe_sql = parent::escape($safe_sql); // parent method adds '' around $dql; return $safe_sql; } (4)注意:当子类中的方法覆盖了父类中的方法时,除非明确地引用(parent::),否则不会自动调用父类的方法。
4、对象解构器
(1)是一个当对象被销毁时调用的方法即让PHP在对象被删除之前调用的一个方法。方法名__destruct() 示例6: class Car{ function __destruct(){ //... } } (2)应用:断开与数据库的连接,保存数据库内容,向远程服务器发送ping命令。 (3)注意:与构造器不同,不能想结构器传递参数,因为你无法确定它会在何时被调用;不能假设PHP会按照某一种特定的次序来逐一销毁对象,故不能在结构器中引用另一个对象--因为PHP可能已经把那个对象删除了。 (4)当脚本停止执行时对象会自动被销毁。要强制销毁一个对象,可以使用unset()函数: $car = new Car; //..; unset($car);
5、访问控制:
(1)访问控制即为方法和属性指定可见性,使用public,protected,private关键字。 (2)关键字介绍: public:公共,用public声明一个方法或属性,任何人都可以调用和修改它。 protected:受保护,被protected修饰的方法或属性,只有当前类及扩展了当前类的子类中才可以访问他们。 private:私有,以private声明的方法或属性,只能在当前类中才能访问。 (3)示例: 示例7: class Person{ public $name; protected $age; private $salary; public function __construct(){//..} protected function set_age(){//...} private function set_salary(){//...} }
6、防止修改类和方法:
(1)关键字:final (2)作用:防止其他开发者重新定义一个子类中的特殊方法,或者在子类化过程重新定义正类。 (3)用final声明一个方法的原因: I:如果有人覆盖这个方法,就会面临现实的风险。 II:这个方法很完美,已经无法再改进了。 (4)用final修饰的类即终类,一个终类是不能被子类化的。终类与都是终方法的类的不同是,后者是可以扩展的(即被继承),并且虽然不能修改任何一个已经存在的方法,但能够添加额外方法。 (5)示例: 示例8: final public function connect($server, $username, $password){//...} final class MySQL{//...}
7、定义字符串化的对象
(1)介绍:PHP为对象提供了一种控制他们如何转换为字符串的方法,这样可以使我们不用写很多代码就能够以一种友好的方式输出一个对象。 (2)示例: 示例9: class Car{ public $type; public $brand; protected $price; function setPrice($price){ $this->price = $price; } function setBrand($brand){ $this->brand = $brand; } function setType($type){ $this->type = $type; } function __toString(){ return "$this->brand, $this->type, $this->price"; } } $car = new Car; $car->setPrice('市场价:16.00 至 31.18万元'); $car->setType('小型'); $car->setBrand('奥迪'); print $car; (3)输出:奥迪, 小型, 市场价:16.00 至 31.18万元 (4)注意:如果你感觉到自己可能会通过__toString()方法返回一个非字符串值的话,可以考虑明确对返回值进行转换,如上面例子中的 function __toString(){ return "$this->brand, $this->type,".(string) $this->price; } (5)直接调用__toString() print htmlentities($car->__toString());
8、接口
(1)定义接口使用关键字interface。在接口的内部,只需要定义方法的原型,而不用实现这些方法。 示例10: interface Nameable{ public function getName(); public function setName($name); } (2)实现接口: 如果一个类实现了一个接口的所以方法,就可以说它实现这个接口,如果未能实现接口中定义的所以方法,或者以不同的原型实现了这些方法,会导致PHP报出致命错误。 一个类可以实现任意多个接口。 (3)实现接口示例: 示例11: class Book implements Nameable{ public $name; public function getName(){ return $this->name; } public function setName($name){ $this->name = $name; } } (4)如何检查一个类是否实现了某一接口? 有两种方式: 方式一: 示例12: class Book implements Nameable{//...} $interface = class_implements("Book"); if(isset($interface['Nameable'])){ print "Book implements Nameable \n"; //输出:Book implements Nameable } 方式二: 示例13: class Book implements Nameable{//...} $rc = new ReflectionClass('Book'); if($rc->implementsInterface('Nameable')){ print "Book implements Nameable \n"; //输出:Book implements Nameable }
9、抽象类
(1)定义:在类的定义前添加abstract关键字就是可以定义一个抽象类,同时,抽象类中必须要定义至少一个抽象方法。在方法定义的前面添加abstract关键字就是定义抽象方法。 (2)抽象方法有两个必要条件: 抽象方法不能定义为私有方法,因为它们需要被继承。 抽象方法不能定义为最终方法,因为他们需要被覆盖。 (3)注意:如果一个类中包涵了一个抽象方法,那么这个类必须声明为抽象类;抽象类可以包含非抽象方法。 (4)应用:抽象类最适用于一系列涉及“是什么”关系的对象。这样就可以使得它们都从一个公共的父类衍生下来具有逻辑上的意义。区别在于子类是实际的类,而父类是抽象的类。 (5)示例14: abstract class DataBase{ abstract public function connect(); abstract public function query(); abstract public function fetch(); abstract public function close(); } class MySQL extends DataBase{ protected $dbh; protected $query; public function connect($server,$username, $password, $database){ $this->dbh = mysql_connect($server, $username, $password, $database); } public function query($sql){ $this->query = mysqli_query($this->dbh, $sql); } public function fetch(){ return mysqli_fetch_row($this->dbh, $this->query); } public function close(){ mysqli_close($this->dbh); } }
10、传递对象引用
(1)传递对象引用:就是用“=”把一个对象的引用赋给一个变量,以便在更新一个对象时,同时更新另一个。 (2)实质:在使用“=”进行对象赋值时,并没有创建该对象的一个副本,而是传递了一个对该对象的引用。所以,修改一个也就同时修改了另一个。这与php5处理其他类型变量的方式不同,对其他类型的变量传递的则是值的副本。也不同与php4,在php4中所有的变量都是传递值的副本。所以,在PHP4中需要使用“=&”来使两个对象建立链接,而现在只需要使用“=”就可以。 (3)示例15: $car = new Car; $car->setPrice('市场价:16.00 至 31.18万元'); $car->setType('小型'); $car->setBrand('奥迪'); $audi = $car; //现在$audi和$car实际上就是一个对象的两个名称
11、克隆对象与聚合类
(1)克隆对象方案:用“=”实现通过引用来拷贝对象,要拷贝对象的值,要使用clone。 (2)clone关键字克隆对象的实质:使用clone关键字克隆的过程中会把一个对象中所有的属性都拷贝到第二个对象中。其中也包括属性中保存的对象,所以克隆的对象不会与原始对象共享一个引用,即改变克隆的对象的属性值不会影响到原来对象的属性值。 (3)聚合类:所谓聚合类是指在类中以某种方式嵌入了其他的类,使得不论访问原始的类,还是嵌入的类都很方便。 (4)示例16: class Address{ protected $city; protected $country; public function setCity($city){ $this->city = $city; } public function getCity(){ return $this->city; } public function setCountry($country){ $this->country = $country; } public function getCountry(){ return $this->country; } } class Person{ protected $name; protected $address;//Person 有一个 Address public function __construct(){ $this->address = new Address; } public function setName($name){ $this->name = $name; } public function getName(){ return $this->name; } public function __call($method, $arguments){ if(method_exists($this->address, $method)){ return call_user_func_array(array($this->address, $method), $arguments); } } } $rasmus = new Person; $rasmus->setName('Rasmus Lerdorf'); $rasmus->setcity('Sunnyvale'); $zeev = clone $rasmus; $zeev->setName("Zeev Surask"); $zeev->setCity('Tel Aviv'); print $rasmus->getName()." live in ".$rasmus->getCity(); echo "\n"; print $zeev->getName().' live in '.$zeev->getCity(); echo "\n"; 输出结果: Rasmus Lerdorf live in Tel Aviv Zeev Surask live in Tel Aviv (5)上方示例的问题:zeev对象调用setName()时没有问题,而当调用getCity()反而结果出现了问题,这是因为$name属性是一个字符串,它在传递时拷贝的是只,但是$address是一个对象,而对象拷贝的是引用,这种形式的对象克隆成为“浅克隆”,或者“浅拷贝。相对的“深克隆”指的是克隆所有有关对象读克隆。要在php5中控制克隆对象,是通过在相应类中实现一个__clone()方法来实现的。在__clone()方法的内部会有一个保存$this变量中的浅拷贝来表示PHP没有 __clone()方法时提供的对象。因为PHP已经拷贝了所有属性,所以只需要覆盖不需要的属性即可。在Person类中,$name是没有问题的,只有$address需要明确地克隆。在上面Person类的最后加上一下代码: public function __clone(){ //需要明确的克隆的属性 $this->address = clone $this->address; } 之后输出: Rasmus Lerdorf live in Sunnyvale Zeev Surask live in Tel Aviv (6)关于__clone()方法: 使用clone操作符克隆保存在属性中的对象,会导致PHP检测这些对象中是否包含__clone()方法,如果包含一个,PHP就会调用它。而且,即使对于嵌套在更深层中的对象也会重复这样检查。这一过程保证了正确地克隆完整的对象,别且也证明了它为什么被称为深度克隆。
12、调用由另一个方法返回对象的方法
如果你想要调用由另一个方法返回的对象的方法,直接在地一个方法后面调用第二个方法,如: $organge = $fruit->get('citrus')->peel(); 这是在php4基础上的一个改进措施。在php4中,要实现同样的功能需要使用一个临时的变量。 $organge = $fruit->get('citrus'); $organge->peel();
13、聚合对象
(1)理解:你想把两个或多个对象组合在一起,使结果就像是一个对象 (2)聚合对象并用__call()方法截获对方法的调用。 __call()方法会捕捉到任何对未在类中定义的方法的调用。在调用这方法时,需要传递两参数,一个是方法名,另一个是保存着要传递给方法的参数的数组$arguments。 检测是通过使用method_exists()方法并为其提供两个参数,第一个参数是对象,第二个参数是方法名来进行的。如果检测结果返回true,也就是说调用的方法是有效的,那么可以调用它。 call_user_func_array()方法是用来解决传递给方法的参数数组$argumentsde ,这个函数可以让我们调用一个用户函数,并且在数组中传递参数。传递给它的地一个参数是你的函数名,第二个参数是数组参。但是,在这里我们想要调用的是对象的一个方法而非是一个函数,有一种特殊的语法可以用于这种情况,即不传递函数名,而是代之以传递一个包含两个元素的数组,这个数组中第一个元素是对象,另一个元素是要被调用的方法名。这样就可以通过call_user_func_array()调用这个对象的方法了,之后必须将call_user_func_array()的结果返回给原始的调用程序,要不然返回的值就会悄无声息地废弃掉。 (3)示例如示例16
14、类常量与全局常量的定义和使用
示例17: define('pi', 10);//全局pi常量,本质上是final属性 class Circle{ const pi = 3.1415926;//类中的pi常量 protected $radius; public function __construct($radius){ $this->radius = $radius; } public function circumference(){ return 2 * pi * $this->radius;//全局pi常量 } public function circumference2(){ return 2 * self::pi * $this->radius;//类中的pi常量 } } $c = new Circle(1); print $c->circumference(); //输出:20 echo "\n"; print $c->circumference2(); //输出:6.2831852 echo "\n";
15、静态属性和方法:
示例18: /*定义静态属性和方法 *调用静态方法通过类名和::来调用,不能在静态方法中使用$this变量 *调用静态属性通过self::$属性名, */ class Format{ public static function number($number, $decimals = 2, $decimal = '.', $thousands = ','){ return number_format($number, $decimals, $decimal, $thousands); } } print Format::number(1234.567); //调用静态方法 class Database{ private static $dbh = NULL; public function __construct($server, $username, $password){ if(self::$dbh == NULL){ //调用静态属性 self::$dbh = db_connect($server, $username, $password) }else{ self::$dbh = self::$dbh ; } } }
16、控制对象的序列化
示例19: class LogFile{ protected $filename; protected $handle; public function __construct($filename){ $this->$filename = $filename; $this->open(); } private function open(){ $this->handle = fopen($this->filename, 'a'); } public function __destruct($filename){ fclose($this->handle); } //当对象序列化调用时调用,返回一个可序列化的对象属性的数组 public function __sleep(){ return array('filename'); } //当对象饭序列化时调用 public function __wakeup(){ $this->open(); } }
17、分析对象
(1)方法:使用反射(Reflection)类来查明对象的信息 (2)示例20: class Person{ public $name; protected $spouse; private $password; public function __construct($name){ $this->name = $name; } public function getName(){ return $this->name; } public function setSpouse(Person $spouse){ if(!isset($this->spouse)){ $this->spouse = $spouse; } } public function setPassword($password){ $this->password = $password; } } Reflection::export(new ReflectionClass('Person')); 运行结果: Class [ <user> class Person ] { @@ /home/suiyc/workspace/sycPro/class/Reflection.php 2-24 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [3] { Property [ <default> public $name ] Property [ <default> protected $spouse ] Property [ <default> private $password ] } - Methods [4] { Method [ <user, ctor> public method __construct ] { @@ /home/suiyc/workspace/sycPro/class/Reflection.php 7 - 9 - Parameters [1] { Parameter #0 [ <required> $name ] } } Method [ <user> public method getName ] { @@ /home/suiyc/workspace/sycPro/class/Reflection.php 11 - 13 } Method [ <user> public method setSpouse ] { @@ /home/suiyc/workspace/sycPro/class/Reflection.php 15 - 19 - Parameters [1] { Parameter #0 [ <required> Person $spouse ] } } Method [ <user> public method setPassword ] { @@ /home/suiyc/workspace/sycPro/class/Reflection.php 21 - 23 - Parameters [1] { Parameter #0 [ <required> $password ] } } } }
18、检查某对象是不是一个特定类的对象
(1)关键字:instanceof (2) 示例21: if($car instanceof Car){ print " car is the instance of class Car! \n"; }else{ print " car is not the instance of class Car! \n"; }