背景

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

先来一个复杂的例子:Mixin(掺入)

Ruby和Python可以用非常漂亮的语法支持掺入,PHP能实现吗?让我们试试吧。

参考其他语言的掺入示例可以查看这篇文章:设计原则:请重新审视“多重继承”,找机会拥抱一下“掺入(Mixin)”

PHP5.4提供的有Traits机制可以方便的模拟掺入,下面的示例是采用5.3版本的机制模拟出来的。

期望的最终效果

 class Playable
{
public function play($that)
{
echo "一起玩吧,".$that->name;
}
} class Base extends _Object
{
public $name = "段光伟<br/>";
} class Child extends Base {} Base::implement(new Playable()); $base = new Base();
$child = new Child(); $base->play();
$child->play();

是不是特别像C#的扩展方法。

实现代码

 class _Object
{
private static $mixins = array(); public static function implement($target)
{
$class = get_called_class(); if (!isset(self::$mixins[$class]))
{
self::$mixins[$class] = array();
} foreach (get_class_methods($target) as $method)
{
self::$mixins[$class][$method] = $target;
}
} public function class_name()
{
return self::get_class($this);
} public function __call($method, $params)
{
$params = array_merge(array($this), $params);
$class = $class = get_called_class(); $mixin = $this->find_mixin($class, $method); call_user_func_array($mixin, $params);
} private function find_mixin($class, $method)
{
while ($class != NULL)
{
if (isset(self::$mixins[$class][$method]))
{
$target = self::$mixins[$class][$method]; return array($target, $method);
} $class = get_parent_class($class);
} throw new MethodException("方法 $method 不存在");
}
}

看不明白不要紧,继续看下文,回头再看这个。

面向对象

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

基本的类型声明

代码

 <?php

 header("content-type: text/html; charset=utf-8");

 // 类型不能有修饰符。
// 成员以应用访问修饰符号,一共有三种访问修饰符:public、protected和private。
class TestClass {
// 属性默认访问级别是private。
private $private_property = "私共成员<br/>"; // var形式的默认访问级别是public。
var $public_property = "公共成员<br/>"; // 方法默认访问级别是public。
public function public_method() {
echo $this->private_property;
}
} $test = new TestClass(); echo $test->public_property;
$test->public_method(); ?>

运行结果

 // 公共成员
// 私共成员

继承:实现继承和接口继承

代码

 <?php 

 header("content-type: text/html; charset=utf-8");

 /*
* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
*/
interface Playable {
function play();
} /*
* 使用abstract关键字可以声明抽象类和方法。
*/
abstract class Base {
private $header = "";
private $rooter = ""; public function __construct($header, $rooter) {
$this->header = $header;
$this->rooter = $rooter;
} public function write() {
$this->writeHeader();
$this->writeContent();
$this->writeRooter();
} private function writeHeader() {
echo $this->header."<br/>";
} private function writeRooter() {
echo $this->rooter."<br/>";
} protected abstract function writeContent();
} /*
* 使用final关键字可以禁止类型或方法被重写。
* 使用parent::在重写的方法里调用父类型的方法。
*/
final class Child extends Base implements Playable {
private $content = ""; public function __construct($header, $rooter, $content) {
parent::__construct($header, $rooter); $this->content = $content;
} protected function writeContent() {
echo $this->content."<br/>";
} public function play() {
echo "游戏中。。。<br/>";
} public function __destruct() {
echo "析构中<br/>";
}
} $child = new Child("头", "尾", "内容");
$child->write("段光伟");
$child->play(); ?>

运行结果


内容

游戏中。。。
析构中

静态成员

代码

 <?php

 header("content-type: text/html; charset=utf-8");

 class StaticBaseClass {
public static $StaticProperty = "父类静态属性<br/>";
const MAX = "父类常量<br/>"; public static function staticMethod() {
/* 在内部可以使用类名或self关键字,但是self访问的其定义时的类型,而不是运行时的类型,
* 这在子类和父类里有同名的静态成员和常量时会出现问题,为了避免这个问题可以使用static
* 关键字。
*/ echo get_called_class()." self::\$StaticProperty ".self::$StaticProperty;
echo get_called_class()." StaticBaseClass::\$StaticProperty ".StaticBaseClass::$StaticProperty;
echo get_called_class()." static::\$StaticProperty ".static::$StaticProperty; echo get_called_class()." self::MAX ".self::MAX;
echo get_called_class()." StaticBaseClass::MAX ".StaticBaseClass::MAX;
echo get_called_class()." static::MAX ".static::MAX;
}
} class StaticChildClass extends StaticBaseClass {
public static $StaticProperty = "子类静态属性<br/>";
const MAX = "子类常量<br/>";
} // 在外部必须使用类名访问。
StaticBaseClass::StaticMethod();
echo StaticBaseClass::$StaticProperty;
echo StaticBaseClass::MAX; // 在子类中可以调用父类的静态成员和常量。
StaticChildClass::StaticMethod();
echo StaticChildClass::$StaticProperty;
echo StaticChildClass::MAX; ?>

运行结果

 StaticBaseClass self::$StaticProperty 父类静态属性
StaticBaseClass StaticBaseClass::$StaticProperty 父类静态属性
StaticBaseClass static::$StaticProperty 父类静态属性
StaticBaseClass self::MAX 父类常量
StaticBaseClass StaticBaseClass::MAX 父类常量
StaticBaseClass static::MAX 父类常量
父类静态属性
父类常量
StaticChildClass self::$StaticProperty 父类静态属性
StaticChildClass StaticBaseClass::$StaticProperty 父类静态属性
StaticChildClass static::$StaticProperty 子类静态属性
StaticChildClass self::MAX 父类常量
StaticChildClass StaticBaseClass::MAX 父类常量
StaticChildClass static::MAX 子类常量
子类静态属性
子类常量

又是魔法方法

代码

 <?php

 header("content-type: text/html; charset=utf-8");

 /*
* 什么叫魔法方法:被解释器在某些特殊情况下调用的实例方法。
*/
class WebDeveloper {
public $info = array(); // 当执行赋值时,而目标成员不存在会调用此方法。
public function __set($item, $value) {
$this->info[$item] = $value;
} // 当执行取值时,而目标成员不存在会调用此方法。
public function __get($item) {
return $this->info[$item];
} // 当执行isset方法时,而目标成员不存在会调用此方法。
public function __isset($item) {
return isset($this->info[$item]);
} // 当执行unset方法时,而目标成员不存在会调用此方法。
public function __unset($item) {
unset($this->info[$item]);
} // 当执行方法调用时,而目标方法不存在会调用此方法。
public function __call($method_name, $args) {
echo $method_name, var_dump($args), "<br/>";
}
} $developer = new WebDeveloper();
$developer->name = "段光伟"; echo "{$developer->name}<br/>";
echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>";
unset($developer->name);
echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>"; $developer->saySomething('hi!','how are you!'); ?>

输出结果

 段光伟
TRUE
FALSE
saySomethingarray(2) { [0]=> string(3) "hi!" [1]=> string(12) "how are you!" }

可调用对象(其实还是魔法方法)

代码

 <?php
header("content-type: text/html; charset=utf-8"); class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
} $obj = new CallableClass; $obj(5);
call_user_func_array($obj, array(5));
var_dump(is_callable($obj)); ?>

输入结果

 int(5) int(5) bool(true) 

Reflection(反射)

代码

 <?php 

 header("content-type: text/html; charset=utf-8");

 /*
* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
*/
interface Playable {
function play();
} /*
* 使用abstract关键字可以声明抽象类和方法。
*/
abstract class Base {
private $header = "";
private $rooter = ""; public static $StaticProperty = "父类静态属性<br/>";
const MAX = "父类常量<br/>"; public function __construct($header, $rooter) {
$this->header = $header;
$this->rooter = $rooter;
} public function write() {
$this->writeHeader();
$this->writeContent();
$this->writeRooter();
} private function writeHeader() {
echo $this->header."<br/>";
} private function writeRooter() {
echo $this->rooter."<br/>";
} protected abstract function writeContent();
} /*
* 使用final关键字可以禁止类型或方法被重写。
* 使用parent::在重写的方法里调用父类型的方法。
*/
final class Child extends Base implements Playable {
public $content = "";
public static $StaticProperty = "子类静态属性<br/>";
const MAX = "子类常量<br/>"; public function __construct($header, $rooter, $content) {
parent::__construct($header, $rooter); $this->content = $content;
} protected function writeContent() {
echo $this->content."<br/>";
} public function play() {
echo "游戏中。。。<br/>";
} public function __destruct() {
echo "析构中";
}
} $child = new Child("开始", "结束", "内容"); echo get_class($child).'<br/>';
print_r(get_class_methods(get_class($child)));
echo '<br/>';
print_r(get_class_vars(get_class($child)));
echo '<br/>';
echo $child->{"play"}();
?>

输出结果

 Child
Array ( [0] => __construct [1] => play [2] => __destruct [3] => write )
Array ( [content] => [StaticProperty] => 子类静态属性
)
游戏中。。。
析构中

备注

PHP属于:静态类型、鸭子类型、弱类型、解释执行的语言 ,后面详细介绍这些概念及其对应的语言特色。

PHP:面向对象学习笔记,重点模拟Mixin(掺入)的更多相关文章

  1. php面向对象学习笔记

    PHP 面向对象技术(全面讲解) Ø 主要内容 v 1.面向对象的概念 v 2.什么是类,什么是对象,类和对象之间的关系 v 3.什么是面向对象编程呢? v 4.如何抽象出一个类? v 5.如何实例化 ...

  2. web进阶之jQuery操作DOM元素&&MySQL记录操作&&PHP面向对象学习笔记

    hi 保持学习数量和质量 1.jQuery操作DOM元素 ----使用attr()方法控制元素的属性 attr()方法的作用是设置或者返回元素的属性,其中attr(属性名)格式是获取元素属性名的值,a ...

  3. js面向对象学习笔记(五):tab切换

    重点是this指向问题 <style> .hide{display: none;} #box div,#box1 div{display: none;} .hover{background ...

  4. 031 Spring Data Elasticsearch学习笔记---重点掌握第5节高级查询和第6节聚合部分

    Elasticsearch提供的Java客户端有一些不太方便的地方: 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的 需要自己把对象序列化为json存储 查询到结果也需要自己 ...

  5. python面向对象学习笔记(一)

    粘贴一些自学过程中的笔记大纲,源文本在pycharm里面写的,有点乱整理一下,部分内容有待补充,书写不一定100%正确,全当数据备份了. 1.面向对象的特性 #你写代码时什么使用面向对象 #处理比较复 ...

  6. Linux学习笔记——重点推荐的Linux网络在线学习资源

     首先非常感谢百度,感谢网络的搜索引擎技术,也非常感谢学习资源的贡献者和组织! 1:http://billie66.github.io/TLCL/book/zh/ 2:http://www.ha97. ...

  7. C#面向对象学习笔记概要

    1.面向对象不是取代面向过程的. 2.面向对象的三个特性:封装.继承.多态. 3.字段.方法.属性(后面讲)都可以叫做类的成员,他们都需要定义访问级别.访问级别的用处在于控制成员在哪些地方可以被访问, ...

  8. Java-马士兵设计模式学习笔记-观察者模式-模拟Awt Button

    一.概述 Java 的Awt是 Observer模式,现用Java自己模拟awt中Button的运行机制 二.代码 1.Test.java import java.text.DateFormat; i ...

  9. JavaScript面向对象学习笔记

    JavaScript 常被描述为一种基于原型的语言 (prototype-based language)--每个对象拥有一个原型对象,对象以其原型为模板.从原型继承方法和属性.原型对象也可能拥有原型, ...

随机推荐

  1. python抓取链家房源信息(二)

    试着用scrapy将之前写的抓取链家网信息的重新写了写 然后先是用了第一页的网页作为测试,调试代码,然后发现总是抓取的时候遇见了 类似于这样的问题,并且抓取不到信息 2017-03-28 17:52: ...

  2. 【hdoj_1042】N!(大数)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1042 题目说明待求阶乘的数最大为10000,而10000!的位数为35660(这个数是上网查的),所以已经 ...

  3. Base Class 慎用箭头函数

    在项目中,child继承base的时候,需要重新修改base.fun的逻辑,但是有些情况下面并不是简单的覆盖,而是在base.fun的逻辑基础上进行加工处理. 刚开始接触es6的时候也许都遇到过,ch ...

  4. DER编码简介

    概念:DER是BER的子集,它为每一个ASN.1类型定义一种唯一的编码方案. DER与BER的区别:DER在BER的基础上增加了如下限制:长度小于等于127,必须使用短型长度表示法.长度大于127,必 ...

  5. CodeForces 803A Maximal Binary Matrix

    枚举. 枚举对角线上放多少个$1$,剩余的贪心放,更新答案. #include <iostream> #include <cstdio> #include <cstrin ...

  6. C++ 四种显示转换

    转自:http://www.jellythink.com/archives/205   (果冻想) 前言 这篇文章总结的是C++中的类型转换,这些小的知识点,有的时候,自己不是很注意,但是在实际开发中 ...

  7. JavaScript with JSONPath

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JavaScript JSO ...

  8. 2017四川省赛D题《Dynamic Graph》

    题意:给出一个n个点m条边的有向无环图(DAG),初始的时候所有的点都为白色.然后有Q次操作,每次操作要把一个点的颜色改变,白色<->黑色,对于每次操作,输出满足下列点对<u,v&g ...

  9. CUDA学习笔记3:CUFFT(CUDA提供了封装好的CUFFT库)的使用例子

    一.FFT介绍 傅里叶变换是数字信号处理领域一个很重要的数学变换,它用来实现将信号从时域到频域的变换,在物理学.数论.组合数学.信号处理.概率.统计.密码学.声学.光学等领域有广泛的应用.离散傅里叶变 ...

  10. Eigen学习笔记2:C++矩阵运算库Eigen介绍

    Eigen常规矩阵定义 1.使用 Eigen的使用在官网上有详细的介绍,这里对我学习过程中用到的基本操作进行介绍.首先是矩阵的定义.在矩阵类的模板参数共有6个.一般情况下我们只需要关注前三个参数即可. ...