failt 发表于 2015-8-26 05:11:09

PHP:面向对象学习笔记,重点模拟Mixin(掺入)

背景
  相对于Python、Node和Ruby来说PHP算是一门容易学习和使用的语言,因为这个特点也使其成为WEB开发领域的佼佼者,本文记录一下我对PHP面向对象部分的学习笔记。

先来一个复杂的例子:Mixin(掺入)
  Ruby和Python可以用非常漂亮的语法支持掺入,PHP能实现吗?让我们试试吧。
  参考其他语言的掺入示例可以查看这篇文章:设计原则:请重新审视“多重继承”,找机会拥抱一下“掺入(Mixin)”。
  PHP5.4提供的有Traits机制可以方便的模拟掺入,下面的示例是采用5.3版本的机制模拟出来的。
  期望的最终效果



1 class Playable
2 {
3   public function play($that)
4   {
5         echo "一起玩吧,".$that->name;
6   }
7 }
8
9 class Base extends _Object
10 {
11   public $name = "段光伟<br/>";
12 }
13
14 class Child extends Base {}
15
16 Base::implement(new Playable());
17
18 $base = new Base();
19 $child = new Child();
20
21 $base->play();
22 $child->play();
  是不是特别像C#的扩展方法。
  实现代码



1 class _Object
2 {
3   private static $mixins = array();
4
5   public static function implement($target)
6   {
7         $class = get_called_class();
8
9         if (!isset(self::$mixins[$class]))
10         {
11             self::$mixins[$class] = array();
12         }
13
14         foreach (get_class_methods($target) as $method)
15         {
16             self::$mixins[$class][$method] = $target;
17         }
18   }
19
20   public function class_name()
21   {
22         return self::get_class($this);
23   }
24
25   public function __call($method, $params)
26   {
27         $params = array_merge(array($this), $params);
28         $class= $class = get_called_class();
29         
30         $mixin = $this->find_mixin($class, $method);
31
32         call_user_func_array($mixin, $params);
33   }
34
35   private function find_mixin($class, $method)
36   {
37         while ($class != NULL)
38         {
39             if (isset(self::$mixins[$class][$method]))
40             {
41               $target = self::$mixins[$class][$method];
42
43               return array($target, $method);
44             }
45
46             $class = get_parent_class($class);
47         }
48
49         throw new MethodException("方法 $method 不存在");
50   }
51 }
  看不明白不要紧,继续看下文,回头再看这个。

面向对象
  很多信息在注释中可以看到,在文中不会再重复一遍。

基本的类型声明
  代码



1 <?php
2
3 header("content-type: text/html; charset=utf-8");
4
5 // 类型不能有修饰符。
6 // 成员以应用访问修饰符号,一共有三种访问修饰符:public、protected和private。
7 class TestClass {
8   // 属性默认访问级别是private。
9   private $private_property = "私共成员<br/>";
10
11   // var形式的默认访问级别是public。
12   var $public_property = "公共成员<br/>";
13
14   // 方法默认访问级别是public。
15   public function public_method() {
16         echo $this->private_property;
17   }
18 }
19
20 $test = new TestClass();
21
22 echo $test->public_property;
23 $test->public_method();
24
25 ?>
  运行结果



1 // 公共成员
2 // 私共成员
继承:实现继承和接口继承
  代码



1 <?php
2
3 header("content-type: text/html; charset=utf-8");
4
5 /*
6* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
7*/
8 interface Playable {
9   function play();
10 }
11
12 /*
13* 使用abstract关键字可以声明抽象类和方法。
14*/
15 abstract class Base {
16   private    $header = "";
17   private    $rooter = "";
18
19   public function __construct($header, $rooter) {
20         $this->header = $header;
21         $this->rooter = $rooter;
22   }
23   
24   public function write() {
25         $this->writeHeader();
26         $this->writeContent();
27         $this->writeRooter();
28   }
29
30   private function writeHeader() {
31         echo $this->header."<br/>";
32   }
33
34   private function writeRooter() {
35         echo $this->rooter."<br/>";
36   }
37
38   protected abstract function writeContent();
39 }
40
41 /*
42* 使用final关键字可以禁止类型或方法被重写。
43* 使用parent::在重写的方法里调用父类型的方法。
44*/
45 final class Child extends Base implements Playable {
46   private    $content = "";
47
48   public function __construct($header, $rooter, $content) {
49         parent::__construct($header, $rooter);
50
51         $this->content = $content;
52   }
53
54   protected function writeContent() {
55         echo $this->content."<br/>";
56   }
57
58   public function play() {
59         echo "游戏中。。。<br/>";
60   }
61
62   public function __destruct() {
63         echo "析构中<br/>";
64   }
65 }
66
67 $child = new Child("头", "尾", "内容");
68 $child->write("段光伟");
69 $child->play();
70
71 ?>
  运行结果



1 头
2 内容
3 尾
4 游戏中。。。
5 析构中
静态成员
  代码



1 <?php
2
3 header("content-type: text/html; charset=utf-8");
4
5 class StaticBaseClass {
6   public static $StaticProperty = "父类静态属性<br/>";
7   const MAX = "父类常量<br/>";
8
9   public static function staticMethod() {
10         /* 在内部可以使用类名或self关键字,但是self访问的其定义时的类型,而不是运行时的类型,
11          * 这在子类和父类里有同名的静态成员和常量时会出现问题,为了避免这个问题可以使用static
12          * 关键字。
13          */
14
15         echo get_called_class()." self::\$StaticProperty ".self::$StaticProperty;
16         echo get_called_class()." StaticBaseClass::\$StaticProperty ".StaticBaseClass::$StaticProperty;
17         echo get_called_class()." static::\$StaticProperty ".static::$StaticProperty;
18
19         echo get_called_class()." self::MAX ".self::MAX;
20         echo get_called_class()." StaticBaseClass::MAX ".StaticBaseClass::MAX;
21         echo get_called_class()." static::MAX ".static::MAX;
22   }
23 }
24
25 class StaticChildClass extends StaticBaseClass {
26   public static $StaticProperty = "子类静态属性<br/>";
27   const MAX = "子类常量<br/>";
28 }
29
30 // 在外部必须使用类名访问。
31 StaticBaseClass::StaticMethod();
32 echo StaticBaseClass::$StaticProperty;
33 echo StaticBaseClass::MAX;
34
35 // 在子类中可以调用父类的静态成员和常量。
36 StaticChildClass::StaticMethod();
37 echo StaticChildClass::$StaticProperty;
38 echo StaticChildClass::MAX;
39
40 ?>
  运行结果



1 StaticBaseClass self::$StaticProperty 父类静态属性
2 StaticBaseClass StaticBaseClass::$StaticProperty 父类静态属性
3 StaticBaseClass static::$StaticProperty 父类静态属性
4 StaticBaseClass self::MAX 父类常量
5 StaticBaseClass StaticBaseClass::MAX 父类常量
6 StaticBaseClass static::MAX 父类常量
7 父类静态属性
8 父类常量
9 StaticChildClass self::$StaticProperty 父类静态属性
10 StaticChildClass StaticBaseClass::$StaticProperty 父类静态属性
11 StaticChildClass static::$StaticProperty 子类静态属性
12 StaticChildClass self::MAX 父类常量
13 StaticChildClass StaticBaseClass::MAX 父类常量
14 StaticChildClass static::MAX 子类常量
15 子类静态属性
16 子类常量
又是魔法方法
  代码



1 <?php
2
3 header("content-type: text/html; charset=utf-8");
4
5 /*
6* 什么叫魔法方法:被解释器在某些特殊情况下调用的实例方法。
7*/
8 class WebDeveloper {
9   public $info = array();
10
11   // 当执行赋值时,而目标成员不存在会调用此方法。
12   public function __set($item, $value) {
13   $this->info[$item] = $value;
14   }
15
16   // 当执行取值时,而目标成员不存在会调用此方法。
17   public function __get($item) {
18   return $this->info[$item];
19   }
20
21   // 当执行isset方法时,而目标成员不存在会调用此方法。
22   public function __isset($item) {
23   return isset($this->info[$item]);
24   }
25
26   // 当执行unset方法时,而目标成员不存在会调用此方法。
27   public function __unset($item) {
28   unset($this->info[$item]);
29   }
30
31   // 当执行方法调用时,而目标方法不存在会调用此方法。
32   public function __call($method_name, $args) {
33   echo $method_name, var_dump($args), "<br/>";
34   }
35 }
36
37 $developer = new WebDeveloper();
38 $developer->name = "段光伟";
39
40 echo "{$developer->name}<br/>";
41 echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>";
42 unset($developer->name);
43 echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>";
44
45 $developer->saySomething('hi!','how are you!');
46
47 ?>
  输出结果



1 段光伟
2 TRUE
3 FALSE
4 saySomethingarray(2) { => string(3) "hi!" => string(12) "how are you!" }
可调用对象(其实还是魔法方法)
  代码



1 <?php
2 header("content-type: text/html; charset=utf-8");
3
4 class CallableClass
5 {
6   public function __invoke($x)
7   {
8         var_dump($x);
9   }
10 }
11
12 $obj = new CallableClass;
13
14 $obj(5);
15 call_user_func_array($obj, array(5));
16 var_dump(is_callable($obj));
17
18 ?>
  输入结果



1 int(5) int(5) bool(true)
Reflection(反射)
  代码



1 <?php
2
3 header("content-type: text/html; charset=utf-8");
4
5 /*
6* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
7*/
8 interface Playable {
9   function play();
10 }
11
12 /*
13* 使用abstract关键字可以声明抽象类和方法。
14*/
15 abstract class Base {
16   private    $header = "";
17   private    $rooter = "";
18
19   public static $StaticProperty = "父类静态属性<br/>";
20   const MAX = "父类常量<br/>";
21
22   public function __construct($header, $rooter) {
23         $this->header = $header;
24         $this->rooter = $rooter;
25   }
26   
27   public function write() {
28         $this->writeHeader();
29         $this->writeContent();
30         $this->writeRooter();
31   }
32
33   private function writeHeader() {
34         echo $this->header."<br/>";
35   }
36
37   private function writeRooter() {
38         echo $this->rooter."<br/>";
39   }
40
41   protected abstract function writeContent();
42 }
43
44 /*
45* 使用final关键字可以禁止类型或方法被重写。
46* 使用parent::在重写的方法里调用父类型的方法。
47*/
48 final class Child extends Base implements Playable {
49   public $content = "";
50   public static $StaticProperty = "子类静态属性<br/>";
51   const MAX = "子类常量<br/>";
52
53   public function __construct($header, $rooter, $content) {
54         parent::__construct($header, $rooter);
55
56         $this->content = $content;
57   }
58
59   protected function writeContent() {
60         echo $this->content."<br/>";
61   }
62
63   public function play() {
64         echo "游戏中。。。<br/>";
65   }
66
67   public function __destruct() {
68         echo "析构中";
69   }
70 }
71
72 $child = new Child("开始", "结束", "内容");
73
74 echo get_class($child).'<br/>';
75 print_r(get_class_methods(get_class($child)));
76 echo '<br/>';
77 print_r(get_class_vars(get_class($child)));
78 echo '<br/>';
79 echo $child->{"play"}();
80 ?>
  输出结果



1 Child
2 Array ( => __construct => play => __destruct => write )
3 Array ( => => 子类静态属性
4 )
5 游戏中。。。
6 析构中
备注
  PHP属于:静态类型、鸭子类型、弱类型、解释执行的语言 ,后面详细介绍这些概念及其对应的语言特色。
  
页: [1]
查看完整版本: PHP:面向对象学习笔记,重点模拟Mixin(掺入)