php 类与对象

1.类与对象

对象:实际存在该类事物中每个实物的个体。$a =new User(); 实例化后的$a

引用:PHP的别名,两个不同的变量名字指向相同的内容

封装: 把对象的属性和方法组织在一个类(逻辑单元)里

继承:以原有的类为基础,创建一个新类,从而代码复用的目的;

多态:允许将子类类型的指针赋值给父类类型的指针。

-------------------------------------



2.自动加载对象:

自动加载通过定义特殊的__autoload函数,当引用没有在脚本中定义的类时会自动调用这个函数.

[php]view plaincopy

print?
  1. function __autoload($class){
  2. require_once("classes/$class.class.php");
  3. }

为什么要使用__autoload

1,首先是不知道这个类文件存放在什么地方,

2,另外一个就是不知道什么时候需要用到这个文件。

3,特别是项目文件特别多时,不可能每个文件都在开始的部分写很长一串的 require …

替代了一

require_once ("classes/Books.class.php") ;

require_once ("classes/Employees.class.php" ) ;

require_once ("classes/Events.class.php") ;

require_once ("classes/Patrons.class.php") ;

zend推荐了一种最流行的办法,在文件名中包含路径。例如下面的例子:

[php]view plaincopy

print?
  1. view sourceprint?
  2. // Main.class
  3. function __autoload($class_name) {
  4. $path = str_replace('_', DIRECTORY_SEPARATOR, $class_name);
  5. require_once $path.'.php';
  6. }

$temp = new Main_Super_Class();

所有的下划线都会被替换成路径中的分隔符,上例中就会去 Main/Super/Class.php文件。

缺点:

是在编码过程中,必须明确的知道代码文件应当所处的位置,

而且由于将文件路径硬编码在了类名中,如果需要修改文件夹的结构时,我们必须手工修改所有的类名。

如果是在一个开发环境中,并且对于速度不是很在意的话,使用'Include All’这个方法是非常方便的。

通过将所有类文件放在一个或几个特定文件夹中,然后通过遍历的方式查找加载。

例如

[php]view plaincopy

print?
  1. <?php
  2. $arr = array (
  3. 'Project/Classes',
  4. 'Project/Classes/Children',
  5. 'Project/Interfaces'
  6. );
  7. foreach($arr as $dir) {
  8. $dir_list = opendir($dir);
  9. while ($file = readdir($dir_list)) {
  10. $path = $dir.DIRECTORY_SEPARATOR.$file;
  11. if(in_array($file, array('.', '..')) || is_dir($path))
  12. continue;
  13. if (strpos($file, ".class.php"))
  14. require_once $path;
  15. }
  16. }
  17. ?>

另外一个方法是在类文件和他的位置之间建立关联的配置文件,例如:

[php]view plaincopy

print?
  1. view sourceprint?
  2. // configuration.php
  3. array_of_associations = array(
  4. 'MainSuperClass' = 'C:/Main/Super/Class.php',
  5. 'MainPoorClass' = 'C:/blablabla/gy.php'
  6. );

调用的文件

[php]view plaincopy

print?
  1. <?php
  2. require 'autoload_generated.php';
  3. function __autoload($className) {
  4. global $autoload_list;
  5. require_once $autoload_list[$className];
  6. }
  7. $x = new A();
  8. ?>

------------------------------------------------



3.构造函数和析构函数

PHP 构造方法 __construct() 允许在实例化一个类之前先执行构造方法。

构造方法是类中的一个特殊方法。当使用 new 操作符创建一个类的实例时,构造方法将会自动调用,其名称必须是 __construct() 。

(在一个类中只能声明一个构造方法,而是只有在每次创建对象的时候都会去调用一次构造方法,不能主动的调用这个方法,

所以通常用它执行一些有用的初始化任务。该方法无返回值。)

作用: 用来创建对象时初始化对象

子类执行分类的构造函数parent::__construct().

析构函数: __destruct ()定义:特殊的内成员函数,没有返回类型,没有参数,不能随意调用,也没有重载;

只是在类对象生命结束的时候,由系统自动调用释放在构造函数中分配的资源。

与构造方法对应的就是析构方法,析构方法允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件、释放结果集等。

析构函数不能带有任何参数,其名称必须是 __destruct() 。

作用:清理了善后工作,例如,在建立对象时使用new 开辟了一个内存空间,应在退出前使用析构函数释放在构造函数中分配的资源。

例子:

[php]view plaincopy

print?
  1. class Person {
  2. public $name;
  3. public $age;
  4. //定义一个构造方法初始化赋值
  5. public function __construct($name,$age) {
  6. $this->name=$name;
  7. $this->age=$age;
  8. }
  9. public function say() {
  10. echo "my name is :".$this->name."<br />";
  11. echo "my age is :".$this->age;
  12. }
  13. //析构函数
  14. function __destruct()
  15. {
  16. echo "goodbye :".$this->name;
  17. }
  18. }
  19. $p1=new Person("ren", 25);
  20. $p1->say();

---------------------------------------------------------------



4 .访问控制

对属性或方法的访问控制,是通过在前面添加关键字 public、protected 或 private 来实现的

public 所定义的类成员可以在任何地方被访问;

protected 所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问);

private 定义的类成员则只能被其所在类访问。

对类成员的访问控制

类成员都必须使用关键字public、protected 或 private 进行定义

对方法的访问控制

类中的方法都必须使用关键字public、protected 或 private 进行定义。如果没有设置这些关键字,则该方法会被设置成默认的 public。

例子:

[php]view plaincopy

print?
  1. class MyClass
  2. {
  3. public $public = 'Public';
  4. protected $protected = 'Protected';
  5. private $private = 'Private';
  6. function printHello()
  7. {
  8. echo $this->public;
  9. echo $this->protected;
  10. echo $this->private;
  11. }
  12. }
  13. $obj = new MyClass();
  14. echo $obj->public; // 这行能被正常执行
  15. echo $obj->protected; // 这行会产生一个致命错误
  16. echo $obj->private; // 这行也会产生一个致命错误
  17. $obj->printHello(); // 输出 Public、Protected 和 Private

-------------------------------------------------------------



5 .对象继承

继承定义:以原有的类为基础,创建一个新类,从而代码复用的目的;

--------------------------------------

覆写是对象继承时用到的

重载是单对象中同方法名不同参数的方法

--------------------------------------

继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。

比如,当扩展一个类,子类就会继承父类的所有公有和保护方法。但是子类的方法会覆盖父类的方法。

继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。

[php]view plaincopy

print?
  1. class Person {
  2. public $name;
  3. public $age;
  4. function say() {
  5. echo "my name is:".$this->name."<br />";
  6. echo "my age is:".$this->age;
  7. }
  8. }
  9. // 类的继承
  10. class Student extends Person {
  11. var $school; //学生所在学校的属性
  12. function study() {
  13. echo "my name is:".$this->name."<br />";
  14. echo "my shool is:".$this->school;
  15. }
  16. }
  17. $t1 = new Student();
  18. $t1->name = "zhangsan";
  19. $t1->school = "beijindaxue";
  20. $t1->study();

------- --------- ------ --------- -------- -----



6 .范围解析操作符(::)

范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员、方法和常量,还可以用于覆盖类中的成员和方法。

当在类的外部访问这些静态成员、方法和常量时,必须使用类的名字。

self 和 parent 这两个特殊的关键字是用于在类的内部对成员或方法进行访问的。

注意:

当一个子类覆盖其父类中的方法时,PHP 不会再执行父类中已被覆盖的方法,直到子类中调用这些方法为止

例子:

[php]view plaincopy

print?
  1. <?php
  2. class OtherClass extends MyClass
  3. {
  4. public static $my_static = 'static var';
  5. public static function doubleColon() {
  6. echo parent::CONST_VALUE . "\n";
  7. echo self::$my_static . "\n";
  8. }
  9. }
  10. OtherClass::doubleColon();
  11. ?>

---------------------------------------------------



7.Static关键字

声明类成员或方法为static,就可以不实例化类而直接访问。不能通过一个对象来访问其中的静态成员(静态方法除外)。

静态成员属于类,不属于任何对象实例,但类的对象实例都能共享。

小结:

在类内部访问静态成员属性或者方法,使用 self::(没有 $ 符号),如:

self:: $country //类内部访问静态成员属性

self:: myCountry()

在子类访问父类静态成员属性或方法,使用 parent::(没有 $ 符号),如:

parent:: $country

parent:: myCountry()

外部访问静态成员属性和方法为 类名/子类名:: ,如:

Person::$country

Person::myCountry()

Student::$country

但静态方法也可以通过普通对象的方式访问

[php]view plaincopy

print?
  1. <?php
  2. Class Person{
  3. // 定义静态成员属性
  4. public static $country = "中国";
  5. // 定义静态成员方法
  6. public static function myCountry() {
  7. // 内部访问静态成员属性
  8. echo "我是".self::$country."人<br />";
  9. }
  10. }
  11. class Student extends Person {
  12. function study() {
  13. echo "我是". parent::$country."人<br />";
  14. }
  15. }
  16. // 输出成员属性值
  17. echo Person::$country."<br />"; // 输出:中国
  18. $p1 = new Person();
  19. //echo $p1->country; // 错误写法
  20. // 访问静态成员方法
  21. Person::myCountry(); // 输出:我是中国人
  22. // 静态方法也可通过对象访问:
  23. $p1->myCountry();
  24. // 子类中输出成员属性值
  25. echo Student::$country."<br />"; // 输出:中国
  26. $t1 = new Student();
  27. $t1->study(); // 输出:我是中国人
  28. ?>

---------------------------------------------------



8.抽象类 PHP5支持抽象类和抽象方法。

抽象类不能直接被实例化,你必须先继承该抽象类,然后再实例化子类。

抽象类中 至少要包含一个抽象方法。如果类方法被声明为抽象的,那么其中就不能包括具体的功能实现。

继承一个抽象类的时候,子类必须实现抽象类中的所有抽象方法;

另外,这些方法的可见性 必须和抽象类中一样(或者更为宽松)。

如果抽象类中某个抽象方法被声明为protected,那么子类中实现的方法就应该声明为protected或者public,而不 能定义为private。

//抽象方法:abstract protected function getValue();

例子1

[php]view plaincopy

print?
  1. abstract class AbstractClass{
  2. // 定义抽象方法
  3. abstract protected function getValue();
  4. // 普通方法
  5. public function printOut(){
  6. print $this->getValue()."<br />";
  7. }
  8. }
  9. class ConcreteClass extends AbstractClass{
  10. protected function getValue(){
  11. return "abstract ";//抽象方法的实现
  12. }
  13. }
  14. $class1 = new ConcreteClass;
  15. $class1->printOut();

例子2

[php]view plaincopy

print?
  1. abstract class AbstractClass
  2. {
  3. // 强制要求子类定义这些方法
  4. abstract protected function getValue();
  5. abstract protected function prefixValue($prefix);
  6. // 普通方法(非抽象方法)
  7. public function printOut() {
  8. print $this->getValue() . "\n";
  9. }
  10. }
  11. class ConcreteClass1 extends AbstractClass
  12. {
  13. protected function getValue() {
  14. return "ConcreteClass1";
  15. }
  16. public function prefixValue($prefix) {
  17. return "{$prefix}ConcreteClass1";
  18. }
  19. }
  20. class ConcreteClass2 extends AbstractClass
  21. {
  22. public function getValue() {
  23. return "ConcreteClass2";
  24. }
  25. public function prefixValue($prefix) {
  26. return "{$prefix}ConcreteClass2";
  27. }
  28. }
  29. $class1 = new ConcreteClass1;
  30. $class1->printOut();
  31. echo $class1->prefixValue('FOO_') ."\n";
  32. $class2 = new ConcreteClass2;
  33. $class2->printOut();
  34. echo $class2->prefixValue('FOO_') ."\n";

/*

* 抽象类不能直接被实例化,你必须先继承该抽象类,然后再实例化子类。

抽象类中 至少要包含一个抽象方法。如果类方法被声明为抽象的,那么其中就不能包括具体的功能实现。

继承一个抽象类的时候,子类必须实现抽象类中的所有抽象方法;

另外,这些方法的可见性 必须和抽象类中一样(或者更为宽松)。

如果抽象类中某个抽象方法被声明为protected,那么子类中实现的方法就应该声明为protected或者public,而不 能定义为private。

*

*/

[php]view plaincopy

print?
  1. class Person {
  2. public $name;
  3. public $age;
  4. function say() {
  5. echo "my name is:".$this->name."<br />";
  6. echo "my age is:".$this->age;
  7. }
  8. }

// 类的继承

[php]view plaincopy

print?
  1. class Student extends Person {
  2. var $school; //学生所在学校的属性
  3. function study() {
  4. echo "my name is:".$this->name."<br />";
  5. echo "my shool is:".$this->school;
  6. }
  7. }
  8. $t1 = new Student();
  9. $t1->name = "zhangsan";
  10. $t1->school = "beijindaxue";
  11. $t1->study();

---------------------------------------------------------------------



9.接口

接口定义:方法和常量值定义的集合

通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口的特性:接口中定义的所有方法都必须是public

接口的实现:一个接口可以使用implements操作符,类中必须实现接口中的所有方法,否则会报fatal错误,如果要实现多个接口,可以使用逗号来分隔多个接口的名称。

抽象类和接口的区别

接口是特殊的抽象类,也可以看做是一个模型的规范。接口与抽象类大致区别如下:

1.一个子类如果 implements 一个接口,就必须实现接口中的所有方法(不管是否需要);如果是继承一个抽象类,只需要实现需要的方法即可。

2.如果一个接口中定义的方法名改变了,那么所有实现此接口的子类需要同步更新方法名;而抽象类中如果方法名改变了,其子类对应的方法名将不受影响,只是变成了一个新的方法而已(相对老的方法实现)。

3.抽象类只能单继承,当一个子类需要实现的功能需要继承自多个父类时,就必须使用接口。

实例1:

// 声明一个'iTemplate'接口

[php]view plaincopy

print?
  1. interface iTemplate
  2. {
  3. public function setVariable($name, $var);
  4. public function getHtml($template);
  5. }

// 实现接口

// 下面的写法是正确的

[php]view plaincopy

print?
  1. class Template implements iTemplate
  2. {
  3. private $vars = array();
  4. public function setVariable($name, $var)
  5. {
  6. $this->vars[$name] = $var;
  7. }
  8. public function getHtml($template)
  9. {
  10. foreach($this->vars as $name => $value) {
  11. $template = str_replace('{' . $name . '}', $value, $template);
  12. }
  13. return $template;
  14. }
  15. }

实例2:

//定义接口

[php]view plaincopy

print?
  1. interface User{
  2. function getDiscount();
  3. function getUserType();
  4. }

//VIP用户 接口实现

[php]view plaincopy

print?
  1. class VipUser implements User{
  2. // VIP 用户折扣系数
  3. private $discount = 0.8;
  4. function getDiscount() {
  5. return $this->discount;
  6. }
  7. function getUserType() {
  8. return "VIP user";
  9. }
  10. }
  11. class Goods{
  12. var $price = 100;
  13. var $vc;
  14. //定义 User 接口类型参数,这时并不知道是什么用户
  15. function run(User $vc){
  16. $this->vc = $vc;
  17. $discount = $this->vc->getDiscount();
  18. $usertype = $this->vc->getUserType();
  19. echo $usertype."goods Price:".$this->price*$discount;
  20. }
  21. }
  22. display ->run(new VipUser); //可以是更多其他用户类型

-------------------------------------------------------------



10.重载

定义:一个类中的方法与另一个方法名称相同,但参数不同

什么情况下执行重载? 当调用当前的环境下未被定义的属性或者方法时,或者当调用当前环境下不可见的属性或方法。

提示:

如果父类定义方法时使用了 final 关键字,则不允许被子类方法覆盖。

访问父类被覆盖的方法

可以通过parent:: 符号来访问父类被覆盖的方法或成员属性:

//PHP 重载方法 __call()

__call()(Method overloading)

为了避免当调用的方法不存在时产生错误,可以使用 __call() 方法来避免。该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。

语法:

// __call()方法重载

[php]view plaincopy

print?
  1. class Test{
  2. public function __call($name,$args){
  3. if($name== 'null' && count($args)==2 ){
  4. $type='num';
  5. foreach($args as $key => $val){
  6. if(!(is_int($val) || is_float($val))){
  7. $type= 'string';
  8. }
  9. }
  10. $method=$name.ucfirst($type);
  11. if(method_exists($this,$method),$args){
  12. call_user_func_array(array($this,$method),$args);
  13. }
  14. }
  15. }
  16. public addNum($i,$j){
  17. echo $i+$j;
  18. }
  19. public addString($i,$j){
  20. echo $i.$j;
  21. }
  22. }
  23. $test =new Test();
  24. $test->add(3,4);
  25. $test->add(3,'4');

案例:

[php]view plaincopy

print?
  1. class MemberTest {
  2. private $data = array();//被重载的数据保存在此
  3. public $declared = 1;/** 重载不能被用在已经定义的属性 */
  4. private $hidden = 2; /** 只有从类外部访问这个属性时,重载才会发生 */
  5. public function __set($name, $value) {
  6. echo "Setting '$name' to '$value'\n";
  7. $this->data[$name] = $value;
  8. }
  9. public function __get($name) {
  10. echo "Getting '$name'\n";
  11. if (array_key_exists($name, $this->data)) {
  12. return $this->data[$name];
  13. }
  14. $trace = debug_backtrace();
  15. trigger_error(
  16. 'Undefined property via __get(): ' . $name .
  17. ' in ' . $trace[0]['file'] .
  18. ' on line ' . $trace[0]['line'],
  19. E_USER_NOTICE);
  20. return null;
  21. }
  22. /** PHP 5.1.0之后版本 */
  23. public function __isset($name) {
  24. echo "Is '$name' set?\n";
  25. return isset($this->data[$name]);
  26. }
  27. /** PHP 5.1.0之后版本 */
  28. public function __unset($name) {
  29. echo "Unsetting '$name'\n";
  30. unset($this->data[$name]);
  31. }
  32. /** 非魔术方法 */
  33. public function getHidden() {
  34. return $this->hidden;
  35. }
  36. }
  37. echo "<pre>\n";
  38. $obj = new MemberTest;
  39. $obj->a = 1;
  40. echo $obj->a . "\n\n";
  41. var_dump(isset($obj->a));
  42. unset($obj->a);
  43. var_dump(isset($obj->a));
  44. echo "\n";
  45. echo $obj->declared . "\n\n";
  46. echo "Let's experiment with the private property named 'hidden':\n";
  47. echo "Privates are visible inside the class, so __get() not used...\n";
  48. echo $obj->getHidden() . "\n";
  49. echo "Privates not visible outside of class, so __get() is used...\n";
  50. echo $obj->hidden . "\n";

//属性重载:__set(),__get(),__isset(),__unset()

[php]view plaincopy

print?
  1. class Person{
  2. private $data =array();
  3. function __set($name,$value){
  4. $this->data[$name]=$value;
  5. }
  6. function __get($name){
  7. return $this->data[$name];
  8. }
  9. }

-----------------------------------------------------------------------------------



11.对象迭代

PHP5提供了一种迭代(iteration)对象的功能,就像使用数组那样,可以通过foreach 来遍历对象中的属性

[php]view plaincopy

print?
  1. class MyClass
  2. {
  3. public $var1 = 'value 1';
  4. public $var2 = 'value 2';
  5. public $var3 = 'value 3';
  6. protected $protected = 'protected var';
  7. private $private = 'private var';
  8. function iterateVisible() {
  9. echo "MyClass::iterateVisible:\n";
  10. foreach($this as $key => $value) {
  11. print "$key => $value\n";
  12. }
  13. }
  14. }
  15. $class = new MyClass();
  16. foreach($class as $key => $value) {
  17. print "$key => $value\n";
  18. }
  19. echo "\n";
  20. $class->iterateVisible();

---------------------------------------------------------------------------



12.设计模式: 工厂模式和 单例模式,  观察者模式,命令链模式和策略模式

 命令链 模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。

如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序,而不影响其他处理程序。

工厂模式

定义:工厂模式(Factory)允许你在代码执行时实例化对象。它之所以被称为工厂模式是因为它负责“生产”对象。工厂方法的参数是你要生成的对象对应的类名称。

工厂模式语法:

[php]view plaincopy

print?
  1. <?php
  2. class Example
  3. {
  4. public static function factory($type)
  5. {
  6. if (include_once 'Drivers/' . $type . '.php') {
  7. $classname = 'Driver_' . $type;
  8. return new $classname;
  9. } else {
  10. throw new Exception ('Driver not found');
  11. }
  12. }
  13. }
  14. ?>

工厂模式案例:

[php]view plaincopy

print?
  1. <?php
  2. interface IUser{
  3. function getName();
  4. }
  5. class User implements IUser{
  6. public function __construct($id){}
  7. public function getName(){
  8. return "haha";
  9. }
  10. }
  11. class UserFactory{
  12. public static function Create($id){
  13. return new User($id);
  14. }
  15. }
  16. $uo =UserFactory::Create(1);
  17. echo $uo->getName();
  18. ?>

单例

定义三要素:1,某个类只能有一个实例 2,必须自行创建这个实例 3,必须自行向系统提供这个实例

作用: 1,如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现。

2,使用单例模式,可以避免大量的new操作消耗资源()

3在一个页面请求中,便于调试,因为所有的代码都集中在一个类中(如数据库操作类) 可以在类中设置钩子,输出日志,从而避免到处都是var_dump

单例模式(Singleton)用于为一个类生成一个唯一的对象。最常用的地方是数据库连接。 使用单例模式生成一个对象后,该对象可以被其它众多对象所使用。

单件模式是我们在开发中经常用到的一种设计模式,利用PHP5面向对象的特性,我们可以很容易的构建单件模式的应用,下面是单件模式在PHP中的几种实现方法:

[php]view plaincopy

print?
  1. class Stat{
  2. static $instance = NULL;
  3. static function getInstance(){
  4. if(self::$instance == NULL){
  5. self::$instance = new Stat();
  6. }
  7. return self::$instance;
  8. }
  9. private function __construct(){
  10. }
  11. private function __clone(){
  12. }
  13. function sayHi(){
  14. return "The Class is saying hi to u ";
  15. }
  16. }
  17. echo Stat::getInstance()->sayHi();

这是一种最通常的方式,在一个getInstance方法中返回唯一的类实例。

对这里例子稍加修改,便可以产生一个通用的方法,只要叫道任何你想用到单件的类里,就可以了。

[php]view plaincopy

print?
  1. class Teacher{
  2. function sayHi(){
  3. return "The teacher smiling and said 'Hello '";
  4. }
  5. static function getInstance(){
  6. static $instance;
  7. if(!isset($instance)){
  8. $c = __CLASS__;
  9. $instance = new $c;
  10. }
  11. return $instance;
  12. }
  13. }
  14. echo Teacher::getInstance()->sayHi();

最后一种是提供一个singleton类,然后通过调用getInstance方法,可以为任何一个类生产出一个实例来。

[php]view plaincopy

print?
  1. class singleton{
  2. function getInstance($class){
  3. static $instances = array();
  4. if(!array_key_exists($class,$instances)){
  5. $instances[$class] = &new $class;
  6. }
  7. $instance = $instances[$class];
  8. return $instance;
  9. }
  10. }
  11. class People{
  12. function sayHi(){
  13. return 'Hello i am a people?';
  14. }
  15. }
  16. echo "<br />";
  17. echo singleton::getInstance('People')->sayHi();

通过这三种方法,我们可以很容易的应用单件模式,如果能够结合工厂模式,将使我们的编程变得更有条理和效率。

---------------------------------------------------------------------------------------



13.魔术方法

定义:PHP把所有以__(两个下划线)开头的类方法当成魔术方法

__construct, __destruct (参看 构造方法和析构方法),

__call, __callStatic, __get, __set, __isset, __unset (参看 重载),

__sleep, __wakeup, __toString, __set_state 和 __clone 等方法在PHP中被称为“魔术方法”(Magic methods)。

你在命名自己的类方法时不能使用这些方法名。

serialize()

作用: 第一. 在序列化之前,关闭对象可能具有的任何数据库连接等.

第二. 指定对象中需要被序列化的成员属性,如果某个属性比较大而不需要储存下来,可以不把它写进__sleep要返回的数组中,这样该属性就不会被序列化

在用serialize序列化对象时,会自动调用__sleep方法,__sleep方法必须返回一个数组,包含需要串行化的属性。

PHP会抛弃其它属性的值, 如果没有__sleep方法,PHP将保存所有属性,包括private属性。

unserialize() 从字节流中创建了一个对象之后,马上检查是否具有__wakeup 的函数的存在。

如果存在,__wakeup 立刻被调用。使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。

下面给出一个序列化的代码:共serialize.php和unserialize.php两个文件。

[php]view plaincopy

print?
  1. <?php
  2. class User
  3. {
  4. public $name;
  5. public $id;
  6. function __construct()
  7. {
  8. $this->id = uniqid(); //give user a unique ID 赋予一个不同的ID
  9. }
  10. function __sleep()
  11. {
  12. return(array("name")); //do not serialize this->id 不串行化id
  13. }
  14. function __wakeup()
  15. {
  16. $this->id = uniqid(); //give user a unique ID
  17. }
  18. }
  19. $u = new User;
  20. $u->name = "HAHA";
  21. $s = serialize($u); //serialize it 串行化 注意不串行化id属性,id的值被抛弃
  22. $u2 = unserialize($s); //unserialize it 反串行化 id被重新赋值
  23. //$u and $u2 have different IDs $u和$u2有不同的ID
  24. var_dump($u);
  25. var_dump($u2);
  26. ?>

---------- PHP debug ----------

object(User)#1 (2) {

["name"]=>

string(4) "HAHA"

["id"]=>

string(13) "47fa045529f69"

}

object(User)#2 (2) {

["name"]=>

string(4) "HAHA"

["id"]=>

string(13) "47fa04552a49a"

}

---序列化--------反序列化--------------------

[php]view plaincopy

print?
  1. class ClassA {
  2. var $int;
  3. var $str;
  4. var $bool;
  5. var $obj;
  6. var $pr;
  7. }
  8. $a = new ClassA();
  9. $a->int = 1;
  10. $a->str = "Hello";
  11. $a->bool = false;
  12. $a->obj = $a;
  13. $a->pr = &$a->str;
  14. echo serialize($a);
  15. //O:6:"ClassA":5:{s:3:"int";i:1;s:3:"str";s:5:"Hello";s:4:"bool";b:0;s:3:"obj";r:1;s:2:"pr";R:3;}

在这个例子中,首先序列化的对象是 ClassA 的一个对象,那么给它编号为 1,接下来要序列化的是这个对象的几个成员,第一个被序列化的成员是 int 字段,那它的编号就为 2,接下来被序列化的成员是 str,那它的编号就是 3,依此类推,到了 obj 成员时,它发现该成员已经被序列化了,并且编号为 1,因此它被序列化时,就被序列化成了 r:1; ,在接下来被序列化的是 pr 成员,它发现该成员实际上是指向 str 成员的一个引用,而 str 成员的编号为 3,因此,pr 就被序列化为 R:3; 了。

===============

//下面举个简单的例子,来说明 Serializable 接口的使用:序列化--------反序列化-

[php]view plaincopy

print?
  1. class MyClass implements Serializable
  2. {
  3. public $member;
  4. function MyClass()
  5. {
  6. $this->member = 'member value';
  7. }
  8. public function serialize()
  9. {
  10. return wddx_serialize_value($this->member);
  11. }
  12. public function unserialize($data)
  13. {
  14. $this->member = wddx_deserialize($data);
  15. }
  16. }
  17. $a = new MyClass();
  18. echo serialize($a);
  19. echo "\n";
  20. print_r(unserialize(serialize($a)));

/*

输出结果为(浏览器中的源代码):

C:7:"MyClass":90:{<wddxPacket version='1.0'><header/><data><string>member value</string></data></wddxPacket>}

MyClass Object

(

[member] => member value

)

因此如果想用其它语言来实现 PHP 序列化中的 C 标示的话,也需要提供一种这样的机制,让用户自定义类时,

能够自己在反序列化时处理 <data> 内容,否则,这些内容就无法被反序列化了。

*/

、、、、

__sleep 和 __wakeup

serialize() 函数会检查是否存在一个魔术方法 __sleep.如果存在,__sleep()方法会先被调用, 然后才执行序列化操作。这个功能可以用于清理对象,并返回一个包含对象中所有变量名称的数组。如果该方法不返回任何内容,则NULL被序列化,导致 一个E_NOTICE错误。

__sleep方法常用于提交未提交的数据,或类似的操作。同时,如果你有一些很大的对象,不需要保存,这个功能就很好用。

与之相反,unserialize()会检查是否存在一个__wakeup方法。如果存在,则会先调用 __wakeup方法,预先准备对象数据。

__wakeup经常用在反序列化操作中,否则是字符串;例如重新建立数据库连接,或执行其它初始化操作

------------------------------------------------------------------------------------------------

14. Final关键字

如果父类中的方法被声明为final,则子类无法覆盖该方法; 如果一个类被声明为final,则不能被继承。

语法:

类使用 final 关键字的例子:

[php]view plaincopy

print?
  1. final class Person
  2. {
  3. ......
  4. }
  5. class BaseClass {
  6. public function test() {
  7. echo "BaseClass::test() called\n";
  8. }
  9. final public function moreTesting() {
  10. echo "BaseClass::moreTesting() called\n";
  11. }
  12. }
  13. class ChildClass extends BaseClass {
  14. public function moreTesting() {
  15. echo "ChildClass::moreTesting() called\n";
  16. }
  17. }

---------------------------------------------

15.对象复制

对象复制可以通过clone关键字来完成(如果对象中存在__clone()方法,会先被调用)。对象中的 __clone()方法不能直接调用。

$copy_of_object = clone $object;

clone 关键字用于克隆一个完全一样的对象,

__clone()

__clone() 方法来重写原本的属性和方法。是深复制

如果想在克隆后改变原对象的内容,需要在类中添加一个特殊的 __clone() 方法来重写原本的属性和方法。

__clone() 方法只会在对象被克隆的时候自动调用。

clone 关键字用于克隆一个完全一样的对象,__clone() 方法来重写原本的属性和方法。

对象克隆

有的时候我们需要在一个项目里面使用两个或多个一样的对象,如果使用 new 关键字重新创建对象,再赋值上相同的属性,这样做比较烦琐而且也容易出错。

PHP 提供了对象克隆功能,可以根据一个对象完全克隆出一个一模一样的对象,而且克隆以后,两个对象互不干扰。

使用关键字 clone 来克隆对象。语法: $object2 = clone $object;

例子1:

2,

__clone()

如果想在克隆后改变原对象的内容,需要在类中添加一个特殊的 __clone() 方法来重写原本的属性和方法。

__clone() 方法只会在对象被克隆的时候自动调用。

//例子1:

[php]view plaincopy

print?
  1. /*
  2. class Person {
  3. private $name;
  4. private $age;
  5. public function __construct($name, $age) {
  6. $this->name=$name;
  7. $this->age=$age;
  8. }
  9. public function say() {
  10. echo "my name is :".$this->name."<br />";
  11. echo "my age is :".$this->age;
  12. }
  13. }
  14. $p1 = new Person("haha", 20);
  15. $p2 = clone $p1;
  16. $p2->say();
  17. */
  18. class Person {
  19. private $name;
  20. private $age;
  21. function __construct($name, $age) {
  22. $this->name = $name;
  23. $this->age = $age;
  24. }
  25. function say() {
  26. echo "my name is :".$this->name."<br/>";
  27. echo " my age is :".$this->age."<br />";
  28. }
  29. function __clone() {
  30. $this->name = "my is error ".$this->name;
  31. $this->age = 30;
  32. }
  33. }
  34. $p1 = new Person("haha", 20);
  35. $p1->say();
  36. $p2 = clone $p1;
  37. $p2->say();

------------------------------------------------------------------



16.对象比较

(==) 如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。

(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

实例1

[php]view plaincopy

print?
  1. class A{
  2. $a=new A;
  3. $b=new A;
  4. if($a==$b){
  5. echo "true";
  6. }else{
  7. echo "false";
  8. }
  9. $c=new A;
  10. $d=&$c;
  11. if($c===$d){
  12. echo "true";
  13. }else{
  14. echo "false";
  15. }
  16. }
  17. 实例2
  18. function bool2str($bool)
  19. {
  20. if ($bool === false) {
  21. return 'FALSE';
  22. } else {
  23. return 'TRUE';
  24. }
  25. }
  26. function compareObjects(&$o1, &$o2)
  27. {
  28. echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
  29. echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
  30. echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
  31. echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
  32. }
  33. class Flag
  34. {
  35. public $flag;
  36. function Flag($flag = true) {
  37. $this->flag = $flag;
  38. }
  39. }
  40. class OtherFlag
  41. {
  42. public $flag;
  43. function OtherFlag($flag = true) {
  44. $this->flag = $flag;
  45. }
  46. }
  47. $o = new Flag();
  48. $p = new Flag();
  49. $q = $o;
  50. $r = new OtherFlag();
  51. echo "Two instances of the same class\n";
  52. compareObjects($o, $p);
  53. echo "\nTwo references to the same instance\n";
  54. compareObjects($o, $q);
  55. echo "\nInstances of two different classes\n";
  56. compareObjects($o, $r);

-----------------------------------------



17.对象和引用

引用:php的引用是别名,就是两个不同的变量名字指向相同的内容

[php]view plaincopy

print?
  1. class A {
  2. public $foo = 1;
  3. }
  4. $a = new A;
  5. $b = $a; // $a ,$b都是同一个标识符的拷贝
  6. // ($a) = ($b) = <id>
  7. $b->foo = 2;
  8. echo $a->foo."\n";
  9. $c = new A;
  10. $d = &$c; // $c ,$d是引用
  11. // ($c,$d) = <id>
  12. $d->foo = 2;
  13. echo $c->foo."\n";
  14. $e = new A;
  15. function foo($obj) {
  16. // ($obj) = ($e) = <id>
  17. $obj->foo = 2;
  18. }
  19. foo($e);
  20. echo $e->foo."\n";

-------------------------------------------------------------------------------

对象序列化

序列化对象 - 在会话中存放对象

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。

unserialize()函数能够重新把字符串变回php原来的值。

序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

为了能够unserialize()一个对象,这个对象的类必须已经定义过。

如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。

如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现

实例1:

[php]view plaincopy

print?
  1. class A {
  2. public $one = 1;
  3. public function show_one() {
  4. echo $this->one;
  5. }
  6. }
  7. // page1.php:
  8. include("classa.inc");
  9. $a = new A;
  10. $s = serialize($a);

// 把变量$s保存起来以便文件page2.php能够读到

file_put_contents('store', $s);

// page2.php:

// 要正确了解序列化,必须包含下面一个文件

include("classa.inc");

$s = file_get_contents('store');

$a = unserialize($s);

// 现在可以使用对象$a里面的函数 show_one()

$a->show_one();

------------------------------------

后期静态绑定)

后期绑定“的意思是说,static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定“,因为它可以用于(但不限于)静态方法的调用

后期静态绑定的功能:用于在继承范围内引用静态调用的类。

self:: 的限制

使用self:: 或者 __CLASS__对当前类的静态引用,取决于定义当前方法所在的类:

实例1:

[php]view plaincopy

print?
  1. <?php
  2. class A {
  3. public static function who() {
  4. echo __CLASS__;
  5. }
  6. public static function test() {
  7. self::who();
  8. }
  9. }
  10. class B extends A {
  11. public static function who() {
  12. echo __CLASS__;
  13. }
  14. }
  15. B::test();
  16. ?>

------------------------------------------------------------------------------------------



18.$this关键字

$this 的含义是表示 实例化后的 具体对象!

this的用法:

1,this是指向对象实例的一个指针,self是对类本身的一个引用,parent是对父类的引用。

1,类的内部使用:如果从类的内部访问不为常量const或者static变量或者方法,那么就必须使用自引用的$this

2,在构造函数中 指该构造函数创建新对象

3,引用$this,代表当前的类,解决变量命名冲突和不确定性问题\

--------------------------------

[php]view plaincopy

print?
  1. class Test{
  2. function __call($name,$args){
  3. if($name=='add' && count($args)==2){
  4. $type='num';
  5. }
  6. foreach ($args as $key=>$val){
  7. if(!is_int($val) || is_float($val)){
  8. $type='string';
  9. }
  10. }
  11. $method=$name.ucfirst($type);
  12. if(method_exists($this,$method)){
  13. call_user_func_array(array($this,$method),$args);
  14. }
  15. }
  16. function addNum(){
  17. echo $i+$j;
  18. }
  19. function addString(){
  20. echo $i.$j;
  21. }
  22. }
  23. $test = new Test();
  24. $test->add(3,5);
  25. $test->add(4,'4');

----------------------------------------------

/*

* 常量 const

在类里面定义常量用 const 关键字,而不是通常的 define() 函数。

语法: const constant = "value";

例子:

运行该例子输出:

中国

我是中国人

*

*/

[php]view plaincopy

print?
  1. Class Person{
  2. // 定义常量
  3. const COUNTRY = "china";
  4. public function myCountry() {
  5. //内部访问常量
  6. echo "my is ".self::COUNTRY." person<br />";
  7. }
  8. }
  9. // 输出常量
  10. echo Person::COUNTRY."<br />";
  11. // 访问方法
  12. $p1 = new Person();
  13. $p1 -> myCountry();
  14. Person::myCountry();

--------------------

/*

* PHP 对象的存储与传输(序列化 serialize 对象)

对象的存储与传输

在实际项目应用中,有些任务在一两个页面是无法完成的,由于变量到脚本执行完毕就释放,我们本页所生成的对象想在其它页面使用时便碰到了麻烦。

如果需要将对象及其方法传递到我们想使用对象的页面,比较简单可行的办法是将对象序列化后存储起来或直接传输给需要的页面,另一种办法是将对象注册为 session 变量。

序列化对象

对象序列化,就是将对象转换成可以存储的字节流。当我们需要把一个对象在网络中传输时或者要把对象写入文件或是数据库时,就需要将对象进行序列化。

序列化完整过程包括两个步骤:一个是序列化,就是把对象转化为二进制的字符串,serialize() 函数用于序列化一个对象;另一个是反序列化,就是把对象被序列转化的二进制字符串再转化为对象,unserialize() 函数来反序列化一个被序列化的对象。这样整个过程下来,对象内的类型结构及数据都是完整的。

语法:

string serialize( mixed value )

mixed unserialize( string str [, string callback] )

*

*/

[php]view plaincopy

print?
  1. class Person {
  2. private $name;
  3. private $age;
  4. function __construct($name, $age) {
  5. $this->name = $name;
  6. $this->age = $age;
  7. }
  8. function say() {
  9. echo "my name is ".$this->name."<br />";
  10. echo " my age is ".$this->age;
  11. }
  12. }
  13. $p1 = new Person("haha", 20);
  14. $p1_string = serialize($p1);
  15. //将对象序列化后写入文件
  16. $fh = fopen("p1.text", "w");
  17. fwrite($fh, $p1_string);
  18. fclose($fh);

--------------

<?php

/*

* PHP面向对象之this 关键字

1,PHP5中为解决变量的命名冲突和不确定性问题,引入关键字“$this”代表其所在当前对象。

2,$this在构造函数中指该构造函数所创建的新对象。

3,在类中使用当前对象的属性和方法,必须使用$this->取值。方法内的局部变量,不属于对象,不使用$this关键字取值。

局部变量和全局变量与 $this 关键字

4,使用当前对象的属性必须使用$this关键字。

局部变量的只在当前对象的方法内有效,所以直接使用。

注意:局部变量和属性可以同名,但用法不一样。在使用中,要尽量避免这样使用,以免混淆。

1234567891011121314 <!-- 验证属性和局部变量使用方法的类 -->

[php]view plaincopy

print?
  1. <?php
  2. class A{
  3. private $a = 99; //这里写一个打印参数的方法.
  4. public function printInt($a){
  5. echo "这里的 \$a 是传递的参数 $a ";
  6. echo "<br>";
  7. echo "这里的 \$this->a 是属性 $this->a";
  8. }
  9. }
  10. $a = new A(); // 这里的$a 可不是类中的任何一个变量了.
  11. $a->printInt(88);
  12. ?>

运行结果:

12 这里的 $a 是传递的参数 88 这里的 $this->a 是属性 99

用$this调用对象中的其它方法

1234567891011121314151617 <!--写一个类,让他自动完成最大值的换算.-->

[php]view plaincopy

print?
  1. <?php
  2. class Math{ //两个数值比较大小.
  3. public function Max($a,$b){
  4. return $a>$b?$a:$b;
  5. } //三个数值比较大小.
  6. public function Max3($a,$b,$c){ //调用类中的其它方法.
  7. $a = $this->Max($a,$b);
  8. return $this->Max($a,$c);
  9. }
  10. }
  11. $math = new Math();
  12. echo "最大值是 ".$math->Max3(99,100,88);
  13. ?>

运行结果:

1 最大值是 100

使用$this调用构造函数

调用构造函数和析构函数的方法一致。

12345678910111213141516

[php]view plaincopy

print?
  1. <? class A{
  2. private $a = 0;
  3. public function __construct(){
  4. $this->a = $this->a + 1 ;
  5. }
  6. public function doSomeThing(){
  7. $this->__construct();
  8. return $this->a;
  9. }
  10. }
  11. $a = new A();
  12. // 这里的$a 可不是类中的任何一个变量了.
  13. echo "现在 \$a 的值是" . $a->doSomeThing();
  14. ?>

运行结果:

1 现在 $a 的值是2

$this 到底指的什么?

$this 就是指当前对象,我们甚至可以返回这个对象使用 $this

12345678910111213

[php]view plaincopy

print?
  1. <?php class A{
  2. public function getASelf(){
  3. return $this;
  4. }
  5. public function __toString(){
  6. return "这是类A的实例.";
  7. }
  8. }
  9. $a = new A();
  10. // 创建A的实例;
  11. $b = $a->getASelf();
  12. //调用方法返回当前实例.
  13. echo $a;
  14. //打印对象会调用它的__toString方法.
  15. ?>

程序运行结果:

1 这是类A的实例.

通过 $this 传递对象

在这个例子中,我们写一个根据不同的年龄发不同工资的类.

我们设置处理年龄和工资的业务模型为一个独立的类.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

[php]view plaincopy

print?
  1. <?php
  2. class User{
  3. private $age ;
  4. private $sal ;
  5. private $payoff ;
  6. //声明全局属性.
  7. //构造函数,中创建Payoff的对象.
  8. public function __construct(){
  9. $this->payoff = new Payoff();
  10. }
  11. public function getAge(){
  12. return $this->age;
  13. }
  14. public function setAge($age){
  15. $this->age = $age;
  16. }
  17. // 获得工资.
  18. public function getSal(){
  19. $this->sal =
  20. $this->payoff->figure($this);
  21. return $this->sal;
  22. }
  23. }
  24. //这是对应工资与年龄关系的类.
  25. class Payoff{
  26. public function figure($a){
  27. $sal =0;
  28. $age = $a->getAge();
  29. if($age >80 || $age <16 ){
  30. $sal = 0;
  31. }elseif ($age > 50){
  32. $sal = 1000;
  33. }else{
  34. $sal = 800;
  35. }
  36. return $sal;
  37. } }
  38. //实例化User
  39. $user = new User();
  40. $user->setAge(55);
  41. echo $user->getAge()."age ,his sal is " . $user->getSal(); echo "<br>";
  42. $user->setAge(20);
  43. echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>";
  44. $user->setAge(-20); echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>";
  45. $user->setAge(150); echo $user->getAge()."age , his sal is " . $user->getSal();
  46. ?>

运行结果:

1234 55age ,his sal is 1000 20age , his sal is 800 -20age , his sal is 0 150age , his sal is 0.

**/